UNPKG

@synerty/vortexjs

Version:

Custom observable data serialisation and routing based on Angular 2+

313 lines 48.1 kB
import { VortexClientABC, VortexClientStateE } from "./VortexClientABC"; import { dateStr, getFiltStr } from "./UtilMisc"; import { PayloadEnvelope } from "./PayloadEnvelope"; // https://developer.mozilla.org/en-US/docs/Web/API/WebSocket export class VortexClientBrowserWebsocket extends VortexClientABC { socket = null; lastReconnectDate = Date.parse("01-Jan-2017"); unsentBuffer = []; // Make note of when we're reconnecting, // otherwise closing the last websocket causes a reconnection // call which then closes the current socket being open (in a loop) reconnectingInProgress = false; _data = ""; _vortexMsgsQueue = []; constructor(vortexStatusService, url, vortexClientName, headers) { super(vortexStatusService, url, vortexClientName, headers); } get isReady() { return this.socket != null && this.socket.readyState === WebSocket.OPEN; } // OVERRIDE Send send(payloadEnvelope) { if (!this.isReady) { throw new Error("Websocked vortex is not online."); } return super.send(payloadEnvelope); } // OVERRIDE reconnect async reconnect() { this.vortexStatusService.logDebug("VortexClientBrowserWebsocket.reconnect called"); if (this.reconnectingInProgress === true) { this.vortexStatusService.logDebug("VortexClientBrowserWebsocket.reconnect returning #1"); return; } this.clearBeatTimer(); this._vortexState = VortexClientStateE.Idle; this.reconnectingInProgress = true; try { await this.createSocket(); } catch (e) { console.error(`${dateStr()} Failed to connect websocket ${e}`); } this.reconnectingInProgress = false; this.restartTimer(); this.vortexStatusService.logDebug("VortexClientBrowserWebsocket.reconnect returning #2"); } sendVortexMsg(vortexMsgs) { this.unsentBuffer.add(vortexMsgs); this.sendMessages(); } sendMessages() { while (this.unsentBuffer.length !== 0) { if (!this.isReady) return; const vortexMsg = this.unsentBuffer.shift(); this.socket.send(vortexMsg + "."); } } _processData() { if (this._data) { let indexOfDot = this._data.indexOf("."); while (indexOfDot !== -1) { const vortexMsg = this._data.slice(0, indexOfDot); this._data = this._data.slice(indexOfDot + 1); if (vortexMsg.length !== 0) { this._vortexMsgsQueue.push(vortexMsg); } indexOfDot = this._data.indexOf("."); } } while (this._vortexMsgsQueue.length !== 0) { this._processVortexMsgs() // .catch((e) => console.log(`ERROR VortexClientBrowserWebsocket: ${e}`)); } } async _processVortexMsgs() { while (this._vortexMsgsQueue.length !== 0) { const vortexMsg = this._vortexMsgsQueue.shift(); try { const payloadEnvelope = await PayloadEnvelope.fromVortexMsg(vortexMsg); this._deliverPayload(payloadEnvelope); } catch (e) { console.log(dateStr() + `ERROR: processing vortexMsg\n${e}\n${vortexMsg}`); } } } _deliverPayload(payloadEnvelope) { this.receive(payloadEnvelope); } async shutdown() { this.vortexStatusService.logDebug("VortexClientBrowserWebsocket.shutdown called"); await this.closeWebsocket(); this.vortexStatusService.logDebug("VortexClientBrowserWebsocket.shutdown" + " returned"); } async closeWebsocket() { this.vortexStatusService.logDebug("VortexClientBrowserWebsocket.closeWebsocket called"); const socket = this.socket; if (socket && socket.readyState !== WebSocket.CLOSED) { await new Promise((resolve) => { this.vortexStatusService.logDebug("Websocket close starting"); this.setClosing(); socket.addEventListener("close", () => { this.vortexStatusService.logInfo("Websocket close complete"); resolve(); }, { once: true }); // Safe on CONNECTING (aborts handshake) and CLOSING (no-op); // the close event still fires in both cases. socket.close(); }); // setClosed() is handled by the permanent onClose listener } else { this.setClosed(); } this.socket = null; this.vortexStatusService.logDebug("VortexClientBrowserWebsocket.closeWebsocket returning"); } async createSocket() { this.vortexStatusService.logDebug("VortexClientBrowserWebsocket.createSocket called"); // If we're already connecting, then do nothing if (this.socket && this.socket.readyState === WebSocket.CONNECTING) { if (!this.isShutdown) { this.vortexStatusService.logDebug("VortexClientBrowserWebsocket.createSocket" + " returning #1"); return; } try { console.log("Aborting WebSocket connection attempt," + " this is probably because of Vortex reconnection"); this.socket.close(); } catch (e) { // pass } } this.vortexStatusService.logDebug("VortexClientBrowserWebsocket.createSocket Closing old websocket"); await this.closeWebsocket(); // Don't continually reconnect let reconnectDiffMs = Date.now() - this.lastReconnectDate; if (reconnectDiffMs < this.RECONNECT_BACKOFF_SECONDS * 1000) { this.vortexStatusService.logDebug("VortexClientBrowserWebsocket.createSocket returning #2 - backoff" + " timeout"); setTimeout(() => this.createSocket(), this.RECONNECT_BACKOFF_SECONDS * 1000 - reconnectDiffMs + 10); return; } this.lastReconnectDate = Date.now(); // Prepare the args to send const args = { vortexUuid: this.uuid, vortexName: this.name, }; this.vortexStatusService.logConnectionInfo(`${dateStr()} WebSocket, connecting to ${this.url}`); // Browser WebSocket cannot send custom handshake headers; auth must // come from cookies or URL. Log which headers were supplied but dropped. const headerKeys = []; for (const key of this.headers.keys()) { headerKeys.push(key); } if (headerKeys.length !== 0) { this.vortexStatusService.logDebug("VortexClientBrowserWebsocket.createSocket" + " ignoring headers (unsupported by browser WebSocket): " + headerKeys.join(", ")); } await new Promise((resolve, reject) => { let promiseCalled = false; const resolveOnce = () => { if (promiseCalled) return; promiseCalled = true; this.vortexStatusService.logDebug("VortexClientBrowserWebsocket.createSocket calling resolve"); resolve(); }; const rejectOnce = () => { if (promiseCalled) return; promiseCalled = true; this.vortexStatusService.logDebug("VortexClientBrowserWebsocket.createSocket calling reject"); reject(); }; this.vortexStatusService.logDebug("VortexClientBrowserWebsocket.createSocket Creating socket"); this.socket = new WebSocket(this.url + getFiltStr(args), []); this.socket.binaryType = "arraybuffer"; this.socket.addEventListener("open", () => { this.vortexStatusService.logDebug("VortexClientBrowserWebsocket.createSocket addEventListener open"); this.onOpen(); }); this.socket.addEventListener("message", (event) => { this.onMessage(event, resolveOnce); }); this.socket.addEventListener("close", (event) => { this.vortexStatusService.logDebug("VortexClientBrowserWebsocket.createSocket addEventListener close"); this.onClose(event); rejectOnce(); }); this.socket.addEventListener("error", (event) => { this.vortexStatusService.logDebug(`VortexClientBrowserWebsocket.createSocket addEventListener error: ${event}`); this.onError(event, rejectOnce); }); }); this.vortexStatusService.logDebug("VortexClientBrowserWebsocket.createSocket returning #3"); } onMessage(event, promiseResolver) { if (!this.isOnline) { this.setOnline(); promiseResolver(); } this.beat(); if (event.data.length == null) { this.vortexStatusService.logConnectionError("WebSocket, We've received a websocket binary message," + " we expect a unicode"); return; } // If the server sends us a '.', that's a heart beat, return it. if (event.data === ".") { this.socket != null && this.socket.send("."); return; } if (event.data[0] !== "{") { this._data += event.data; } else { // It starts with {, ensure it ends with } if (event.data[event.data.length - 1] !== "}") { console.log(dateStr() + " ERROR, Payload should end with }," + " but it doesn't: " + event.data); return; } this._vortexMsgsQueue.push(event.data); } this._processData(); } onOpen() { super .send(new PayloadEnvelope()) .catch((e) => console.log(`ERROR VortexClientBrowserWebsocket onOpen Send: ${e}`)); this.vortexStatusService.logConnectionInfo("WebSocket, connected"); } onClose(event) { this.vortexStatusService.logConnectionInfo("WebSocket, closed"); if (!(this.socket && this.socket.readyState === WebSocket.OPEN)) { this.setClosed(); } // The base class will reconnect } onError(event, reject) { reject(event.error?.toString() || "Websocket failed to connect"); // onClose will get called as well // Skip the session-expiry check when the socket was already online. // onError on an established socket means a transient drop (Azure // proxy's 4-minute idle close, network blip, etc.); onClose fires // next and the normal retry cycle handles reconnection. Without this // guard, the async probe below can race with a successful reconnect // and override Online with NetworkOnlineNoWebsocketResource. if (this.isOnline) return; this.testIfOnlineAndLoggedOut().catch((e) => console.log(`ERROR testIfOnlineAndLoggedOut: ${e}`)); } async testIfOnlineAndLoggedOut() { // wss://host/... → https://host/... const httpUrl = "http" + this._url.substring(2); const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), 5000); let response; try { // redirect: "manual" gives us response.type === "opaqueredirect" // for a 302 instead of silently following to the Entra login page // and returning the login HTML with status 200 (which is what the // old XMLHttpRequest did — it couldn't distinguish a valid session // from an expired one). response = await fetch(httpUrl, { redirect: "manual", cache: "no-store", signal: controller.signal, }); } catch (e) { // Network error or timeout — server unreachable. Don't change // vortex state; the retry timer will try again. this.vortexStatusService.logConnectionInfo("VortexClientBrowserWebsocket: server unreachable during" + " session check"); return; } finally { clearTimeout(timeoutId); } if (response.type === "opaqueredirect") { // Proxy redirected to the Entra login page — session expired. // Unregister the service worker and reload so the browser // re-authenticates from scratch. this.vortexStatusService.logConnectionError("VortexClientBrowserWebsocket: session expired," + " unregistering service worker and reloading"); try { const registrations = await navigator.serviceWorker.getRegistrations(); await Promise.all(registrations.map((r) => r.unregister())); } catch (e) { // Ignore — reload still proceeds. } window.location.reload(); return; } // Server is up and the session is valid — the WebSocket failure // is transient. The retry timer will reconnect; no state change // needed here. this.vortexStatusService.logConnectionInfo("VortexClientBrowserWebsocket: server online, session valid," + " WebSocket error is transient"); } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiVm9ydGV4Q2xpZW50QnJvd3NlcldlYnNvY2tldC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy92b3J0ZXgvVm9ydGV4Q2xpZW50QnJvd3NlcldlYnNvY2tldC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsZUFBZSxFQUFFLGtCQUFrQixFQUFFLE1BQU0sbUJBQW1CLENBQUM7QUFFeEUsT0FBTyxFQUFFLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxZQUFZLENBQUM7QUFDakQsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLG1CQUFtQixDQUFDO0FBR3BELDZEQUE2RDtBQUU3RCxNQUFNLE9BQU8sNEJBQTZCLFNBQVEsZUFBZTtJQUNyRCxNQUFNLEdBQXFCLElBQUksQ0FBQztJQUVoQyxpQkFBaUIsR0FBVyxJQUFJLENBQUMsS0FBSyxDQUFDLGFBQWEsQ0FBQyxDQUFDO0lBRXRELFlBQVksR0FBYSxFQUFFLENBQUM7SUFFcEMsd0NBQXdDO0lBQ3hDLDZEQUE2RDtJQUM3RCxvRUFBb0U7SUFDNUQsc0JBQXNCLEdBQVksS0FBSyxDQUFDO0lBRXhDLEtBQUssR0FBVyxFQUFFLENBQUM7SUFDbkIsZ0JBQWdCLEdBQWEsRUFBRSxDQUFDO0lBRXhDLFlBQ0ksbUJBQXdDLEVBQ3hDLEdBQVcsRUFDWCxnQkFBd0IsRUFDeEIsT0FBb0I7UUFFcEIsS0FBSyxDQUFDLG1CQUFtQixFQUFFLEdBQUcsRUFBRSxnQkFBZ0IsRUFBRSxPQUFPLENBQUMsQ0FBQztJQUMvRCxDQUFDO0lBRUQsSUFBSSxPQUFPO1FBQ1AsT0FBTyxJQUFJLENBQUMsTUFBTSxJQUFJLElBQUksSUFBSSxJQUFJLENBQUMsTUFBTSxDQUFDLFVBQVUsS0FBSyxTQUFTLENBQUMsSUFBSSxDQUFDO0lBQzVFLENBQUM7SUFFRCxnQkFBZ0I7SUFDaEIsSUFBSSxDQUFDLGVBQW9EO1FBQ3JELElBQUksQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFFO1lB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QU0sRUFBRSxVQUFVLENBQUMsTUFBTTthQUM1QixDQUFDLENBQUM7U0FDTjtRQUFDLE9BQU8sQ0FBQyxFQUFFO1lBQ1IsOERBQThEO1lBQzlELGdEQUFnRDtZQUNoRCxJQUFJLENBQUMsbUJBQW1CLENBQUMsaUJBQWlCLENBQ3RDLHlEQUF5RDtnQkFDckQsZ0JBQWdCLENBQ3ZCLENBQUM7WUFDRixPQUFPO1NBQ1Y7Z0JBQVM7WUFDTixZQUFZLENBQUMsU0FBUyxDQUFDLENBQUM7U0FDM0I7UUFFRCxJQUFJLFFBQVEsQ0FBQyxJQUFJLEtBQUssZ0JBQWdCLEVBQUU7WUFDcEMsOERBQThEO1lBQzlELDBEQUEwRDtZQUMxRCxpQ0FBaUM7WUFDakMsSUFBSSxDQUFDLG1CQUFtQixDQUFDLGtCQUFrQixDQUN2QyxnREFBZ0Q7Z0JBQzVDLDZDQUE2QyxDQUNwRCxDQUFDO1lBQ0YsSUFBSTtnQkFDQSxNQUFNLGFBQWEsR0FDZixNQUFNLFNBQVMsQ0FBQyxhQUFhLENBQUMsZ0JBQWdCLEVBQUUsQ0FBQztnQkFDckQsTUFBTSxPQUFPLENBQUMsR0FBRyxDQUFDLGFBQWEsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxVQUFVLEVBQUUsQ0FBQyxDQUFDLENBQUM7YUFDL0Q7WUFBQyxPQUFPLENBQUMsRUFBRTtnQkFDUixrQ0FBa0M7YUFDckM7WUFDRCxNQUFNLENBQUMsUUFBUSxDQUFDLE1BQU0sRUFBRSxDQUFDO1lBQ3pCLE9BQU87U0FDVjtRQUVELGdFQUFnRTtRQUNoRSxnRUFBZ0U7UUFDaEUsZUFBZTtRQUNmLElBQUksQ0FBQyxtQkFBbUIsQ0FBQyxpQkFBaUIsQ0FDdEMsNkRBQTZEO1lBQ3pELCtCQUErQixDQUN0QyxDQUFDO0lBQ04sQ0FBQztDQUNKIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgVm9ydGV4Q2xpZW50QUJDLCBWb3J0ZXhDbGllbnRTdGF0ZUUgfSBmcm9tIFwiLi9Wb3J0ZXhDbGllbnRBQkNcIjtcbmltcG9ydCB7IFZvcnRleFN0YXR1c1NlcnZpY2UgfSBmcm9tIFwiLi9Wb3J0ZXhTdGF0dXNTZXJ2aWNlXCI7XG5pbXBvcnQgeyBkYXRlU3RyLCBnZXRGaWx0U3RyIH0gZnJvbSBcIi4vVXRpbE1pc2NcIjtcbmltcG9ydCB7IFBheWxvYWRFbnZlbG9wZSB9IGZyb20gXCIuL1BheWxvYWRFbnZlbG9wZVwiO1xuaW1wb3J0IHsgSHR0cEhlYWRlcnMgfSBmcm9tIFwiQGFuZ3VsYXIvY29tbW9uL2h0dHBcIjtcblxuLy8gaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZW4tVVMvZG9jcy9XZWIvQVBJL1dlYlNvY2tldFxuXG5leHBvcnQgY2xhc3MgVm9ydGV4Q2xpZW50QnJvd3NlcldlYnNvY2tldCBleHRlbmRzIFZvcnRleENsaWVudEFCQyB7XG4gICAgcHJpdmF0ZSBzb2NrZXQ6IFdlYlNvY2tldCB8IG51bGwgPSBudWxsO1xuXG4gICAgcHJpdmF0ZSBsYXN0UmVjb25uZWN0RGF0ZTogbnVtYmVyID0gRGF0ZS5wYXJzZShcIjAxLUphbi0yMDE3XCIpO1xuXG4gICAgcHJpdmF0ZSB1bnNlbnRCdWZmZXI6IHN0cmluZ1tdID0gW107XG5cbiAgICAvLyBNYWtlIG5vdGUgb2Ygd2hlbiB3ZSdyZSByZWNvbm5lY3RpbmcsXG4gICAgLy8gb3RoZXJ3aXNlIGNsb3NpbmcgdGhlIGxhc3Qgd2Vic29ja2V0IGNhdXNlcyBhIHJlY29ubmVjdGlvblxuICAgIC8vIGNhbGwgd2hpY2ggdGhlbiBjbG9zZXMgdGhlIGN1cnJlbnQgc29ja2V0IGJlaW5nIG9wZW4gKGluICBhIGxvb3ApXG4gICAgcHJpdmF0ZSByZWNvbm5lY3RpbmdJblByb2dyZXNzOiBib29sZWFuID0gZmFsc2U7XG5cbiAgICBwcml2YXRlIF9kYXRhOiBzdHJpbmcgPSBcIlwiO1xuICAgIHByaXZhdGUgX3ZvcnRleE1zZ3NRdWV1ZTogc3RyaW5nW10gPSBbXTtcblxuICAgIGNvbnN0cnVjdG9yKFxuICAgICAgICB2b3J0ZXhTdGF0dXNTZXJ2aWNlOiBWb3J0ZXhTdGF0dXNTZXJ2aWNlLFxuICAgICAgICB1cmw6IHN0cmluZyxcbiAgICAgICAgdm9ydGV4Q2xpZW50TmFtZTogc3RyaW5nLFxuICAgICAgICBoZWFkZXJzOiBIdHRwSGVhZGVycyxcbiAgICApIHtcbiAgICAgICAgc3VwZXIodm9ydGV4U3RhdHVzU2VydmljZSwgdXJsLCB2b3J0ZXhDbGllbnROYW1lLCBoZWFkZXJzKTtcbiAgICB9XG5cbiAgICBnZXQgaXNSZWFkeSgpOiBib29sZWFuIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuc29ja2V0ICE9IG51bGwgJiYgdGhpcy5zb2NrZXQucmVhZHlTdGF0ZSA9PT0gV2ViU29ja2V0Lk9QRU47XG4gICAgfVxuXG4gICAgLy8gT1ZFUlJJREUgU2VuZFxuICAgIHNlbmQocGF5bG9hZEVudmVsb3BlOiBQYXlsb2FkRW52ZWxvcGUgfCBQYXlsb2FkRW52ZWxvcGVbXSk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBpZiAoIXRoaXMuaXNSZWFkeSkge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKFwiV2Vic29ja2VkIHZvcnRleCBpcyBub3Qgb25saW5lLlwiKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiBzdXBlci5zZW5kKHBheWxvYWRFbnZlbG9wZSk7XG4gICAgfVxuXG4gICAgLy8gT1ZFUlJJREUgcmVjb25uZWN0XG4gICAgYXN5bmMgcmVjb25uZWN0KCk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICB0aGlzLnZvcnRleFN0YXR1c1NlcnZpY2UubG9nRGVidWcoXG4gICAgICAgICAgICBcIlZvcnRleENsaWVudEJyb3dzZXJXZWJzb2NrZXQucmVjb25uZWN0IGNhbGxlZFwiLFxuICAgICAgICApO1xuXG4gICAgICAgIGlmICh0aGlzLnJlY29ubmVjdGluZ0luUHJvZ3Jlc3MgPT09IHRydWUpIHtcbiAgICAgICAgICAgIHRoaXMudm9ydGV4U3RhdHVzU2VydmljZS5sb2dEZWJ1ZyhcbiAgICAgICAgICAgICAgICBcIlZvcnRleENsaWVudEJyb3dzZXJXZWJzb2NrZXQucmVjb25uZWN0IHJldHVybmluZyAjMVwiLFxuICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICB0aGlzLmNsZWFyQmVhdFRpbWVyKCk7XG4gICAgICAgIHRoaXMuX3ZvcnRleFN0YXRlID0gVm9ydGV4Q2xpZW50U3RhdGVFLklkbGU7XG4gICAgICAgIHRoaXMucmVjb25uZWN0aW5nSW5Qcm9ncmVzcyA9IHRydWU7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICBhd2FpdCB0aGlzLmNyZWF0ZVNvY2tldCgpO1xuICAgICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgICAgICBjb25zb2xlLmVycm9yKGAke2RhdGVTdHIoKX0gRmFpbGVkIHRvIGNvbm5lY3Qgd2Vic29ja2V0ICR7ZX1gKTtcbiAgICAgICAgfVxuICAgICAgICB0aGlzLnJlY29ubmVjdGluZ0luUHJvZ3Jlc3MgPSBmYWxzZTtcbiAgICAgICAgdGhpcy5yZXN0YXJ0VGltZXIoKTtcblxuICAgICAgICB0aGlzLnZvcnRleFN0YXR1c1NlcnZpY2UubG9nRGVidWcoXG4gICAgICAgICAgICBcIlZvcnRleENsaWVudEJyb3dzZXJXZWJzb2NrZXQucmVjb25uZWN0IHJldHVybmluZyAjMlwiLFxuICAgICAgICApO1xuICAgIH1cblxuICAgIHByb3RlY3RlZCBzZW5kVm9ydGV4TXNnKHZvcnRleE1zZ3M6IHN0cmluZ1tdKTogdm9pZCB7XG4gICAgICAgIHRoaXMudW5zZW50QnVmZmVyLmFkZCh2b3J0ZXhNc2dzKTtcblxuICAgICAgICB0aGlzLnNlbmRNZXNzYWdlcygpO1xuICAgIH1cblxuICAgIHByaXZhdGUgc2VuZE1lc3NhZ2VzKCkge1xuICAgICAgICB3aGlsZSAodGhpcy51bnNlbnRCdWZmZXIubGVuZ3RoICE9PSAwKSB7XG4gICAgICAgICAgICBpZiAoIXRoaXMuaXNSZWFkeSkgcmV0dXJuO1xuXG4gICAgICAgICAgICBjb25zdCB2b3J0ZXhNc2cgPSB0aGlzLnVuc2VudEJ1ZmZlci5zaGlmdCgpO1xuICAgICAgICAgICAgdGhpcy5zb2NrZXQuc2VuZCh2b3J0ZXhNc2cgKyBcIi5cIik7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBwcml2YXRlIF9wcm9jZXNzRGF0YSgpOiB2b2lkIHtcbiAgICAgICAgaWYgKHRoaXMuX2RhdGEpIHtcbiAgICAgICAgICAgIGxldCBpbmRleE9mRG90OiBudW1iZXIgPSB0aGlzLl9kYXRhLmluZGV4T2YoXCIuXCIpO1xuICAgICAgICAgICAgd2hpbGUgKGluZGV4T2ZEb3QgIT09IC0xKSB7XG4gICAgICAgICAgICAgICAgY29uc3Qgdm9ydGV4TXNnID0gdGhpcy5fZGF0YS5zbGljZSgwLCBpbmRleE9mRG90KTtcbiAgICAgICAgICAgICAgICB0aGlzLl9kYXRhID0gdGhpcy5fZGF0YS5zbGljZShpbmRleE9mRG90ICsgMSk7XG5cbiAgICAgICAgICAgICAgICBpZiAodm9ydGV4TXNnLmxlbmd0aCAhPT0gMCkge1xuICAgICAgICAgICAgICAgICAgICB0aGlzLl92b3J0ZXhNc2dzUXVldWUucHVzaCh2b3J0ZXhNc2cpO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIGluZGV4T2ZEb3QgPSB0aGlzLl9kYXRhLmluZGV4T2YoXCIuXCIpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgd2hpbGUgKHRoaXMuX3ZvcnRleE1zZ3NRdWV1ZS5sZW5ndGggIT09IDApIHtcbiAgICAgICAgICAgIHRoaXMuX3Byb2Nlc3NWb3J0ZXhNc2dzKCkgLy9cbiAgICAgICAgICAgICAgICAuY2F0Y2goKGUpID0+XG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKGBFUlJPUiBWb3J0ZXhDbGllbnRCcm93c2VyV2Vic29ja2V0OiAke2V9YCksXG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHByaXZhdGUgYXN5bmMgX3Byb2Nlc3NWb3J0ZXhNc2dzKCkge1xuICAgICAgICB3aGlsZSAodGhpcy5fdm9ydGV4TXNnc1F1ZXVlLmxlbmd0aCAhPT0gMCkge1xuICAgICAgICAgICAgY29uc3Qgdm9ydGV4TXNnID0gdGhpcy5fdm9ydGV4TXNnc1F1ZXVlLnNoaWZ0KCk7XG5cbiAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgY29uc3QgcGF5bG9hZEVudmVsb3BlID1cbiAgICAgICAgICAgICAgICAgICAgYXdhaXQgUGF5bG9hZEVudmVsb3BlLmZyb21Wb3J0ZXhNc2codm9ydGV4TXNnKTtcblxuICAgICAgICAgICAgICAgIHRoaXMuX2RlbGl2ZXJQYXlsb2FkKHBheWxvYWRFbnZlbG9wZSk7XG4gICAgICAgICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgICAgICAgICAgY29uc29sZS5sb2coXG4gICAgICAgICAgICAgICAgICAgIGRhdGVTdHIoKSArXG4gICAgICAgICAgICAgICAgICAgICAgICBgRVJST1I6IHByb2Nlc3Npbmcgdm9ydGV4TXNnXFxuJHtlfVxcbiR7dm9ydGV4TXNnfWAsXG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cblxuICAgIHByaXZhdGUgX2RlbGl2ZXJQYXlsb2FkKHBheWxvYWRFbnZlbG9wZTogUGF5bG9hZEVudmVsb3BlKSB7XG4gICAgICAgIHRoaXMucmVjZWl2ZShwYXlsb2FkRW52ZWxvcGUpO1xuICAgIH1cblxuICAgIHByb3RlY3RlZCBhc3luYyBzaHV0ZG93bigpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgdGhpcy52b3J0ZXhTdGF0dXNTZXJ2aWNlLmxvZ0RlYnVnKFxuICAgICAgICAgICAgXCJWb3J0ZXhDbGllbnRCcm93c2VyV2Vic29ja2V0LnNodXRkb3duIGNhbGxlZFwiLFxuICAgICAgICApO1xuICAgICAgICBhd2FpdCB0aGlzLmNsb3NlV2Vic29ja2V0KCk7XG4gICAgICAgIHRoaXMudm9ydGV4U3RhdHVzU2VydmljZS5sb2dEZWJ1ZyhcbiAgICAgICAgICAgIFwiVm9ydGV4Q2xpZW50QnJvd3NlcldlYnNvY2tldC5zaHV0ZG93blwiICsgXCIgcmV0dXJuZWRcIixcbiAgICAgICAgKTtcbiAgICB9XG5cbiAgICBwcml2YXRlIGFzeW5jIGNsb3NlV2Vic29ja2V0KCk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICB0aGlzLnZvcnRleFN0YXR1c1NlcnZpY2UubG9nRGVidWcoXG4gICAgICAgICAgICBcIlZvcnRleENsaWVudEJyb3dzZXJXZWJzb2NrZXQuY2xvc2VXZWJzb2NrZXQgY2FsbGVkXCIsXG4gICAgICAgICk7XG4gICAgICAgIGNvbnN0IHNvY2tldCA9IHRoaXMuc29ja2V0O1xuICAgICAgICBpZiAoc29ja2V0ICYmIHNvY2tldC5yZWFkeVN0YXRlICE9PSBXZWJTb2NrZXQuQ0xPU0VEKSB7XG4gICAgICAgICAgICBhd2FpdCBuZXcgUHJvbWlzZTx2b2lkPigocmVzb2x2ZSkgPT4ge1xuICAgICAgICAgICAgICAgIHRoaXMudm9ydGV4U3RhdHVzU2VydmljZS5sb2dEZWJ1ZyhcIldlYnNvY2tldCBjbG9zZSBzdGFydGluZ1wiKTtcbiAgICAgICAgICAgICAgICB0aGlzLnNldENsb3NpbmcoKTtcbiAgICAgICAgICAgICAgICBzb2NrZXQuYWRkRXZlbnRMaXN0ZW5lcihcbiAgICAgICAgICAgICAgICAgICAgXCJjbG9zZVwiLFxuICAgICAgICAgICAgICAgICAgICAoKSA9PiB7XG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLnZvcnRleFN0YXR1c1NlcnZpY2UubG9nSW5mbyhcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBcIldlYnNvY2tldCBjbG9zZSBjb21wbGV0ZVwiLFxuICAgICAgICAgICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHJlc29sdmUoKTtcbiAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgeyBvbmNlOiB0cnVlIH0sXG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgICAvLyBTYWZlIG9uIENPTk5FQ1RJTkcgKGFib3J0cyBoYW5kc2hha2UpIGFuZCBDTE9TSU5HIChuby1vcCk7XG4gICAgICAgICAgICAgICAgLy8gdGhlIGNsb3NlIGV2ZW50IHN0aWxsIGZpcmVzIGluIGJvdGggY2FzZXMuXG4gICAgICAgICAgICAgICAgc29ja2V0LmNsb3NlKCk7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIC8vIHNldENsb3NlZCgpIGlzIGhhbmRsZWQgYnkgdGhlIHBlcm1hbmVudCBvbkNsb3NlIGxpc3RlbmVyXG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICB0aGlzLnNldENsb3NlZCgpO1xuICAgICAgICB9XG5cbiAgICAgICAgdGhpcy5zb2NrZXQgPSBudWxsO1xuXG4gICAgICAgIHRoaXMudm9ydGV4U3RhdHVzU2VydmljZS5sb2dEZWJ1ZyhcbiAgICAgICAgICAgIFwiVm9ydGV4Q2xpZW50QnJvd3NlcldlYnNvY2tldC5jbG9zZVdlYnNvY2tldCByZXR1cm5pbmdcIixcbiAgICAgICAgKTtcbiAgICB9XG5cbiAgICBwcml2YXRlIGFzeW5jIGNyZWF0ZVNvY2tldCgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgdGhpcy52b3J0ZXhTdGF0dXNTZXJ2aWNlLmxvZ0RlYnVnKFxuICAgICAgICAgICAgXCJWb3J0ZXhDbGllbnRCcm93c2VyV2Vic29ja2V0LmNyZWF0ZVNvY2tldCBjYWxsZWRcIixcbiAgICAgICAgKTtcbiAgICAgICAgLy8gSWYgd2UncmUgYWxyZWFkeSBjb25uZWN0aW5nLCB0aGVuIGRvIG5vdGhpbmdcbiAgICAgICAgaWYgKHRoaXMuc29ja2V0ICYmIHRoaXMuc29ja2V0LnJlYWR5U3RhdGUgPT09IFdlYlNvY2tldC5DT05ORUNUSU5HKSB7XG4gICAgICAgICAgICBpZiAoIXRoaXMuaXNTaHV0ZG93bikge1xuICAgICAgICAgICAgICAgIHRoaXMudm9ydGV4U3RhdHVzU2VydmljZS5sb2dEZWJ1ZyhcbiAgICAgICAgICAgICAgICAgICAgXCJWb3J0ZXhDbGllbnRCcm93c2VyV2Vic29ja2V0LmNyZWF0ZVNvY2tldFwiICsgXCIgcmV0dXJuaW5nICMxXCIsXG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKFxuICAgICAgICAgICAgICAgICAgICBcIkFib3J0aW5nIFdlYlNvY2tldCBjb25uZWN0aW9uIGF0dGVtcHQsXCIgK1xuICAgICAgICAgICAgICAgICAgICAgICAgXCIgdGhpcyBpcyBwcm9iYWJseSBiZWNhdXNlIG9mIFZvcnRleCByZWNvbm5lY3Rpb25cIixcbiAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICAgIHRoaXMuc29ja2V0LmNsb3NlKCk7XG4gICAgICAgICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgICAgICAgICAgLy8gcGFzc1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgdGhpcy52b3J0ZXhTdGF0dXNTZXJ2aWNlLmxvZ0RlYnVnKFxuICAgICAgICAgICAgXCJWb3J0ZXhDbGllbnRCcm93c2VyV2Vic29ja2V0LmNyZWF0ZVNvY2tldCBDbG9zaW5nIG9sZCB3ZWJzb2NrZXRcIixcbiAgICAgICAgKTtcbiAgICAgICAgYXdhaXQgdGhpcy5jbG9zZVdlYnNvY2tldCgpO1xuXG4gICAgICAgIC8vIERvbid0IGNvbnRpbnVhbGx5IHJlY29ubmVjdFxuICAgICAgICBsZXQgcmVjb25uZWN0RGlmZk1zID0gRGF0ZS5ub3coKSAtIHRoaXMubGFzdFJlY29ubmVjdERhdGU7XG4gICAgICAgIGlmIChyZWNvbm5lY3REaWZmTXMgPCB0aGlzLlJFQ09OTkVDVF9CQUNLT0ZGX1NFQ09ORFMgKiAxMDAwKSB7XG4gICAgICAgICAgICB0aGlzLnZvcnRleFN0YXR1c1NlcnZpY2UubG9nRGVidWcoXG4gICAgICAgICAgICAgICAgXCJWb3J0ZXhDbGllbnRCcm93c2VyV2Vic29ja2V0LmNyZWF0ZVNvY2tldCByZXR1cm5pbmcgIzIgLSBiYWNrb2ZmXCIgK1xuICAgICAgICAgICAgICAgICAgICBcIiB0aW1lb3V0XCIsXG4gICAgICAgICAgICApO1xuICAgICAgICAgICAgc2V0VGltZW91dChcbiAgICAgICAgICAgICAgICAoKSA9PiB0aGlzLmNyZWF0ZVNvY2tldCgpLFxuICAgICAgICAgICAgICAgIHRoaXMuUkVDT05ORUNUX0JBQ0tPRkZfU0VDT05EUyAqIDEwMDAgLSByZWNvbm5lY3REaWZmTXMgKyAxMCxcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLmxhc3RSZWNvbm5lY3REYXRlID0gRGF0ZS5ub3coKTtcblxuICAgICAgICAvLyBQcmVwYXJlIHRoZSBhcmdzIHRvIHNlbmRcbiAgICAgICAgY29uc3QgYXJncyA9IHtcbiAgICAgICAgICAgIHZvcnRleFV1aWQ6IHRoaXMudXVpZCxcbiAgICAgICAgICAgIHZvcnRleE5hbWU6IHRoaXMubmFtZSxcbiAgICAgICAgfTtcblxuICAgICAgICB0aGlzLnZvcnRleFN0YXR1c1NlcnZpY2UubG9nQ29ubmVjdGlvbkluZm8oXG4gICAgICAgICAgICBgJHtkYXRlU3RyKCl9IFdlYlNvY2tldCwgY29ubmVjdGluZyB0byAke3RoaXMudXJsfWAsXG4gICAgICAgICk7XG5cbiAgICAgICAgLy8gQnJvd3NlciBXZWJTb2NrZXQgY2Fubm90IHNlbmQgY3VzdG9tIGhhbmRzaGFrZSBoZWFkZXJzOyBhdXRoIG11c3RcbiAgICAgICAgLy8gY29tZSBmcm9tIGNvb2tpZXMgb3IgVVJMLiBMb2cgd2hpY2ggaGVhZGVycyB3ZXJlIHN1cHBsaWVkIGJ1dCBkcm9wcGVkLlxuICAgICAgICBjb25zdCBoZWFkZXJLZXlzOiBzdHJpbmdbXSA9IFtdO1xuICAgICAgICBmb3IgKGNvbnN0IGtleSBvZiB0aGlzLmhlYWRlcnMua2V5cygpKSB7XG4gICAgICAgICAgICBoZWFkZXJLZXlzLnB1c2goa2V5KTtcbiAgICAgICAgfVxuICAgICAgICBpZiAoaGVhZGVyS2V5cy5sZW5ndGggIT09IDApIHtcbiAgICAgICAgICAgIHRoaXMudm9ydGV4U3RhdHVzU2VydmljZS5sb2dEZWJ1ZyhcbiAgICAgICAgICAgICAgICBcIlZvcnRleENsaWVudEJyb3dzZXJXZWJzb2NrZXQuY3JlYXRlU29ja2V0XCIgK1xuICAgICAgICAgICAgICAgICAgICBcIiBpZ25vcmluZyBoZWFkZXJzICh1bnN1cHBvcnRlZCBieSBicm93c2VyIFdlYlNvY2tldCk6IFwiICtcbiAgICAgICAgICAgICAgICAgICAgaGVhZGVyS2V5cy5qb2luKFwiLCBcIiksXG4gICAgICAgICAgICApO1xuICAgICAgICB9XG5cbiAgICAgICAgYXdhaXQgbmV3IFByb21pc2U8dm9pZD4oKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgICAgICAgbGV0IHByb21pc2VDYWxsZWQgPSBmYWxzZTtcblxuICAgICAgICAgICAgY29uc3QgcmVzb2x2ZU9uY2UgPSAoKSA9PiB7XG4gICAgICAgICAgICAgICAgaWYgKHByb21pc2VDYWxsZWQpIHJldHVybjtcbiAgICAgICAgICAgICAgICBwcm9taXNlQ2FsbGVkID0gdHJ1ZTtcbiAgICAgICAgICAgICAgICB0aGlzLnZvcnRleFN0YXR1c1NlcnZpY2UubG9nRGVidWcoXG4gICAgICAgICAgICAgICAgICAgIFwiVm9ydGV4Q2xpZW50QnJvd3NlcldlYnNvY2tldC5jcmVhdGVTb2NrZXQgY2FsbGluZyByZXNvbHZlXCIsXG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgICByZXNvbHZlKCk7XG4gICAgICAgICAgICB9O1xuXG4gICAgICAgICAgICBjb25zdCByZWplY3RPbmNlID0gKCkgPT4ge1xuICAgICAgICAgICAgICAgIGlmIChwcm9taXNlQ2FsbGVkKSByZXR1cm47XG4gICAgICAgICAgICAgICAgcHJvbWlzZUNhbGxlZCA9IHRydWU7XG4gICAgICAgICAgICAgICAgdGhpcy52b3J0ZXhTdGF0dXNTZXJ2aWNlLmxvZ0RlYnVnKFxuICAgICAgICAgICAgICAgICAgICBcIlZvcnRleENsaWVudEJyb3dzZXJXZWJzb2NrZXQuY3JlYXRlU29ja2V0IGNhbGxpbmcgcmVqZWN0XCIsXG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgICByZWplY3QoKTtcbiAgICAgICAgICAgIH07XG5cbiAgICAgICAgICAgIHRoaXMudm9ydGV4U3RhdHVzU2VydmljZS5sb2dEZWJ1ZyhcbiAgICAgICAgICAgICAgICBcIlZvcnRleENsaWVudEJyb3dzZXJXZWJzb2NrZXQuY3JlYXRlU29ja2V0IENyZWF0aW5nIHNvY2tldFwiLFxuICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIHRoaXMuc29ja2V0ID0gbmV3IFdlYlNvY2tldCh0aGlzLnVybCArIGdldEZpbHRTdHIoYXJncyksIFtdKTtcblxuICAgICAgICAgICAgdGhpcy5zb2NrZXQuYmluYXJ5VHlwZSA9IFwiYXJyYXlidWZmZXJcIjtcblxuICAgICAgICAgICAgdGhpcy5zb2NrZXQuYWRkRXZlbnRMaXN0ZW5lcihcIm9wZW5cIiwgKCkgPT4ge1xuICAgICAgICAgICAgICAgIHRoaXMudm9ydGV4U3RhdHVzU2VydmljZS5sb2dEZWJ1ZyhcbiAgICAgICAgICAgICAgICAgICAgXCJWb3J0ZXhDbGllbnRCcm93c2VyV2Vic29ja2V0LmNyZWF0ZVNvY2tldCBhZGRFdmVudExpc3RlbmVyIG9wZW5cIixcbiAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICAgIHRoaXMub25PcGVuKCk7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIHRoaXMuc29ja2V0LmFkZEV2ZW50TGlzdGVuZXIoXCJtZXNzYWdlXCIsIChldmVudCkgPT4ge1xuICAgICAgICAgICAgICAgIHRoaXMub25NZXNzYWdlKGV2ZW50LCByZXNvbHZlT25jZSk7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIHRoaXMuc29ja2V0LmFkZEV2ZW50TGlzdGVuZXIoXCJjbG9zZVwiLCAoZXZlbnQpID0+IHtcbiAgICAgICAgICAgICAgICB0aGlzLnZvcnRleFN0YXR1c1NlcnZpY2UubG9nRGVidWcoXG4gICAgICAgICAgICAgICAgICAgIFwiVm9ydGV4Q2xpZW50QnJvd3NlcldlYnNvY2tldC5jcmVhdGVTb2NrZXQgYWRkRXZlbnRMaXN0ZW5lciBjbG9zZVwiLFxuICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgICAgdGhpcy5vbkNsb3NlKGV2ZW50KTtcbiAgICAgICAgICAgICAgICByZWplY3RPbmNlKCk7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIHRoaXMuc29ja2V0LmFkZEV2ZW50TGlzdGVuZXIoXCJlcnJvclwiLCAoZXZlbnQpID0+IHtcbiAgICAgICAgICAgICAgICB0aGlzLnZvcnRleFN0YXR1c1NlcnZpY2UubG9nRGVidWcoXG4gICAgICAgICAgICAgICAgICAgIGBWb3J0ZXhDbGllbnRCcm93c2VyV2Vic29ja2V0LmNyZWF0ZVNvY2tldCBhZGRFdmVudExpc3RlbmVyIGVycm9yOiAke2V2ZW50fWAsXG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgICB0aGlzLm9uRXJyb3IoZXZlbnQsIHJlamVjdE9uY2UpO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH0pO1xuXG4gICAgICAgIHRoaXMudm9ydGV4U3RhdHVzU2VydmljZS5sb2dEZWJ1ZyhcbiAgICAgICAgICAgIFwiVm9ydGV4Q2xpZW50QnJvd3NlcldlYnNvY2tldC5jcmVhdGVTb2NrZXQgcmV0dXJuaW5nICMzXCIsXG4gICAgICAgICk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBvbk1lc3NhZ2UoZXZlbnQsIHByb21pc2VSZXNvbHZlcjogKCkgPT4gdm9pZCkge1xuICAgICAgICBpZiAoIXRoaXMuaXNPbmxpbmUpIHtcbiAgICAgICAgICAgIHRoaXMuc2V0T25saW5lKCk7XG4gICAgICAgICAgICBwcm9taXNlUmVzb2x2ZXIoKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMuYmVhdCgpO1xuXG4gICAgICAgIGlmIChldmVudC5kYXRhLmxlbmd0aCA9PSBudWxsKSB7XG4gICAgICAgICAgICB0aGlzLnZvcnRleFN0YXR1c1NlcnZpY2UubG9nQ29ubmVjdGlvbkVycm9yKFxuICAgICAgICAgICAgICAgIFwiV2ViU29ja2V0LCBXZSd2ZSByZWNlaXZlZCBhIHdlYnNvY2tldCBiaW5hcnkgbWVzc2FnZSxcIiArXG4gICAgICAgICAgICAgICAgICAgIFwiIHdlIGV4cGVjdCBhIHVuaWNvZGVcIixcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICAvLyBJZiB0aGUgc2VydmVyIHNlbmRzIHVzIGEgJy4nLCB0aGF0J3MgYSBoZWFydCBiZWF0LCByZXR1cm4gaXQuXG4gICAgICAgIGlmIChldmVudC5kYXRhID09PSBcIi5cIikge1xuICAgICAgICAgICAgdGhpcy5zb2NrZXQgIT0gbnVsbCAmJiB0aGlzLnNvY2tldC5zZW5kKFwiLlwiKTtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChldmVudC5kYXRhWzBdICE9PSBcIntcIikge1xuICAgICAgICAgICAgdGhpcy5fZGF0YSArPSBldmVudC5kYXRhO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgLy8gSXQgc3RhcnRzIHdpdGggeywgZW5zdXJlIGl0IGVuZHMgd2l0aCB9XG4gICAgICAgICAgICBpZiAoZXZlbnQuZGF0YVtldmVudC5kYXRhLmxlbmd0aCAtIDFdICE9PSBcIn1cIikge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKFxuICAgICAgICAgICAgICAgICAgICBkYXRlU3RyKCkgK1xuICAgICAgICAgICAgICAgICAgICAgICAgXCIgRVJST1IsIFBheWxvYWQgc2hvdWxkIGVuZCB3aXRoIH0sXCIgK1xuICAgICAgICAgICAgICAgICAgICAgICAgXCIgYnV0IGl0IGRvZXNuJ3Q6IFwiICtcbiAgICAgICAgICAgICAgICAgICAgICAgIGV2ZW50LmRhdGEsXG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB0aGlzLl92b3J0ZXhNc2dzUXVldWUucHVzaChldmVudC5kYXRhKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMuX3Byb2Nlc3NEYXRhKCk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBvbk9wZW4oKSB7XG4gICAgICAgIHN1cGVyXG4gICAgICAgICAgICAuc2VuZChuZXcgUGF5bG9hZEVudmVsb3BlKCkpXG4gICAgICAgICAgICAuY2F0Y2goKGUpID0+XG4gICAgICAgICAgICAgICAgY29uc29sZS5sb2coXG4gICAgICAgICAgICAgICAgICAgIGBFUlJPUiBWb3J0ZXhDbGllbnRCcm93c2VyV2Vic29ja2V0IG9uT3BlbiBTZW5kOiAke2V9YCxcbiAgICAgICAgICAgICAgICApLFxuICAgICAgICAgICAgKTtcbiAgICAgICAgdGhpcy52b3J0ZXhTdGF0dXNTZXJ2aWNlLmxvZ0Nvbm5lY3Rpb25JbmZvKFwiV2ViU29ja2V0LCBjb25uZWN0ZWRcIik7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBvbkNsb3NlKGV2ZW50KSB7XG4gICAgICAgIHRoaXMudm9ydGV4U3RhdHVzU2VydmljZS5sb2dDb25uZWN0aW9uSW5mbyhcIldlYlNvY2tldCwgY2xvc2VkXCIpO1xuICAgICAgICBpZiAoISh0aGlzLnNvY2tldCAmJiB0aGlzLnNvY2tldC5yZWFkeVN0YXRlID09PSBXZWJTb2NrZXQuT1BFTikpIHtcbiAgICAgICAgICAgIHRoaXMuc2V0Q2xvc2VkKCk7XG4gICAgICAgIH1cbiAgICAgICAgLy8gVGhlIGJhc2UgY2xhc3Mgd2lsbCByZWNvbm5lY3RcbiAgICB9XG5cbiAgICBwcml2YXRlIG9uRXJyb3IoZXZlbnQsIHJlamVjdCkge1xuICAgICAgICByZWplY3QoZXZlbnQuZXJyb3I/LnRvU3RyaW5nKCkgfHwgXCJXZWJzb2NrZXQgZmFpbGVkIHRvIGNvbm5lY3RcIik7XG4gICAgICAgIC8vIG9uQ2xvc2Ugd2lsbCBnZXQgY2FsbGVkIGFzIHdlbGxcblxuICAgICAgICAvLyBTa2lwIHRoZSBzZXNzaW9uLWV4cGlyeSBjaGVjayB3aGVuIHRoZSBzb2NrZXQgd2FzIGFscmVhZHkgb25saW5lLlxuICAgICAgICAvLyBvbkVycm9yIG9uIGFuIGVzdGFibGlzaGVkIHNvY2tldCBtZWFucyBhIHRyYW5zaWVudCBkcm9wIChBenVyZVxuICAgICAgICAvLyBwcm94eSdzIDQtbWludXRlIGlkbGUgY2xvc2UsIG5ldHdvcmsgYmxpcCwgZXRjLik7IG9uQ2xvc2UgZmlyZXNcbiAgICAgICAgLy8gbmV4dCBhbmQgdGhlIG5vcm1hbCByZXRyeSBjeWNsZSBoYW5kbGVzIHJlY29ubmVjdGlvbi4gV2l0aG91dCB0aGlzXG4gICAgICAgIC8vIGd1YXJkLCB0aGUgYXN5bmMgcHJvYmUgYmVsb3cgY2FuIHJhY2Ugd2l0aCBhIHN1Y2Nlc3NmdWwgcmVjb25uZWN0XG4gICAgICAgIC8vIGFuZCBvdmVycmlkZSBPbmxpbmUgd2l0aCBOZXR3b3JrT25saW5lTm9XZWJzb2NrZXRSZXNvdXJjZS5cbiAgICAgICAgaWYgKHRoaXMuaXNPbmxpbmUpIHJldHVybjtcblxuICAgICAgICB0aGlzLnRlc3RJZk9ubGluZUFuZExvZ2dlZE91dCgpLmNhdGNoKChlKSA9PlxuICAgICAgICAgICAgY29uc29sZS5sb2coYEVSUk9SIHRlc3RJZk9ubGluZUFuZExvZ2dlZE91dDogJHtlfWApLFxuICAgICAgICApO1xuICAgIH1cblxuICAgIHByaXZhdGUgYXN5bmMgdGVzdElmT25saW5lQW5kTG9nZ2VkT3V0KCk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICAvLyB3c3M6Ly9ob3N0Ly4uLiDihpIgaHR0cHM6Ly9ob3N0Ly4uLlxuICAgICAgICBjb25zdCBodHRwVXJsID0gXCJodHRwXCIgKyB0aGlzLl91cmwuc3Vic3RyaW5nKDIpO1xuXG4gICAgICAgIGNvbnN0IGNvbnRyb2xsZXIgPSBuZXcgQWJvcnRDb250cm9sbGVyKCk7XG4gICAgICAgIGNvbnN0IHRpbWVvdXRJZCA9IHNldFRpbWVvdXQoKCkgPT4gY29udHJvbGxlci5hYm9ydCgpLCA1MDAwKTtcblxuICAgICAgICBsZXQgcmVzcG9uc2U6IFJlc3BvbnNlO1xuICAgICAgICB0cnkge1xuICAgICAgICAgICAgLy8gcmVkaXJlY3Q6IFwibWFudWFsXCIgZ2l2ZXMgdXMgcmVzcG9uc2UudHlwZSA9PT0gXCJvcGFxdWVyZWRpcmVjdFwiXG4gICAgICAgICAgICAvLyBmb3IgYSAzMDIgaW5zdGVhZCBvZiBzaWxlbnRseSBmb2xsb3dpbmcgdG8gdGhlIEVudHJhIGxvZ2luIHBhZ2VcbiAgICAgICAgICAgIC8vIGFuZCByZXR1cm5pbmcgdGhlIGxvZ2luIEhUTUwgd2l0aCBzdGF0dXMgMjAwICh3aGljaCBpcyB3aGF0IHRoZVxuICAgICAgICAgICAgLy8gb2xkIFhNTEh0dHBSZXF1ZXN0IGRpZCDigJQgaXQgY291bGRuJ3QgZGlzdGluZ3Vpc2ggYSB2YWxpZCBzZXNzaW9uXG4gICAgICAgICAgICAvLyBmcm9tIGFuIGV4cGlyZWQgb25lKS5cbiAgICAgICAgICAgIHJlc3BvbnNlID0gYXdhaXQgZmV0Y2goaHR0cFVybCwge1xuICAgICAgICAgICAgICAgIHJlZGlyZWN0OiBcIm1hbnVhbFwiLFxuICAgICAgICAgICAgICAgIGNhY2hlOiBcIm5vLXN0b3JlXCIsXG4gICAgICAgICAgICAgICAgc2lnbmFsOiBjb250cm9sbGVyLnNpZ25hbCxcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgICAgICAvLyBOZXR3b3JrIGVycm9yIG9yIHRpbWVvdXQg4oCUIHNlcnZlciB1bnJlYWNoYWJsZS4gRG9uJ3QgY2hhbmdlXG4gICAgICAgICAgICAvLyB2b3J0ZXggc3RhdGU7IHRoZSByZXRyeSB0aW1lciB3aWxsIHRyeSBhZ2Fpbi5cbiAgICAgICAgICAgIHRoaXMudm9ydGV4U3RhdHVzU2VydmljZS5sb2dDb25uZWN0aW9uSW5mbyhcbiAgICAgICAgICAgICAgICBcIlZvcnRleENsaWVudEJyb3dzZXJXZWJzb2NrZXQ6IHNlcnZlciB1bnJlYWNoYWJsZSBkdXJpbmdcIiArXG4gICAgICAgICAgICAgICAgICAgIFwiIHNlc3Npb24gY2hlY2tcIixcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH0gZmluYWxseSB7XG4gICAgICAgICAgICBjbGVhclRpbWVvdXQodGltZW91dElkKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChyZXNwb25zZS50eXBlID09PSBcIm9wYXF1ZXJlZGlyZWN0XCIpIHtcbiAgICAgICAgICAgIC8vIFByb3h5IHJlZGlyZWN0ZWQgdG8gdGhlIEVudHJhIGxvZ2luIHBhZ2Ug4oCUIHNlc3Npb24gZXhwaXJlZC5cbiAgICAgICAgICAgIC8vIFVucmVnaXN0ZXIgdGhlIHNlcnZpY2Ugd29ya2VyIGFuZCByZWxvYWQgc28gdGhlIGJyb3dzZXJcbiAgICAgICAgICAgIC8vIHJlLWF1dGhlbnRpY2F0ZXMgZnJvbSBzY3JhdGNoLlxuICAgICAgICAgICAgdGhpcy52b3J0ZXhTdGF0dXNTZXJ2aWNlLmxvZ0Nvbm5lY3Rpb25FcnJvcihcbiAgICAgICAgICAgICAgICBcIlZvcnRleENsaWVudEJyb3dzZXJXZWJzb2NrZXQ6IHNlc3Npb24gZXhwaXJlZCxcIiArXG4gICAgICAgICAgICAgICAgICAgIFwiIHVucmVnaXN0ZXJpbmcgc2VydmljZSB3b3JrZXIgYW5kIHJlbG9hZGluZ1wiLFxuICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgY29uc3QgcmVnaXN0cmF0aW9ucyA9XG4gICAgICAgICAgICAgICAgICAgIGF3YWl0IG5hdmlnYXRvci5zZXJ2aWNlV29ya2VyLmdldFJlZ2lzdHJhdGlvbnMoKTtcbiAgICAgICAgICAgICAgICBhd2FpdCBQcm9taXNlLmFsbChyZWdpc3RyYXRpb25zLm1hcCgocikgPT4gci51bnJlZ2lzdGVyKCkpKTtcbiAgICAgICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICAgICAgICAvLyBJZ25vcmUg4oCUIHJlbG9hZCBzdGlsbCBwcm9jZWVkcy5cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHdpbmRvdy5sb2NhdGlvbi5yZWxvYWQoKTtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIFNlcnZlciBpcyB1cCBhbmQgdGhlIHNlc3Npb24gaXMgdmFsaWQg4oCUIHRoZSBXZWJTb2NrZXQgZmFpbHVyZVxuICAgICAgICAvLyBpcyB0cmFuc2llbnQuIFRoZSByZXRyeSB0aW1lciB3aWxsIHJlY29ubmVjdDsgbm8gc3RhdGUgY2hhbmdlXG4gICAgICAgIC8vIG5lZWRlZCBoZXJlLlxuICAgICAgICB0aGlzLnZvcnRleFN0YXR1c1NlcnZpY2UubG9nQ29ubmVjdGlvbkluZm8oXG4gICAgICAgICAgICBcIlZvcnRleENsaWVudEJyb3dzZXJXZWJzb2NrZXQ6IHNlcnZlciBvbmxpbmUsIHNlc3Npb24gdmFsaWQsXCIgK1xuICAgICAgICAgICAgICAgIFwiIFdlYlNvY2tldCBlcnJvciBpcyB0cmFuc2llbnRcIixcbiAgICAgICAgKTtcbiAgICB9XG59XG4iXX0=