websocket13
Version:
Simple WebSocket protocol 13 client with no native or heavy dependencies
180 lines • 17.3 kB
JavaScript
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const crypto_1 = require("crypto");
const permessage_deflate_1 = __importDefault(require("permessage-deflate"));
const tiny_typed_emitter_1 = require("tiny-typed-emitter");
const url_1 = require("url");
const websocket_extensions_1 = __importDefault(require("websocket-extensions"));
const WebSocketServerConnection_1 = __importDefault(require("./WebSocketServerConnection"));
const HTTPStatusCodes_1 = __importDefault(require("./enums/HTTPStatusCodes"));
const HTTP_VERSION = 1.1;
const WEBSOCKET_VERSION = 13;
// eslint-disable-next-line
const PACKAGE_VERSION = require('../package.json').version;
class WebSocketServer extends tiny_typed_emitter_1.TypedEmitter {
constructor(options) {
super();
this.options = {
pingInterval: 10000,
pingTimeout: 10000,
pingFailures: 3,
permessageDeflate: true
};
options = options || {};
Object.assign(this.options, options);
this.protocols = this.options.protocols || [];
}
http(server) {
server.on('upgrade', (req, socket, head) => {
if (!req.headers.upgrade || req.headers.upgrade.toLowerCase() != 'websocket') {
bail('Invalid upgrade type. Supported: websocket');
return;
}
if (!req.headers.connection ||
!req.headers.connection.toLowerCase().split(',').map(i => i.trim()).includes('upgrade')) {
bail('Invalid upgrade request.');
return;
}
let httpV = req.httpVersion.split('.');
if (parseInt(httpV[0]) < 1 || parseInt(httpV[1]) < 1) {
bail('Invalid HTTP version for websocket upgrade.');
return;
}
if (req.method.toUpperCase() != 'GET') {
bail('Bad HTTP method. Required: GET');
return;
}
if (!req.headers['sec-websocket-key'] || Buffer.from(req.headers['sec-websocket-key'], 'base64').length != 16) {
bail('Missing or invalid Sec-WebSocket-Key.');
return;
}
if (req.headers['sec-websocket-version'] != WEBSOCKET_VERSION.toString()) {
bail(`Sec-WebSocket-Version must be ${WEBSOCKET_VERSION}.`);
return;
}
if (!socket.remoteAddress) {
bail('Unable to determine IP address.');
return;
}
let selectedProtocol = null;
let protocols = [];
if (req.headers['sec-websocket-protocol']) {
protocols = req.headers['sec-websocket-protocol'].split(',').map(protocol => protocol.trim());
// Do any of these match?
for (let i = 0; i < protocols.length; i++) {
if (this.protocols.indexOf(protocols[i]) != -1) {
selectedProtocol = protocols[i];
break;
}
}
}
let uri = (0, url_1.parse)(req.url, true);
let extensions = new websocket_extensions_1.default();
if (this.options.permessageDeflate) {
extensions.add(permessage_deflate_1.default);
}
let selectedExtensions = extensions.generateResponse(req.headers['sec-websocket-extensions']);
let handshakeData = {
path: uri.pathname,
query: uri.query,
headers: req.headers,
httpVersion: req.httpVersion,
origin: req.headers.origin || null,
protocols: protocols || [],
selectedProtocol: selectedProtocol || null,
auth: null,
cookies: {},
remoteAddress: socket.remoteAddress.replace(/^::ffff:/, ''),
socket
};
// Does it have HTTP authorization?
if (req.headers.authorization) {
let match = req.headers.authorization.match(/basic ((?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{4}))/i);
if (match) {
handshakeData.auth = Buffer.from(match[1], 'base64').toString('utf8');
}
}
// Does it have cookies?
if (req.headers.cookie) {
req.headers.cookie.split(';').map(cookie => cookie.trim().split('=')).forEach(cookie => {
handshakeData.cookies[cookie[0].trim()] = decodeURIComponent(cookie.slice(1).join('=').trim());
});
}
// Everything looks okay so far, make sure we'd like to accept this.
this.emit('handshake', handshakeData, (statusCode, body, headers) => {
// REJECT
req.statusCode = statusCode || 403;
headers = headers || {};
socket.end(buildResponse(statusCode || 403, headers, body));
}, (response) => {
// ACCEPT
response = response || {};
let headers = response.headers || {};
let options = {
pingInterval: this.options.pingInterval,
pingTimeout: this.options.pingTimeout,
pingFailures: this.options.pingFailures,
};
headers.Upgrade = 'websocket';
headers.Connection = 'Upgrade';
headers['Sec-WebSocket-Accept'] = (0, crypto_1.createHash)('sha1').update(req.headers['sec-websocket-key'] + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11').digest('base64');
// Check if the accept method overrode our selected subprotocol
if (typeof response.protocol !== 'undefined') {
handshakeData.selectedProtocol = response.protocol || null;
}
if (typeof response.permessageDeflate != 'undefined') {
extensions = new websocket_extensions_1.default();
if (response.permessageDeflate) {
extensions.add(permessage_deflate_1.default);
}
selectedExtensions = extensions.generateResponse(req.headers['sec-websocket-extensions']);
}
if (selectedExtensions) {
headers['Sec-WebSocket-Extensions'] = selectedExtensions;
}
if (handshakeData.selectedProtocol) {
headers['Sec-WebSocket-Protocol'] = handshakeData.selectedProtocol;
}
socket.write(buildResponse(101, headers));
response.options = response.options || {};
Object.assign(options, response.options);
let websocket = new WebSocketServerConnection_1.default(socket, options, handshakeData, head, extensions);
this.emit('connection', websocket);
return websocket;
});
function bail(err) {
if (server.listenerCount('upgrade') != 1) {
// Something else could pick this up
return;
}
socket.end(buildResponse(400, null, err));
}
});
}
}
exports.default = WebSocketServer;
function buildResponse(code, headers, body) {
let response = `HTTP/${HTTP_VERSION} ${code} ${HTTPStatusCodes_1.default[code] || 'Unknown Response'}\r\n`;
headers = headers || {};
headers.Server = `node-websocket13/${PACKAGE_VERSION}`;
headers.Date = new Date().toUTCString();
if (typeof body === 'object') {
body = JSON.stringify(body);
headers['Content-Type'] = 'application/json';
}
if (body) {
headers['Content-Length'] = Buffer.byteLength(body);
}
else if (code != 204 && code != 101) {
headers['Content-Length'] = 0;
}
for (let i in headers) {
response += `${i}: ${headers[i]}\r\n`;
}
response += '\r\n' + (typeof body !== 'undefined' ? body : '');
return response;
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiV2ViU29ja2V0U2VydmVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL1dlYlNvY2tldFNlcnZlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztBQUFBLG1DQUFrQztBQUdsQyw0RUFBbUQ7QUFDbkQsMkRBQWdEO0FBQ2hELDZCQUFzQztBQUN0QyxnRkFBdUQ7QUFHdkQsNEZBQW9FO0FBQ3BFLDhFQUFzRDtBQUd0RCxNQUFNLFlBQVksR0FBRyxHQUFHLENBQUM7QUFDekIsTUFBTSxpQkFBaUIsR0FBRyxFQUFFLENBQUM7QUFFN0IsMkJBQTJCO0FBQzNCLE1BQU0sZUFBZSxHQUFHLE9BQU8sQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDLE9BQU8sQ0FBQztBQUUzRCxNQUFxQixlQUFnQixTQUFRLGlDQUFtQztJQUkvRSxZQUFZLE9BQWdDO1FBQzNDLEtBQUssRUFBRSxDQUFDO1FBRVIsSUFBSSxDQUFDLE9BQU8sR0FBRztZQUNkLFlBQVksRUFBRSxLQUFLO1lBQ25CLFdBQVcsRUFBRSxLQUFLO1lBQ2xCLFlBQVksRUFBRSxDQUFDO1lBQ2YsaUJBQWlCLEVBQUUsSUFBSTtTQUN2QixDQUFDO1FBRUYsT0FBTyxHQUFHLE9BQU8sSUFBSSxFQUFFLENBQUM7UUFDeEIsTUFBTSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFFLE9BQU8sQ0FBQyxDQUFDO1FBRXJDLElBQUksQ0FBQyxTQUFTLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxTQUFTLElBQUksRUFBRSxDQUFDO0lBQy9DLENBQUM7SUFFRCxJQUFJLENBQUMsTUFBa0I7UUFDdEIsTUFBTSxDQUFDLEVBQUUsQ0FBQyxTQUFTLEVBQUUsQ0FBQyxHQUFvQixFQUFFLE1BQWMsRUFBRSxJQUFZLEVBQUUsRUFBRTtZQUMzRSxJQUFJLENBQUMsR0FBRyxDQUFDLE9BQU8sQ0FBQyxPQUFPLElBQUksR0FBRyxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsV0FBVyxFQUFFLElBQUksV0FBVyxFQUFFO2dCQUM3RSxJQUFJLENBQUMsNENBQTRDLENBQUMsQ0FBQztnQkFDbkQsT0FBTzthQUNQO1lBRUQsSUFDQyxDQUFDLEdBQUcsQ0FBQyxPQUFPLENBQUMsVUFBVTtnQkFDdkIsQ0FBQyxHQUFHLENBQUMsT0FBTyxDQUFDLFVBQVUsQ0FBQyxXQUFXLEVBQUUsQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLElBQUksRUFBRSxDQUFDLENBQUMsUUFBUSxDQUFDLFNBQVMsQ0FBQyxFQUN0RjtnQkFDRCxJQUFJLENBQUMsMEJBQTBCLENBQUMsQ0FBQztnQkFDakMsT0FBTzthQUNQO1lBRUQsSUFBSSxLQUFLLEdBQUcsR0FBRyxDQUFDLFdBQVcsQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUM7WUFDdkMsSUFBSSxRQUFRLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxJQUFJLFFBQVEsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLEVBQUU7Z0JBQ3JELElBQUksQ0FBQyw2Q0FBNkMsQ0FBQyxDQUFDO2dCQUNwRCxPQUFPO2FBQ1A7WUFFRCxJQUFJLEdBQUcsQ0FBQyxNQUFNLENBQUMsV0FBVyxFQUFFLElBQUksS0FBSyxFQUFFO2dCQUN0QyxJQUFJLENBQUMsZ0NBQWdDLENBQUMsQ0FBQztnQkFDdkMsT0FBTzthQUNQO1lBRUQsSUFBSSxDQUFDLEdBQUcsQ0FBQyxPQUFPLENBQUMsbUJBQW1CLENBQUMsSUFBSSxNQUFNLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxPQUFPLENBQUMsbUJBQW1CLENBQVcsRUFBRSxRQUFRLENBQUMsQ0FBQyxNQUFNLElBQUksRUFBRSxFQUFFO2dCQUN4SCxJQUFJLENBQUMsdUNBQXVDLENBQUMsQ0FBQztnQkFDOUMsT0FBTzthQUNQO1lBRUQsSUFBSyxHQUFHLENBQUMsT0FBTyxDQUFDLHVCQUF1QixDQUFZLElBQUksaUJBQWlCLENBQUMsUUFBUSxFQUFFLEVBQUU7Z0JBQ3JGLElBQUksQ0FBQyxpQ0FBaUMsaUJBQWlCLEdBQUcsQ0FBQyxDQUFDO2dCQUM1RCxPQUFPO2FBQ1A7WUFFRCxJQUFJLENBQUMsTUFBTSxDQUFDLGFBQWEsRUFBRTtnQkFDMUIsSUFBSSxDQUFDLGlDQUFpQyxDQUFDLENBQUM7Z0JBQ3hDLE9BQU87YUFDUDtZQUVELElBQUksZ0JBQWdCLEdBQUcsSUFBSSxDQUFDO1lBQzVCLElBQUksU0FBUyxHQUFHLEVBQUUsQ0FBQztZQUNuQixJQUFJLEdBQUcsQ0FBQyxPQUFPLENBQUMsd0JBQXdCLENBQUMsRUFBRTtnQkFDMUMsU0FBUyxHQUFJLEdBQUcsQ0FBQyxPQUFPLENBQUMsd0JBQXdCLENBQVksQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUMsR0FBRyxDQUFDLFFBQVEsQ0FBQyxFQUFFLENBQUMsUUFBUSxDQUFDLElBQUksRUFBRSxDQUFDLENBQUM7Z0JBQzFHLHlCQUF5QjtnQkFFekIsS0FBSyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLFNBQVMsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxFQUFFLEVBQUU7b0JBQzFDLElBQUksSUFBSSxDQUFDLFNBQVMsQ0FBQyxPQUFPLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLEVBQUU7d0JBQy9DLGdCQUFnQixHQUFHLFNBQVMsQ0FBQyxDQUFDLENBQUMsQ0FBQzt3QkFDaEMsTUFBTTtxQkFDTjtpQkFDRDthQUNEO1lBRUQsSUFBSSxHQUFHLEdBQUcsSUFBQSxXQUFRLEVBQUMsR0FBRyxDQUFDLEdBQUcsRUFBRSxJQUFJLENBQUMsQ0FBQztZQUVsQyxJQUFJLFVBQVUsR0FBRyxJQUFJLDhCQUFtQixFQUFFLENBQUM7WUFDM0MsSUFBSSxJQUFJLENBQUMsT0FBTyxDQUFDLGlCQUFpQixFQUFFO2dCQUNuQyxVQUFVLENBQUMsR0FBRyxDQUFDLDRCQUFpQixDQUFDLENBQUM7YUFDbEM7WUFDRCxJQUFJLGtCQUFrQixHQUFHLFVBQVUsQ0FBQyxnQkFBZ0IsQ0FBQyxHQUFHLENBQUMsT0FBTyxDQUFDLDBCQUEwQixDQUFDLENBQUMsQ0FBQztZQUU5RixJQUFJLGFBQWEsR0FBaUI7Z0JBQ2pDLElBQUksRUFBRSxHQUFHLENBQUMsUUFBUTtnQkFDbEIsS0FBSyxFQUFFLEdBQUcsQ0FBQyxLQUFpQztnQkFDNUMsT0FBTyxFQUFFLEdBQUcsQ0FBQyxPQUFtQztnQkFDaEQsV0FBVyxFQUFFLEdBQUcsQ0FBQyxXQUFXO2dCQUM1QixNQUFNLEVBQUUsR0FBRyxDQUFDLE9BQU8sQ0FBQyxNQUFNLElBQUksSUFBSTtnQkFDbEMsU0FBUyxFQUFFLFNBQVMsSUFBSSxFQUFFO2dCQUMxQixnQkFBZ0IsRUFBRSxnQkFBZ0IsSUFBSSxJQUFJO2dCQUMxQyxJQUFJLEVBQUUsSUFBSTtnQkFDVixPQUFPLEVBQUUsRUFBRTtnQkFDWCxhQUFhLEVBQUUsTUFBTSxDQUFDLGFBQWEsQ0FBQyxPQUFPLENBQUMsVUFBVSxFQUFFLEVBQUUsQ0FBQztnQkFDM0QsTUFBTTthQUNOLENBQUM7WUFFRixtQ0FBbUM7WUFDbkMsSUFBSSxHQUFHLENBQUMsT0FBTyxDQUFDLGFBQWEsRUFBRTtnQkFDOUIsSUFBSSxLQUFLLEdBQUcsR0FBRyxDQUFDLE9BQU8sQ0FBQyxhQUFhLENBQUMsS0FBSyxDQUFDLHlGQUF5RixDQUFDLENBQUM7Z0JBQ3ZJLElBQUksS0FBSyxFQUFFO29CQUNWLGFBQWEsQ0FBQyxJQUFJLEdBQUcsTUFBTSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLEVBQUUsUUFBUSxDQUFDLENBQUMsUUFBUSxDQUFDLE1BQU0sQ0FBQyxDQUFDO2lCQUN0RTthQUNEO1lBRUQsd0JBQXdCO1lBQ3hCLElBQUksR0FBRyxDQUFDLE9BQU8sQ0FBQyxNQUFNLEVBQUU7Z0JBQ3ZCLEdBQUcsQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQyxNQUFNLENBQUMsSUFBSSxFQUFFLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxFQUFFO29CQUN0RixhQUFhLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxHQUFHLGtCQUFrQixDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksRUFBRSxDQUFDLENBQUM7Z0JBQ2hHLENBQUMsQ0FBQyxDQUFDO2FBQ0g7WUFFRCxvRUFBb0U7WUFDcEUsSUFBSSxDQUFDLElBQUksQ0FBQyxXQUFXLEVBQUUsYUFBYSxFQUFFLENBQUMsVUFBVSxFQUFFLElBQUksRUFBRSxPQUFPLEVBQUUsRUFBRTtnQkFDbkUsU0FBUztnQkFDVCxHQUFHLENBQUMsVUFBVSxHQUFHLFVBQVUsSUFBSSxHQUFHLENBQUM7Z0JBQ25DLE9BQU8sR0FBRyxPQUFPLElBQUksRUFBRSxDQUFDO2dCQUN4QixNQUFNLENBQUMsR0FBRyxDQUFDLGFBQWEsQ0FBQyxVQUFVLElBQUksR0FBRyxFQUFFLE9BQU8sRUFBRSxJQUFJLENBQUMsQ0FBQyxDQUFDO1lBQzdELENBQUMsRUFBRSxDQUFDLFFBQVEsRUFBRSxFQUFFO2dCQUNmLFNBQVM7Z0JBQ1QsUUFBUSxHQUFHLFFBQVEsSUFBSSxFQUFFLENBQUM7Z0JBQzFCLElBQUksT0FBTyxHQUFHLFFBQVEsQ0FBQyxPQUFPLElBQUksRUFBRSxDQUFDO2dCQUVyQyxJQUFJLE9BQU8sR0FBd0I7b0JBQ2xDLFlBQVksRUFBRSxJQUFJLENBQUMsT0FBTyxDQUFDLFlBQVk7b0JBQ3ZDLFdBQVcsRUFBRSxJQUFJLENBQUMsT0FBTyxDQUFDLFdBQVc7b0JBQ3JDLFlBQVksRUFBRSxJQUFJLENBQUMsT0FBTyxDQUFDLFlBQVk7aUJBQ3ZDLENBQUM7Z0JBRUYsT0FBTyxDQUFDLE9BQU8sR0FBRyxXQUFXLENBQUM7Z0JBQzlCLE9BQU8sQ0FBQyxVQUFVLEdBQUcsU0FBUyxDQUFDO2dCQUMvQixPQUFPLENBQUMsc0JBQXNCLENBQUMsR0FBRyxJQUFBLG1CQUFVLEVBQUMsTUFBTSxDQUFDLENBQUMsTUFBTSxDQUMxRCxHQUFHLENBQUMsT0FBTyxDQUFDLG1CQUFtQixDQUFDLEdBQUcsc0NBQXNDLENBQ3pFLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxDQUFDO2dCQUVuQiwrREFBK0Q7Z0JBQy9ELElBQUksT0FBTyxRQUFRLENBQUMsUUFBUSxLQUFLLFdBQVcsRUFBRTtvQkFDN0MsYUFBYSxDQUFDLGdCQUFnQixHQUFHLFFBQVEsQ0FBQyxRQUFRLElBQUksSUFBSSxDQUFDO2lCQUMzRDtnQkFFRCxJQUFJLE9BQU8sUUFBUSxDQUFDLGlCQUFpQixJQUFJLFdBQVcsRUFBRTtvQkFDckQsVUFBVSxHQUFHLElBQUksOEJBQW1CLEVBQUUsQ0FBQztvQkFDdkMsSUFBSSxRQUFRLENBQUMsaUJBQWlCLEVBQUU7d0JBQy9CLFVBQVUsQ0FBQyxHQUFHLENBQUMsNEJBQWlCLENBQUMsQ0FBQztxQkFDbEM7b0JBQ0Qsa0JBQWtCLEdBQUcsVUFBVSxDQUFDLGdCQUFnQixDQUFDLEdBQUcsQ0FBQyxPQUFPLENBQUMsMEJBQTBCLENBQUMsQ0FBQyxDQUFDO2lCQUMxRjtnQkFFRCxJQUFJLGtCQUFrQixFQUFFO29CQUN2QixPQUFPLENBQUMsMEJBQTBCLENBQUMsR0FBRyxrQkFBa0IsQ0FBQztpQkFDekQ7Z0JBRUQsSUFBSSxhQUFhLENBQUMsZ0JBQWdCLEVBQUU7b0JBQ25DLE9BQU8sQ0FBQyx3QkFBd0IsQ0FBQyxHQUFHLGFBQWEsQ0FBQyxnQkFBZ0IsQ0FBQztpQkFDbkU7Z0JBRUQsTUFBTSxDQUFDLEtBQUssQ0FBQyxhQUFhLENBQUMsR0FBRyxFQUFFLE9BQU8sQ0FBQyxDQUFDLENBQUM7Z0JBRTFDLFFBQVEsQ0FBQyxPQUFPLEdBQUcsUUFBUSxDQUFDLE9BQU8sSUFBSSxFQUFFLENBQUM7Z0JBQzFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsT0FBTyxFQUFFLFFBQVEsQ0FBQyxPQUFPLENBQUMsQ0FBQztnQkFFekMsSUFBSSxTQUFTLEdBQUcsSUFBSSxtQ0FBeUIsQ0FBQyxNQUFNLEVBQUUsT0FBTyxFQUFFLGFBQWEsRUFBRSxJQUFJLEVBQUUsVUFBVSxDQUFDLENBQUM7Z0JBQ2hHLElBQUksQ0FBQyxJQUFJLENBQUMsWUFBWSxFQUFFLFNBQVMsQ0FBQyxDQUFDO2dCQUNuQyxPQUFPLFNBQVMsQ0FBQztZQUNsQixDQUFDLENBQUMsQ0FBQztZQUVILFNBQVMsSUFBSSxDQUFDLEdBQUc7Z0JBQ2hCLElBQUksTUFBTSxDQUFDLGFBQWEsQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLEVBQUU7b0JBQ3pDLG9DQUFvQztvQkFDcEMsT0FBTztpQkFDUDtnQkFFRCxNQUFNLENBQUMsR0FBRyxDQUFDLGFBQWEsQ0FBQyxHQUFHLEVBQUUsSUFBSSxFQUFFLEdBQUcsQ0FBQyxDQUFDLENBQUM7WUFDM0MsQ0FBQztRQUNGLENBQUMsQ0FBQyxDQUFDO0lBQ0osQ0FBQztDQUNEO0FBaExELGtDQWdMQztBQUlELFNBQVMsYUFBYSxDQUFDLElBQVksRUFBRSxPQUE4QixFQUFFLElBQW9CO0lBQ3hGLElBQUksUUFBUSxHQUFHLFFBQVEsWUFBWSxJQUFJLElBQUksSUFBSSx5QkFBZSxDQUFDLElBQUksQ0FBQyxJQUFJLGtCQUFrQixNQUFNLENBQUM7SUFFakcsT0FBTyxHQUFHLE9BQU8sSUFBSSxFQUFFLENBQUM7SUFDeEIsT0FBTyxDQUFDLE1BQU0sR0FBRyxvQkFBb0IsZUFBZSxFQUFFLENBQUM7SUFDdkQsT0FBTyxDQUFDLElBQUksR0FBRyxJQUFJLElBQUksRUFBRSxDQUFDLFdBQVcsRUFBRSxDQUFDO0lBRXhDLElBQUksT0FBTyxJQUFJLEtBQUssUUFBUSxFQUFFO1FBQzdCLElBQUksR0FBRyxJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQzVCLE9BQU8sQ0FBQyxjQUFjLENBQUMsR0FBRyxrQkFBa0IsQ0FBQztLQUM3QztJQUVELElBQUksSUFBSSxFQUFFO1FBQ1QsT0FBTyxDQUFDLGdCQUFnQixDQUFDLEdBQUcsTUFBTSxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsQ0FBQztLQUNwRDtTQUFNLElBQUksSUFBSSxJQUFJLEdBQUcsSUFBSSxJQUFJLElBQUksR0FBRyxFQUFFO1FBQ3RDLE9BQU8sQ0FBQyxnQkFBZ0IsQ0FBQyxHQUFHLENBQUMsQ0FBQztLQUM5QjtJQUVELEtBQUssSUFBSSxDQUFDLElBQUksT0FBTyxFQUFFO1FBQ3RCLFFBQVEsSUFBSSxHQUFHLENBQUMsS0FBSyxPQUFPLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQztLQUN0QztJQUVELFFBQVEsSUFBSSxNQUFNLEdBQUcsQ0FBQyxPQUFPLElBQUksS0FBSyxXQUFXLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUM7SUFDL0QsT0FBTyxRQUFRLENBQUM7QUFDakIsQ0FBQyJ9
;