UNPKG

tedious

Version:

A TDS driver, for connecting to MS SQLServer databases.

170 lines (165 loc) • 28.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _nativeDuplexpair = _interopRequireDefault(require("native-duplexpair")); var tls = _interopRequireWildcard(require("tls")); var _net = require("net"); var _events = require("events"); var _message = _interopRequireDefault(require("./message")); var _packet = require("./packet"); var _incomingMessageStream = _interopRequireDefault(require("./incoming-message-stream")); var _outgoingMessageStream = _interopRequireDefault(require("./outgoing-message-stream")); function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); } function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } class MessageIO extends _events.EventEmitter { // Node's `tls.TLSSocket#setMaxSendFragment` silently ignores values outside this range. // (A static class property rather than a named export, as the `module.exports` // assignment at the bottom of this file would clobber named exports for CJS consumers.) static MAX_TLS_SEND_FRAGMENT_SIZE = 16384; constructor(socket, packetSize, debug) { super(); this.socket = socket; this.debug = debug; this.tlsNegotiationComplete = false; this.incomingMessageStream = new _incomingMessageStream.default(this.debug); this.incomingMessageIterator = this.incomingMessageStream[Symbol.asyncIterator](); this.outgoingMessageStream = new _outgoingMessageStream.default(this.debug, { packetSize: packetSize }); this.socket.pipe(this.incomingMessageStream); this.outgoingMessageStream.pipe(this.socket); } packetSize(...args) { if (args.length > 0) { const packetSize = args[0]; this.debug.log('Packet size changed from ' + this.outgoingMessageStream.packetSize + ' to ' + packetSize); this.outgoingMessageStream.packetSize = packetSize; } const maxSendFragment = Math.min(this.outgoingMessageStream.packetSize, MessageIO.MAX_TLS_SEND_FRAGMENT_SIZE); if (this.securePair) { // Classic `encrypt: true` path: TLS is layered over a `DuplexPair`. this.securePair.cleartext.setMaxSendFragment(maxSendFragment); } else if (this.socket instanceof tls.TLSSocket) { // `encrypt: "strict"` (TDS 8.0) path: the socket itself is the TLS socket. Without // this, a server-initiated packet size change (ENVCHANGE) after login would leave // the original cap in place, potentially allowing oversized TLS records again. this.socket.setMaxSendFragment(maxSendFragment); } return this.outgoingMessageStream.packetSize; } // Negotiate TLS encryption. startTls(credentialsDetails, hostname, trustServerCertificate) { if (!credentialsDetails.maxVersion || !['TLSv1.2', 'TLSv1.1', 'TLSv1'].includes(credentialsDetails.maxVersion)) { credentialsDetails.maxVersion = 'TLSv1.2'; } const secureContext = tls.createSecureContext(credentialsDetails); return new Promise((resolve, reject) => { const duplexpair = new _nativeDuplexpair.default(); const securePair = this.securePair = { cleartext: tls.connect({ socket: duplexpair.socket1, // The `host` is used to verify the server's certificate identity. // It is not used to establish a connection as a `socket` is // specified. host: hostname, // RFC 6066 does not allow IP addresses to be used as the server // name, so omit the SNI extension in that case. servername: (0, _net.isIP)(hostname) ? '' : hostname, secureContext: secureContext, rejectUnauthorized: !trustServerCertificate }), encrypted: duplexpair.socket2 }; const onSecureConnect = () => { securePair.encrypted.removeListener('readable', onReadable); securePair.cleartext.removeListener('error', onError); securePair.cleartext.removeListener('secureConnect', onSecureConnect); // If we encounter any errors from this point on, // we just forward them to the actual network socket. securePair.cleartext.once('error', err => { this.socket.destroy(err); }); const cipher = securePair.cleartext.getCipher(); if (cipher) { this.debug.log('TLS negotiated (' + cipher.name + ', ' + cipher.version + ')'); } this.emit('secure', securePair.cleartext); securePair.cleartext.setMaxSendFragment(Math.min(this.outgoingMessageStream.packetSize, MessageIO.MAX_TLS_SEND_FRAGMENT_SIZE)); this.outgoingMessageStream.unpipe(this.socket); this.socket.unpipe(this.incomingMessageStream); this.socket.pipe(securePair.encrypted); securePair.encrypted.pipe(this.socket); securePair.cleartext.pipe(this.incomingMessageStream); this.outgoingMessageStream.pipe(securePair.cleartext); this.tlsNegotiationComplete = true; resolve(); }; const onError = err => { securePair.encrypted.removeListener('readable', onReadable); securePair.cleartext.removeListener('error', onError); securePair.cleartext.removeListener('secureConnect', onSecureConnect); securePair.cleartext.destroy(); securePair.encrypted.destroy(); reject(err); }; const onReadable = () => { // When there is handshake data on the encrypted stream of the secure pair, // we wrap it into a `PRELOGIN` message and send it to the server. // // For each `PRELOGIN` message we sent we get back exactly one response message // that contains the server's handshake response data. const message = new _message.default({ type: _packet.TYPE.PRELOGIN, resetConnection: false }); let chunk; while (chunk = securePair.encrypted.read()) { message.write(chunk); } this.outgoingMessageStream.write(message); message.end(); this.readMessage().then(async response => { // Setup readable handler for the next round of handshaking. // If we encounter a `secureConnect` on the cleartext side // of the secure pair, the `readable` handler is cleared // and no further handshake handling will happen. securePair.encrypted.once('readable', onReadable); for await (const data of response) { // We feed the server's handshake response back into the // encrypted end of the secure pair. securePair.encrypted.write(data); } }).catch(onError); }; securePair.cleartext.once('error', onError); securePair.cleartext.once('secureConnect', onSecureConnect); securePair.encrypted.once('readable', onReadable); }); } // TODO listen for 'drain' event when socket.write returns false. // TODO implement incomplete request cancellation (2.2.1.6) sendMessage(packetType, data, resetConnection) { const message = new _message.default({ type: packetType, resetConnection: resetConnection }); message.end(data); this.outgoingMessageStream.write(message); return message; } /** * Read the next incoming message from the socket. */ async readMessage() { const result = await this.incomingMessageIterator.next(); if (result.done) { throw new Error('unexpected end of message stream'); } return result.value; } } var _default = exports.default = MessageIO; module.exports = MessageIO; //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfbmF0aXZlRHVwbGV4cGFpciIsIl9pbnRlcm9wUmVxdWlyZURlZmF1bHQiLCJyZXF1aXJlIiwidGxzIiwiX2ludGVyb3BSZXF1aXJlV2lsZGNhcmQiLCJfbmV0IiwiX2V2ZW50cyIsIl9tZXNzYWdlIiwiX3BhY2tldCIsIl9pbmNvbWluZ01lc3NhZ2VTdHJlYW0iLCJfb3V0Z29pbmdNZXNzYWdlU3RyZWFtIiwiZSIsInQiLCJXZWFrTWFwIiwiciIsIm4iLCJfX2VzTW9kdWxlIiwibyIsImkiLCJmIiwiX19wcm90b19fIiwiZGVmYXVsdCIsImhhcyIsImdldCIsInNldCIsImhhc093blByb3BlcnR5IiwiY2FsbCIsIk9iamVjdCIsImRlZmluZVByb3BlcnR5IiwiZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yIiwiTWVzc2FnZUlPIiwiRXZlbnRFbWl0dGVyIiwiTUFYX1RMU19TRU5EX0ZSQUdNRU5UX1NJWkUiLCJjb25zdHJ1Y3RvciIsInNvY2tldCIsInBhY2tldFNpemUiLCJkZWJ1ZyIsInRsc05lZ290aWF0aW9uQ29tcGxldGUiLCJpbmNvbWluZ01lc3NhZ2VTdHJlYW0iLCJJbmNvbWluZ01lc3NhZ2VTdHJlYW0iLCJpbmNvbWluZ01lc3NhZ2VJdGVyYXRvciIsIlN5bWJvbCIsImFzeW5jSXRlcmF0b3IiLCJvdXRnb2luZ01lc3NhZ2VTdHJlYW0iLCJPdXRnb2luZ01lc3NhZ2VTdHJlYW0iLCJwaXBlIiwiYXJncyIsImxlbmd0aCIsImxvZyIsIm1heFNlbmRGcmFnbWVudCIsIk1hdGgiLCJtaW4iLCJzZWN1cmVQYWlyIiwiY2xlYXJ0ZXh0Iiwic2V0TWF4U2VuZEZyYWdtZW50IiwiVExTU29ja2V0Iiwic3RhcnRUbHMiLCJjcmVkZW50aWFsc0RldGFpbHMiLCJob3N0bmFtZSIsInRydXN0U2VydmVyQ2VydGlmaWNhdGUiLCJtYXhWZXJzaW9uIiwiaW5jbHVkZXMiLCJzZWN1cmVDb250ZXh0IiwiY3JlYXRlU2VjdXJlQ29udGV4dCIsIlByb21pc2UiLCJyZXNvbHZlIiwicmVqZWN0IiwiZHVwbGV4cGFpciIsIkR1cGxleFBhaXIiLCJjb25uZWN0Iiwic29ja2V0MSIsImhvc3QiLCJzZXJ2ZXJuYW1lIiwiaXNJUCIsInJlamVjdFVuYXV0aG9yaXplZCIsImVuY3J5cHRlZCIsInNvY2tldDIiLCJvblNlY3VyZUNvbm5lY3QiLCJyZW1vdmVMaXN0ZW5lciIsIm9uUmVhZGFibGUiLCJvbkVycm9yIiwib25jZSIsImVyciIsImRlc3Ryb3kiLCJjaXBoZXIiLCJnZXRDaXBoZXIiLCJuYW1lIiwidmVyc2lvbiIsImVtaXQiLCJ1bnBpcGUiLCJtZXNzYWdlIiwiTWVzc2FnZSIsInR5cGUiLCJUWVBFIiwiUFJFTE9HSU4iLCJyZXNldENvbm5lY3Rpb24iLCJjaHVuayIsInJlYWQiLCJ3cml0ZSIsImVuZCIsInJlYWRNZXNzYWdlIiwidGhlbiIsInJlc3BvbnNlIiwiZGF0YSIsImNhdGNoIiwic2VuZE1lc3NhZ2UiLCJwYWNrZXRUeXBlIiwicmVzdWx0IiwibmV4dCIsImRvbmUiLCJFcnJvciIsInZhbHVlIiwiX2RlZmF1bHQiLCJleHBvcnRzIiwibW9kdWxlIl0sInNvdXJjZXMiOlsiLi4vc3JjL21lc3NhZ2UtaW8udHMiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IER1cGxleFBhaXIgZnJvbSAnbmF0aXZlLWR1cGxleHBhaXInO1xuXG5pbXBvcnQgeyBEdXBsZXggfSBmcm9tICdzdHJlYW0nO1xuaW1wb3J0ICogYXMgdGxzIGZyb20gJ3Rscyc7XG5pbXBvcnQgeyBpc0lQLCBTb2NrZXQgfSBmcm9tICduZXQnO1xuaW1wb3J0IHsgRXZlbnRFbWl0dGVyIH0gZnJvbSAnZXZlbnRzJztcblxuaW1wb3J0IERlYnVnIGZyb20gJy4vZGVidWcnO1xuXG5pbXBvcnQgTWVzc2FnZSBmcm9tICcuL21lc3NhZ2UnO1xuaW1wb3J0IHsgVFlQRSB9IGZyb20gJy4vcGFja2V0JztcblxuaW1wb3J0IEluY29taW5nTWVzc2FnZVN0cmVhbSBmcm9tICcuL2luY29taW5nLW1lc3NhZ2Utc3RyZWFtJztcbmltcG9ydCBPdXRnb2luZ01lc3NhZ2VTdHJlYW0gZnJvbSAnLi9vdXRnb2luZy1tZXNzYWdlLXN0cmVhbSc7XG5cbmNsYXNzIE1lc3NhZ2VJTyBleHRlbmRzIEV2ZW50RW1pdHRlciB7XG4gIC8vIE5vZGUncyBgdGxzLlRMU1NvY2tldCNzZXRNYXhTZW5kRnJhZ21lbnRgIHNpbGVudGx5IGlnbm9yZXMgdmFsdWVzIG91dHNpZGUgdGhpcyByYW5nZS5cbiAgLy8gKEEgc3RhdGljIGNsYXNzIHByb3BlcnR5IHJhdGhlciB0aGFuIGEgbmFtZWQgZXhwb3J0LCBhcyB0aGUgYG1vZHVsZS5leHBvcnRzYFxuICAvLyBhc3NpZ25tZW50IGF0IHRoZSBib3R0b20gb2YgdGhpcyBmaWxlIHdvdWxkIGNsb2JiZXIgbmFtZWQgZXhwb3J0cyBmb3IgQ0pTIGNvbnN1bWVycy4pXG4gIHN0YXRpYyByZWFkb25seSBNQVhfVExTX1NFTkRfRlJBR01FTlRfU0laRSA9IDE2Mzg0O1xuXG4gIGRlY2xhcmUgc29ja2V0OiBTb2NrZXQ7XG4gIGRlY2xhcmUgZGVidWc6IERlYnVnO1xuXG4gIGRlY2xhcmUgdGxzTmVnb3RpYXRpb25Db21wbGV0ZTogYm9vbGVhbjtcblxuICBkZWNsYXJlIHByaXZhdGUgaW5jb21pbmdNZXNzYWdlU3RyZWFtOiBJbmNvbWluZ01lc3NhZ2VTdHJlYW07XG4gIGRlY2xhcmUgb3V0Z29pbmdNZXNzYWdlU3RyZWFtOiBPdXRnb2luZ01lc3NhZ2VTdHJlYW07XG5cbiAgZGVjbGFyZSBzZWN1cmVQYWlyPzoge1xuICAgIGNsZWFydGV4dDogdGxzLlRMU1NvY2tldDtcbiAgICBlbmNyeXB0ZWQ6IER1cGxleDtcbiAgfTtcblxuICBkZWNsYXJlIGluY29taW5nTWVzc2FnZUl0ZXJhdG9yOiBBc3luY0l0ZXJhYmxlSXRlcmF0b3I8TWVzc2FnZT47XG5cbiAgY29uc3RydWN0b3Ioc29ja2V0OiBTb2NrZXQsIHBhY2tldFNpemU6IG51bWJlciwgZGVidWc6IERlYnVnKSB7XG4gICAgc3VwZXIoKTtcblxuICAgIHRoaXMuc29ja2V0ID0gc29ja2V0O1xuICAgIHRoaXMuZGVidWcgPSBkZWJ1ZztcblxuICAgIHRoaXMudGxzTmVnb3RpYXRpb25Db21wbGV0ZSA9IGZhbHNlO1xuXG4gICAgdGhpcy5pbmNvbWluZ01lc3NhZ2VTdHJlYW0gPSBuZXcgSW5jb21pbmdNZXNzYWdlU3RyZWFtKHRoaXMuZGVidWcpO1xuICAgIHRoaXMuaW5jb21pbmdNZXNzYWdlSXRlcmF0b3IgPSB0aGlzLmluY29taW5nTWVzc2FnZVN0cmVhbVtTeW1ib2wuYXN5bmNJdGVyYXRvcl0oKTtcblxuICAgIHRoaXMub3V0Z29pbmdNZXNzYWdlU3RyZWFtID0gbmV3IE91dGdvaW5nTWVzc2FnZVN0cmVhbSh0aGlzLmRlYnVnLCB7IHBhY2tldFNpemU6IHBhY2tldFNpemUgfSk7XG5cbiAgICB0aGlzLnNvY2tldC5waXBlKHRoaXMuaW5jb21pbmdNZXNzYWdlU3RyZWFtKTtcbiAgICB0aGlzLm91dGdvaW5nTWVzc2FnZVN0cmVhbS5waXBlKHRoaXMuc29ja2V0KTtcbiAgfVxuXG4gIHBhY2tldFNpemUoLi4uYXJnczogW251bWJlcl0pIHtcbiAgICBpZiAoYXJncy5sZW5ndGggPiAwKSB7XG4gICAgICBjb25zdCBwYWNrZXRTaXplID0gYXJnc1swXTtcbiAgICAgIHRoaXMuZGVidWcubG9nKCdQYWNrZXQgc2l6ZSBjaGFuZ2VkIGZyb20gJyArIHRoaXMub3V0Z29pbmdNZXNzYWdlU3RyZWFtLnBhY2tldFNpemUgKyAnIHRvICcgKyBwYWNrZXRTaXplKTtcbiAgICAgIHRoaXMub3V0Z29pbmdNZXNzYWdlU3RyZWFtLnBhY2tldFNpemUgPSBwYWNrZXRTaXplO1xuICAgIH1cblxuICAgIGNvbnN0IG1heFNlbmRGcmFnbWVudCA9IE1hdGgubWluKHRoaXMub3V0Z29pbmdNZXNzYWdlU3RyZWFtLnBhY2tldFNpemUsIE1lc3NhZ2VJTy5NQVhfVExTX1NFTkRfRlJBR01FTlRfU0laRSk7XG5cbiAgICBpZiAodGhpcy5zZWN1cmVQYWlyKSB7XG4gICAgICAvLyBDbGFzc2ljIGBlbmNyeXB0OiB0cnVlYCBwYXRoOiBUTFMgaXMgbGF5ZXJlZCBvdmVyIGEgYER1cGxleFBhaXJgLlxuICAgICAgdGhpcy5zZWN1cmVQYWlyLmNsZWFydGV4dC5zZXRNYXhTZW5kRnJhZ21lbnQobWF4U2VuZEZyYWdtZW50KTtcbiAgICB9IGVsc2UgaWYgKHRoaXMuc29ja2V0IGluc3RhbmNlb2YgdGxzLlRMU1NvY2tldCkge1xuICAgICAgLy8gYGVuY3J5cHQ6IFwic3RyaWN0XCJgIChURFMgOC4wKSBwYXRoOiB0aGUgc29ja2V0IGl0c2VsZiBpcyB0aGUgVExTIHNvY2tldC4gV2l0aG91dFxuICAgICAgLy8gdGhpcywgYSBzZXJ2ZXItaW5pdGlhdGVkIHBhY2tldCBzaXplIGNoYW5nZSAoRU5WQ0hBTkdFKSBhZnRlciBsb2dpbiB3b3VsZCBsZWF2ZVxuICAgICAgLy8gdGhlIG9yaWdpbmFsIGNhcCBpbiBwbGFjZSwgcG90ZW50aWFsbHkgYWxsb3dpbmcgb3ZlcnNpemVkIFRMUyByZWNvcmRzIGFnYWluLlxuICAgICAgdGhpcy5zb2NrZXQuc2V0TWF4U2VuZEZyYWdtZW50KG1heFNlbmRGcmFnbWVudCk7XG4gICAgfVxuXG4gICAgcmV0dXJuIHRoaXMub3V0Z29pbmdNZXNzYWdlU3RyZWFtLnBhY2tldFNpemU7XG4gIH1cblxuICAvLyBOZWdvdGlhdGUgVExTIGVuY3J5cHRpb24uXG4gIHN0YXJ0VGxzKGNyZWRlbnRpYWxzRGV0YWlsczogdGxzLlNlY3VyZUNvbnRleHRPcHRpb25zLCBob3N0bmFtZTogc3RyaW5nLCB0cnVzdFNlcnZlckNlcnRpZmljYXRlOiBib29sZWFuKSB7XG4gICAgaWYgKCFjcmVkZW50aWFsc0RldGFpbHMubWF4VmVyc2lvbiB8fCAhWydUTFN2MS4yJywgJ1RMU3YxLjEnLCAnVExTdjEnXS5pbmNsdWRlcyhjcmVkZW50aWFsc0RldGFpbHMubWF4VmVyc2lvbikpIHtcbiAgICAgIGNyZWRlbnRpYWxzRGV0YWlscy5tYXhWZXJzaW9uID0gJ1RMU3YxLjInO1xuICAgIH1cblxuICAgIGNvbnN0IHNlY3VyZUNvbnRleHQgPSB0bHMuY3JlYXRlU2VjdXJlQ29udGV4dChjcmVkZW50aWFsc0RldGFpbHMpO1xuXG4gICAgcmV0dXJuIG5ldyBQcm9taXNlPHZvaWQ+KChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICAgIGNvbnN0IGR1cGxleHBhaXIgPSBuZXcgRHVwbGV4UGFpcigpO1xuICAgICAgY29uc3Qgc2VjdXJlUGFpciA9IHRoaXMuc2VjdXJlUGFpciA9IHtcbiAgICAgICAgY2xlYXJ0ZXh0OiB0bHMuY29ubmVjdCh7XG4gICAgICAgICAgc29ja2V0OiBkdXBsZXhwYWlyLnNvY2tldDEgYXMgU29ja2V0LFxuICAgICAgICAgIC8vIFRoZSBgaG9zdGAgaXMgdXNlZCB0byB2ZXJpZnkgdGhlIHNlcnZlcidzIGNlcnRpZmljYXRlIGlkZW50aXR5LlxuICAgICAgICAgIC8vIEl0IGlzIG5vdCB1c2VkIHRvIGVzdGFibGlzaCBhIGNvbm5lY3Rpb24gYXMgYSBgc29ja2V0YCBpc1xuICAgICAgICAgIC8vIHNwZWNpZmllZC5cbiAgICAgICAgICBob3N0OiBob3N0bmFtZSxcbiAgICAgICAgICAvLyBSRkMgNjA2NiBkb2VzIG5vdCBhbGxvdyBJUCBhZGRyZXNzZXMgdG8gYmUgdXNlZCBhcyB0aGUgc2VydmVyXG4gICAgICAgICAgLy8gbmFtZSwgc28gb21pdCB0aGUgU05JIGV4dGVuc2lvbiBpbiB0aGF0IGNhc2UuXG4gICAgICAgICAgc2VydmVybmFtZTogaXNJUChob3N0bmFtZSkgPyAnJyA6IGhvc3RuYW1lLFxuICAgICAgICAgIHNlY3VyZUNvbnRleHQ6IHNlY3VyZUNvbnRleHQsXG4gICAgICAgICAgcmVqZWN0VW5hdXRob3JpemVkOiAhdHJ1c3RTZXJ2ZXJDZXJ0aWZpY2F0ZVxuICAgICAgICB9KSxcbiAgICAgICAgZW5jcnlwdGVkOiBkdXBsZXhwYWlyLnNvY2tldDJcbiAgICAgIH07XG5cbiAgICAgIGNvbnN0IG9uU2VjdXJlQ29ubmVjdCA9ICgpID0+IHtcbiAgICAgICAgc2VjdXJlUGFpci5lbmNyeXB0ZWQucmVtb3ZlTGlzdGVuZXIoJ3JlYWRhYmxlJywgb25SZWFkYWJsZSk7XG4gICAgICAgIHNlY3VyZVBhaXIuY2xlYXJ0ZXh0LnJlbW92ZUxpc3RlbmVyKCdlcnJvcicsIG9uRXJyb3IpO1xuICAgICAgICBzZWN1cmVQYWlyLmNsZWFydGV4dC5yZW1vdmVMaXN0ZW5lcignc2VjdXJlQ29ubmVjdCcsIG9uU2VjdXJlQ29ubmVjdCk7XG5cbiAgICAgICAgLy8gSWYgd2UgZW5jb3VudGVyIGFueSBlcnJvcnMgZnJvbSB0aGlzIHBvaW50IG9uLFxuICAgICAgICAvLyB3ZSBqdXN0IGZvcndhcmQgdGhlbSB0byB0aGUgYWN0dWFsIG5ldHdvcmsgc29ja2V0LlxuICAgICAgICBzZWN1cmVQYWlyLmNsZWFydGV4dC5vbmNlKCdlcnJvcicsIChlcnIpID0+IHtcbiAgICAgICAgICB0aGlzLnNvY2tldC5kZXN0cm95KGVycik7XG4gICAgICAgIH0pO1xuXG4gICAgICAgIGNvbnN0IGNpcGhlciA9IHNlY3VyZVBhaXIuY2xlYXJ0ZXh0LmdldENpcGhlcigpO1xuICAgICAgICBpZiAoY2lwaGVyKSB7XG4gICAgICAgICAgdGhpcy5kZWJ1Zy5sb2coJ1RMUyBuZWdvdGlhdGVkICgnICsgY2lwaGVyLm5hbWUgKyAnLCAnICsgY2lwaGVyLnZlcnNpb24gKyAnKScpO1xuICAgICAgICB9XG5cbiAgICAgICAgdGhpcy5lbWl0KCdzZWN1cmUnLCBzZWN1cmVQYWlyLmNsZWFydGV4dCk7XG5cbiAgICAgICAgc2VjdXJlUGFpci5jbGVhcnRleHQuc2V0TWF4U2VuZEZyYWdtZW50KE1hdGgubWluKHRoaXMub3V0Z29pbmdNZXNzYWdlU3RyZWFtLnBhY2tldFNpemUsIE1lc3NhZ2VJTy5NQVhfVExTX1NFTkRfRlJBR01FTlRfU0laRSkpO1xuXG4gICAgICAgIHRoaXMub3V0Z29pbmdNZXNzYWdlU3RyZWFtLnVucGlwZSh0aGlzLnNvY2tldCk7XG4gICAgICAgIHRoaXMuc29ja2V0LnVucGlwZSh0aGlzLmluY29taW5nTWVzc2FnZVN0cmVhbSk7XG5cbiAgICAgICAgdGhpcy5zb2NrZXQucGlwZShzZWN1cmVQYWlyLmVuY3J5cHRlZCk7XG4gICAgICAgIHNlY3VyZVBhaXIuZW5jcnlwdGVkLnBpcGUodGhpcy5zb2NrZXQpO1xuXG4gICAgICAgIHNlY3VyZVBhaXIuY2xlYXJ0ZXh0LnBpcGUodGhpcy5pbmNvbWluZ01lc3NhZ2VTdHJlYW0pO1xuICAgICAgICB0aGlzLm91dGdvaW5nTWVzc2FnZVN0cmVhbS5waXBlKHNlY3VyZVBhaXIuY2xlYXJ0ZXh0KTtcblxuICAgICAgICB0aGlzLnRsc05lZ290aWF0aW9uQ29tcGxldGUgPSB0cnVlO1xuXG4gICAgICAgIHJlc29sdmUoKTtcbiAgICAgIH07XG5cbiAgICAgIGNvbnN0IG9uRXJyb3IgPSAoZXJyPzogRXJyb3IpID0+IHtcbiAgICAgICAgc2VjdXJlUGFpci5lbmNyeXB0ZWQucmVtb3ZlTGlzdGVuZXIoJ3JlYWRhYmxlJywgb25SZWFkYWJsZSk7XG4gICAgICAgIHNlY3VyZVBhaXIuY2xlYXJ0ZXh0LnJlbW92ZUxpc3RlbmVyKCdlcnJvcicsIG9uRXJyb3IpO1xuICAgICAgICBzZWN1cmVQYWlyLmNsZWFydGV4dC5yZW1vdmVMaXN0ZW5lcignc2VjdXJlQ29ubmVjdCcsIG9uU2VjdXJlQ29ubmVjdCk7XG5cbiAgICAgICAgc2VjdXJlUGFpci5jbGVhcnRleHQuZGVzdHJveSgpO1xuICAgICAgICBzZWN1cmVQYWlyLmVuY3J5cHRlZC5kZXN0cm95KCk7XG5cbiAgICAgICAgcmVqZWN0KGVycik7XG4gICAgICB9O1xuXG4gICAgICBjb25zdCBvblJlYWRhYmxlID0gKCkgPT4ge1xuICAgICAgICAvLyBXaGVuIHRoZXJlIGlzIGhhbmRzaGFrZSBkYXRhIG9uIHRoZSBlbmNyeXB0ZWQgc3RyZWFtIG9mIHRoZSBzZWN1cmUgcGFpcixcbiAgICAgICAgLy8gd2Ugd3JhcCBpdCBpbnRvIGEgYFBSRUxPR0lOYCBtZXNzYWdlIGFuZCBzZW5kIGl0IHRvIHRoZSBzZXJ2ZXIuXG4gICAgICAgIC8vXG4gICAgICAgIC8vIEZvciBlYWNoIGBQUkVMT0dJTmAgbWVzc2FnZSB3ZSBzZW50IHdlIGdldCBiYWNrIGV4YWN0bHkgb25lIHJlc3BvbnNlIG1lc3NhZ2VcbiAgICAgICAgLy8gdGhhdCBjb250YWlucyB0aGUgc2VydmVyJ3MgaGFuZHNoYWtlIHJlc3BvbnNlIGRhdGEuXG4gICAgICAgIGNvbnN0IG1lc3NhZ2UgPSBuZXcgTWVzc2FnZSh7IHR5cGU6IFRZUEUuUFJFTE9HSU4sIHJlc2V0Q29ubmVjdGlvbjogZmFsc2UgfSk7XG5cbiAgICAgICAgbGV0IGNodW5rO1xuICAgICAgICB3aGlsZSAoY2h1bmsgPSBzZWN1cmVQYWlyLmVuY3J5cHRlZC5yZWFkKCkpIHtcbiAgICAgICAgICBtZXNzYWdlLndyaXRlKGNodW5rKTtcbiAgICAgICAgfVxuICAgICAgICB0aGlzLm91dGdvaW5nTWVzc2FnZVN0cmVhbS53cml0ZShtZXNzYWdlKTtcbiAgICAgICAgbWVzc2FnZS5lbmQoKTtcblxuICAgICAgICB0aGlzLnJlYWRNZXNzYWdlKCkudGhlbihhc3luYyAocmVzcG9uc2UpID0+IHtcbiAgICAgICAgICAvLyBTZXR1cCByZWFkYWJsZSBoYW5kbGVyIGZvciB0aGUgbmV4dCByb3VuZCBvZiBoYW5kc2hha2luZy5cbiAgICAgICAgICAvLyBJZiB3ZSBlbmNvdW50ZXIgYSBgc2VjdXJlQ29ubmVjdGAgb24gdGhlIGNsZWFydGV4dCBzaWRlXG4gICAgICAgICAgLy8gb2YgdGhlIHNlY3VyZSBwYWlyLCB0aGUgYHJlYWRhYmxlYCBoYW5kbGVyIGlzIGNsZWFyZWRcbiAgICAgICAgICAvLyBhbmQgbm8gZnVydGhlciBoYW5kc2hha2UgaGFuZGxpbmcgd2lsbCBoYXBwZW4uXG4gICAgICAgICAgc2VjdXJlUGFpci5lbmNyeXB0ZWQub25jZSgncmVhZGFibGUnLCBvblJlYWRhYmxlKTtcblxuICAgICAgICAgIGZvciBhd2FpdCAoY29uc3QgZGF0YSBvZiByZXNwb25zZSkge1xuICAgICAgICAgICAgLy8gV2UgZmVlZCB0aGUgc2VydmVyJ3MgaGFuZHNoYWtlIHJlc3BvbnNlIGJhY2sgaW50byB0aGVcbiAgICAgICAgICAgIC8vIGVuY3J5cHRlZCBlbmQgb2YgdGhlIHNlY3VyZSBwYWlyLlxuICAgICAgICAgICAgc2VjdXJlUGFpci5lbmNyeXB0ZWQud3JpdGUoZGF0YSk7XG4gICAgICAgICAgfVxuICAgICAgICB9KS5jYXRjaChvbkVycm9yKTtcbiAgICAgIH07XG5cbiAgICAgIHNlY3VyZVBhaXIuY2xlYXJ0ZXh0Lm9uY2UoJ2Vycm9yJywgb25FcnJvcik7XG4gICAgICBzZWN1cmVQYWlyLmNsZWFydGV4dC5vbmNlKCdzZWN1cmVDb25uZWN0Jywgb25TZWN1cmVDb25uZWN0KTtcbiAgICAgIHNlY3VyZVBhaXIuZW5jcnlwdGVkLm9uY2UoJ3JlYWRhYmxlJywgb25SZWFkYWJsZSk7XG4gICAgfSk7XG4gIH1cblxuICAvLyBUT0RPIGxpc3RlbiBmb3IgJ2RyYWluJyBldmVudCB3aGVuIHNvY2tldC53cml0ZSByZXR1cm5zIGZhbHNlLlxuICAvLyBUT0RPIGltcGxlbWVudCBpbmNvbXBsZXRlIHJlcXVlc3QgY2FuY2VsbGF0aW9uICgyLjIuMS42KVxuICBzZW5kTWVzc2FnZShwYWNrZXRUeXBlOiBudW1iZXIsIGRhdGE/OiBCdWZmZXIsIHJlc2V0Q29ubmVjdGlvbj86IGJvb2xlYW4pIHtcbiAgICBjb25zdCBtZXNzYWdlID0gbmV3IE1lc3NhZ2UoeyB0eXBlOiBwYWNrZXRUeXBlLCByZXNldENvbm5lY3Rpb246IHJlc2V0Q29ubmVjdGlvbiB9KTtcbiAgICBtZXNzYWdlLmVuZChkYXRhKTtcbiAgICB0aGlzLm91dGdvaW5nTWVzc2FnZVN0cmVhbS53cml0ZShtZXNzYWdlKTtcbiAgICByZXR1cm4gbWVzc2FnZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZWFkIHRoZSBuZXh0IGluY29taW5nIG1lc3NhZ2UgZnJvbSB0aGUgc29ja2V0LlxuICAgKi9cbiAgYXN5bmMgcmVhZE1lc3NhZ2UoKTogUHJvbWlzZTxNZXNzYWdlPiB7XG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgdGhpcy5pbmNvbWluZ01lc3NhZ2VJdGVyYXRvci5uZXh0KCk7XG5cbiAgICBpZiAocmVzdWx0LmRvbmUpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcigndW5leHBlY3RlZCBlbmQgb2YgbWVzc2FnZSBzdHJlYW0nKTtcbiAgICB9XG5cbiAgICByZXR1cm4gcmVzdWx0LnZhbHVlO1xuICB9XG59XG5cbmV4cG9ydCBkZWZhdWx0IE1lc3NhZ2VJTztcbm1vZHVsZS5leHBvcnRzID0gTWVzc2FnZUlPO1xuIl0sIm1hcHBpbmdzIjoiOzs7Ozs7QUFBQSxJQUFBQSxpQkFBQSxHQUFBQyxzQkFBQSxDQUFBQyxPQUFBO0FBR0EsSUFBQUMsR0FBQSxHQUFBQyx1QkFBQSxDQUFBRixPQUFBO0FBQ0EsSUFBQUcsSUFBQSxHQUFBSCxPQUFBO0FBQ0EsSUFBQUksT0FBQSxHQUFBSixPQUFBO0FBSUEsSUFBQUssUUFBQSxHQUFBTixzQkFBQSxDQUFBQyxPQUFBO0FBQ0EsSUFBQU0sT0FBQSxHQUFBTixPQUFBO0FBRUEsSUFBQU8sc0JBQUEsR0FBQVIsc0JBQUEsQ0FBQUMsT0FBQTtBQUNBLElBQUFRLHNCQUFBLEdBQUFULHNCQUFBLENBQUFDLE9BQUE7QUFBOEQsU0FBQUUsd0JBQUFPLENBQUEsRUFBQUMsQ0FBQSw2QkFBQUMsT0FBQSxNQUFBQyxDQUFBLE9BQUFELE9BQUEsSUFBQUUsQ0FBQSxPQUFBRixPQUFBLFlBQUFULHVCQUFBLFlBQUFBLENBQUFPLENBQUEsRUFBQUMsQ0FBQSxTQUFBQSxDQUFBLElBQUFELENBQUEsSUFBQUEsQ0FBQSxDQUFBSyxVQUFBLFNBQUFMLENBQUEsTUFBQU0sQ0FBQSxFQUFBQyxDQUFBLEVBQUFDLENBQUEsS0FBQUMsU0FBQSxRQUFBQyxPQUFBLEVBQUFWLENBQUEsaUJBQUFBLENBQUEsdUJBQUFBLENBQUEseUJBQUFBLENBQUEsU0FBQVEsQ0FBQSxNQUFB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LENBQUM7UUFFYixJQUFJLENBQUNDLFdBQVcsQ0FBQyxDQUFDLENBQUNDLElBQUksQ0FBQyxNQUFPQyxRQUFRLElBQUs7VUFDMUM7VUFDQTtVQUNBO1VBQ0E7VUFDQWxELFVBQVUsQ0FBQ3VCLFNBQVMsQ0FBQ00sSUFBSSxDQUFDLFVBQVUsRUFBRUYsVUFBVSxDQUFDO1VBRWpELFdBQVcsTUFBTXdCLElBQUksSUFBSUQsUUFBUSxFQUFFO1lBQ2pDO1lBQ0E7WUFDQWxELFVBQVUsQ0FBQ3VCLFNBQVMsQ0FBQ3VCLEtBQUssQ0FBQ0ssSUFBSSxDQUFDO1VBQ2xDO1FBQ0YsQ0FBQyxDQUFDLENBQUNDLEtBQUssQ0FBQ3hCLE9BQU8sQ0FBQztNQUNuQixDQUFDO01BRUQ1QixVQUFVLENBQUNDLFNBQVMsQ0FBQzRCLElBQUksQ0FBQyxPQUFPLEVBQUVELE9BQU8sQ0FBQztNQUMzQzVCLFVBQVUsQ0FBQ0MsU0FBUyxDQUFDNEIsSUFBSSxDQUFDLGVBQWUsRUFBRUosZUFBZSxDQUFDO01BQzNEekIsVUFBVSxDQUFDdUIsU0FBUyxDQUFDTSxJQUFJLENBQUMsVUFBVSxFQUFFRixVQUFVLENBQUM7SUFDbkQsQ0FBQyxDQUFDO0VBQ0o7O0VBRUE7RUFDQTtFQUNBMEIsV0FBV0EsQ0FBQ0MsVUFBa0IsRUFBRUgsSUFBYSxFQUFFUixlQUF5QixFQUFFO0lBQ3hFLE1BQU1MLE9BQU8sR0FBRyxJQUFJQyxnQkFBTyxDQUFDO01BQUVDLElBQUksRUFBRWMsVUFBVTtNQUFFWCxlQUFlLEVBQUVBO0lBQWdCLENBQUMsQ0FBQztJQUNuRkwsT0FBTyxDQUFDUyxHQUFHLENBQUNJLElBQUksQ0FBQztJQUNqQixJQUFJLENBQUM1RCxxQkFBcUIsQ0FBQ3VELEtBQUssQ0FBQ1IsT0FBTyxDQUFDO0lBQ3pDLE9BQU9BLE9BQU87RUFDaEI7O0VBRUE7QUFDRjtBQUNBO0VBQ0UsTUFBTVUsV0FBV0EsQ0FBQSxFQUFxQjtJQUNwQyxNQUFNTyxNQUFNLEdBQUcsTUFBTSxJQUFJLENBQUNuRSx1QkFBdUIsQ0FBQ29FLElBQUksQ0FBQyxDQUFDO0lBRXhELElBQUlELE1BQU0sQ0FBQ0UsSUFBSSxFQUFFO01BQ2YsTUFBTSxJQUFJQyxLQUFLLENBQUMsa0NBQWtDLENBQUM7SUFDckQ7SUFFQSxPQUFPSCxNQUFNLENBQUNJLEtBQUs7RUFDckI7QUFDRjtBQUFDLElBQUFDLFFBQUEsR0FBQUMsT0FBQSxDQUFBNUYsT0FBQSxHQUVjUyxTQUFTO0FBQ3hCb0YsTUFBTSxDQUFDRCxPQUFPLEdBQUduRixTQUFTIiwiaWdub3JlTGlzdCI6W119