@dbx-tools/appkit-lakebox 0.0.0-stage → 0.9.131
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/README.md +104 -2
- package/index.ts +32 -0
- package/lib/index.d.ts +28 -0
- package/lib/index.js +26 -0
- package/lib/src/_package.d.ts +2 -0
- package/lib/src/_package.js +11 -0
- package/lib/src/_resource-registry.d.ts +41 -0
- package/lib/src/_resource-registry.js +128 -0
- package/lib/src/_service-snapshots.d.ts +55 -0
- package/lib/src/_service-snapshots.js +65 -0
- package/lib/src/config.d.ts +40 -0
- package/lib/src/config.js +86 -0
- package/lib/src/desktop.d.ts +6 -0
- package/lib/src/desktop.js +88 -0
- package/lib/src/metrics.d.ts +59 -0
- package/lib/src/metrics.js +151 -0
- package/lib/src/plugin.d.ts +259 -0
- package/lib/src/plugin.js +329 -0
- package/lib/src/routes.d.ts +75 -0
- package/lib/src/routes.js +441 -0
- package/lib/src/runtime.d.ts +154 -0
- package/lib/src/runtime.js +760 -0
- package/lib/src/services.d.ts +101 -0
- package/lib/src/services.js +188 -0
- package/lib/src/skills.d.ts +5 -0
- package/lib/src/skills.js +21 -0
- package/lib/src/user.d.ts +46 -0
- package/lib/src/user.js +76 -0
- package/lib/src/workspace.d.ts +43 -0
- package/lib/src/workspace.js +138 -0
- package/lib/tsconfig.tsbuildinfo +1 -0
- package/package.json +167 -4
- package/scripts/desktop-prerequisites.sh +66 -0
- package/scripts/desktop-session.sh +307 -0
- package/scripts/lakebox-admin.sh +234 -0
- package/scripts/lakebox-service.py +449 -0
- package/skills/html-page/SKILL.md +40 -0
- package/skills/jupyter-lab/SKILL.md +32 -0
- package/skills/lakebox-showcase/SKILL.md +66 -0
- package/skills/network-speed-test/SKILL.md +29 -0
- package/skills/realtime-whiteboard/SKILL.md +28 -0
- package/src/_package.ts +11 -0
- package/src/_resource-registry.ts +160 -0
- package/src/_service-snapshots.ts +84 -0
- package/src/config.ts +128 -0
- package/src/desktop.ts +97 -0
- package/src/metrics.ts +178 -0
- package/src/plugin.ts +446 -0
- package/src/routes.ts +524 -0
- package/src/runtime.ts +952 -0
- package/src/services.ts +239 -0
- package/src/skills.ts +23 -0
- package/src/user.ts +88 -0
- package/src/workspace.ts +185 -0
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
import { createServer } from "node:net";
|
|
2
|
+
import { PassThrough, Readable } from "node:stream";
|
|
3
|
+
import { ACCESS_TOKEN_HEADER, USER_EMAIL_HEADER, USER_ID_HEADER } from "@dbx-tools/appkit";
|
|
4
|
+
import { errorUtils, log } from "@dbx-tools/shared-core";
|
|
5
|
+
import ProxyModule from "http-proxy-3";
|
|
6
|
+
const MAX_PENDING_BYTES = 1024 * 1024;
|
|
7
|
+
const FORWARDING_READY_TIMEOUT_MS = 15_000;
|
|
8
|
+
const logger = log.logger("appkit-lakebox:routes");
|
|
9
|
+
/**
|
|
10
|
+
* Proxies `<prefix>/<user-hash>/<service>` to that user's service port.
|
|
11
|
+
*
|
|
12
|
+
* The user hash is the only routing key. A request is forwarded only when its
|
|
13
|
+
* authenticated identity hashes to the same value, and the port is derived from
|
|
14
|
+
* the hash and service name, so there is no stored route table. Requests
|
|
15
|
+
* without an identity are rejected with 404, including WebSocket upgrades.
|
|
16
|
+
*/
|
|
17
|
+
export class LakeboxRouteRegistry {
|
|
18
|
+
prefix;
|
|
19
|
+
resolver;
|
|
20
|
+
forwardingReadyTimeoutMs;
|
|
21
|
+
bridges = new Map();
|
|
22
|
+
upgrades = new Set();
|
|
23
|
+
pending = new WeakMap();
|
|
24
|
+
proxy = ProxyModule.createProxyServer({ ws: true, xfwd: true });
|
|
25
|
+
shuttingDown = false;
|
|
26
|
+
/** Creates a registry beneath one AppKit route prefix. */
|
|
27
|
+
constructor(prefix, resolver, forwardingReadyTimeoutMs = FORWARDING_READY_TIMEOUT_MS) {
|
|
28
|
+
this.prefix = prefix;
|
|
29
|
+
this.resolver = resolver;
|
|
30
|
+
this.forwardingReadyTimeoutMs = forwardingReadyTimeoutMs;
|
|
31
|
+
this.proxy.on("error", (error, request, response) => {
|
|
32
|
+
if (this.shuttingDown && isConnectionClosureError(error)) {
|
|
33
|
+
response.destroy();
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const pending = this.pending.get(request);
|
|
37
|
+
if (pending && "writeHead" in response && !response.headersSent) {
|
|
38
|
+
this.resolver.unavailable(pending.user, pending.service);
|
|
39
|
+
sendLoadingResponse(request.headers.accept, response);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
logger.warn("Lakebox route proxy failed", {
|
|
43
|
+
method: request.method,
|
|
44
|
+
path: request.url,
|
|
45
|
+
error,
|
|
46
|
+
});
|
|
47
|
+
if ("writeHead" in response) {
|
|
48
|
+
if (response.headersSent) {
|
|
49
|
+
response.destroy();
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
response.writeHead(502);
|
|
53
|
+
response.end("Lakebox route unavailable");
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
else if (!response.destroyed) {
|
|
57
|
+
response.destroy();
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
this.proxy.on("proxyRes", (proxyResponse, request, response) => {
|
|
61
|
+
const pending = this.pending.get(request);
|
|
62
|
+
if (pending && [502, 503, 504].includes(proxyResponse.statusCode ?? 0)) {
|
|
63
|
+
proxyResponse.resume();
|
|
64
|
+
this.resolver.unavailable(pending.user, pending.service);
|
|
65
|
+
sendLoadingResponse(request.headers.accept, response);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
if (pending?.head) {
|
|
69
|
+
void injectHtmlHead(proxyResponse, response, pending.head);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
pipeProxyResponse(proxyResponse, response);
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
/** Returns the route path segment match for a request path. */
|
|
76
|
+
match(path) {
|
|
77
|
+
const root = this.prefix.replace(/\/+$/, "");
|
|
78
|
+
if (!path.startsWith(`${root}/`))
|
|
79
|
+
return undefined;
|
|
80
|
+
const [userHash, service] = path.slice(root.length + 1).split("/");
|
|
81
|
+
if (!userHash || !service)
|
|
82
|
+
return undefined;
|
|
83
|
+
return { userHash, service, mount: `${root}/${userHash}/${service}` };
|
|
84
|
+
}
|
|
85
|
+
/** Express middleware that authorizes, rewrites, and proxies matching requests. */
|
|
86
|
+
middleware() {
|
|
87
|
+
return async (request, response, next) => {
|
|
88
|
+
const match = this.match(request.path);
|
|
89
|
+
if (!match)
|
|
90
|
+
return next();
|
|
91
|
+
const user = this.authorize(request.headers, match);
|
|
92
|
+
if (!user) {
|
|
93
|
+
response.statusCode = 404;
|
|
94
|
+
response.end("Not Found");
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
if (request.path === match.mount) {
|
|
98
|
+
response.statusCode = 308;
|
|
99
|
+
response.setHeader("location", routeRedirectUrl(request.url, match.mount));
|
|
100
|
+
response.end();
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
let resolution;
|
|
104
|
+
try {
|
|
105
|
+
resolution = await this.resolver.resolve(user, match.service);
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
return next(error);
|
|
109
|
+
}
|
|
110
|
+
if (resolution.kind === "missing") {
|
|
111
|
+
response.statusCode = 404;
|
|
112
|
+
response.end("Not Found");
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
if (resolution.kind === "loading") {
|
|
116
|
+
sendLoadingResponse(request.header("accept"), response);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
if (resolution.kind === "failed") {
|
|
120
|
+
response.statusCode = 502;
|
|
121
|
+
response.setHeader("content-type", "text/plain; charset=utf-8");
|
|
122
|
+
response.end(`Lakebox service failed: ${resolution.message}`);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
const bridge = await this.bridge(resolution.session, resolution.route.port);
|
|
126
|
+
const head = request.method === "GET" && request.path === `${match.mount}/`
|
|
127
|
+
? resolution.route.htmlHead
|
|
128
|
+
: undefined;
|
|
129
|
+
if (head)
|
|
130
|
+
request.headers["accept-encoding"] = "identity";
|
|
131
|
+
this.pending.set(request, { user, service: match.service, ...(head ? { head } : {}) });
|
|
132
|
+
const replayBody = replayParsedJsonBody(request);
|
|
133
|
+
if (resolution.route.stripPrefix)
|
|
134
|
+
request.url = outboundPath(request.url, match.mount);
|
|
135
|
+
stripIdentityHeaders(request);
|
|
136
|
+
this.proxy.web(request, response, {
|
|
137
|
+
target: bridge.target,
|
|
138
|
+
changeOrigin: true,
|
|
139
|
+
selfHandleResponse: true,
|
|
140
|
+
...(replayBody ? { buffer: replayBody } : {}),
|
|
141
|
+
});
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
/** Number of open forwarded HTTP and WebSocket connections. */
|
|
145
|
+
activeConnections() {
|
|
146
|
+
let count = this.upgrades.size;
|
|
147
|
+
for (const bridge of this.bridges.values())
|
|
148
|
+
count += bridge.sockets.size;
|
|
149
|
+
return count;
|
|
150
|
+
}
|
|
151
|
+
/** Returns whether an upgrade path belongs to the Lakebox route prefix. */
|
|
152
|
+
canHandleUpgrade(path) {
|
|
153
|
+
return Boolean(this.match(path));
|
|
154
|
+
}
|
|
155
|
+
/** Proxies an authorized WebSocket upgrade. Returns false when it was not handled. */
|
|
156
|
+
async handleUpgrade(request, socket, head) {
|
|
157
|
+
const url = new URL(request.url ?? "/", "http://localhost");
|
|
158
|
+
const match = this.match(url.pathname);
|
|
159
|
+
if (!match)
|
|
160
|
+
return false;
|
|
161
|
+
const user = this.authorize(request.headers, match);
|
|
162
|
+
if (!user)
|
|
163
|
+
return false;
|
|
164
|
+
const resolution = await this.resolver.resolve(user, match.service);
|
|
165
|
+
if (resolution.kind !== "ready")
|
|
166
|
+
return false;
|
|
167
|
+
if (resolution.route.stripPrefix)
|
|
168
|
+
request.url = outboundPath(request.url, match.mount);
|
|
169
|
+
this.upgrades.add(socket);
|
|
170
|
+
socket.once("close", () => this.upgrades.delete(socket));
|
|
171
|
+
proxyWebSocket(request, socket, head, resolution.session, resolution.route.port, this.forwardingReadyTimeoutMs);
|
|
172
|
+
return true;
|
|
173
|
+
}
|
|
174
|
+
/** Closes every forwarding bridge, for example after the Lakebox stops. */
|
|
175
|
+
async closeBridges() {
|
|
176
|
+
this.upgrades.forEach((socket) => socket.destroy());
|
|
177
|
+
this.upgrades.clear();
|
|
178
|
+
const bridges = [...this.bridges.values()];
|
|
179
|
+
this.bridges.clear();
|
|
180
|
+
await Promise.all(bridges.map(closeBridge));
|
|
181
|
+
}
|
|
182
|
+
/** Suppresses expected proxy errors once shutdown begins. */
|
|
183
|
+
beginShutdown() {
|
|
184
|
+
this.shuttingDown = true;
|
|
185
|
+
}
|
|
186
|
+
/** Closes bridges and the shared proxy. */
|
|
187
|
+
async close() {
|
|
188
|
+
this.beginShutdown();
|
|
189
|
+
await this.closeBridges();
|
|
190
|
+
this.proxy.close();
|
|
191
|
+
}
|
|
192
|
+
authorize(headers, match) {
|
|
193
|
+
const user = this.resolver.user(headers);
|
|
194
|
+
return user?.hash === match.userHash ? user : undefined;
|
|
195
|
+
}
|
|
196
|
+
async bridge(session, port) {
|
|
197
|
+
const existing = this.bridges.get(port);
|
|
198
|
+
if (existing?.session === session)
|
|
199
|
+
return existing;
|
|
200
|
+
if (existing) {
|
|
201
|
+
this.bridges.delete(port);
|
|
202
|
+
void closeBridge(existing);
|
|
203
|
+
}
|
|
204
|
+
const sockets = new Set();
|
|
205
|
+
const server = createServer((socket) => {
|
|
206
|
+
sockets.add(socket);
|
|
207
|
+
socket.once("close", () => sockets.delete(socket));
|
|
208
|
+
bridgeSocket(socket, session.forwardOut("127.0.0.1", port), this.forwardingReadyTimeoutMs);
|
|
209
|
+
});
|
|
210
|
+
const target = await listenTarget(server);
|
|
211
|
+
const bridge = { session, server, target, sockets };
|
|
212
|
+
const raced = this.bridges.get(port);
|
|
213
|
+
if (raced?.session === session) {
|
|
214
|
+
void closeBridge(bridge);
|
|
215
|
+
return raced;
|
|
216
|
+
}
|
|
217
|
+
this.bridges.set(port, bridge);
|
|
218
|
+
return bridge;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
async function closeBridge(bridge) {
|
|
222
|
+
bridge.sockets.forEach((socket) => socket.destroy());
|
|
223
|
+
await closeServer(bridge.server);
|
|
224
|
+
}
|
|
225
|
+
function stripIdentityHeaders(request) {
|
|
226
|
+
for (const name of [ACCESS_TOKEN_HEADER, "authorization", "cookie"])
|
|
227
|
+
delete request.headers[name];
|
|
228
|
+
}
|
|
229
|
+
function sendLoadingResponse(accept, response) {
|
|
230
|
+
response.statusCode = 202;
|
|
231
|
+
response.setHeader("cache-control", "no-store");
|
|
232
|
+
response.setHeader("retry-after", "2");
|
|
233
|
+
const acceptsHtml = (Array.isArray(accept) ? accept.join(",") : (accept ?? ""))
|
|
234
|
+
.toLowerCase()
|
|
235
|
+
.includes("text/html");
|
|
236
|
+
if (!acceptsHtml) {
|
|
237
|
+
response.setHeader("content-type", "application/json; charset=utf-8");
|
|
238
|
+
response.end(JSON.stringify({ status: "loading", message: "Starting Lakebox service" }));
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
response.setHeader("content-type", "text/html; charset=utf-8");
|
|
242
|
+
response.end(`<!doctype html>
|
|
243
|
+
<html lang="en">
|
|
244
|
+
<head>
|
|
245
|
+
<meta charset="utf-8">
|
|
246
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
247
|
+
<meta http-equiv="refresh" content="2">
|
|
248
|
+
<title>Starting Lakebox service</title>
|
|
249
|
+
<style>
|
|
250
|
+
html{color-scheme:light dark;font-family:system-ui,sans-serif}body{min-height:100vh;margin:0;display:grid;place-items:center;background:Canvas;color:CanvasText}.loading{display:flex;align-items:center;gap:.75rem}.spinner{width:1.25rem;height:1.25rem;border:.2rem solid color-mix(in srgb,CanvasText 20%,transparent);border-top-color:CanvasText;border-radius:50%;animation:spin .8s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}
|
|
251
|
+
</style>
|
|
252
|
+
</head>
|
|
253
|
+
<body><div class="loading" role="status"><span class="spinner" aria-hidden="true"></span><span>Starting Lakebox service</span></div></body>
|
|
254
|
+
</html>`);
|
|
255
|
+
}
|
|
256
|
+
function isConnectionClosureError(error) {
|
|
257
|
+
const code = error && typeof error === "object" && "code" in error
|
|
258
|
+
? String(error.code)
|
|
259
|
+
: undefined;
|
|
260
|
+
return (code === "ECONNRESET" ||
|
|
261
|
+
code === "EPIPE" ||
|
|
262
|
+
code === "ERR_STREAM_PREMATURE_CLOSE" ||
|
|
263
|
+
code === "ERR_STREAM_DESTROYED");
|
|
264
|
+
}
|
|
265
|
+
function replayParsedJsonBody(request) {
|
|
266
|
+
if (!request.readableEnded || request.body === undefined)
|
|
267
|
+
return undefined;
|
|
268
|
+
if (!request.header("content-type")?.toLowerCase().includes("json"))
|
|
269
|
+
return undefined;
|
|
270
|
+
const body = Buffer.from(JSON.stringify(request.body));
|
|
271
|
+
request.headers["content-length"] = String(body.byteLength);
|
|
272
|
+
delete request.headers["transfer-encoding"];
|
|
273
|
+
return Readable.from(body);
|
|
274
|
+
}
|
|
275
|
+
async function injectHtmlHead(proxyResponse, response, head) {
|
|
276
|
+
try {
|
|
277
|
+
const chunks = [];
|
|
278
|
+
for await (const chunk of proxyResponse) {
|
|
279
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
280
|
+
}
|
|
281
|
+
const source = Buffer.concat(chunks).toString("utf8");
|
|
282
|
+
const body = source.includes("<head>")
|
|
283
|
+
? source.replace("<head>", `<head>${head}`)
|
|
284
|
+
: source.includes("</head>")
|
|
285
|
+
? source.replace("</head>", `${head}</head>`)
|
|
286
|
+
: source;
|
|
287
|
+
for (const [name, value] of Object.entries(proxyResponse.headers)) {
|
|
288
|
+
if (value === undefined || name === "content-length" || name === "transfer-encoding") {
|
|
289
|
+
continue;
|
|
290
|
+
}
|
|
291
|
+
response.setHeader(name, value);
|
|
292
|
+
}
|
|
293
|
+
response.statusCode = proxyResponse.statusCode ?? 200;
|
|
294
|
+
if (proxyResponse.statusMessage)
|
|
295
|
+
response.statusMessage = proxyResponse.statusMessage;
|
|
296
|
+
response.setHeader("content-length", Buffer.byteLength(body));
|
|
297
|
+
response.end(body);
|
|
298
|
+
}
|
|
299
|
+
catch (error) {
|
|
300
|
+
logger.warn("Lakebox route HTML injection failed", {
|
|
301
|
+
error: errorUtils.toError(error),
|
|
302
|
+
});
|
|
303
|
+
if (!response.headersSent)
|
|
304
|
+
response.writeHead(502);
|
|
305
|
+
response.end("Lakebox route unavailable");
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
function pipeProxyResponse(proxyResponse, response) {
|
|
309
|
+
for (const [name, value] of Object.entries(proxyResponse.headers)) {
|
|
310
|
+
if (value !== undefined)
|
|
311
|
+
response.setHeader(name, value);
|
|
312
|
+
}
|
|
313
|
+
response.statusCode = proxyResponse.statusCode ?? 200;
|
|
314
|
+
if (proxyResponse.statusMessage)
|
|
315
|
+
response.statusMessage = proxyResponse.statusMessage;
|
|
316
|
+
proxyResponse.pipe(response);
|
|
317
|
+
}
|
|
318
|
+
async function closeServer(server) {
|
|
319
|
+
await new Promise((resolve) => server.close(() => resolve()));
|
|
320
|
+
}
|
|
321
|
+
function routeRedirectUrl(url, routePath) {
|
|
322
|
+
const parsed = new URL(url, "http://localhost");
|
|
323
|
+
parsed.pathname = `${routePath}/`;
|
|
324
|
+
return `${parsed.pathname}${parsed.search}`;
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Keep the AppKit request URL on the inbound IncomingMessage so AppKit's
|
|
328
|
+
* request logging and tracing see the public route. Prefix
|
|
329
|
+
* stripping is applied only on the outbound proxy request.
|
|
330
|
+
*/
|
|
331
|
+
function outboundPath(requestUrl, stripPrefix) {
|
|
332
|
+
const url = requestUrl && requestUrl.length > 0 ? requestUrl : "/";
|
|
333
|
+
const queryIndex = url.indexOf("?");
|
|
334
|
+
const pathname = queryIndex >= 0 ? url.slice(0, queryIndex) : url;
|
|
335
|
+
const query = queryIndex >= 0 ? url.slice(queryIndex) : "";
|
|
336
|
+
if (!stripPrefix)
|
|
337
|
+
return url.startsWith("/") ? url : `/${url}`;
|
|
338
|
+
return `${pathname.slice(stripPrefix.length) || "/"}${query}`;
|
|
339
|
+
}
|
|
340
|
+
async function listenTarget(server) {
|
|
341
|
+
await new Promise((resolve, reject) => {
|
|
342
|
+
server.once("error", reject);
|
|
343
|
+
server.listen(0, "127.0.0.1", resolve);
|
|
344
|
+
});
|
|
345
|
+
const address = server.address();
|
|
346
|
+
if (!address || typeof address === "string") {
|
|
347
|
+
throw new Error("Failed to bind Lakebox route bridge");
|
|
348
|
+
}
|
|
349
|
+
return { host: "127.0.0.1", port: address.port, protocol: "http:" };
|
|
350
|
+
}
|
|
351
|
+
function bridgeSocket(socket, channelPromise, readyTimeoutMs, initialData) {
|
|
352
|
+
const pending = new PassThrough({ highWaterMark: MAX_PENDING_BYTES });
|
|
353
|
+
let channel;
|
|
354
|
+
let timedOut = false;
|
|
355
|
+
const readyTimeout = setTimeout(() => {
|
|
356
|
+
timedOut = true;
|
|
357
|
+
pending.destroy();
|
|
358
|
+
channel?.destroy();
|
|
359
|
+
if (!socket.destroyed) {
|
|
360
|
+
socket.end("HTTP/1.1 504 Gateway Timeout\r\nConnection: close\r\nContent-Length: 0\r\n\r\n");
|
|
361
|
+
}
|
|
362
|
+
}, readyTimeoutMs);
|
|
363
|
+
readyTimeout.unref?.();
|
|
364
|
+
const clearReadyTimeout = () => clearTimeout(readyTimeout);
|
|
365
|
+
if (initialData)
|
|
366
|
+
pending.write(initialData);
|
|
367
|
+
socket.once("error", () => {
|
|
368
|
+
clearReadyTimeout();
|
|
369
|
+
pending.destroy();
|
|
370
|
+
channel?.destroy();
|
|
371
|
+
});
|
|
372
|
+
socket.pipe(pending);
|
|
373
|
+
socket.once("close", () => {
|
|
374
|
+
clearReadyTimeout();
|
|
375
|
+
pending.destroy();
|
|
376
|
+
channel?.destroy();
|
|
377
|
+
});
|
|
378
|
+
void channelPromise.then((openedChannel) => {
|
|
379
|
+
if (timedOut || socket.destroyed) {
|
|
380
|
+
pending.destroy();
|
|
381
|
+
openedChannel.destroy();
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
channel = openedChannel;
|
|
385
|
+
clearReadyTimeout();
|
|
386
|
+
let channelEnded = false;
|
|
387
|
+
channel.once("end", () => {
|
|
388
|
+
channelEnded = true;
|
|
389
|
+
});
|
|
390
|
+
channel.once("close", () => {
|
|
391
|
+
if (!channelEnded && !socket.destroyed)
|
|
392
|
+
socket.destroy();
|
|
393
|
+
});
|
|
394
|
+
channel.once("error", () => socket.destroy());
|
|
395
|
+
channel.pipe(socket);
|
|
396
|
+
pending.pipe(channel);
|
|
397
|
+
}, (error) => {
|
|
398
|
+
clearReadyTimeout();
|
|
399
|
+
if (!timedOut && !socket.destroyed) {
|
|
400
|
+
logger.warn("Lakebox route forwarding channel failed", {
|
|
401
|
+
error: errorUtils.toError(error),
|
|
402
|
+
});
|
|
403
|
+
socket.end("HTTP/1.1 502 Bad Gateway\r\nConnection: close\r\nContent-Length: 0\r\n\r\n");
|
|
404
|
+
}
|
|
405
|
+
pending.destroy();
|
|
406
|
+
if (timedOut || socket.destroyed)
|
|
407
|
+
socket.destroy();
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
function proxyWebSocket(request, socket, head, session, port, readyTimeoutMs) {
|
|
411
|
+
stripWebsocketIdentityHeaders(request);
|
|
412
|
+
request.headers.origin = `http://127.0.0.1:${port}`;
|
|
413
|
+
request.headers.host = `127.0.0.1:${port}`;
|
|
414
|
+
bridgeSocket(socket, session.forwardOut("127.0.0.1", port), readyTimeoutMs, serializeUpgradeRequest(request, head));
|
|
415
|
+
}
|
|
416
|
+
function stripWebsocketIdentityHeaders(request) {
|
|
417
|
+
for (const name of [
|
|
418
|
+
"authorization",
|
|
419
|
+
"cookie",
|
|
420
|
+
ACCESS_TOKEN_HEADER,
|
|
421
|
+
USER_EMAIL_HEADER,
|
|
422
|
+
USER_ID_HEADER,
|
|
423
|
+
]) {
|
|
424
|
+
delete request.headers[name];
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
function serializeUpgradeRequest(request, head) {
|
|
428
|
+
const lines = [
|
|
429
|
+
`${request.method ?? "GET"} ${request.url ?? "/"} HTTP/${request.httpVersion || "1.1"}`,
|
|
430
|
+
];
|
|
431
|
+
for (const [name, value] of Object.entries(request.headers)) {
|
|
432
|
+
if (value === undefined)
|
|
433
|
+
continue;
|
|
434
|
+
for (const item of Array.isArray(value) ? value : [value]) {
|
|
435
|
+
lines.push(`${name}: ${item}`);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
lines.push("", "");
|
|
439
|
+
return Buffer.concat([Buffer.from(lines.join("\r\n")), head]);
|
|
440
|
+
}
|
|
441
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGVzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3JvdXRlcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFDQSxPQUFPLEVBQUUsWUFBWSxFQUE0QixNQUFNLFVBQVUsQ0FBQztBQUNsRSxPQUFPLEVBQWUsV0FBVyxFQUFFLFFBQVEsRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUNqRSxPQUFPLEVBQUUsbUJBQW1CLEVBQUUsaUJBQWlCLEVBQUUsY0FBYyxFQUFFLE1BQU0sbUJBQW1CLENBQUM7QUFFM0YsT0FBTyxFQUFFLFVBQVUsRUFBRSxHQUFHLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUd6RCxPQUFPLFdBQVcsTUFBTSxjQUFjLENBQUM7QUFHdkMsTUFBTSxpQkFBaUIsR0FBRyxJQUFJLEdBQUcsSUFBSSxDQUFDO0FBQ3RDLE1BQU0sMkJBQTJCLEdBQUcsTUFBTSxDQUFDO0FBQzNDLE1BQU0sTUFBTSxHQUFHLEdBQUcsQ0FBQyxNQUFNLENBQUMsdUJBQXVCLENBQUMsQ0FBQztBQXlDbkQ7Ozs7Ozs7R0FPRztBQUNILE1BQU0sT0FBTyxvQkFBb0I7SUFTWjtJQUNBO0lBQ0E7SUFWRixPQUFPLEdBQUcsSUFBSSxHQUFHLEVBQWtCLENBQUM7SUFDcEMsUUFBUSxHQUFHLElBQUksR0FBRyxFQUFVLENBQUM7SUFDN0IsT0FBTyxHQUFHLElBQUksT0FBTyxFQUFvQyxDQUFDO0lBQzFELEtBQUssR0FBRyxXQUFXLENBQUMsaUJBQWlCLENBQUMsRUFBRSxFQUFFLEVBQUUsSUFBSSxFQUFFLElBQUksRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDO0lBQ3pFLFlBQVksR0FBRyxLQUFLLENBQUM7SUFFN0IsMERBQTBEO0lBQzFELFlBQ21CLE1BQWMsRUFDZCxRQUE4QixFQUM5QiwyQkFBMkIsMkJBQTJCO1FBRnRELFdBQU0sR0FBTixNQUFNLENBQVE7UUFDZCxhQUFRLEdBQVIsUUFBUSxDQUFzQjtRQUM5Qiw2QkFBd0IsR0FBeEIsd0JB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SUFDRixLQUFLLE1BQU0sQ0FBQyxJQUFJLEVBQUUsS0FBSyxDQUFDLElBQUksTUFBTSxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQztRQUM1RCxJQUFJLEtBQUssS0FBSyxTQUFTO1lBQUUsU0FBUztRQUNsQyxLQUFLLE1BQU0sSUFBSSxJQUFJLEtBQUssQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDO1lBQzFELEtBQUssQ0FBQyxJQUFJLENBQUMsR0FBRyxJQUFJLEtBQUssSUFBSSxFQUFFLENBQUMsQ0FBQztRQUNqQyxDQUFDO0lBQ0gsQ0FBQztJQUNELEtBQUssQ0FBQyxJQUFJLENBQUMsRUFBRSxFQUFFLEVBQUUsQ0FBQyxDQUFDO0lBQ25CLE9BQU8sTUFBTSxDQUFDLE1BQU0sQ0FBQyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQyxFQUFFLElBQUksQ0FBQyxDQUFDLENBQUM7QUFDaEUsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB0eXBlIHsgSW5jb21pbmdIdHRwSGVhZGVycywgSW5jb21pbmdNZXNzYWdlLCBTZXJ2ZXJSZXNwb25zZSB9IGZyb20gXCJub2RlOmh0dHBcIjtcbmltcG9ydCB7IGNyZWF0ZVNlcnZlciwgdHlwZSBTZXJ2ZXIsIHR5cGUgU29ja2V0IH0gZnJvbSBcIm5vZGU6bmV0XCI7XG5pbXBvcnQgeyB0eXBlIER1cGxleCwgUGFzc1Rocm91Z2gsIFJlYWRhYmxlIH0gZnJvbSBcIm5vZGU6c3RyZWFtXCI7XG5pbXBvcnQgeyBBQ0NFU1NfVE9LRU5fSEVBREVSLCBVU0VSX0VNQUlMX0hFQURFUiwgVVNFUl9JRF9IRUFERVIgfSBmcm9tIFwiQGRieC10b29scy9hcHBraXRcIjtcbmltcG9ydCB0eXBlIHsgU3NoU2Vzc2lvbiB9IGZyb20gXCJAZGJ4LXRvb2xzL2xha2Vib3gvc2Vzc2lvblwiO1xuaW1wb3J0IHsgZXJyb3JVdGlscywgbG9nIH0gZnJvbSBcIkBkYngtdG9vbHMvc2hhcmVkLWNvcmVcIjtcbmltcG9ydCB0eXBlIHsgTGFrZWJveFVzZXIgfSBmcm9tIFwiQGRieC10b29scy9zaGFyZWQtbGFrZWJveFwiO1xuaW1wb3J0IHR5cGUgeyBOZXh0RnVuY3Rpb24sIFJlcXVlc3QsIFJlc3BvbnNlIH0gZnJvbSBcImV4cHJlc3NcIjtcbmltcG9ydCBQcm94eU1vZHVsZSBmcm9tIFwiaHR0cC1wcm94eS0zXCI7XG5pbXBvcnQgdHlwZSB7IExha2Vib3hTZXJ2aWNlUm91dGUgfSBmcm9tIFwiLi9zZXJ2aWNlcy50c1wiO1xuXG5jb25zdCBNQVhfUEVORElOR19CWVRFUyA9IDEwMjQgKiAxMDI0O1xuY29uc3QgRk9SV0FSRElOR19SRUFEWV9USU1FT1VUX01TID0gMTVfMDAwO1xuY29uc3QgbG9nZ2VyID0gbG9nLmxvZ2dlcihcImFwcGtpdC1sYWtlYm94OnJvdXRlc1wiKTtcblxuLyoqIFJlc3VsdCBvZiByZXNvbHZpbmcgb25lIHVzZXIncyBzZXJ2aWNlIGZvciBhIHJvdXRlZCByZXF1ZXN0LiAqL1xuZXhwb3J0IHR5cGUgTGFrZWJveFJvdXRlUmVzb2x1dGlvbiA9XG4gIHwgeyBraW5kOiBcInJlYWR5XCI7IHNlc3Npb246IFNzaFNlc3Npb247IHJvdXRlOiBMYWtlYm94U2VydmljZVJvdXRlIH1cbiAgfCB7IGtpbmQ6IFwibG9hZGluZ1wiIH1cbiAgfCB7IGtpbmQ6IFwiZmFpbGVkXCI7IG1lc3NhZ2U6IHN0cmluZyB9XG4gIHwgeyBraW5kOiBcIm1pc3NpbmdcIiB9O1xuXG4vKiogUnVudGltZSBjYWxsYmFja3MgdGhhdCBhdXRob3JpemUgYW5kIHJlc29sdmUgZGV0ZXJtaW5pc3RpYyByb3V0ZXMuICovXG5leHBvcnQgaW50ZXJmYWNlIExha2Vib3hSb3V0ZVJlc29sdmVyIHtcbiAgLyoqIFJldHVybnMgdGhlIGF1dGhlbnRpY2F0ZWQgTGFrZWJveCB1c2VyIGZvciByZXF1ZXN0IGhlYWRlcnMsIG9yIGB1bmRlZmluZWRgLiAqL1xuICB1c2VyKGhlYWRlcnM6IEluY29taW5nSHR0cEhlYWRlcnMpOiBMYWtlYm94VXNlciB8IHVuZGVmaW5lZDtcbiAgLyoqXG4gICAqIFJlc29sdmVzIGEgcnVubmluZyBzZXJ2aWNlLiBSZXR1cm5zIGBsb2FkaW5nYCBhZnRlciBzdGFydGluZyB0aGUgTGFrZWJveFxuICAgKiBvciBzZXJ2aWNlIGluIHRoZSBiYWNrZ3JvdW5kLCBhbmQgYG1pc3NpbmdgIGZvciBhbiB1bmtub3duIHNlcnZpY2UuXG4gICAqL1xuICByZXNvbHZlKHVzZXI6IExha2Vib3hVc2VyLCBzZXJ2aWNlOiBzdHJpbmcpOiBQcm9taXNlPExha2Vib3hSb3V0ZVJlc29sdXRpb24+O1xuICAvKiogUmVwb3J0cyB0aGF0IGEgcmVhZHkgc2VydmljZSByZWZ1c2VkIGEgZm9yd2FyZGVkIGNvbm5lY3Rpb24uICovXG4gIHVuYXZhaWxhYmxlKHVzZXI6IExha2Vib3hVc2VyLCBzZXJ2aWNlOiBzdHJpbmcpOiB2b2lkO1xufVxuXG5pbnRlcmZhY2UgUm91dGVNYXRjaCB7XG4gIHVzZXJIYXNoOiBzdHJpbmc7XG4gIHNlcnZpY2U6IHN0cmluZztcbiAgbW91bnQ6IHN0cmluZztcbn1cblxuaW50ZXJmYWNlIEJyaWRnZSB7XG4gIHNlc3Npb246IFNzaFNlc3Npb247XG4gIHNlcnZlcjogU2VydmVyO1xuICB0YXJnZXQ6IHsgaG9zdDogc3RyaW5nOyBwb3J0OiBudW1iZXI7IHByb3RvY29sOiBzdHJpbmcgfTtcbiAgc29ja2V0czogU2V0PFNvY2tldD47XG59XG5cbmludGVyZmFjZSBQZW5kaW5nUmVzcG9uc2Uge1xuICB1c2VyOiBMYWtlYm94VXNlcjtcbiAgc2VydmljZTogc3RyaW5nO1xuICBoZWFkPzogc3RyaW5nO1xufVxuXG4vKipcbiAqIFByb3hpZXMgYDxwcmVmaXg+Lzx1c2VyLWhhc2g+LzxzZXJ2aWNlPmAgdG8gdGhhdCB1c2VyJ3Mgc2VydmljZSBwb3J0LlxuICpcbiAqIFRoZSB1c2VyIGhhc2ggaXMgdGhlIG9ubHkgcm91dGluZyBrZXkuIEEgcmVxdWVzdCBpcyBmb3J3YXJkZWQgb25seSB3aGVuIGl0c1xuICogYXV0aGVudGljYXRlZCBpZGVudGl0eSBoYXNoZXMgdG8gdGhlIHNhbWUgdmFsdWUsIGFuZCB0aGUgcG9ydCBpcyBkZXJpdmVkIGZyb21cbiAqIHRoZSBoYXNoIGFuZCBzZXJ2aWNlIG5hbWUsIHNvIHRoZXJlIGlzIG5vIHN0b3JlZCByb3V0ZSB0YWJsZS4gUmVxdWVzdHNcbiAqIHdpdGhvdXQgYW4gaWRlbnRpdHkgYXJlIHJlamVjdGVkIHdpdGggNDA0LCBpbmNsdWRpbmcgV2ViU29ja2V0IHVwZ3JhZGVzLlxuICovXG5leHBvcnQgY2xhc3MgTGFrZWJveFJvdXRlUmVnaXN0cnkge1xuICBwcml2YXRlIHJlYWRvbmx5IGJyaWRnZXMgPSBuZXcgTWFwPG51bWJlciwgQnJpZGdlPigpO1xuICBwcml2YXRlIHJlYWRvbmx5IHVwZ3JhZGVzID0gbmV3IFNldDxTb2NrZXQ+KCk7XG4gIHByaXZhdGUgcmVhZG9ubHkgcGVuZGluZyA9IG5ldyBXZWFrTWFwPEluY29taW5nTWVzc2FnZSwgUGVuZGluZ1Jlc3BvbnNlPigpO1xuICBwcml2YXRlIHJlYWRvbmx5IHByb3h5ID0gUHJveHlNb2R1bGUuY3JlYXRlUHJveHlTZXJ2ZXIoeyB3czogdHJ1ZSwgeGZ3ZDogdHJ1ZSB9KTtcbiAgcHJpdmF0ZSBzaHV0dGluZ0Rvd24gPSBmYWxzZTtcblxuICAvKiogQ3JlYXRlcyBhIHJlZ2lzdHJ5IGJlbmVhdGggb25lIEFwcEtpdCByb3V0ZSBwcmVmaXguICovXG4gIGNvbnN0cnVjdG9yKFxuICAgIHByaXZhdGUgcmVhZG9ubHkgcHJlZml4OiBzdHJpbmcsXG4gICAgcHJpdmF0ZSByZWFkb25seSByZXNvbHZlcjogTGFrZWJveFJvdXRlUmVzb2x2ZXIsXG4gICAgcHJpdmF0ZSByZWFkb25seSBmb3J3YXJkaW5nUmVhZHlUaW1lb3V0TXMgPSBGT1JXQVJESU5HX1JFQURZX1RJTUVPVVRfTVMsXG4gICkge1xuICAgIHRoaXMucHJveHkub24oXCJlcnJvclwiLCAoZXJyb3IsIHJlcXVlc3QsIHJlc3BvbnNlKSA9PiB7XG4gICAgICBpZiAodGhpcy5zaHV0dGluZ0Rvd24gJiYgaXNDb25uZWN0aW9uQ2xvc3VyZUVycm9yKGVycm9yKSkge1xuICAgICAgICByZXNwb25zZS5kZXN0cm95KCk7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIGNvbnN0IHBlbmRpbmcgPSB0aGlzLnBlbmRpbmcuZ2V0KHJlcXVlc3QpO1xuICAgICAgaWYgKHBlbmRpbmcgJiYgXCJ3cml0ZUhlYWRcIiBpbiByZXNwb25zZSAmJiAhcmVzcG9uc2UuaGVhZGVyc1NlbnQpIHtcbiAgICAgICAgdGhpcy5yZXNvbHZlci51bmF2YWlsYWJsZShwZW5kaW5nLnVzZXIsIHBlbmRpbmcuc2VydmljZSk7XG4gICAgICAgIHNlbmRMb2FkaW5nUmVzcG9uc2UocmVxdWVzdC5oZWFkZXJzLmFjY2VwdCwgcmVzcG9uc2UpO1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICBsb2dnZXIud2FybihcIkxha2Vib3ggcm91dGUgcHJveHkgZmFpbGVkXCIsIHtcbiAgICAgICAgbWV0aG9kOiByZXF1ZXN0Lm1ldGhvZCxcbiAgICAgICAgcGF0aDogcmVxdWVzdC51cmwsXG4gICAgICAgIGVycm9yLFxuICAgICAgfSk7XG4gICAgICBpZiAoXCJ3cml0ZUhlYWRcIiBpbiByZXNwb25zZSkge1xuICAgICAgICBpZiAocmVzcG9uc2UuaGVhZGVyc1NlbnQpIHtcbiAgICAgICAgICByZXNwb25zZS5kZXN0cm95KCk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgcmVzcG9uc2Uud3JpdGVIZWFkKDUwMik7XG4gICAgICAgICAgcmVzcG9uc2UuZW5kKFwiTGFrZWJveCByb3V0ZSB1bmF2YWlsYWJsZVwiKTtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIGlmICghcmVzcG9uc2UuZGVzdHJveWVkKSB7XG4gICAgICAgIHJlc3BvbnNlLmRlc3Ryb3koKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgICB0aGlzLnByb3h5Lm9uKFwicHJveHlSZXNcIiwgKHByb3h5UmVzcG9uc2UsIHJlcXVlc3QsIHJlc3BvbnNlKSA9PiB7XG4gICAgICBjb25zdCBwZW5kaW5nID0gdGhpcy5wZW5kaW5nLmdldChyZXF1ZXN0KTtcbiAgICAgIGlmIChwZW5kaW5nICYmIFs1MDIsIDUwMywgNTA0XS5pbmNsdWRlcyhwcm94eVJlc3BvbnNlLnN0YXR1c0NvZGUgPz8gMCkpIHtcbiAgICAgICAgcHJveHlSZXNwb25zZS5yZXN1bWUoKTtcbiAgICAgICAgdGhpcy5yZXNvbHZlci51bmF2YWlsYWJsZShwZW5kaW5nLnVzZXIsIHBlbmRpbmcuc2VydmljZSk7XG4gICAgICAgIHNlbmRMb2FkaW5nUmVzcG9uc2UocmVxdWVzdC5oZWFkZXJzLmFjY2VwdCwgcmVzcG9uc2UpO1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICBpZiAocGVuZGluZz8uaGVhZCkge1xuICAgICAgICB2b2lkIGluamVjdEh0bWxIZWFkKHByb3h5UmVzcG9uc2UsIHJlc3BvbnNlLCBwZW5kaW5nLmhlYWQpO1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICBwaXBlUHJveHlSZXNwb25zZShwcm94eVJlc3BvbnNlLCByZXNwb25zZSk7XG4gICAgfSk7XG4gIH1cblxuICAvKiogUmV0dXJucyB0aGUgcm91dGUgcGF0aCBzZWdtZW50IG1hdGNoIGZvciBhIHJlcXVlc3QgcGF0aC4gKi9cbiAgbWF0Y2gocGF0aDogc3RyaW5nKTogUm91dGVNYXRjaCB8IHVuZGVmaW5lZCB7XG4gICAgY29uc3Qgcm9vdCA9IHRoaXMucHJlZml4LnJlcGxhY2UoL1xcLyskLywgXCJcIik7XG4gICAgaWYgKCFwYXRoLnN0YXJ0c1dpdGgoYCR7cm9vdH0vYCkpIHJldHVybiB1bmRlZmluZWQ7XG4gICAgY29uc3QgW3VzZXJIYXNoLCBzZXJ2aWNlXSA9IHBhdGguc2xpY2Uocm9vdC5sZW5ndGggKyAxKS5zcGxpdChcIi9cIik7XG4gICAgaWYgKCF1c2VySGFzaCB8fCAhc2VydmljZSkgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICByZXR1cm4geyB1c2VySGFzaCwgc2VydmljZSwgbW91bnQ6IGAke3Jvb3R9LyR7dXNlckhhc2h9LyR7c2VydmljZX1gIH07XG4gIH1cblxuICAvKiogRXhwcmVzcyBtaWRkbGV3YXJlIHRoYXQgYXV0aG9yaXplcywgcmV3cml0ZXMsIGFuZCBwcm94aWVzIG1hdGNoaW5nIHJlcXVlc3RzLiAqL1xuICBtaWRkbGV3YXJlKCkge1xuICAgIHJldHVybiBhc3luYyAocmVxdWVzdDogUmVxdWVzdCwgcmVzcG9uc2U6IFJlc3BvbnNlLCBuZXh0OiBOZXh0RnVuY3Rpb24pID0+IHtcbiAgICAgIGNvbnN0IG1hdGNoID0gdGhpcy5tYXRjaChyZXF1ZXN0LnBhdGgpO1xuICAgICAgaWYgKCFtYXRjaCkgcmV0dXJuIG5leHQoKTtcbiAgICAgIGNvbnN0IHVzZXIgPSB0aGlzLmF1dGhvcml6ZShyZXF1ZXN0LmhlYWRlcnMsIG1hdGNoKTtcbiAgICAgIGlmICghdXNlcikge1xuICAgICAgICByZXNwb25zZS5zdGF0dXNDb2RlID0gNDA0O1xuICAgICAgICByZXNwb25zZS5lbmQoXCJOb3QgRm91bmRcIik7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIGlmIChyZXF1ZXN0LnBhdGggPT09IG1hdGNoLm1vdW50KSB7XG4gICAgICAgIHJlc3BvbnNlLnN0YXR1c0NvZGUgPSAzMDg7XG4gICAgICAgIHJlc3BvbnNlLnNldEhlYWRlcihcImxvY2F0aW9uXCIsIHJvdXRlUmVkaXJlY3RVcmwocmVxdWVzdC51cmwsIG1hdGNoLm1vdW50KSk7XG4gICAgICAgIHJlc3BvbnNlLmVuZCgpO1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICBsZXQgcmVzb2x1dGlvbjogTGFrZWJveFJvdXRlUmVzb2x1dGlvbjtcbiAgICAgIHRyeSB7XG4gICAgICAgIHJlc29sdXRpb24gPSBhd2FpdCB0aGlzLnJlc29sdmVyLnJlc29sdmUodXNlciwgbWF0Y2guc2VydmljZSk7XG4gICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICByZXR1cm4gbmV4dChlcnJvcik7XG4gICAgICB9XG4gICAgICBpZiAocmVzb2x1dGlvbi5raW5kID09PSBcIm1pc3NpbmdcIikge1xuICAgICAgICByZXNwb25zZS5zdGF0dXNDb2RlID0gNDA0O1xuICAgICAgICByZXNwb25zZS5lbmQoXCJOb3QgRm91bmRcIik7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIGlmIChyZXNvbHV0aW9uLmtpbmQgPT09IFwibG9hZGluZ1wiKSB7XG4gICAgICAgIHNlbmRMb2FkaW5nUmVzcG9uc2UocmVxdWVzdC5oZWFkZXIoXCJhY2NlcHRcIiksIHJlc3BvbnNlKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgaWYgKHJlc29sdXRpb24ua2luZCA9PT0gXCJmYWlsZWRcIikge1xuICAgICAgICByZXNwb25zZS5zdGF0dXNDb2RlID0gNTAyO1xuICAgICAgICByZXNwb25zZS5zZXRIZWFkZXIoXCJjb250ZW50LXR5cGVcIiwgXCJ0ZXh0L3BsYWluOyBjaGFyc2V0PXV0Zi04XCIpO1xuICAgICAgICByZXNwb25zZS5lbmQoYExha2Vib3ggc2VydmljZSBmYWlsZWQ6ICR7cmVzb2x1dGlvbi5tZXNzYWdlfWApO1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICBjb25zdCBicmlkZ2UgPSBhd2FpdCB0aGlzLmJyaWRnZShyZXNvbHV0aW9uLnNlc3Npb24sIHJlc29sdXRpb24ucm91dGUucG9ydCk7XG4gICAgICBjb25zdCBoZWFkID1cbiAgICAgICAgcmVxdWVzdC5tZXRob2QgPT09IFwiR0VUXCIgJiYgcmVxdWVzdC5wYXRoID09PSBgJHttYXRjaC5tb3VudH0vYFxuICAgICAgICAgID8gcmVzb2x1dGlvbi5yb3V0ZS5odG1sSGVhZFxuICAgICAgICAgIDogdW5kZWZpbmVkO1xuICAgICAgaWYgKGhlYWQpIHJlcXVlc3QuaGVhZGVyc1tcImFjY2VwdC1lbmNvZGluZ1wiXSA9IFwiaWRlbnRpdHlcIjtcbiAgICAgIHRoaXMucGVuZGluZy5zZXQocmVxdWVzdCwgeyB1c2VyLCBzZXJ2aWNlOiBtYXRjaC5zZXJ2aWNlLCAuLi4oaGVhZCA/IHsgaGVhZCB9IDoge30pIH0pO1xuICAgICAgY29uc3QgcmVwbGF5Qm9keSA9IHJlcGxheVBhcnNlZEpzb25Cb2R5KHJlcXVlc3QpO1xuICAgICAgaWYgKHJlc29sdXRpb24ucm91dGUuc3RyaXBQcmVmaXgpIHJlcXVlc3QudXJsID0gb3V0Ym91bmRQYXRoKHJlcXVlc3QudXJsLCBtYXRjaC5tb3VudCk7XG4gICAgICBzdHJpcElkZW50aXR5SGVhZGVycyhyZXF1ZXN0KTtcbiAgICAgIHRoaXMucHJveHkud2ViKHJlcXVlc3QsIHJlc3BvbnNlLCB7XG4gICAgICAgIHRhcmdldDogYnJpZGdlLnRhcmdldCxcbiAgICAgICAgY2hhbmdlT3JpZ2luOiB0cnVlLFxuICAgICAgICBzZWxmSGFuZGxlUmVzcG9uc2U6IHRydWUsXG4gICAgICAgIC4uLihyZXBsYXlCb2R5ID8geyBidWZmZXI6IHJlcGxheUJvZHkgfSA6IHt9KSxcbiAgICAgIH0pO1xuICAgIH07XG4gIH1cblxuICAvKiogTnVtYmVyIG9mIG9wZW4gZm9yd2FyZGVkIEhUVFAgYW5kIFdlYlNvY2tldCBjb25uZWN0aW9ucy4gKi9cbiAgYWN0aXZlQ29ubmVjdGlvbnMoKTogbnVtYmVyIHtcbiAgICBsZXQgY291bnQgPSB0aGlzLnVwZ3JhZGVzLnNpemU7XG4gICAgZm9yIChjb25zdCBicmlkZ2Ugb2YgdGhpcy5icmlkZ2VzLnZhbHVlcygpKSBjb3VudCArPSBicmlkZ2Uuc29ja2V0cy5zaXplO1xuICAgIHJldHVybiBjb3VudDtcbiAgfVxuXG4gIC8qKiBSZXR1cm5zIHdoZXRoZXIgYW4gdXBncmFkZSBwYXRoIGJlbG9uZ3MgdG8gdGhlIExha2Vib3ggcm91dGUgcHJlZml4LiAqL1xuICBjYW5IYW5kbGVVcGdyYWRlKHBhdGg6IHN0cmluZyk6IGJvb2xlYW4ge1xuICAgIHJldHVybiBCb29sZWFuKHRoaXMubWF0Y2gocGF0aCkpO1xuICB9XG5cbiAgLyoqIFByb3hpZXMgYW4gYXV0aG9yaXplZCBXZWJTb2NrZXQgdXBncmFkZS4gUmV0dXJucyBmYWxzZSB3aGVuIGl0IHdhcyBub3QgaGFuZGxlZC4gKi9cbiAgYXN5bmMgaGFuZGxlVXBncmFkZShyZXF1ZXN0OiBJbmNvbWluZ01lc3NhZ2UsIHNvY2tldDogU29ja2V0LCBoZWFkOiBCdWZmZXIpOiBQcm9taXNlPGJvb2xlYW4+IHtcbiAgICBjb25zdCB1cmwgPSBuZXcgVVJMKHJlcXVlc3QudXJsID8/IFwiL1wiLCBcImh0dHA6Ly9sb2NhbGhvc3RcIik7XG4gICAgY29uc3QgbWF0Y2ggPSB0aGlzLm1hdGNoKHVybC5wYXRobmFtZSk7XG4gICAgaWYgKCFtYXRjaCkgcmV0dXJuIGZhbHNlO1xuICAgIGNvbnN0IHVzZXIgPSB0aGlzLmF1dGhvcml6ZShyZXF1ZXN0LmhlYWRlcnMsIG1hdGNoKTtcbiAgICBpZiAoIXVzZXIpIHJldHVybiBmYWxzZTtcbiAgICBjb25zdCByZXNvbHV0aW9uID0gYXdhaXQgdGhpcy5yZXNvbHZlci5yZXNvbHZlKHVzZXIsIG1hdGNoLnNlcnZpY2UpO1xuICAgIGlmIChyZXNvbHV0aW9uLmtpbmQgIT09IFwicmVhZHlcIikgcmV0dXJuIGZhbHNlO1xuICAgIGlmIChyZXNvbHV0aW9uLnJvdXRlLnN0cmlwUHJlZml4KSByZXF1ZXN0LnVybCA9IG91dGJvdW5kUGF0aChyZXF1ZXN0LnVybCwgbWF0Y2gubW91bnQpO1xuICAgIHRoaXMudXBncmFkZXMuYWRkKHNvY2tldCk7XG4gICAgc29ja2V0Lm9uY2UoXCJjbG9zZVwiLCAoKSA9PiB0aGlzLnVwZ3JhZGVzLmRlbGV0ZShzb2NrZXQpKTtcbiAgICBwcm94eVdlYlNvY2tldChcbiAgICAgIHJlcXVlc3QsXG4gICAgICBzb2NrZXQsXG4gICAgICBoZWFkLFxuICAgICAgcmVzb2x1dGlvbi5zZXNzaW9uLFxuICAgICAgcmVzb2x1dGlvbi5yb3V0ZS5wb3J0LFxuICAgICAgdGhpcy5mb3J3YXJkaW5nUmVhZHlUaW1lb3V0TXMsXG4gICAgKTtcbiAgICByZXR1cm4gdHJ1ZTtcbiAgfVxuXG4gIC8qKiBDbG9zZXMgZXZlcnkgZm9yd2FyZGluZyBicmlkZ2UsIGZvciBleGFtcGxlIGFmdGVyIHRoZSBMYWtlYm94IHN0b3BzLiAqL1xuICBhc3luYyBjbG9zZUJyaWRnZXMoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgdGhpcy51cGdyYWRlcy5mb3JFYWNoKChzb2NrZXQpID0+IHNvY2tldC5kZXN0cm95KCkpO1xuICAgIHRoaXMudXBncmFkZXMuY2xlYXIoKTtcbiAgICBjb25zdCBicmlkZ2VzID0gWy4uLnRoaXMuYnJpZGdlcy52YWx1ZXMoKV07XG4gICAgdGhpcy5icmlkZ2VzLmNsZWFyKCk7XG4gICAgYXdhaXQgUHJvbWlzZS5hbGwoYnJpZGdlcy5tYXAoY2xvc2VCcmlkZ2UpKTtcbiAgfVxuXG4gIC8qKiBTdXBwcmVzc2VzIGV4cGVjdGVkIHByb3h5IGVycm9ycyBvbmNlIHNodXRkb3duIGJlZ2lucy4gKi9cbiAgYmVnaW5TaHV0ZG93bigpOiB2b2lkIHtcbiAgICB0aGlzLnNodXR0aW5nRG93biA9IHRydWU7XG4gIH1cblxuICAvKiogQ2xvc2VzIGJyaWRnZXMgYW5kIHRoZSBzaGFyZWQgcHJveHkuICovXG4gIGFzeW5jIGNsb3NlKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIHRoaXMuYmVnaW5TaHV0ZG93bigpO1xuICAgIGF3YWl0IHRoaXMuY2xvc2VCcmlkZ2VzKCk7XG4gICAgdGhpcy5wcm94eS5jbG9zZSgpO1xuICB9XG5cbiAgcHJpdmF0ZSBhdXRob3JpemUoaGVhZGVyczogSW5jb21pbmdIdHRwSGVhZGVycywgbWF0Y2g6IFJvdXRlTWF0Y2gpOiBMYWtlYm94VXNlciB8IHVuZGVmaW5lZCB7XG4gICAgY29uc3QgdXNlciA9IHRoaXMucmVzb2x2ZXIudXNlcihoZWFkZXJzKTtcbiAgICByZXR1cm4gdXNlcj8uaGFzaCA9PT0gbWF0Y2gudXNlckhhc2ggPyB1c2VyIDogdW5kZWZpbmVkO1xuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyBicmlkZ2Uoc2Vzc2lvbjogU3NoU2Vzc2lvbiwgcG9ydDogbnVtYmVyKTogUHJvbWlzZTxCcmlkZ2U+IHtcbiAgICBjb25zdCBleGlzdGluZyA9IHRoaXMuYnJpZGdlcy5nZXQocG9ydCk7XG4gICAgaWYgKGV4aXN0aW5nPy5zZXNzaW9uID09PSBzZXNzaW9uKSByZXR1cm4gZXhpc3Rpbmc7XG4gICAgaWYgKGV4aXN0aW5nKSB7XG4gICAgICB0aGlzLmJyaWRnZXMuZGVsZXRlKHBvcnQpO1xuICAgICAgdm9pZCBjbG9zZUJyaWRnZShleGlzdGluZyk7XG4gICAgfVxuICAgIGNvbnN0IHNvY2tldHMgPSBuZXcgU2V0PFNvY2tldD4oKTtcbiAgICBjb25zdCBzZXJ2ZXIgPSBjcmVhdGVTZXJ2ZXIoKHNvY2tldCkgPT4ge1xuICAgICAgc29ja2V0cy5hZGQoc29ja2V0KTtcbiAgICAgIHNvY2tldC5vbmNlKFwiY2xvc2VcIiwgKCkgPT4gc29ja2V0cy5kZWxldGUoc29ja2V0KSk7XG4gICAgICBicmlkZ2VTb2NrZXQoc29ja2V0LCBzZXNzaW9uLmZvcndhcmRPdXQoXCIxMjcuMC4wLjFcIiwgcG9ydCksIHRoaXMuZm9yd2FyZGluZ1JlYWR5VGltZW91dE1zKTtcbiAgICB9KTtcbiAgICBjb25zdCB0YXJnZXQgPSBhd2FpdCBsaXN0ZW5UYXJnZXQoc2VydmVyKTtcbiAgICBjb25zdCBicmlkZ2UgPSB7IHNlc3Npb24sIHNlcnZlciwgdGFyZ2V0LCBzb2NrZXRzIH07XG4gICAgY29uc3QgcmFjZWQgPSB0aGlzLmJyaWRnZXMuZ2V0KHBvcnQpO1xuICAgIGlmIChyYWNlZD8uc2Vzc2lvbiA9PT0gc2Vzc2lvbikge1xuICAgICAgdm9pZCBjbG9zZUJyaWRnZShicmlkZ2UpO1xuICAgICAgcmV0dXJuIHJhY2VkO1xuICAgIH1cbiAgICB0aGlzLmJyaWRnZXMuc2V0KHBvcnQsIGJyaWRnZSk7XG4gICAgcmV0dXJuIGJyaWRnZTtcbiAgfVxufVxuXG5hc3luYyBmdW5jdGlvbiBjbG9zZUJyaWRnZShicmlkZ2U6IEJyaWRnZSk6IFByb21pc2U8dm9pZD4ge1xuICBicmlkZ2Uuc29ja2V0cy5mb3JFYWNoKChzb2NrZXQpID0+IHNvY2tldC5kZXN0cm95KCkpO1xuICBhd2FpdCBjbG9zZVNlcnZlcihicmlkZ2Uuc2VydmVyKTtcbn1cblxuZnVuY3Rpb24gc3RyaXBJZGVudGl0eUhlYWRlcnMocmVxdWVzdDogSW5jb21pbmdNZXNzYWdlKTogdm9pZCB7XG4gIGZvciAoY29uc3QgbmFtZSBvZiBbQUNDRVNTX1RPS0VOX0hFQURFUiwgXCJhdXRob3JpemF0aW9uXCIsIFwiY29va2llXCJdKSBkZWxldGUgcmVxdWVzdC5oZWFkZXJzW25hbWVdO1xufVxuXG5mdW5jdGlvbiBzZW5kTG9hZGluZ1Jlc3BvbnNlKFxuICBhY2NlcHQ6IHN0cmluZyB8IHN0cmluZ1tdIHwgdW5kZWZpbmVkLFxuICByZXNwb25zZTogUGljazxTZXJ2ZXJSZXNwb25zZSwgXCJzdGF0dXNDb2RlXCIgfCBcInNldEhlYWRlclwiIHwgXCJlbmRcIj4sXG4pOiB2b2lkIHtcbiAgcmVzcG9uc2Uuc3RhdHVzQ29kZSA9IDIwMjtcbiAgcmVzcG9uc2Uuc2V0SGVhZGVyKFwiY2FjaGUtY29udHJvbFwiLCBcIm5vLXN0b3JlXCIpO1xuICByZXNwb25zZS5zZXRIZWFkZXIoXCJyZXRyeS1hZnRlclwiLCBcIjJcIik7XG4gIGNvbnN0IGFjY2VwdHNIdG1sID0gKEFycmF5LmlzQXJyYXkoYWNjZXB0KSA/IGFjY2VwdC5qb2luKFwiLFwiKSA6IChhY2NlcHQgPz8gXCJcIikpXG4gICAgLnRvTG93ZXJDYXNlKClcbiAgICAuaW5jbHVkZXMoXCJ0ZXh0L2h0bWxcIik7XG4gIGlmICghYWNjZXB0c0h0bWwpIHtcbiAgICByZXNwb25zZS5zZXRIZWFkZXIoXCJjb250ZW50LXR5cGVcIiwgXCJhcHBsaWNhdGlvbi9qc29uOyBjaGFyc2V0PXV0Zi04XCIpO1xuICAgIHJlc3BvbnNlLmVuZChKU09OLnN0cmluZ2lmeSh7IHN0YXR1czogXCJsb2FkaW5nXCIsIG1lc3NhZ2U6IFwiU3RhcnRpbmcgTGFrZWJveCBzZXJ2aWNlXCIgfSkpO1xuICAgIHJldHVybjtcbiAgfVxuICByZXNwb25zZS5zZXRIZWFkZXIoXCJjb250ZW50LXR5cGVcIiwgXCJ0ZXh0L2h0bWw7IGNoYXJzZXQ9dXRmLThcIik7XG4gIHJlc3BvbnNlLmVuZChgPCFkb2N0eXBlIGh0bWw+XG48aHRtbCBsYW5nPVwiZW5cIj5cbjxoZWFkPlxuPG1ldGEgY2hhcnNldD1cInV0Zi04XCI+XG48bWV0YSBuYW1lPVwidmlld3BvcnRcIiBjb250ZW50PVwid2lkdGg9ZGV2aWNlLXdpZHRoLGluaXRpYWwtc2NhbGU9MVwiPlxuPG1ldGEgaHR0cC1lcXVpdj1cInJlZnJlc2hcIiBjb250ZW50PVwiMlwiPlxuPHRpdGxlPlN0YXJ0aW5nIExha2Vib3ggc2VydmljZTwvdGl0bGU+XG48c3R5bGU+XG5odG1se2NvbG9yLXNjaGVtZTpsaWdodCBkYXJrO2ZvbnQtZmFtaWx5OnN5c3RlbS11aSxzYW5zLXNlcmlmfWJvZHl7bWluLWhlaWdodDoxMDB2aDttYXJnaW46MDtkaXNwbGF5OmdyaWQ7cGxhY2UtaXRlbXM6Y2VudGVyO2JhY2tncm91bmQ6Q2FudmFzO2NvbG9yOkNhbnZhc1RleHR9LmxvYWRpbmd7ZGlzcGxheTpmbGV4O2FsaWduLWl0ZW1zOmNlbnRlcjtnYXA6Ljc1cmVtfS5zcGlubmVye3dpZHRoOjEuMjVyZW07aGVpZ2h0OjEuMjVyZW07Ym9yZGVyOi4ycmVtIHNvbGlkIGNvbG9yLW1peChpbiBzcmdiLENhbnZhc1RleHQgMjAlLHRyYW5zcGFyZW50KTtib3JkZXItdG9wLWNvbG9yOkNhbnZhc1RleHQ7Ym9yZGVyLXJhZGl1czo1MCU7YW5pbWF0aW9uOnNwaW4gLjhzIGxpbmVhciBpbmZpbml0ZX1Aa2V5ZnJhbWVzIHNwaW57dG97dHJhbnNmb3JtOnJvdGF0ZSgzNjBkZWcpfX1cbjwvc3R5bGU+XG48L2hlYWQ+XG48Ym9keT48ZGl2IGNsYXNzPVwibG9hZGluZ1wiIHJvbGU9XCJzdGF0dXNcIj48c3BhbiBjbGFzcz1cInNwaW5uZXJcIiBhcmlhLWhpZGRlbj1cInRydWVcIj48L3NwYW4+PHNwYW4+U3RhcnRpbmcgTGFrZWJveCBzZXJ2aWNlPC9zcGFuPjwvZGl2PjwvYm9keT5cbjwvaHRtbD5gKTtcbn1cblxuZnVuY3Rpb24gaXNDb25uZWN0aW9uQ2xvc3VyZUVycm9yKGVycm9yOiB1bmtub3duKTogYm9vbGVhbiB7XG4gIGNvbnN0IGNvZGUgPVxuICAgIGVycm9yICYmIHR5cGVvZiBlcnJvciA9PT0gXCJvYmplY3RcIiAmJiBcImNvZGVcIiBpbiBlcnJvclxuICAgICAgPyBTdHJpbmcoKGVycm9yIGFzIHsgY29kZT86IHVua25vd24gfSkuY29kZSlcbiAgICAgIDogdW5kZWZpbmVkO1xuICByZXR1cm4gKFxuICAgIGNvZGUgPT09IFwiRUNPTk5SRVNFVFwiIHx8XG4gICAgY29kZSA9PT0gXCJFUElQRVwiIHx8XG4gICAgY29kZSA9PT0gXCJFUlJfU1RSRUFNX1BSRU1BVFVSRV9DTE9TRVwiIHx8XG4gICAgY29kZSA9PT0gXCJFUlJfU1RSRUFNX0RFU1RST1lFRFwiXG4gICk7XG59XG5cbmZ1bmN0aW9uIHJlcGxheVBhcnNlZEpzb25Cb2R5KHJlcXVlc3Q6IFJlcXVlc3QpOiBSZWFkYWJsZSB8IHVuZGVmaW5lZCB7XG4gIGlmICghcmVxdWVzdC5yZWFkYWJsZUVuZGVkIHx8IHJlcXVlc3QuYm9keSA9PT0gdW5kZWZpbmVkKSByZXR1cm4gdW5kZWZpbmVkO1xuICBpZiAoIXJlcXVlc3QuaGVhZGVyKFwiY29udGVudC10eXBlXCIpPy50b0xvd2VyQ2FzZSgpLmluY2x1ZGVzKFwianNvblwiKSkgcmV0dXJuIHVuZGVmaW5lZDtcbiAgY29uc3QgYm9keSA9IEJ1ZmZlci5mcm9tKEpTT04uc3RyaW5naWZ5KHJlcXVlc3QuYm9keSkpO1xuICByZXF1ZXN0LmhlYWRlcnNbXCJjb250ZW50LWxlbmd0aFwiXSA9IFN0cmluZyhib2R5LmJ5dGVMZW5ndGgpO1xuICBkZWxldGUgcmVxdWVzdC5oZWFkZXJzW1widHJhbnNmZXItZW5jb2RpbmdcIl07XG4gIHJldHVybiBSZWFkYWJsZS5mcm9tKGJvZHkpO1xufVxuXG5hc3luYyBmdW5jdGlvbiBpbmplY3RIdG1sSGVhZChcbiAgcHJveHlSZXNwb25zZTogSW5jb21pbmdNZXNzYWdlLFxuICByZXNwb25zZTogU2VydmVyUmVzcG9uc2UsXG4gIGhlYWQ6IHN0cmluZyxcbik6IFByb21pc2U8dm9pZD4ge1xuICB0cnkge1xuICAgIGNvbnN0IGNodW5rczogQnVmZmVyW10gPSBbXTtcbiAgICBmb3IgYXdhaXQgKGNvbnN0IGNodW5rIG9mIHByb3h5UmVzcG9uc2UpIHtcbiAgICAgIGNodW5rcy5wdXNoKEJ1ZmZlci5pc0J1ZmZlcihjaHVuaykgPyBjaHVuayA6IEJ1ZmZlci5mcm9tKGNodW5rKSk7XG4gICAgfVxuICAgIGNvbnN0IHNvdXJjZSA9IEJ1ZmZlci5jb25jYXQoY2h1bmtzKS50b1N0cmluZyhcInV0ZjhcIik7XG4gICAgY29uc3QgYm9keSA9IHNvdXJjZS5pbmNsdWRlcyhcIjxoZWFkPlwiKVxuICAgICAgPyBzb3VyY2UucmVwbGFjZShcIjxoZWFkPlwiLCBgPGhlYWQ+JHtoZWFkfWApXG4gICAgICA6IHNvdXJjZS5pbmNsdWRlcyhcIjwvaGVhZD5cIilcbiAgICAgICAgPyBzb3VyY2UucmVwbGFjZShcIjwvaGVhZD5cIiwgYCR7aGVhZH08L2hlYWQ+YClcbiAgICAgICAgOiBzb3VyY2U7XG4gICAgZm9yIChjb25zdCBbbmFtZSwgdmFsdWVdIG9mIE9iamVjdC5lbnRyaWVzKHByb3h5UmVzcG9uc2UuaGVhZGVycykpIHtcbiAgICAgIGlmICh2YWx1ZSA9PT0gdW5kZWZpbmVkIHx8IG5hbWUgPT09IFwiY29udGVudC1sZW5ndGhcIiB8fCBuYW1lID09PSBcInRyYW5zZmVyLWVuY29kaW5nXCIpIHtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG4gICAgICByZXNwb25zZS5zZXRIZWFkZXIobmFtZSwgdmFsdWUpO1xuICAgIH1cbiAgICByZXNwb25zZS5zdGF0dXNDb2RlID0gcHJveHlSZXNwb25zZS5zdGF0dXNDb2RlID8/IDIwMDtcbiAgICBpZiAocHJveHlSZXNwb25zZS5zdGF0dXNNZXNzYWdlKSByZXNwb25zZS5zdGF0dXNNZXNzYWdlID0gcHJveHlSZXNwb25zZS5zdGF0dXNNZXNzYWdlO1xuICAgIHJlc3BvbnNlLnNldEhlYWRlcihcImNvbnRlbnQtbGVuZ3RoXCIsIEJ1ZmZlci5ieXRlTGVuZ3RoKGJvZHkpKTtcbiAgICByZXNwb25zZS5lbmQoYm9keSk7XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgbG9nZ2VyLndhcm4oXCJMYWtlYm94IHJvdXRlIEhUTUwgaW5qZWN0aW9uIGZhaWxlZFwiLCB7XG4gICAgICBlcnJvcjogZXJyb3JVdGlscy50b0Vycm9yKGVycm9yKSxcbiAgICB9KTtcbiAgICBpZiAoIXJlc3BvbnNlLmhlYWRlcnNTZW50KSByZXNwb25zZS53cml0ZUhlYWQoNTAyKTtcbiAgICByZXNwb25zZS5lbmQoXCJMYWtlYm94IHJvdXRlIHVuYXZhaWxhYmxlXCIpO1xuICB9XG59XG5cbmZ1bmN0aW9uIHBpcGVQcm94eVJlc3BvbnNlKHByb3h5UmVzcG9uc2U6IEluY29taW5nTWVzc2FnZSwgcmVzcG9uc2U6IFNlcnZlclJlc3BvbnNlKTogdm9pZCB7XG4gIGZvciAoY29uc3QgW25hbWUsIHZhbHVlXSBvZiBPYmplY3QuZW50cmllcyhwcm94eVJlc3BvbnNlLmhlYWRlcnMpKSB7XG4gICAgaWYgKHZhbHVlICE9PSB1bmRlZmluZWQpIHJlc3BvbnNlLnNldEhlYWRlcihuYW1lLCB2YWx1ZSk7XG4gIH1cbiAgcmVzcG9uc2Uuc3RhdHVzQ29kZSA9IHByb3h5UmVzcG9uc2Uuc3RhdHVzQ29kZSA/PyAyMDA7XG4gIGlmIChwcm94eVJlc3BvbnNlLnN0YXR1c01lc3NhZ2UpIHJlc3BvbnNlLnN0YXR1c01lc3NhZ2UgPSBwcm94eVJlc3BvbnNlLnN0YXR1c01lc3NhZ2U7XG4gIHByb3h5UmVzcG9uc2UucGlwZShyZXNwb25zZSk7XG59XG5cbmFzeW5jIGZ1bmN0aW9uIGNsb3NlU2VydmVyKHNlcnZlcjogU2VydmVyKTogUHJvbWlzZTx2b2lkPiB7XG4gIGF3YWl0IG5ldyBQcm9taXNlPHZvaWQ+KChyZXNvbHZlKSA9PiBzZXJ2ZXIuY2xvc2UoKCkgPT4gcmVzb2x2ZSgpKSk7XG59XG5cbmZ1bmN0aW9uIHJvdXRlUmVkaXJlY3RVcmwodXJsOiBzdHJpbmcsIHJvdXRlUGF0aDogc3RyaW5nKTogc3RyaW5nIHtcbiAgY29uc3QgcGFyc2VkID0gbmV3IFVSTCh1cmwsIFwiaHR0cDovL2xvY2FsaG9zdFwiKTtcbiAgcGFyc2VkLnBhdGhuYW1lID0gYCR7cm91dGVQYXRofS9gO1xuICByZXR1cm4gYCR7cGFyc2VkLnBhdGhuYW1lfSR7cGFyc2VkLnNlYXJjaH1gO1xufVxuXG4vKipcbiAqIEtlZXAgdGhlIEFwcEtpdCByZXF1ZXN0IFVSTCBvbiB0aGUgaW5ib3VuZCBJbmNvbWluZ01lc3NhZ2Ugc28gQXBwS2l0J3NcbiAqIHJlcXVlc3QgbG9nZ2luZyBhbmQgdHJhY2luZyBzZWUgdGhlIHB1YmxpYyByb3V0ZS4gUHJlZml4XG4gKiBzdHJpcHBpbmcgaXMgYXBwbGllZCBvbmx5IG9uIHRoZSBvdXRib3VuZCBwcm94eSByZXF1ZXN0LlxuICovXG5mdW5jdGlvbiBvdXRib3VuZFBhdGgocmVxdWVzdFVybDogc3RyaW5nIHwgdW5kZWZpbmVkLCBzdHJpcFByZWZpeD86IHN0cmluZyk6IHN0cmluZyB7XG4gIGNvbnN0IHVybCA9IHJlcXVlc3RVcmwgJiYgcmVxdWVzdFVybC5sZW5ndGggPiAwID8gcmVxdWVzdFVybCA6IFwiL1wiO1xuICBjb25zdCBxdWVyeUluZGV4ID0gdXJsLmluZGV4T2YoXCI/XCIpO1xuICBjb25zdCBwYXRobmFtZSA9IHF1ZXJ5SW5kZXggPj0gMCA/IHVybC5zbGljZSgwLCBxdWVyeUluZGV4KSA6IHVybDtcbiAgY29uc3QgcXVlcnkgPSBxdWVyeUluZGV4ID49IDAgPyB1cmwuc2xpY2UocXVlcnlJbmRleCkgOiBcIlwiO1xuICBpZiAoIXN0cmlwUHJlZml4KSByZXR1cm4gdXJsLnN0YXJ0c1dpdGgoXCIvXCIpID8gdXJsIDogYC8ke3VybH1gO1xuICByZXR1cm4gYCR7cGF0aG5hbWUuc2xpY2Uoc3RyaXBQcmVmaXgubGVuZ3RoKSB8fCBcIi9cIn0ke3F1ZXJ5fWA7XG59XG5cbmFzeW5jIGZ1bmN0aW9uIGxpc3RlblRhcmdldChcbiAgc2VydmVyOiBTZXJ2ZXIsXG4pOiBQcm9taXNlPHsgaG9zdDogc3RyaW5nOyBwb3J0OiBudW1iZXI7IHByb3RvY29sOiBzdHJpbmcgfT4ge1xuICBhd2FpdCBuZXcgUHJvbWlzZTx2b2lkPigocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgc2VydmVyLm9uY2UoXCJlcnJvclwiLCByZWplY3QpO1xuICAgIHNlcnZlci5saXN0ZW4oMCwgXCIxMjcuMC4wLjFcIiwgcmVzb2x2ZSk7XG4gIH0pO1xuICBjb25zdCBhZGRyZXNzID0gc2VydmVyLmFkZHJlc3MoKTtcbiAgaWYgKCFhZGRyZXNzIHx8IHR5cGVvZiBhZGRyZXNzID09PSBcInN0cmluZ1wiKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKFwiRmFpbGVkIHRvIGJpbmQgTGFrZWJveCByb3V0ZSBicmlkZ2VcIik7XG4gIH1cbiAgcmV0dXJuIHsgaG9zdDogXCIxMjcuMC4wLjFcIiwgcG9ydDogYWRkcmVzcy5wb3J0LCBwcm90b2NvbDogXCJodHRwOlwiIH07XG59XG5cbmZ1bmN0aW9uIGJyaWRnZVNvY2tldChcbiAgc29ja2V0OiBTb2NrZXQsXG4gIGNoYW5uZWxQcm9taXNlOiBQcm9taXNlPER1cGxleD4sXG4gIHJlYWR5VGltZW91dE1zOiBudW1iZXIsXG4gIGluaXRpYWxEYXRhPzogQnVmZmVyLFxuKTogdm9pZCB7XG4gIGNvbnN0IHBlbmRpbmcgPSBuZXcgUGFzc1Rocm91Z2goeyBoaWdoV2F0ZXJNYXJrOiBNQVhfUEVORElOR19CWVRFUyB9KTtcbiAgbGV0IGNoYW5uZWw6IER1cGxleCB8IHVuZGVmaW5lZDtcbiAgbGV0IHRpbWVkT3V0ID0gZmFsc2U7XG4gIGNvbnN0IHJlYWR5VGltZW91dCA9IHNldFRpbWVvdXQoKCkgPT4ge1xuICAgIHRpbWVkT3V0ID0gdHJ1ZTtcbiAgICBwZW5kaW5nLmRlc3Ryb3koKTtcbiAgICBjaGFubmVsPy5kZXN0cm95KCk7XG4gICAgaWYgKCFzb2NrZXQuZGVzdHJveWVkKSB7XG4gICAgICBzb2NrZXQuZW5kKFwiSFRUUC8xLjEgNTA0IEdhdGV3YXkgVGltZW91dFxcclxcbkNvbm5lY3Rpb246IGNsb3NlXFxyXFxuQ29udGVudC1MZW5ndGg6IDBcXHJcXG5cXHJcXG5cIik7XG4gICAgfVxuICB9LCByZWFkeVRpbWVvdXRNcyk7XG4gIHJlYWR5VGltZW91dC51bnJlZj8uKCk7XG4gIGNvbnN0IGNsZWFyUmVhZHlUaW1lb3V0ID0gKCkgPT4gY2xlYXJUaW1lb3V0KHJlYWR5VGltZW91dCk7XG4gIGlmIChpbml0aWFsRGF0YSkgcGVuZGluZy53cml0ZShpbml0aWFsRGF0YSk7XG4gIHNvY2tldC5vbmNlKFwiZXJyb3JcIiwgKCkgPT4ge1xuICAgIGNsZWFyUmVhZHlUaW1lb3V0KCk7XG4gICAgcGVuZGluZy5kZXN0cm95KCk7XG4gICAgY2hhbm5lbD8uZGVzdHJveSgpO1xuICB9KTtcbiAgc29ja2V0LnBpcGUocGVuZGluZyk7XG4gIHNvY2tldC5vbmNlKFwiY2xvc2VcIiwgKCkgPT4ge1xuICAgIGNsZWFyUmVhZHlUaW1lb3V0KCk7XG4gICAgcGVuZGluZy5kZXN0cm95KCk7XG4gICAgY2hhbm5lbD8uZGVzdHJveSgpO1xuICB9KTtcblxuICB2b2lkIGNoYW5uZWxQcm9taXNlLnRoZW4oXG4gICAgKG9wZW5lZENoYW5uZWwpID0+IHtcbiAgICAgIGlmICh0aW1lZE91dCB8fCBzb2NrZXQuZGVzdHJveWVkKSB7XG4gICAgICAgIHBlbmRpbmcuZGVzdHJveSgpO1xuICAgICAgICBvcGVuZWRDaGFubmVsLmRlc3Ryb3koKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgY2hhbm5lbCA9IG9wZW5lZENoYW5uZWw7XG4gICAgICBjbGVhclJlYWR5VGltZW91dCgpO1xuICAgICAgbGV0IGNoYW5uZWxFbmRlZCA9IGZhbHNlO1xuICAgICAgY2hhbm5lbC5vbmNlKFwiZW5kXCIsICgpID0+IHtcbiAgICAgICAgY2hhbm5lbEVuZGVkID0gdHJ1ZTtcbiAgICAgIH0pO1xuICAgICAgY2hhbm5lbC5vbmNlKFwiY2xvc2VcIiwgKCkgPT4ge1xuICAgICAgICBpZiAoIWNoYW5uZWxFbmRlZCAmJiAhc29ja2V0LmRlc3Ryb3llZCkgc29ja2V0LmRlc3Ryb3koKTtcbiAgICAgIH0pO1xuICAgICAgY2hhbm5lbC5vbmNlKFwiZXJyb3JcIiwgKCkgPT4gc29ja2V0LmRlc3Ryb3koKSk7XG4gICAgICBjaGFubmVsLnBpcGUoc29ja2V0KTtcbiAgICAgIHBlbmRpbmcucGlwZShjaGFubmVsKTtcbiAgICB9LFxuICAgIChlcnJvcikgPT4ge1xuICAgICAgY2xlYXJSZWFkeVRpbWVvdXQoKTtcbiAgICAgIGlmICghdGltZWRPdXQgJiYgIXNvY2tldC5kZXN0cm95ZWQpIHtcbiAgICAgICAgbG9nZ2VyLndhcm4oXCJMYWtlYm94IHJvdXRlIGZvcndhcmRpbmcgY2hhbm5lbCBmYWlsZWRcIiwge1xuICAgICAgICAgIGVycm9yOiBlcnJvclV0aWxzLnRvRXJyb3IoZXJyb3IpLFxuICAgICAgICB9KTtcbiAgICAgICAgc29ja2V0LmVuZChcIkhUVFAvMS4xIDUwMiBCYWQgR2F0ZXdheVxcclxcbkNvbm5lY3Rpb246IGNsb3NlXFxyXFxuQ29udGVudC1MZW5ndGg6IDBcXHJcXG5cXHJcXG5cIik7XG4gICAgICB9XG4gICAgICBwZW5kaW5nLmRlc3Ryb3koKTtcbiAgICAgIGlmICh0aW1lZE91dCB8fCBzb2NrZXQuZGVzdHJveWVkKSBzb2NrZXQuZGVzdHJveSgpO1xuICAgIH0sXG4gICk7XG59XG5cbmZ1bmN0aW9uIHByb3h5V2ViU29ja2V0KFxuICByZXF1ZXN0OiBJbmNvbWluZ01lc3NhZ2UsXG4gIHNvY2tldDogU29ja2V0LFxuICBoZWFkOiBCdWZmZXIsXG4gIHNlc3Npb246IFNzaFNlc3Npb24sXG4gIHBvcnQ6IG51bWJlcixcbiAgcmVhZHlUaW1lb3V0TXM6IG51bWJlcixcbik6IHZvaWQge1xuICBzdHJpcFdlYnNvY2tldElkZW50aXR5SGVhZGVycyhyZXF1ZXN0KTtcbiAgcmVxdWVzdC5oZWFkZXJzLm9yaWdpbiA9IGBodHRwOi8vMTI3LjAuMC4xOiR7cG9ydH1gO1xuICByZXF1ZXN0LmhlYWRlcnMuaG9zdCA9IGAxMjcuMC4wLjE6JHtwb3J0fWA7XG4gIGJyaWRnZVNvY2tldChcbiAgICBzb2NrZXQsXG4gICAgc2Vzc2lvbi5mb3J3YXJkT3V0KFwiMTI3LjAuMC4xXCIsIHBvcnQpLFxuICAgIHJlYWR5VGltZW91dE1zLFxuICAgIHNlcmlhbGl6ZVVwZ3JhZGVSZXF1ZXN0KHJlcXVlc3QsIGhlYWQpLFxuICApO1xufVxuXG5mdW5jdGlvbiBzdHJpcFdlYnNvY2tldElkZW50aXR5SGVhZGVycyhyZXF1ZXN0OiBJbmNvbWluZ01lc3NhZ2UpOiB2b2lkIHtcbiAgZm9yIChjb25zdCBuYW1lIG9mIFtcbiAgICBcImF1dGhvcml6YXRpb25cIixcbiAgICBcImNvb2tpZVwiLFxuICAgIEFDQ0VTU19UT0tFTl9IRUFERVIsXG4gICAgVVNFUl9FTUFJTF9IRUFERVIsXG4gICAgVVNFUl9JRF9IRUFERVIsXG4gIF0pIHtcbiAgICBkZWxldGUgcmVxdWVzdC5oZWFkZXJzW25hbWVdO1xuICB9XG59XG5cbmZ1bmN0aW9uIHNlcmlhbGl6ZVVwZ3JhZGVSZXF1ZXN0KHJlcXVlc3Q6IEluY29taW5nTWVzc2FnZSwgaGVhZDogQnVmZmVyKTogQnVmZmVyIHtcbiAgY29uc3QgbGluZXMgPSBbXG4gICAgYCR7cmVxdWVzdC5tZXRob2QgPz8gXCJHRVRcIn0gJHtyZXF1ZXN0LnVybCA/PyBcIi9cIn0gSFRUUC8ke3JlcXVlc3QuaHR0cFZlcnNpb24gfHwgXCIxLjFcIn1gLFxuICBdO1xuICBmb3IgKGNvbnN0IFtuYW1lLCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMocmVxdWVzdC5oZWFkZXJzKSkge1xuICAgIGlmICh2YWx1ZSA9PT0gdW5kZWZpbmVkKSBjb250aW51ZTtcbiAgICBmb3IgKGNvbnN0IGl0ZW0gb2YgQXJyYXkuaXNBcnJheSh2YWx1ZSkgPyB2YWx1ZSA6IFt2YWx1ZV0pIHtcbiAgICAgIGxpbmVzLnB1c2goYCR7bmFtZX06ICR7aXRlbX1gKTtcbiAgICB9XG4gIH1cbiAgbGluZXMucHVzaChcIlwiLCBcIlwiKTtcbiAgcmV0dXJuIEJ1ZmZlci5jb25jYXQoW0J1ZmZlci5mcm9tKGxpbmVzLmpvaW4oXCJcXHJcXG5cIikpLCBoZWFkXSk7XG59XG4iXX0=
|