"use strict"; /* * Copyright 2015 Google Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (g && (g = 0, op[0] && (_ = 0)), _) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; var __values = (this && this.__values) || function(o) { var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; if (m) return m.call(o); if (o && typeof o.length === "number") return { next: function () { if (o && i >= o.length) o = void 0; return { value: o && o[i++], done: !o }; } }; throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.run = run; exports.stop = stop; var http = require("http"); var httpProxy = require("http-proxy"); var path = require("path"); var url = require("url"); var jupyter = require("./jupyter"); var logging = require("./logging"); var python_lsp_1 = require("./python_lsp"); var reverseProxy = require("./reverseProxy"); var socketio_to_dap_1 = require("./socketio_to_dap"); var socketio_to_pty_1 = require("./socketio_to_pty"); var sockets = require("./sockets"); var server; /** * The application settings instance. */ var appSettings; var fileshim; /** * Handles all requests. * @param request the incoming HTTP request. * @param response the out-going HTTP response. * @path the parsed path in the request. */ function handleRequest(request, response, requestPath) { return __awaiter(this, void 0, void 0, function () { var host, url_1, projectId; return __generator(this, function (_a) { switch (_a.label) { case 0: // /files and /static are only used in runlocal. if (fileshim && ((requestPath.indexOf('/api/contents') === 0) || (requestPath.indexOf('/files') === 0))) { fileshim.web(request, response, null); return [2 /*return*/]; } // The explicit set of paths we proxy to jupyter. if ((requestPath.indexOf('/api') === 0) || (requestPath.indexOf('/nbextensions') === 0) || (requestPath.indexOf('/files') === 0) || (requestPath.indexOf('/static') === 0)) { jupyter.handleRequest(request, response); return [2 /*return*/]; } if (!(appSettings.colabRedirect && requestPath === '/')) return [3 /*break*/, 3]; host = process.env['WEB_HOST'] || ''; url_1 = appSettings.colabRedirect.replace('{jupyter_host}', host); if (!appSettings.colabRedirect.includes('{project_id}')) return [3 /*break*/, 2]; return [4 /*yield*/, readGceProjectId()]; case 1: projectId = _a.sent(); url_1 = url_1.replace('{project_id}', projectId); _a.label = 2; case 2: response.writeHead(302, { 'Location': url_1, }); response.end(); return [2 /*return*/]; case 3: // Not Found response.statusCode = 404; response.end(); return [2 /*return*/]; } }); }); } /** * Base logic for handling all requests sent to the proxy web server. Some * requests are handled within the server, while some are proxied to the * Jupyter notebook server. * * Error handling is left to the caller. * * @param request the incoming HTTP request. * @param response the out-going HTTP response. */ function uncheckedRequestHandler(request, response) { var e_1, _a; var parsedUrl = url.parse(request.url || '', true); var urlpath = parsedUrl.pathname || ''; logging.logRequest(request, response); try { for (var socketIoHandlers_1 = __values(socketIoHandlers), socketIoHandlers_1_1 = socketIoHandlers_1.next(); !socketIoHandlers_1_1.done; socketIoHandlers_1_1 = socketIoHandlers_1.next()) { var handler = socketIoHandlers_1_1.value; if (handler.isPathProxied(urlpath)) { // Will automatically be handled by socket.io. return; } } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (socketIoHandlers_1_1 && !socketIoHandlers_1_1.done && (_a = socketIoHandlers_1.return)) _a.call(socketIoHandlers_1); } finally { if (e_1) throw e_1.error; } } var proxyPort = reverseProxy.getRequestPort(urlpath); if (sockets.isSocketIoPath(urlpath)) { // Will automatically be handled by socket.io. } else if (proxyPort && proxyPort !== request.socket.localPort) { // Do not allow proxying to this same port, as that can be used to mask the // target path. reverseProxy.handleRequest(request, response, proxyPort); } else { handleRequest(request, response, urlpath); } } /** * Handles all requests sent to the proxy web server. Some requests are handled * within the server, while some are proxied to the Jupyter notebook server. * @param request the incoming HTTP request. * @param response the out-going HTTP response. */ function requestHandler(request, response) { try { uncheckedRequestHandler(request, response); } catch (e) { logging.getLogger().error("Uncaught error handling a request to \"".concat(request.url, "\": ").concat(e)); } } var socketIoHandlers = []; /** * Runs the proxy web server. * @param settings the configuration settings to use. */ function run(settings) { jupyter.init(settings); reverseProxy.init(settings); appSettings = settings; if (settings.fileHandlerAddr) { fileshim = httpProxy.createProxyServer({ target: "http://".concat(appSettings.fileHandlerAddr) }); fileshim.on('error', function (error, request, response) { logging.getLogger().error(error, "fileshim error for ".concat(request.url)); response.writeHead(500, 'Internal Server Error'); response.end(); }); } server = http.createServer(requestHandler); // Disable HTTP keep-alive connection timeouts in order to avoid connection // flakes. Details: b/112151064 server.keepAliveTimeout = 0; var socketIoServer = sockets.init(server, settings); socketIoHandlers.push(new socketio_to_pty_1.SocketIoToPty('/tty', server)); var dapServer; if (settings.debugAdapterMultiplexerPath) { dapServer = new socketio_to_dap_1.DapServer(settings.debugAdapterMultiplexerPath, socketIoServer); } var contentDir = path.join(settings.datalabRoot, settings.contentDir); var logsDir = path.join(settings.datalabRoot, '/var/colab/'); var pipLogsDir = path.join(settings.datalabRoot, '/var/log/'); // Handler manages its own lifetime. // tslint:disable-next-line:no-unused-expression new python_lsp_1.SocketIOToLsp(socketIoServer, __dirname, contentDir, logsDir, pipLogsDir, settings.languageServerProxy, settings.languageServerProxyArgs); server.on('upgrade', function (request, socket, head) { var parsedUrl = url.parse(request.url || '', true); var urlpath = parsedUrl.pathname || ''; var proxyPort = reverseProxy.getRequestPort(urlpath); if (proxyPort && proxyPort !== request.socket.localPort) { reverseProxy.handleUpgrade(request, socket, head, proxyPort); return; } if (request.url === '/colab/tty') { (0, socketio_to_pty_1.WebSocketToPty)(request, socket, head); return; } if (request.url === '/colab/dap') { dapServer === null || dapServer === void 0 ? void 0 : dapServer.handleUpgrade(request, socket, head); return; } if (request.url === '/colab/lsp') { (0, python_lsp_1.WebSocketToLsp)(request, socket, head, __dirname, contentDir, logsDir, pipLogsDir, settings.languageServerProxy, settings.languageServerProxyArgs); return; } jupyter.handleSocket(request, socket, head); }); logging.getLogger().info('Starting server at http://localhost:%d', settings.serverPort); process.on('SIGINT', function () { return process.exit(); }); var options = { port: settings.serverPort, ipv6Only: false, host: settings.serverHost || '' }; if ('TEST_TMPDIR' in process.env) { // Required to avoid "EAFNOSUPPORT: address family not supported" on // IPv6-only environments (notably, even with the host override below). options['ipv6Only'] = true; // ipv6Only alone isn't enough to avoid attempting to bind to 0.0.0.0 (which // fails on IPv6-only environments). Need to specify an IP address because // DNS resolution even of ip6-localhost fails on some such environments. options['host'] = '::1'; } server.listen(options); } /** * Stops the server and associated Jupyter server. */ function stop() { jupyter.close(); } function readGceProjectId() { return __awaiter(this, void 0, Promise, function () { var metadataHost, port, portParts, projectId; return __generator(this, function (_a) { switch (_a.label) { case 0: metadataHost = 'metadata.google.internal'; port = undefined; if (process.env['GCE_METADATA_HOST']) { metadataHost = process.env['GCE_METADATA_HOST']; portParts = metadataHost.match(/(.*):(\d+)/); if (portParts) { metadataHost = portParts[1]; if (metadataHost.startsWith('[')) { metadataHost = metadataHost.substring(1, metadataHost.length - 1); } port = Number(portParts[2]); } } return [4 /*yield*/, new Promise(function (resolve, reject) { http.get({ hostname: metadataHost, port: port, path: '/computeMetadata/v1/project/project-id', headers: { 'Metadata-Flavor': 'Google' } }, function (response) { var data = ''; response.on('data', function (chunk) { data += chunk; }); response.on('end', function () { resolve(data); }); }) .on('error', reject) .end(); })]; case 1: projectId = _a.sent(); return [2 /*return*/, projectId.trim()]; } }); }); } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VydmVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vdGhpcmRfcGFydHkvY29sYWIvc291cmNlcy9zZXJ2ZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7Ozs7Ozs7Ozs7OztHQWNHOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBZ0lILGtCQXNGQztBQUtELG9CQUVDO0FBM05ELDJCQUE2QjtBQUM3QixzQ0FBd0M7QUFFeEMsMkJBQTZCO0FBQzdCLHlCQUEyQjtBQUczQixtQ0FBcUM7QUFDckMsbUNBQXFDO0FBQ3JDLDJDQUEyRDtBQUMzRCw2Q0FBK0M7QUFDL0MscURBQTRDO0FBQzVDLHFEQUFnRTtBQUNoRSxtQ0FBcUM7QUFFckMsSUFBSSxNQUFtQixDQUFDO0FBQ3hCOztHQUVHO0FBQ0gsSUFBSSxXQUF3QixDQUFDO0FBRTdCLElBQUksUUFBK0IsQ0FBQztBQUdwQzs7Ozs7R0FLRztBQUNILFNBQWUsYUFBYSxDQUN4QixPQUE2QixFQUFFLFFBQTZCLEVBQzVELFdBQW1COzs7Ozs7b0JBQ3JCLGdEQUFnRDtvQkFFaEQsSUFBSSxRQUFRO3dCQUNSLENBQUMsQ0FBQyxXQUFXLENBQUMsT0FBTyxDQUFDLGVBQWUsQ0FBQyxLQUFLLENBQUMsQ0FBQzs0QkFDNUMsQ0FBQyxXQUFXLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQzt3QkFDNUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxPQUFPLEVBQUUsUUFBUSxFQUFFLElBQUksQ0FBQyxDQUFDO3dCQUN0QyxzQkFBTztvQkFDVCxDQUFDO29CQUNELGlEQUFpRDtvQkFDakQsSUFBSSxDQUFDLFdBQVcsQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDO3dCQUNuQyxDQUFDLFdBQVcsQ0FBQyxPQUFPLENBQUMsZUFBZSxDQUFDLEtBQUssQ0FB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QUFDLEdBQUcsQ0FDQTtnQ0FDRSxRQUFRLEVBQUUsWUFBWTtnQ0FDdEIsSUFBSSxNQUFBO2dDQUNKLElBQUksRUFBRSx3Q0FBd0M7Z0NBQzlDLE9BQU8sRUFBRSxFQUFDLGlCQUFpQixFQUFFLFFBQVEsRUFBQzs2QkFDdkMsRUFDRCxVQUFDLFFBQVE7Z0NBQ1AsSUFBSSxJQUFJLEdBQUcsRUFBRSxDQUFDO2dDQUNkLFFBQVEsQ0FBQyxFQUFFLENBQUMsTUFBTSxFQUFFLFVBQUMsS0FBSztvQ0FDeEIsSUFBSSxJQUFJLEtBQUssQ0FBQztnQ0FDaEIsQ0FBQyxDQUFDLENBQUM7Z0NBQ0gsUUFBUSxDQUFDLEVBQUUsQ0FBQyxLQUFLLEVBQUU7b0NBQ2pCLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQztnQ0FDaEIsQ0FBQyxDQUFDLENBQUM7NEJBQ0wsQ0FBQyxDQUFDO2lDQUNMLEVBQUUsQ0FBQyxPQUFPLEVBQUUsTUFBTSxDQUFDO2lDQUNuQixHQUFHLEVBQUUsQ0FBQzt3QkFDYixDQUFDLENBQUMsRUFBQTs7b0JBbkJJLFNBQVMsR0FBRyxTQW1CaEI7b0JBQ0Ysc0JBQU8sU0FBUyxDQUFDLElBQUksRUFBRSxFQUFDOzs7O0NBQ3pCIiwic291cmNlc0NvbnRlbnQiOlsiLypcbiAqIENvcHlyaWdodCAyMDE1IEdvb2dsZSBJbmMuIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKlxuICogTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTsgeW91IG1heSBub3RcbiAqIHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS4gWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mXG4gKiB0aGUgTGljZW5zZSBhdFxuICpcbiAqIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuICpcbiAqIFVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbiAqIGRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUywgV0lUSE9VVFxuICogV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLiBTZWUgdGhlXG4gKiBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZCBsaW1pdGF0aW9ucyB1bmRlclxuICogdGhlIExpY2Vuc2UuXG4gKi9cblxuaW1wb3J0ICogYXMgaHR0cCBmcm9tICdodHRwJztcbmltcG9ydCAqIGFzIGh0dHBQcm94eSBmcm9tICdodHRwLXByb3h5JztcbmltcG9ydCAqIGFzIG5ldCBmcm9tICduZXQnO1xuaW1wb3J0ICogYXMgcGF0aCBmcm9tICdwYXRoJztcbmltcG9ydCAqIGFzIHVybCBmcm9tICd1cmwnO1xuXG5pbXBvcnQge0FwcFNldHRpbmdzfSBmcm9tICcuL2FwcFNldHRpbmdzJztcbmltcG9ydCAqIGFzIGp1cHl0ZXIgZnJvbSAnLi9qdXB5dGVyJztcbmltcG9ydCAqIGFzIGxvZ2dpbmcgZnJvbSAnLi9sb2dnaW5nJztcbmltcG9ydCB7U29ja2V0SU9Ub0xzcCwgV2ViU29ja2V0VG9Mc3B9IGZyb20gJy4vcHl0aG9uX2xzcCc7XG5pbXBvcnQgKiBhcyByZXZlcnNlUHJveHkgZnJvbSAnLi9yZXZlcnNlUHJveHknO1xuaW1wb3J0IHtEYXBTZXJ2ZXJ9IGZyb20gJy4vc29ja2V0aW9fdG9fZGFwJztcbmltcG9ydCB7U29ja2V0SW9Ub1B0eSwgV2ViU29ja2V0VG9QdHl9IGZyb20gJy4vc29ja2V0aW9fdG9fcHR5JztcbmltcG9ydCAqIGFzIHNvY2tldHMgZnJvbSAnLi9zb2NrZXRzJztcblxubGV0IHNlcnZlcjogaHR0cC5TZXJ2ZXI7XG4vKipcbiAqIFRoZSBhcHBsaWNhdGlvbiBzZXR0aW5ncyBpbnN0YW5jZS5cbiAqL1xubGV0IGFwcFNldHRpbmdzOiBBcHBTZXR0aW5ncztcblxubGV0IGZpbGVzaGltOiBodHRwUHJveHkuUHJveHlTZXJ2ZXI7XG5cblxuLyoqXG4gKiBIYW5kbGVzIGFsbCByZXF1ZXN0cy5cbiAqIEBwYXJhbSByZXF1ZXN0IHRoZSBpbmNvbWluZyBIVFRQIHJlcXVlc3QuXG4gKiBAcGFyYW0gcmVzcG9uc2UgdGhlIG91dC1nb2luZyBIVFRQIHJlc3BvbnNlLlxuICogQHBhdGggdGhlIHBhcnNlZCBwYXRoIGluIHRoZSByZXF1ZXN0LlxuICovXG5hc3luYyBmdW5jdGlvbiBoYW5kbGVSZXF1ZXN0KFxuICAgIHJlcXVlc3Q6IGh0dHAuSW5jb21pbmdNZXNzYWdlLCByZXNwb25zZTogaHR0cC5TZXJ2ZXJSZXNwb25zZSxcbiAgICByZXF1ZXN0UGF0aDogc3RyaW5nKSB7XG4gIC8vIC9maWxlcyBhbmQgL3N0YXRpYyBhcmUgb25seSB1c2VkIGluIHJ1bmxvY2FsLlxuXG4gIGlmIChmaWxlc2hpbSAmJlxuICAgICAgKChyZXF1ZXN0UGF0aC5pbmRleE9mKCcvYXBpL2NvbnRlbnRzJykgPT09IDApIHx8XG4gICAgICAgKHJlcXVlc3RQYXRoLmluZGV4T2YoJy9maWxlcycpID09PSAwKSkpIHtcbiAgICBmaWxlc2hpbS53ZWIocmVxdWVzdCwgcmVzcG9uc2UsIG51bGwpO1xuICAgIHJldHVybjtcbiAgfVxuICAvLyBUaGUgZXhwbGljaXQgc2V0IG9mIHBhdGhzIHdlIHByb3h5IHRvIGp1cHl0ZXIuXG4gIGlmICgocmVxdWVzdFBhdGguaW5kZXhPZignL2FwaScpID09PSAwKSB8fFxuICAgICAgKHJlcXVlc3RQYXRoLmluZGV4T2YoJy9uYmV4dGVuc2lvbnMnKSA9PT0gMCkgfHxcbiAgICAgIChyZXF1ZXN0UGF0aC5pbmRleE9mKCcvZmlsZXMnKSA9PT0gMCkgfHxcbiAgICAgIChyZXF1ZXN0UGF0aC5pbmRleE9mKCcvc3RhdGljJykgPT09IDApKSB7XG4gICAganVweXRlci5oYW5kbGVSZXF1ZXN0KHJlcXVlc3QsIHJlc3BvbnNlKTtcbiAgICByZXR1cm47XG4gIH1cbiAgaWYgKGFwcFNldHRpbmdzLmNvbGFiUmVkaXJlY3QgJiYgcmVxdWVzdFBhdGggPT09ICcvJykge1xuICAgIGNvbnN0IGhvc3QgPSBwcm9jZXNzLmVudlsnV0VCX0hPU1QnXSB8fCAnJztcbiAgICBsZXQgdXJsID0gYXBwU2V0dGluZ3MuY29sYWJSZWRpcmVjdC5yZXBsYWNlKCd7anVweXRlcl9ob3N0fScsIGhvc3QpO1xuICAgIGlmIChhcHBTZXR0aW5ncy5jb2xhYlJlZGlyZWN0LmluY2x1ZGVzKCd7cHJvamVjdF9pZH0nKSkge1xuICAgICAgY29uc3QgcHJvamVjdElkID0gYXdhaXQgcmVhZEdjZVByb2plY3RJZCgpO1xuICAgICAgdXJsID0gdXJsLnJlcGxhY2UoJ3twcm9qZWN0X2lkfScsIHByb2plY3RJZCk7XG4gICAgfVxuICAgIHJlc3BvbnNlLndyaXRlSGVhZCgzMDIsIHtcbiAgICAgICdMb2NhdGlvbic6IHVybCxcbiAgICB9KTtcbiAgICByZXNwb25zZS5lbmQoKTtcbiAgICByZXR1cm47XG4gIH1cblxuICAvLyBOb3QgRm91bmRcbiAgcmVzcG9uc2Uuc3RhdHVzQ29kZSA9IDQwNDtcbiAgcmVzcG9uc2UuZW5kKCk7XG59XG5cbi8qKlxuICogQmFzZSBsb2dpYyBmb3IgaGFuZGxpbmcgYWxsIHJlcXVlc3RzIHNlbnQgdG8gdGhlIHByb3h5IHdlYiBzZXJ2ZXIuIFNvbWVcbiAqIHJlcXVlc3RzIGFyZSBoYW5kbGVkIHdpdGhpbiB0aGUgc2VydmVyLCB3aGlsZSBzb21lIGFyZSBwcm94aWVkIHRvIHRoZVxuICogSnVweXRlciBub3RlYm9vayBzZXJ2ZXIuXG4gKlxuICogRXJyb3IgaGFuZGxpbmcgaXMgbGVmdCB0byB0aGUgY2FsbGVyLlxuICpcbiAqIEBwYXJhbSByZXF1ZXN0IHRoZSBpbmNvbWluZyBIVFRQIHJlcXVlc3QuXG4gKiBAcGFyYW0gcmVzcG9uc2UgdGhlIG91dC1nb2luZyBIVFRQIHJlc3BvbnNlLlxuICovXG5mdW5jdGlvbiB1bmNoZWNrZWRSZXF1ZXN0SGFuZGxlcihcbiAgICByZXF1ZXN0OiBodHRwLkluY29taW5nTWVzc2FnZSwgcmVzcG9uc2U6IGh0dHAuU2VydmVyUmVzcG9uc2UpIHtcbiAgY29uc3QgcGFyc2VkVXJsID0gdXJsLnBhcnNlKHJlcXVlc3QudXJsIHx8ICcnLCB0cnVlKTtcbiAgY29uc3QgdXJscGF0aCA9IHBhcnNlZFVybC5wYXRobmFtZSB8fCAnJztcblxuICBsb2dnaW5nLmxvZ1JlcXVlc3QocmVxdWVzdCwgcmVzcG9uc2UpO1xuXG4gIGZvciAoY29uc3QgaGFuZGxlciBvZiBzb2NrZXRJb0hhbmRsZXJzKSB7XG4gICAgaWYgKGhhbmRsZXIuaXNQYXRoUHJveGllZCh1cmxwYXRoKSkge1xuICAgICAgLy8gV2lsbCBhdXRvbWF0aWNhbGx5IGJlIGhhbmRsZWQgYnkgc29ja2V0LmlvLlxuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgfVxuXG4gIGNvbnN0IHByb3h5UG9ydCA9IHJldmVyc2VQcm94eS5nZXRSZXF1ZXN0UG9ydCh1cmxwYXRoKTtcbiAgaWYgKHNvY2tldHMuaXNTb2NrZXRJb1BhdGgodXJscGF0aCkpIHtcbiAgICAvLyBXaWxsIGF1dG9tYXRpY2FsbHkgYmUgaGFuZGxlZCBieSBzb2NrZXQuaW8uXG4gIH0gZWxzZSBpZiAocHJveHlQb3J0ICYmIHByb3h5UG9ydCAhPT0gcmVxdWVzdC5zb2NrZXQubG9jYWxQb3J0KSB7XG4gICAgLy8gRG8gbm90IGFsbG93IHByb3h5aW5nIHRvIHRoaXMgc2FtZSBwb3J0LCBhcyB0aGF0IGNhbiBiZSB1c2VkIHRvIG1hc2sgdGhlXG4gICAgLy8gdGFyZ2V0IHBhdGguXG4gICAgcmV2ZXJzZVByb3h5LmhhbmRsZVJlcXVlc3QocmVxdWVzdCwgcmVzcG9uc2UsIHByb3h5UG9ydCk7XG4gIH0gZWxzZSB7XG4gICAgaGFuZGxlUmVxdWVzdChyZXF1ZXN0LCByZXNwb25zZSwgdXJscGF0aCk7XG4gIH1cbn1cblxuLyoqXG4gKiBIYW5kbGVzIGFsbCByZXF1ZXN0cyBzZW50IHRvIHRoZSBwcm94eSB3ZWIgc2VydmVyLiBTb21lIHJlcXVlc3RzIGFyZSBoYW5kbGVkXG4gKiB3aXRoaW4gdGhlIHNlcnZlciwgd2hpbGUgc29tZSBhcmUgcHJveGllZCB0byB0aGUgSnVweXRlciBub3RlYm9vayBzZXJ2ZXIuXG4gKiBAcGFyYW0gcmVxdWVzdCB0aGUgaW5jb21pbmcgSFRUUCByZXF1ZXN0LlxuICogQHBhcmFtIHJlc3BvbnNlIHRoZSBvdXQtZ29pbmcgSFRUUCByZXNwb25zZS5cbiAqL1xuZnVuY3Rpb24gcmVxdWVzdEhhbmRsZXIoXG4gICAgcmVxdWVzdDogaHR0cC5JbmNvbWluZ01lc3NhZ2UsIHJlc3BvbnNlOiBodHRwLlNlcnZlclJlc3BvbnNlKSB7XG4gIHRyeSB7XG4gICAgdW5jaGVja2VkUmVxdWVzdEhhbmRsZXIocmVxdWVzdCwgcmVzcG9uc2UpO1xuICB9IGNhdGNoIChlKSB7XG4gICAgbG9nZ2luZy5nZXRMb2dnZXIoKS5lcnJvcihcbiAgICAgICAgYFVuY2F1Z2h0IGVycm9yIGhhbmRsaW5nIGEgcmVxdWVzdCB0byBcIiR7cmVxdWVzdC51cmx9XCI6ICR7ZX1gKTtcbiAgfVxufVxuXG5jb25zdCBzb2NrZXRJb0hhbmRsZXJzOiBTb2NrZXRJb1RvUHR5W10gPSBbXTtcblxuLyoqXG4gKiBSdW5zIHRoZSBwcm94eSB3ZWIgc2VydmVyLlxuICogQHBhcmFtIHNldHRpbmdzIHRoZSBjb25maWd1cmF0aW9uIHNldHRpbmdzIHRvIHVzZS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJ1bihzZXR0aW5nczogQXBwU2V0dGluZ3MpOiB2b2lkIHtcbiAganVweXRlci5pbml0KHNldHRpbmdzKTtcbiAgcmV2ZXJzZVByb3h5LmluaXQoc2V0dGluZ3MpO1xuICBhcHBTZXR0aW5ncyA9IHNldHRpbmdzO1xuXG4gIGlmIChzZXR0aW5ncy5maWxlSGFuZGxlckFkZHIpIHtcbiAgICBmaWxlc2hpbSA9IGh0dHBQcm94eS5jcmVhdGVQcm94eVNlcnZlcihcbiAgICAgICAge3RhcmdldDogYGh0dHA6Ly8ke2FwcFNldHRpbmdzLmZpbGVIYW5kbGVyQWRkcn1gfSk7XG4gICAgZmlsZXNoaW0ub24oJ2Vycm9yJywgKGVycm9yLCByZXF1ZXN0LCByZXNwb25zZSkgPT4ge1xuICAgICAgbG9nZ2luZy5nZXRMb2dnZXIoKS5lcnJvcihlcnJvciwgYGZpbGVzaGltIGVycm9yIGZvciAke3JlcXVlc3QudXJsfWApO1xuICAgICAgcmVzcG9uc2Uud3JpdGVIZWFkKDUwMCwgJ0ludGVybmFsIFNlcnZlciBFcnJvcicpO1xuICAgICAgcmVzcG9uc2UuZW5kKCk7XG4gICAgfSk7XG4gIH1cblxuICBzZXJ2ZXIgPSBodHRwLmNyZWF0ZVNlcnZlcihyZXF1ZXN0SGFuZGxlcik7XG4gIC8vIERpc2FibGUgSFRUUCBrZWVwLWFsaXZlIGNvbm5lY3Rpb24gdGltZW91dHMgaW4gb3JkZXIgdG8gYXZvaWQgY29ubmVjdGlvblxuICAvLyBmbGFrZXMuIERldGFpbHM6IGIvMTEyMTUxMDY0XG4gIHNlcnZlci5rZWVwQWxpdmVUaW1lb3V0ID0gMDtcblxuICBjb25zdCBzb2NrZXRJb1NlcnZlciA9IHNvY2tldHMuaW5pdChzZXJ2ZXIsIHNldHRpbmdzKTtcblxuICBzb2NrZXRJb0hhbmRsZXJzLnB1c2gobmV3IFNvY2tldElvVG9QdHkoJy90dHknLCBzZXJ2ZXIpKTtcblxuICBsZXQgZGFwU2VydmVyOiBEYXBTZXJ2ZXI7XG4gIGlmIChzZXR0aW5ncy5kZWJ1Z0FkYXB0ZXJNdWx0aXBsZXhlclBhdGgpIHtcbiAgICBkYXBTZXJ2ZXIgPVxuICAgICAgICBuZXcgRGFwU2VydmVyKHNldHRpbmdzLmRlYnVnQWRhcHRlck11bHRpcGxleGVyUGF0aCwgc29ja2V0SW9TZXJ2ZXIpO1xuICB9XG5cbiAgY29uc3QgY29udGVudERpciA9IHBhdGguam9pbihzZXR0aW5ncy5kYXRhbGFiUm9vdCwgc2V0dGluZ3MuY29udGVudERpcik7XG4gIGNvbnN0IGxvZ3NEaXIgPSBwYXRoLmpvaW4oc2V0dGluZ3MuZGF0YWxhYlJvb3QsICcvdmFyL2NvbGFiLycpO1xuICBjb25zdCBwaXBMb2dzRGlyID0gcGF0aC5qb2luKHNldHRpbmdzLmRhdGFsYWJSb290LCAnL3Zhci9sb2cvJyk7XG5cbiAgLy8gSGFuZGxlciBtYW5hZ2VzIGl0cyBvd24gbGlmZXRpbWUuXG4gIC8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZTpuby11bnVzZWQtZXhwcmVzc2lvblxuICBuZXcgU29ja2V0SU9Ub0xzcChcbiAgICAgIHNvY2tldElvU2VydmVyLCBfX2Rpcm5hbWUsIGNvbnRlbnREaXIsIGxvZ3NEaXIsIHBpcExvZ3NEaXIsXG4gICAgICBzZXR0aW5ncy5sYW5ndWFnZVNlcnZlclByb3h5LCBzZXR0aW5ncy5sYW5ndWFnZVNlcnZlclByb3h5QXJncyk7XG5cbiAgc2VydmVyLm9uKFxuICAgICAgJ3VwZ3JhZGUnLFxuICAgICAgKHJlcXVlc3Q6IGh0dHAuSW5jb21pbmdNZXNzYWdlLCBzb2NrZXQ6IG5ldC5Tb2NrZXQsIGhlYWQ6IEJ1ZmZlcikgPT4ge1xuICAgICAgICBjb25zdCBwYXJzZWRVcmwgPSB1cmwucGFyc2UocmVxdWVzdC51cmwgfHwgJycsIHRydWUpO1xuICAgICAgICBjb25zdCB1cmxwYXRoID0gcGFyc2VkVXJsLnBhdGhuYW1lIHx8ICcnO1xuICAgICAgICBjb25zdCBwcm94eVBvcnQgPSByZXZlcnNlUHJveHkuZ2V0UmVxdWVzdFBvcnQodXJscGF0aCk7XG4gICAgICAgIGlmIChwcm94eVBvcnQgJiYgcHJveHlQb3J0ICE9PSByZXF1ZXN0LnNvY2tldC5sb2NhbFBvcnQpIHtcbiAgICAgICAgICByZXZlcnNlUHJveHkuaGFuZGxlVXBncmFkZShyZXF1ZXN0LCBzb2NrZXQsIGhlYWQsIHByb3h5UG9ydCk7XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIGlmIChyZXF1ZXN0LnVybCA9PT0gJy9jb2xhYi90dHknKSB7XG4gICAgICAgICAgV2ViU29ja2V0VG9QdHkocmVxdWVzdCwgc29ja2V0LCBoZWFkKTtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHJlcXVlc3QudXJsID09PSAnL2NvbGFiL2RhcCcpIHtcbiAgICAgICAgICBkYXBTZXJ2ZXI/LmhhbmRsZVVwZ3JhZGUocmVxdWVzdCwgc29ja2V0LCBoZWFkKTtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHJlcXVlc3QudXJsID09PSAnL2NvbGFiL2xzcCcpIHtcbiAgICAgICAgICBXZWJTb2NrZXRUb0xzcChcbiAgICAgICAgICAgICAgcmVxdWVzdCwgc29ja2V0LCBoZWFkLCBfX2Rpcm5hbWUsIGNvbnRlbnREaXIsIGxvZ3NEaXIsIHBpcExvZ3NEaXIsXG4gICAgICAgICAgICAgIHNldHRpbmdzLmxhbmd1YWdlU2VydmVyUHJveHksIHNldHRpbmdzLmxhbmd1YWdlU2VydmVyUHJveHlBcmdzKTtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAganVweXRlci5oYW5kbGVTb2NrZXQocmVxdWVzdCwgc29ja2V0LCBoZWFkKTtcbiAgICAgIH0pO1xuXG5cbiAgbG9nZ2luZy5nZXRMb2dnZXIoKS5pbmZvKFxuICAgICAgJ1N0YXJ0aW5nIHNlcnZlciBhdCBodHRwOi8vbG9jYWxob3N0OiVkJywgc2V0dGluZ3Muc2VydmVyUG9ydCk7XG4gIHByb2Nlc3Mub24oJ1NJR0lOVCcsICgpID0+IHByb2Nlc3MuZXhpdCgpKTtcbiAgY29uc3Qgb3B0aW9ucyA9IHtcbiAgICBwb3J0OiBzZXR0aW5ncy5zZXJ2ZXJQb3J0LFxuICAgIGlwdjZPbmx5OiBmYWxzZSxcbiAgICBob3N0OiBzZXR0aW5ncy5zZXJ2ZXJIb3N0IHx8ICcnXG4gIH07XG4gIGlmICgnVEVTVF9UTVBESVInIGluIHByb2Nlc3MuZW52KSB7XG4gICAgLy8gUmVxdWlyZWQgdG8gYXZvaWQgXCJFQUZOT1NVUFBPUlQ6IGFkZHJlc3MgZmFtaWx5IG5vdCBzdXBwb3J0ZWRcIiBvblxuICAgIC8vIElQdjYtb25seSBlbnZpcm9ubWVudHMgKG5vdGFibHksIGV2ZW4gd2l0aCB0aGUgaG9zdCBvdmVycmlkZSBiZWxvdykuXG4gICAgb3B0aW9uc1snaXB2Nk9ubHknXSA9IHRydWU7XG4gICAgLy8gaXB2Nk9ubHkgYWxvbmUgaXNuJ3QgZW5vdWdoIHRvIGF2b2lkIGF0dGVtcHRpbmcgdG8gYmluZCB0byAwLjAuMC4wICh3aGljaFxuICAgIC8vIGZhaWxzIG9uIElQdjYtb25seSBlbnZpcm9ubWVudHMpLiAgTmVlZCB0byBzcGVjaWZ5IGFuIElQIGFkZHJlc3MgYmVjYXVzZVxuICAgIC8vIEROUyByZXNvbHV0aW9uIGV2ZW4gb2YgaXA2LWxvY2FsaG9zdCBmYWlscyBvbiBzb21lIHN1Y2ggZW52aXJvbm1lbnRzLlxuICAgIG9wdGlvbnNbJ2hvc3QnXSA9ICc6OjEnO1xuICB9XG4gIHNlcnZlci5saXN0ZW4ob3B0aW9ucyk7XG59XG5cbi8qKlxuICogU3RvcHMgdGhlIHNlcnZlciBhbmQgYXNzb2NpYXRlZCBKdXB5dGVyIHNlcnZlci5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHN0b3AoKTogdm9pZCB7XG4gIGp1cHl0ZXIuY2xvc2UoKTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gcmVhZEdjZVByb2plY3RJZCgpOiBQcm9taXNlPHN0cmluZz4ge1xuICBsZXQgbWV0YWRhdGFIb3N0ID0gJ21ldGFkYXRhLmdvb2dsZS5pbnRlcm5hbCc7XG4gIGxldCBwb3J0OiB1bmRlZmluZWR8bnVtYmVyID0gdW5kZWZpbmVkO1xuICBpZiAocHJvY2Vzcy5lbnZbJ0dDRV9NRVRBREFUQV9IT1NUJ10pIHtcbiAgICBtZXRhZGF0YUhvc3QgPSBwcm9jZXNzLmVudlsnR0NFX01FVEFEQVRBX0hPU1QnXTtcbiAgICBjb25zdCBwb3J0UGFydHMgPSBtZXRhZGF0YUhvc3QubWF0Y2goLyguKik6KFxcZCspLyk7XG4gICAgaWYgKHBvcnRQYXJ0cykge1xuICAgICAgbWV0YWRhdGFIb3N0ID0gcG9ydFBhcnRzWzFdO1xuICAgICAgaWYgKG1ldGFkYXRhSG9zdC5zdGFydHNXaXRoKCdbJykpIHtcbiAgICAgICAgbWV0YWRhdGFIb3N0ID0gbWV0YWRhdGFIb3N0LnN1YnN0cmluZygxLCBtZXRhZGF0YUhvc3QubGVuZ3RoIC0gMSk7XG4gICAgICB9XG4gICAgICBwb3J0ID0gTnVtYmVyKHBvcnRQYXJ0c1syXSk7XG4gICAgfVxuICB9XG4gIGNvbnN0IHByb2plY3RJZCA9IGF3YWl0IG5ldyBQcm9taXNlPHN0cmluZz4oKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgIGh0dHAuZ2V0KFxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICBob3N0bmFtZTogbWV0YWRhdGFIb3N0LFxuICAgICAgICAgICAgICBwb3J0LFxuICAgICAgICAgICAgICBwYXRoOiAnL2NvbXB1dGVNZXRhZGF0YS92MS9wcm9qZWN0L3Byb2plY3QtaWQnLFxuICAgICAgICAgICAgICBoZWFkZXJzOiB7J01ldGFkYXRhLUZsYXZvcic6ICdHb29nbGUnfVxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIChyZXNwb25zZSkgPT4ge1xuICAgICAgICAgICAgICBsZXQgZGF0YSA9ICcnO1xuICAgICAgICAgICAgICByZXNwb25zZS5vbignZGF0YScsIChjaHVuaykgPT4ge1xuICAgICAgICAgICAgICAgIGRhdGEgKz0gY2h1bms7XG4gICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICByZXNwb25zZS5vbignZW5kJywgKCkgPT4ge1xuICAgICAgICAgICAgICAgIHJlc29sdmUoZGF0YSk7XG4gICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfSlcbiAgICAgICAgLm9uKCdlcnJvcicsIHJlamVjdClcbiAgICAgICAgLmVuZCgpO1xuICB9KTtcbiAgcmV0dXJuIHByb2plY3RJZC50cmltKCk7XG59XG4iXX0=