UNPKG

websocket13

Version:

Simple WebSocket protocol 13 client with no native or heavy dependencies

215 lines 20.3 kB
"use strict"; 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 http_1 = require("http"); const https_1 = require("https"); const os_1 = require("os"); const stdlib_1 = __importDefault(require("@doctormckay/stdlib")); const url_1 = require("url"); const websocket_extensions_1 = __importDefault(require("websocket-extensions")); const WebSocketBase_1 = __importDefault(require("./WebSocketBase")); const State_1 = __importDefault(require("./enums/State")); const WEBSOCKET_VERSION = 13; // eslint-disable-next-line const PACKAGE_VERSION = require('../package.json').version; class WebSocket extends WebSocketBase_1.default { constructor(url, options) { super(); let parsedUri = (0, url_1.parse)(url); switch (parsedUri.protocol.toLowerCase()) { case 'ws:': this.secure = false; break; case 'wss:': this.secure = true; break; default: throw new Error(`Unknown protocol scheme ${parsedUri.protocol}`); } options = options || {}; Object.assign(this.options, options); let connectOptions = options.connection || {}; for (let element in parsedUri) { if (parsedUri[element] !== null) { connectOptions[element] = parsedUri[element]; } } connectOptions.protocol = this.secure ? 'https:' : 'http:'; this.hostname = parsedUri.hostname; this.port = connectOptions.port = parseInt((parsedUri.port || (this.secure ? 443 : 80)).toString(), 10); this.path = parsedUri.path || '/'; this._connectOptions = connectOptions; // clone the headers object so we don't unexpectedly modify the object that was passed in this.headers = JSON.parse(JSON.stringify(this.options.headers || {})); // Lowercase all the header names so we don't conflict (but only if they aren't already lowercase) for (let i in this.headers) { if (i.toLowerCase() != i) { this.headers[i.toLowerCase()] = this.headers[i]; delete this.headers[i]; } } this.headers.host = this.headers.host || parsedUri.host; this.headers.upgrade = 'websocket'; this.headers.connection = 'Upgrade'; this.headers['sec-websocket-version'] = WEBSOCKET_VERSION; this.headers['user-agent'] = this.headers['user-agent'] || [ `node.js/${process.versions.node} (${process.platform} ${(0, os_1.release)()} ${(0, os_1.arch)()})`, `node-websocket13/${PACKAGE_VERSION}` ].join(' '); // permessageDeflate defaults to true, so only if it's false should we disable it if (this.options.permessageDeflate === false) { this._extensions = new websocket_extensions_1.default(); } let extOffer = this._extensions.generateOffer(); if (extOffer) { this.headers['sec-websocket-extensions'] = extOffer; } if (this.options.protocols) { this.options.protocols = this.options.protocols.map(protocol => protocol.trim().toLowerCase()); this.headers['sec-websocket-protocol'] = this.options.protocols.join(', '); } if (this.options.cookies) { this.headers.cookie = Object.keys(this.options.cookies).map(name => name.trim() + '=' + encodeURIComponent(this.options.cookies[name])).join('; '); } this._type = 'client'; this._connect(); } _generateNonce() { this._nonce = (0, crypto_1.randomBytes)(16).toString('base64'); this.headers['sec-websocket-key'] = this._nonce; } _connect() { this._generateNonce(); this.state = State_1.default.Connecting; if (this.options.handshakeBody) { this.headers['content-length'] = this.options.handshakeBody.length; } this._connectOptions.headers = this.headers; if (this.secure && this.headers.host && typeof this._connectOptions.servername == 'undefined') { this._connectOptions.servername = this.headers.host.split(':')[0]; } if (this.options.httpProxy) { if (this._connectOptions.agent) { console.error('[websocket13] Warning: "agent" connection option specified; httpProxy option ignored'); } else { this._connectOptions.agent = stdlib_1.default.HTTP.getProxyAgent(this.secure, this.options.httpProxy, this.options.proxyTimeout); } } let reqFunc = this.secure ? https_1.request : http_1.request; let req = reqFunc(this._connectOptions, (res) => { let serverHttpVersion = res.httpVersion; let responseCode = res.statusCode; let responseText = res.statusMessage; let err = new Error(); err.responseCode = responseCode; err.responseText = responseText; err.httpVersion = serverHttpVersion; err.headers = res.headers; err.body = ''; res.on('data', chunk => { err.body += chunk; }); res.on('end', () => { if (this.state != State_1.default.Connecting) { return; // we don't care at this point } if (responseCode != 101) { err.message = `Response code ${responseCode}`; this._closeError(err); return; } err.message = 'Server not upgrading connection'; this._closeError(err); }); }); req.on('upgrade', (res, socket, head) => { let serverHttpVersion = res.httpVersion; let responseCode = res.statusCode; let responseText = res.statusMessage; let headers = res.headers; let err = new Error(); err.responseCode = responseCode; err.responseText = responseText; err.httpVersion = serverHttpVersion; err.headers = res.headers; if (!headers.upgrade || !headers.connection || !headers.upgrade.match(/websocket/i) || !headers.connection.match(/upgrade/i)) { err.message = 'Invalid server upgrade response'; this._closeError(err); return; } if (!headers['sec-websocket-accept']) { err.message = 'Missing Sec-WebSocket-Accept response header'; this._closeError(err); return; } let hash = (0, crypto_1.createHash)('sha1').update(this._nonce + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11').digest('base64'); if (headers['sec-websocket-accept'] != hash) { err.message = 'Mismatching Sec-WebSocket-Accept header'; err.expected = hash; err.actual = headers['sec-websocket-accept']; this._closeError(err); return; } if (this.state == State_1.default.Closing) { // we wanted to abort this connection this.emit('debug', 'Closing newly-established connection due to abort'); socket.end(); socket.destroy(); return; } if (headers['sec-websocket-protocol']) { let protocol = headers['sec-websocket-protocol'].toLowerCase(); if (this.options.protocols.indexOf(protocol) == -1) { err.message = `Server is using unsupported protocol ${protocol}`; this._closeError(err); return; } this.protocol = protocol; } try { this._extensions.activate(headers['sec-websocket-extensions']); } catch (ex) { err.message = ex.message; this._closeError(err); return; } this._socket = socket; this._prepSocketEvents(); this._resetUserTimeout(); // Everything is okay! this.state = State_1.default.Connected; let connectEventArgs = { headers: headers, httpVersion: serverHttpVersion, responseCode, responseText }; this.emit('connected', connectEventArgs); this.emit('connect', connectEventArgs); // save people from typos this._onConnected(); if (head && head.length > 0) { this._handleData(head); } }); req.on('error', (err) => { if (this.state != State_1.default.Connecting) { return; } err.state = this.state; this.emit('error', err); }); req.end(this.options.handshakeBody); } _sendFrame(frame, bypassQueue = false) { frame.maskKey = (0, crypto_1.randomBytes)(4).readUInt32BE(0); super._sendFrame(frame, bypassQueue); } } exports.default = WebSocket; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiV2ViU29ja2V0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL1dlYlNvY2tldC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztBQUFBLG1DQUErQztBQUMvQywrQkFBbUU7QUFDbkUsaUNBQThDO0FBQzlDLDJCQUF3RDtBQUN4RCxpRUFBeUM7QUFDekMsNkJBQXNDO0FBQ3RDLGdGQUF1RDtBQUV2RCxvRUFBNEM7QUFHNUMsMERBQWtDO0FBRWxDLE1BQU0saUJBQWlCLEdBQUcsRUFBRSxDQUFDO0FBRTdCLDJCQUEyQjtBQUMzQixNQUFNLGVBQWUsR0FBRyxPQUFPLENBQUMsaUJBQWlCLENBQUMsQ0FBQyxPQUFPLENBQUM7QUFFM0QsTUFBcUIsU0FBVSxTQUFRLHVCQUFhO0lBV25ELFlBQVksR0FBVyxFQUFFLE9BQWdDO1FBQ3hELEtBQUssRUFBRSxDQUFDO1FBRVIsSUFBSSxTQUFTLEdBQUcsSUFBQSxXQUFRLEVBQUMsR0FBRyxDQUFDLENBQUM7UUFFOUIsUUFBUSxTQUFTLENBQUMsUUFBUSxDQUFDLFdBQVcsRUFBRSxFQUFFO1lBQ3pDLEtBQUssS0FBSztnQkFDVCxJQUFJLENBQUMsTUFBTSxHQUFHLEtBQUssQ0FBQztnQkFDcEIsTUFBTTtZQUVQLEtBQUssTUFBTTtnQkFDVixJQUFJLENBQUMsTUFBTSxHQUFHLElBQUksQ0FBQztnQkFDbkIsTUFBTTtZQUVQO2dCQUNDLE1BQU0sSUFBSSxLQUFLLENBQUMsMkJBQTJCLFNBQVMsQ0FBQyxRQUFRLEVBQUUsQ0FBQyxDQUFDO1NBQ2xFO1FBRUQsT0FBTyxHQUFHLE9BQU8sSUFBSSxFQUFFLENBQUM7UUFDeEIsTUFBTSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFFLE9BQU8sQ0FBQyxDQUFDO1FBRXJDLElBQUksY0FBYyxHQUFPLE9BQU8sQ0FBQyxVQUFVLElBQUksRUFBRSxDQUFDO1FBQ2xELEtBQUssSUFBSSxPQUFPLElBQUksU0FBUyxFQUFFO1lBQzlCLElBQUksU0FBUyxDQUFDLE9BQU8sQ0FBQyxLQUFLLElBQUksRUFBRTtnQkFDaEMsY0FBYyxDQUFDLE9BQU8sQ0FBQyxHQUFHLFNBQVMsQ0FBQyxPQUFPLENBQUMsQ0FBQzthQUM3QztTQUNEO1FBRUQsY0FBYyxDQUFDLFFBQVEsR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQztRQUUzRCxJQUFJLENBQUMsUUFBUSxHQUFHLFNBQVMsQ0FBQyxRQUFRLENBQUM7UUFDbkMsSUFBSSxDQUFDLElBQUksR0FBRyxjQUFjLENBQUMsSUFBSSxHQUFHLFFBQVEsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxJQUFJLElBQUksQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsUUFBUSxFQUFFLEVBQUUsRUFBRSxDQUFDLENBQUM7UUFDeEcsSUFBSSxDQUFDLElBQUksR0FBRyxTQUFTLENBQUMsSUFBSSxJQUFJLEdBQUcsQ0FBQztRQUVsQyxJQUFJLENBQUMsZUFBZSxHQUFHLGNBQWMsQ0FBQztRQUV0Qyx5RkFBeUY7UUFDekYsSUFBSSxDQUFDLE9BQU8sR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxPQUFPLElBQUksRUFBRSxDQUFDLENBQUMsQ0FBQztRQUN0RSxrR0FBa0c7UUFDbEcsS0FBSyxJQUFJLENBQUMsSUFBSSxJQUFJLENBQUMsT0FBTyxFQUFFO1lBQzNCLElBQUksQ0FBQyxDQUFDLFdBQVcsRUFBRSxJQUFJLENBQUMsRUFBRTtnQkFDekIsSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsV0FBVyxFQUFFLENBQUMsR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxDQUFDO2dCQUNoRCxPQUFPLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLENBQUM7YUFDdkI7U0FDRDtRQUVELElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxJQUFJLFNBQVMsQ0FBQyxJQUFJLENBQUM7UUFDeEQsSUFBSSxDQUFDLE9BQU8sQ0FBQyxPQUFPLEdBQUcsV0FBVyxDQUFDO1FBQ25DLElBQUksQ0FBQyxPQUFPLENBQUMsVUFBVSxHQUFHLFNBQVMsQ0FBQztRQUNwQyxJQUFJLENBQUMsT0FBTyxDQUFDLHVCQUF1QixDQUFDLEdBQUcsaUJBQWlCLENBQUM7UUFDMUQsSUFBSSxDQUFDLE9BQU8sQ0FBQyxZQUFZLENBQUMsR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLFlBQVksQ0FBQztZQUN0RDtnQkFDQyxXQUFXLE9BQU8sQ0FBQyxRQUFRLENBQUMsSUFBSSxLQUFLLE9BQU8sQ0FBQyxRQUFRLElBQUksSUFBQSxZQUFTLEdBQUUsSUFBSSxJQUFBLFNBQU0sR0FBRSxHQUFHO2dCQUNuRixvQkFBb0IsZUFBZSxFQUFFO2FBQ3JDLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBRWIsaUZBQWlGO1FBQ2pGLElBQUksSUFBSSxDQUFDLE9BQU8sQ0FBQyxpQkFBaUIsS0FBSyxLQUFLLEVBQUU7WUFDN0MsSUFBSSxDQUFDLFdBQVcsR0FBRyxJQUFJLDhCQUFtQixFQUFFLENBQUM7U0FDN0M7UUFFRCxJQUFJLFFBQVEsR0FBRyxJQUFJLENBQUMsV0FBVyxDQUFDLGFBQWEsRUFBRSxDQUFDO1FBQ2hELElBQUksUUFBUSxFQUFFO1lBQ2IsSUFBSSxDQUFDLE9BQU8sQ0FBQywwQkFBMEIsQ0FBQyxHQUFHLFFBQVEsQ0FBQztTQUNwRDtRQUVELElBQUksSUFBSSxDQUFDLE9BQU8sQ0FBQyxTQUFTLEVBQUU7WUFDM0IsSUFBSSxDQUFDLE9BQU8sQ0FBQyxTQUFTLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxTQUFTLENBQUMsR0FBRyxDQUFDLFFBQVEsQ0FBQyxFQUFFLENBQUMsUUFBUSxDQUFDLElBQUksRUFBRSxDQUFDLFdBQVcsRUFBRSxDQUFDLENBQUM7WUFDL0YsSUFBSSxDQUFDLE9BQU8sQ0FBQyx3QkFBd0IsQ0FBQyxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQztTQUMzRTtRQUVELElBQUksSUFBSSxDQUFDLE9BQU8sQ0FBQyxPQUFPLEVBQUU7WUFDekIsSUFBSSxDQUFDLE9BQU8sQ0FBQyxNQUFNLEdBQUcsTUFBTSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsR0FBRyxHQUFHLEdBQUcsa0JBQWtCLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQztTQUNuSjtRQUVELElBQUksQ0FBQyxLQUFLLEdBQUcsUUFBUSxDQUFDO1FBRXRCLElBQUksQ0FBQyxRQUFRLEVBQUUsQ0FBQztJQUNqQixDQUFDO0lBRUQsY0FBYztRQUNiLElBQUksQ0FBQyxNQUFNLEdBQUcsSUFBQSxvQkFBVyxFQUFDLEVBQUUsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxRQUFRLENBQUMsQ0FBQztRQUNqRCxJQUFJLENBQUMsT0FBTyxDQUFDLG1CQUFtQixDQUFDLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQztJQUNqRCxDQUFDO0lBRUQsUUFBUTtRQUNQLElBQUksQ0FBQyxjQUFjLEVBQUUsQ0FBQztRQUV0QixJQUFJLENBQUMsS0FBSyxHQUFHLGVBQUssQ0FBQyxVQUFVLENBQUM7UUFFOUIsSUFBSSxJQUFJLENBQUMsT0FBTyxDQUFDLGFBQWEsRUFBRTtZQUMvQixJQUFJLENBQUMsT0FBTyxDQUFDLGdCQUFnQixDQUFDLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxhQUFhLENBQUMsTUFBTSxDQUFDO1NBQ25FO1FBRUQsSUFBSSxDQUFDLGVBQWUsQ0FBQyxPQUFPLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQztRQUM1QyxJQUFJLElBQUksQ0FBQyxNQUFNLElBQUksSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLElBQUksT0FBTyxJQUFJLENBQUMsZUFBZSxDQUFDLFVBQVUsSUFBSSxXQUFXLEVBQUU7WUFDOUYsSUFBSSxDQUFDLGVBQWUsQ0FBQyxVQUFVLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO1NBQ2xFO1FBRUQsSUFBSSxJQUFJLENBQUMsT0FBTyxDQUFDLFNBQVMsRUFBRTtZQUMzQixJQUFJLElBQUksQ0FBQyxlQUFlLENBQUMsS0FBSyxFQUFFO2dCQUMvQixPQUFPLENBQUMsS0FBSyxDQUFDLHNGQUFzRixDQUFDLENBQUM7YUFDdEc7aUJBQU07Z0JBQ04sSUFBSSxDQUFDLGVBQWUsQ0FBQyxLQUFLLEdBQUcsZ0JBQU0sQ0FBQyxJQUFJLENBQUMsYUFBYSxDQUFDLElBQUksQ0FBQyxNQUFNLEVBQUUsSUFBSSxDQUFDLE9BQU8sQ0FBQyxTQUFTLEVBQUUsSUFBSSxDQUFDLE9BQU8sQ0FBQyxZQUFZLENBQVUsQ0FBQzthQUNoSTtTQUNEO1FBRUQsSUFBSSxPQUFPLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsZUFBWSxDQUFDLENBQUMsQ0FBQyxjQUFXLENBQUM7UUFDdkQsSUFBSSxHQUFHLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxlQUFpQyxFQUFFLENBQUMsR0FBRyxFQUFFLEVBQUU7WUFDakUsSUFBSSxpQkFBaUIsR0FBRyxHQUFHLENBQUMsV0FBVyxDQUFDO1lBQ3hDLElBQUksWUFBWSxHQUFHLEdBQUcsQ0FBQyxVQUFVLENBQUM7WUFDbEMsSUFBSSxZQUFZLEdBQUcsR0FBRyxDQUFDLGFBQWEsQ0FBQztZQUVyQyxJQUFJLEdBQUcsR0FBTyxJQUFJLEtBQUssRUFBRSxDQUFDO1lBQzFCLEdBQUcsQ0FBQyxZQUFZLEdBQUcsWUFBWSxDQUFDO1lBQ2hDLEdBQUcsQ0FBQyxZQUFZLEdBQUcsWUFBWSxDQUFDO1lBQ2hDLEdBQUcsQ0FBQyxXQUFXLEdBQUcsaUJBQWlCLENBQUM7WUFDcEMsR0FBRyxDQUFDLE9BQU8sR0FBRyxHQUFHLENBQUMsT0FBTyxDQUFDO1lBRTFCLEdBQUcsQ0FBQyxJQUFJLEdBQUcsRUFBRSxDQUFDO1lBRWQsR0FBRyxDQUFDLEVBQUUsQ0FBQyxNQUFNLEVBQUUsS0FBSyxDQUFDLEVBQUU7Z0JBQ3RCLEdBQUcsQ0FBQyxJQUFJLElBQUksS0FBSyxDQUFDO1lBQ25CLENBQUMsQ0FBQyxDQUFDO1lBRUgsR0FBRyxDQUFDLEVBQUUsQ0FBQyxLQUFLLEVBQUUsR0FBRyxFQUFFO2dCQUNsQixJQUFJLElBQUksQ0FBQyxLQUFLLElBQUksZUFBSyxDQUFDLFVBQVUsRUFBRTtvQkFDbkMsT0FBTyxDQUFDLDhCQUE4QjtpQkFDdEM7Z0JBRUQsSUFBSSxZQUFZLElBQUksR0FBRyxFQUFFO29CQUN4QixHQUFHLENBQUMsT0FBTyxHQUFHLGlCQUFpQixZQUFZLEVBQUUsQ0FBQztvQkFDOUMsSUFBSSxDQUFDLFdBQVcsQ0FBQyxHQUFHLENBQUMsQ0FBQztvQkFDdEIsT0FBTztpQkFDUDtnQkFFRCxHQUFHLENBQUMsT0FBTyxHQUFHLGlDQUFpQyxDQUFDO2dCQUNoRCxJQUFJLENBQUMsV0FBVyxDQUFDLEdBQUcsQ0FBQyxDQUFDO1lBQ3ZCLENBQUMsQ0FBQyxDQUFDO1FBQ0osQ0FBQyxDQUFDLENBQUM7UUFFSCxHQUFHLENBQUMsRUFBRSxDQUFDLFNBQVMsRUFBRSxDQUFDLEdBQUcsRUFBRSxNQUFNLEVBQUUsSUFBSSxFQUFFLEVBQUU7WUFDdkMsSUFBSSxpQkFBaUIsR0FBRyxHQUFHLENBQUMsV0FBVyxDQUFDO1lBQ3hDLElBQUksWUFBWSxHQUFHLEdBQUcsQ0FBQyxVQUFVLENBQUM7WUFDbEMsSUFBSSxZQUFZLEdBQUcsR0FBRyxDQUFDLGFBQWEsQ0FBQztZQUNyQyxJQUFJLE9BQU8sR0FBRyxHQUFHLENBQUMsT0FBTyxDQUFDO1lBRTFCLElBQUksR0FBRyxHQUFPLElBQUksS0FBSyxFQUFFLENBQUM7WUFDMUIsR0FBRyxDQUFDLFlBQVksR0FBRyxZQUFZLENBQUM7WUFDaEMsR0FBRyxDQUFDLFlBQVksR0FBRyxZQUFZLENBQUM7WUFDaEMsR0FBRyxDQUFDLFdBQVcsR0FBRyxpQkFBaUIsQ0FBQztZQUNwQyxHQUFHLENBQUMsT0FBTyxHQUFHLEdBQUcsQ0FBQyxPQUFPLENBQUM7WUFFMUIsSUFBSSxDQUFDLE9BQU8sQ0FBQyxPQUFPLElBQUksQ0FBQyxPQUFPLENBQUMsVUFBVSxJQUFJLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsWUFBWSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsVUFBVSxDQUFDLEtBQUssQ0FBQyxVQUFVLENBQUMsRUFBRTtnQkFDN0gsR0FBRyxDQUFDLE9BQU8sR0FBRyxpQ0FBaUMsQ0FBQztnQkFDaEQsSUFBSSxDQUFDLFdBQVcsQ0FBQyxHQUFHLENBQUMsQ0FBQztnQkFDdEIsT0FBTzthQUNQO1lBRUQsSUFBSSxDQUFDLE9BQU8sQ0FBQyxzQkFBc0IsQ0FBQyxFQUFFO2dCQUNyQyxHQUFHLENBQUMsT0FBTyxHQUFHLDhDQUE4QyxDQUFDO2dCQUM3RCxJQUFJLENBQUMsV0FBVyxDQUFDLEdBQUcsQ0FBQyxDQUFDO2dCQUN0QixPQUFPO2FBQ1A7WUFFRCxJQUFJLElBQUksR0FBRyxJQUFBLG1CQUFVLEVBQUMsTUFBTSxDQUFDLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxNQUFNLEdBQUcsc0NBQXNDLENBQUMsQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDLENBQUM7WUFDNUcsSUFBSSxPQUFPLENBQUMsc0JBQXNCLENBQUMsSUFBSSxJQUFJLEVBQUU7Z0JBQzVDLEdBQUcsQ0FBQyxPQUFPLEdBQUcseUNBQXlDLENBQUM7Z0JBQ3hELEdBQUcsQ0FBQyxRQUFRLEdBQUcsSUFBSSxDQUFDO2dCQUNwQixHQUFHLENBQUMsTUFBTSxHQUFHLE9BQU8sQ0FBQyxzQkFBc0IsQ0FBQyxDQUFDO2dCQUM3QyxJQUFJLENBQUMsV0FBVyxDQUFDLEdBQUcsQ0FBQyxDQUFDO2dCQUN0QixPQUFPO2FBQ1A7WUFFRCxJQUFJLElBQUksQ0FBQyxLQUFLLElBQUksZUFBSyxDQUFDLE9BQU8sRUFBRTtnQkFDaEMscUNBQXFDO2dCQUNyQyxJQUFJLENBQUMsSUFBSSxDQUFDLE9BQU8sRUFBRSxtREFBbUQsQ0FBQyxDQUFDO2dCQUN4RSxNQUFNLENBQUMsR0FBRyxFQUFFLENBQUM7Z0JBQ2IsTUFBTSxDQUFDLE9BQU8sRUFBRSxDQUFDO2dCQUNqQixPQUFPO2FBQ1A7WUFFRCxJQUFJLE9BQU8sQ0FBQyx3QkFBd0IsQ0FBQyxFQUFFO2dCQUN0QyxJQUFJLFFBQVEsR0FBSSxPQUFPLENBQUMsd0JBQXdCLENBQVksQ0FBQyxXQUFXLEVBQUUsQ0FBQztnQkFDM0UsSUFBSSxJQUFJLENBQUMsT0FBTyxDQUFDLFNBQVMsQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxDQUFDLEVBQUU7b0JBQ25ELEdBQUcsQ0FBQyxPQUFPLEdBQUcsd0NBQXdDLFFBQVEsRUFBRSxDQUFDO29CQUNqRSxJQUFJLENBQUMsV0FBVyxDQUFDLEdBQUcsQ0FBQyxDQUFDO29CQUN0QixPQUFPO2lCQUNQO2dCQUVELElBQUksQ0FBQyxRQUFRLEdBQUcsUUFBUSxDQUFDO2FBQ3pCO1lBRUQsSUFBSTtnQkFDSCxJQUFJLENBQUMsV0FBVyxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUMsMEJBQTBCLENBQUMsQ0FBQyxDQUFDO2FBQy9EO1lBQUMsT0FBTyxFQUFFLEVBQUU7Z0JBQ1osR0FBRyxDQUFDLE9BQU8sR0FBRyxFQUFFLENBQUMsT0FBTyxDQUFDO2dCQUN6QixJQUFJLENBQUMsV0FBVyxDQUFDLEdBQUcsQ0FBQyxDQUFDO2dCQUN0QixPQUFPO2FBQ1A7WUFFRCxJQUFJLENBQUMsT0FBTyxHQUFHLE1BQU0sQ0FBQztZQUN0QixJQUFJLENBQUMsaUJBQWlCLEVBQUUsQ0FBQztZQUN6QixJQUFJLENBQUMsaUJBQWlCLEVBQUUsQ0FBQztZQUV6QixzQkFBc0I7WUFDdEIsSUFBSSxDQUFDLEtBQUssR0FBRyxlQUFLLENBQUMsU0FBUyxDQUFDO1lBQzdCLElBQUksZ0JBQWdCLEdBQTZCO2dCQUNoRCxPQUFPLEVBQUUsT0FBbUM7Z0JBQzVDLFdBQVcsRUFBRSxpQkFBaUI7Z0JBQzlCLFlBQVk7Z0JBQ1osWUFBWTthQUNaLENBQUM7WUFDRixJQUFJLENBQUMsSUFBSSxDQUFDLFdBQVcsRUFBRSxnQkFBZ0IsQ0FBQyxDQUFDO1lBQ3pDLElBQUksQ0FBQyxJQUFJLENBQUMsU0FBUyxFQUFFLGdCQUFnQixDQUFDLENBQUMsQ0FBQyx5QkFBeUI7WUFDakUsSUFBSSxDQUFDLFlBQVksRUFBRSxDQUFDO1lBRXBCLElBQUksSUFBSSxJQUFJLElBQUksQ0FBQyxNQUFNLEdBQUcsQ0FBQyxFQUFFO2dCQUM1QixJQUFJLENBQUMsV0FBVyxDQUFDLElBQUksQ0FBQyxDQUFDO2FBQ3ZCO1FBQ0YsQ0FBQyxDQUFDLENBQUM7UUFFSCxHQUFHLENBQUMsRUFBRSxDQUFDLE9BQU8sRUFBRSxDQUFDLEdBQU8sRUFBRSxFQUFFO1lBQzNCLElBQUksSUFBSSxDQUFDLEtBQUssSUFBSSxlQUFLLENBQUMsVUFBVSxFQUFFO2dCQUNuQyxPQUFPO2FBQ1A7WUFFRCxHQUFHLENBQUMsS0FBSyxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUM7WUFDdkIsSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPLEVBQUUsR0FBRyxDQUFDLENBQUM7UUFDekIsQ0FBQyxDQUFDLENBQUM7UUFFSCxHQUFHLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsYUFBYSxDQUFDLENBQUM7SUFDckMsQ0FBQztJQUVELFVBQVUsQ0FBQyxLQUFjLEVBQUUsV0FBVyxHQUFHLEtBQUs7UUFDN0MsS0FBSyxDQUFDLE9BQU8sR0FBRyxJQUFBLG9CQUFXLEVBQUMsQ0FBQyxDQUFDLENBQUMsWUFBWSxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQy9DLEtBQUssQ0FBQyxVQUFVLENBQUMsS0FBSyxFQUFFLFdBQVcsQ0FBQyxDQUFDO0lBQ3RDLENBQUM7Q0FDRDtBQXpQRCw0QkF5UEMifQ==