@synerty/vortexjs
Version:
Custom observable data serialisation and routing based on Angular 2+
265 lines • 40.5 kB
JavaScript
import { VortexClientABC, VortexClientStateE } from "./VortexClientABC";
import { dateStr, getFiltStr } from "./UtilMisc";
import { PayloadEnvelope } from "./PayloadEnvelope";
export class VortexClientWebsocket extends VortexClientABC {
Socket = WebSocket || MozWebSocket;
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) {
super(vortexStatusService, url, vortexClientName);
}
get isReady() {
return (this.socket != null && this.socket.readyState === this.Socket.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("VortexClientWebsocket.reconnect called");
if (this.reconnectingInProgress === true) {
this.vortexStatusService.logDebug("VortexClientWebsocket.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("VortexClientWebsocket.reconnect returning #2");
}
sendVortexMsg(vortexMsgs) {
this.unsentBuffer.add(vortexMsgs);
this.sendMessages();
}
sendMessages() {
while (this.unsentBuffer.length !== 0) {
if (!this.isReady)
return;
let 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 VortexClientWebsocket: ${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("VortexClientWebsocket.shutdown called");
await this.closeWebsocket();
this.vortexStatusService.logDebug("VortexClientWebsocket.shutdown" + " returned");
}
async closeWebsocket() {
this.vortexStatusService.logDebug("VortexClientWebsocket.closeWebsocket called");
// If we're open then close
if (this.socket && this.socket.readyState === this.Socket.OPEN) {
await new Promise((resolve, reject) => {
this.vortexStatusService.logDebug("Websocket close starting");
this.setClosing();
this.socket.addEventListener("close", () => {
this.vortexStatusService.logInfo("Websocket close complete");
this.setClosed();
resolve();
});
this.socket.close();
});
}
this.socket = null;
this.vortexStatusService.logDebug("VortexClientWebsocket.closeWebsocket returning");
}
async createSocket() {
this.vortexStatusService.logDebug("VortexClientWebsocket.createSocket called");
// If we're already connecting, then do nothing
if (this.socket && this.socket.readyState === this.Socket.CONNECTING) {
if (!this.isShutdown) {
this.vortexStatusService.logDebug("VortexClientWebsocket.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("VortexClientWebsocket.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("VortexClientWebsocket.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}`);
await new Promise((resolve, reject) => {
// Construct + open the socket
let promiseCalled = false;
this.vortexStatusService.logDebug("VortexClientWebsocket.createSocket Creating socket");
this.socket = new this.Socket(this.url + getFiltStr(args), []);
this.socket.binaryType = "arraybuffer";
this.socket.addEventListener("message", (event) => {
this.onMessage(event, () => {
this.vortexStatusService.logDebug("VortexClientWebsocket.createSocket calling resolve" +
" from message");
resolve();
promiseCalled = true;
});
});
this.socket.addEventListener("close", (event) => {
this.vortexStatusService.logDebug("VortexClientWebsocket.createSocket addEventListener close");
this.onClose(event);
if (!promiseCalled) {
this.vortexStatusService.logDebug("VortexClientWebsocket.createSocket calling reject" +
" from close");
promiseCalled = true;
reject();
}
});
this.socket.addEventListener("error", (event) => {
this.vortexStatusService.logDebug(`VortexClientWebsocket.createSocket addEventListener error: ${event}`);
this.onError(event, () => {
this.vortexStatusService.logDebug("VortexClientWebsocket.createSocket calling reject" +
" from error");
reject();
promiseCalled = true;
});
});
this.socket.addEventListener("open", (event) => {
this.vortexStatusService.logDebug("VortexClientWebsocket.createSocket addEventListener open");
this.onOpen(event);
});
});
this.vortexStatusService.logDebug("VortexClientWebsocket.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(event) {
super
.send(new PayloadEnvelope())
.catch((e) => console.log(`ERROR VortexClientWebsocket onOpen Send: ${e}`));
this.vortexStatusService.logConnectionInfo("WebSocket, connecting");
}
onClose(event) {
this.vortexStatusService.logConnectionInfo("WebSocket, closed");
if (!(this.socket && this.socket.readyState === this.Socket.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
// Check if the server is still up, if it is we've been logged out
this.testIfOnlineAndLoggedOut()
.then(() => console.log("Server is unreachable"))
.catch(() => {
this.vortexStatusService.logConnectionError("Vortex Websocket doesn't connect but server is online," +
" Marking vortex as logged out");
this.vortexStatusService.setHttpSessionLoggedOut();
return;
});
}
testIfOnlineAndLoggedOut() {
return new Promise((resolve, reject) => {
const httpUrl = "http" + this._url.substring(2);
let xmlHttp = new XMLHttpRequest();
xmlHttp.timeout = 2000;
xmlHttp.responseType = "document";
xmlHttp.onload = function (event) {
reject();
};
xmlHttp.onerror = function (event) {
resolve();
};
xmlHttp.open("GET", httpUrl, true); // true for asynchronous
xmlHttp.send(null);
});
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiVm9ydGV4Q2xpZW50V2Vic29ja2V0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL3ZvcnRleC9Wb3J0ZXhDbGllbnRXZWJzb2NrZXQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLGVBQWUsRUFBRSxrQkFBa0IsRUFBRSxNQUFNLG1CQUFtQixDQUFDO0FBRXhFLE9BQU8sRUFBRSxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sWUFBWSxDQUFDO0FBQ2pELE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQztBQU9wRCxNQUFNLE9BQU8scUJBQXNCLFNBQVEsZUFBZTtJQUM5QyxNQUFNLEdBQUcsU0FBUyxJQUFJLFlBQVksQ0FBQztJQUNuQyxNQUFNLEdBQXFCLElBQUksQ0FBQztJQUVoQyxpQkFBaUIsR0FBVyxJQUFJLENBQUMsS0FBSyxDQUFDLGFBQWEsQ0FBQyxDQUFDO0lBRXRELFlBQVksR0FBYSxFQUFFLENBQUM7SUFFcEMsd0NBQXdDO0lBQ3hDLDZEQUE2RDtJQUM3RCxvRUFBb0U7SUFDNUQsc0JBQXNCLEdBQVksS0FBSyxDQUFDO0lBRXhDLEtBQUssR0FBVyxFQUFFLENBQUM7SUFDbkIsZ0JBQWdCLEdBQWEsRUFBRSxDQUFDO0lBRXhDLFlBQ0ksbUJBQXdDLEVBQ3hDLEdBQVcsRUFDWCxnQkFBd0I7UUFFeEIsS0FBSyxDQUFDLG1CQUFtQixFQUFFLEdBQUcsRUFBRSxnQkFBZ0IsQ0FBQyxDQUFDO0lBQ3RELENBQUM7SUFFRCxJQUFJLE9BQU87UUFDUCxPQUFPLENBQ0gsSUFBSSxDQUFDLE1BQU0sSUFBSSxJQUFJLElBQUksSUFBSSxDQUFDLE1BQU0sQ0FBQyxVQUFVLEtBQUssSUFBSSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQ3JFLENBQUM7SUFDTixDQUFDO0lBRUQsZ0JBQWdCO0lBQ2hCLElBQUksQ0FBQyxlQUFvRDtRQUNyRCxJQUFJLENBQUMsSUFBSSxDQUFDLE9BQU8s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QUFDLHdCQUF3QixFQUFFO2FBQzFCLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLHVCQUF1QixDQUFDLENBQUM7YUFDaEQsS0FBSyxDQUFDLEdBQUcsRUFBRTtZQUNSLElBQUksQ0FBQyxtQkFBbUIsQ0FBQyxrQkFBa0IsQ0FDdkMsd0RBQXdEO2dCQUNwRCwrQkFBK0IsQ0FDdEMsQ0FBQztZQUNGLElBQUksQ0FBQyxtQkFBbUIsQ0FBQyx1QkFBdUIsRUFBRSxDQUFDO1lBQ25ELE9BQU87UUFDWCxDQUFDLENBQUMsQ0FBQztJQUNYLENBQUM7SUFFTyx3QkFBd0I7UUFDNUIsT0FBTyxJQUFJLE9BQU8sQ0FBTyxDQUFDLE9BQU8sRUFBRSxNQUFNLEVBQUUsRUFBRTtZQUN6QyxNQUFNLE9BQU8sR0FBRyxNQUFNLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLENBQUM7WUFDaEQsSUFBSSxPQUFPLEdBQUcsSUFBSSxjQUFjLEVBQUUsQ0FBQztZQUNuQyxPQUFPLENBQUMsT0FBTyxHQUFHLElBQUksQ0FBQztZQUN2QixPQUFPLENBQUMsWUFBWSxHQUFHLFVBQVUsQ0FBQztZQUNsQyxPQUFPLENBQUMsTUFBTSxHQUFHLFVBQVUsS0FBSztnQkFDNUIsTUFBTSxFQUFFLENBQUM7WUFDYixDQUFDLENBQUM7WUFDRixPQUFPLENBQUMsT0FBTyxHQUFHLFVBQVUsS0FBSztnQkFDN0IsT0FBTyxFQUFFLENBQUM7WUFDZCxDQUFDLENBQUM7WUFDRixPQUFPLENBQUMsSUFBSSxDQUFDLEtBQUssRUFBRSxPQUFPLEVBQUUsSUFBSSxDQUFDLENBQUMsQ0FBQyx3QkFBd0I7WUFDNUQsT0FBTyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUN2QixDQUFDLENBQUMsQ0FBQztJQUNQLENBQUM7Q0FDSiIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IFZvcnRleENsaWVudEFCQywgVm9ydGV4Q2xpZW50U3RhdGVFIH0gZnJvbSBcIi4vVm9ydGV4Q2xpZW50QUJDXCI7XG5pbXBvcnQgeyBWb3J0ZXhTdGF0dXNTZXJ2aWNlIH0gZnJvbSBcIi4vVm9ydGV4U3RhdHVzU2VydmljZVwiO1xuaW1wb3J0IHsgZGF0ZVN0ciwgZ2V0RmlsdFN0ciB9IGZyb20gXCIuL1V0aWxNaXNjXCI7XG5pbXBvcnQgeyBQYXlsb2FkRW52ZWxvcGUgfSBmcm9tIFwiLi9QYXlsb2FkRW52ZWxvcGVcIjtcblxuLy8gaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZW4tVVMvZG9jcy9XZWIvQVBJL1dlYlNvY2tldFxuXG5kZWNsYXJlIGNvbnN0IFdlYlNvY2tldDogYW55O1xuZGVjbGFyZSBjb25zdCBNb3pXZWJTb2NrZXQ6IGFueTtcblxuZXhwb3J0IGNsYXNzIFZvcnRleENsaWVudFdlYnNvY2tldCBleHRlbmRzIFZvcnRleENsaWVudEFCQyB7XG4gICAgcHJpdmF0ZSBTb2NrZXQgPSBXZWJTb2NrZXQgfHwgTW96V2ViU29ja2V0O1xuICAgIHByaXZhdGUgc29ja2V0OiBXZWJTb2NrZXQgfCBudWxsID0gbnVsbDtcblxuICAgIHByaXZhdGUgbGFzdFJlY29ubmVjdERhdGU6IG51bWJlciA9IERhdGUucGFyc2UoXCIwMS1KYW4tMjAxN1wiKTtcblxuICAgIHByaXZhdGUgdW5zZW50QnVmZmVyOiBzdHJpbmdbXSA9IFtdO1xuXG4gICAgLy8gTWFrZSBub3RlIG9mIHdoZW4gd2UncmUgcmVjb25uZWN0aW5nLFxuICAgIC8vIG90aGVyd2lzZSBjbG9zaW5nIHRoZSBsYXN0IHdlYnNvY2tldCBjYXVzZXMgYSByZWNvbm5lY3Rpb25cbiAgICAvLyBjYWxsIHdoaWNoIHRoZW4gY2xvc2VzIHRoZSBjdXJyZW50IHNvY2tldCBiZWluZyBvcGVuIChpbiAgYSBsb29wKVxuICAgIHByaXZhdGUgcmVjb25uZWN0aW5nSW5Qcm9ncmVzczogYm9vbGVhbiA9IGZhbHNlO1xuXG4gICAgcHJpdmF0ZSBfZGF0YTogc3RyaW5nID0gXCJcIjtcbiAgICBwcml2YXRlIF92b3J0ZXhNc2dzUXVldWU6IHN0cmluZ1tdID0gW107XG5cbiAgICBjb25zdHJ1Y3RvcihcbiAgICAgICAgdm9ydGV4U3RhdHVzU2VydmljZTogVm9ydGV4U3RhdHVzU2VydmljZSxcbiAgICAgICAgdXJsOiBzdHJpbmcsXG4gICAgICAgIHZvcnRleENsaWVudE5hbWU6IHN0cmluZyxcbiAgICApIHtcbiAgICAgICAgc3VwZXIodm9ydGV4U3RhdHVzU2VydmljZSwgdXJsLCB2b3J0ZXhDbGllbnROYW1lKTtcbiAgICB9XG5cbiAgICBnZXQgaXNSZWFkeSgpOiBib29sZWFuIHtcbiAgICAgICAgcmV0dXJuIChcbiAgICAgICAgICAgIHRoaXMuc29ja2V0ICE9IG51bGwgJiYgdGhpcy5zb2NrZXQucmVhZHlTdGF0ZSA9PT0gdGhpcy5Tb2NrZXQuT1BFTlxuICAgICAgICApO1xuICAgIH1cblxuICAgIC8vIE9WRVJSSURFIFNlbmRcbiAgICBzZW5kKHBheWxvYWRFbnZlbG9wZTogUGF5bG9hZEVudmVsb3BlIHwgUGF5bG9hZEVudmVsb3BlW10pOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgaWYgKCF0aGlzLmlzUmVhZHkpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihcIldlYnNvY2tlZCB2b3J0ZXggaXMgbm90IG9ubGluZS5cIik7XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gc3VwZXIuc2VuZChwYXlsb2FkRW52ZWxvcGUpO1xuICAgIH1cblxuICAgIC8vIE9WRVJSSURFIHJlY29ubmVjdFxuICAgIGFzeW5jIHJlY29ubmVjdCgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgdGhpcy52b3J0ZXhTdGF0dXNTZXJ2aWNlLmxvZ0RlYnVnKFxuICAgICAgICAgICAgXCJWb3J0ZXhDbGllbnRXZWJzb2NrZXQucmVjb25uZWN0IGNhbGxlZFwiLFxuICAgICAgICApO1xuXG4gICAgICAgIGlmICh0aGlzLnJlY29ubmVjdGluZ0luUHJvZ3Jlc3MgPT09IHRydWUpIHtcbiAgICAgICAgICAgIHRoaXMudm9ydGV4U3RhdHVzU2VydmljZS5sb2dEZWJ1ZyhcbiAgICAgICAgICAgICAgICBcIlZvcnRleENsaWVudFdlYnNvY2tldC5yZWNvbm5lY3QgcmV0dXJuaW5nICMxXCIsXG4gICAgICAgICAgICApO1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIHRoaXMuY2xlYXJCZWF0VGltZXIoKTtcbiAgICAgICAgdGhpcy5fdm9ydGV4U3RhdGUgPSBWb3J0ZXhDbGllbnRTdGF0ZUUuSWRsZTtcbiAgICAgICAgdGhpcy5yZWNvbm5lY3RpbmdJblByb2dyZXNzID0gdHJ1ZTtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGF3YWl0IHRoaXMuY3JlYXRlU29ja2V0KCk7XG4gICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoYCR7ZGF0ZVN0cigpfSBGYWlsZWQgdG8gY29ubmVjdCB3ZWJzb2NrZXQgJHtlfWApO1xuICAgICAgICB9XG4gICAgICAgIHRoaXMucmVjb25uZWN0aW5nSW5Qcm9ncmVzcyA9IGZhbHNlO1xuICAgICAgICB0aGlzLnJlc3RhcnRUaW1lcigpO1xuXG4gICAgICAgIHRoaXMudm9ydGV4U3RhdHVzU2VydmljZS5sb2dEZWJ1ZyhcbiAgICAgICAgICAgIFwiVm9ydGV4Q2xpZW50V2Vic29ja2V0LnJlY29ubmVjdCByZXR1cm5pbmcgIzJcIixcbiAgICAgICAgKTtcbiAgICB9XG5cbiAgICBwcm90ZWN0ZWQgc2VuZFZvcnRleE1zZyh2b3J0ZXhNc2dzOiBzdHJpbmdbXSk6IHZvaWQge1xuICAgICAgICB0aGlzLnVuc2VudEJ1ZmZlci5hZGQodm9ydGV4TXNncyk7XG5cbiAgICAgICAgdGhpcy5zZW5kTWVzc2FnZXMoKTtcbiAgICB9XG5cbiAgICBwcml2YXRlIHNlbmRNZXNzYWdlcygpIHtcbiAgICAgICAgd2hpbGUgKHRoaXMudW5zZW50QnVmZmVyLmxlbmd0aCAhPT0gMCkge1xuICAgICAgICAgICAgaWYgKCF0aGlzLmlzUmVhZHkpIHJldHVybjtcblxuICAgICAgICAgICAgbGV0IHZvcnRleE1zZyA9IHRoaXMudW5zZW50QnVmZmVyLnNoaWZ0KCk7XG4gICAgICAgICAgICB0aGlzLnNvY2tldC5zZW5kKHZvcnRleE1zZyArIFwiLlwiKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHByaXZhdGUgX3Byb2Nlc3NEYXRhKCk6IHZvaWQge1xuICAgICAgICBpZiAodGhpcy5fZGF0YSkge1xuICAgICAgICAgICAgbGV0IGluZGV4T2ZEb3Q6IG51bWJlciA9IHRoaXMuX2RhdGEuaW5kZXhPZihcIi5cIik7XG4gICAgICAgICAgICB3aGlsZSAoaW5kZXhPZkRvdCAhPT0gLTEpIHtcbiAgICAgICAgICAgICAgICBjb25zdCB2b3J0ZXhNc2cgPSB0aGlzLl9kYXRhLnNsaWNlKDAsIGluZGV4T2ZEb3QpO1xuICAgICAgICAgICAgICAgIHRoaXMuX2RhdGEgPSB0aGlzLl9kYXRhLnNsaWNlKGluZGV4T2ZEb3QgKyAxKTtcblxuICAgICAgICAgICAgICAgIGlmICh2b3J0ZXhNc2cubGVuZ3RoICE9PSAwKSB7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuX3ZvcnRleE1zZ3NRdWV1ZS5wdXNoKHZvcnRleE1zZyk7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgaW5kZXhPZkRvdCA9IHRoaXMuX2RhdGEuaW5kZXhPZihcIi5cIik7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICB3aGlsZSAodGhpcy5fdm9ydGV4TXNnc1F1ZXVlLmxlbmd0aCAhPT0gMCkge1xuICAgICAgICAgICAgdGhpcy5fcHJvY2Vzc1ZvcnRleE1zZ3MoKSAvL1xuICAgICAgICAgICAgICAgIC5jYXRjaCgoZSkgPT4gY29uc29sZS5sb2coYEVSUk9SIFZvcnRleENsaWVudFdlYnNvY2tldDogJHtlfWApKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHByaXZhdGUgYXN5bmMgX3Byb2Nlc3NWb3J0ZXhNc2dzKCkge1xuICAgICAgICB3aGlsZSAodGhpcy5fdm9ydGV4TXNnc1F1ZXVlLmxlbmd0aCAhPT0gMCkge1xuICAgICAgICAgICAgY29uc3Qgdm9ydGV4TXNnID0gdGhpcy5fdm9ydGV4TXNnc1F1ZXVlLnNoaWZ0KCk7XG5cbiAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgY29uc3QgcGF5bG9hZEVudmVsb3BlID1cbiAgICAgICAgICAgICAgICAgICAgYXdhaXQgUGF5bG9hZEVudmVsb3BlLmZyb21Wb3J0ZXhNc2codm9ydGV4TXNnKTtcblxuICAgICAgICAgICAgICAgIHRoaXMuX2RlbGl2ZXJQYXlsb2FkKHBheWxvYWRFbnZlbG9wZSk7XG4gICAgICAgICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgICAgICAgICAgY29uc29sZS5sb2coXG4gICAgICAgICAgICAgICAgICAgIGRhdGVTdHIoKSArXG4gICAgICAgICAgICAgICAgICAgICAgICBgRVJST1I6IHByb2Nlc3Npbmcgdm9ydGV4TXNnXFxuJHtlfVxcbiR7dm9ydGV4TXNnfWAsXG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cblxuICAgIHByaXZhdGUgX2RlbGl2ZXJQYXlsb2FkKHBheWxvYWRFbnZlbG9wZTogUGF5bG9hZEVudmVsb3BlKSB7XG4gICAgICAgIHRoaXMucmVjZWl2ZShwYXlsb2FkRW52ZWxvcGUpO1xuICAgIH1cblxuICAgIHByb3RlY3RlZCBhc3luYyBzaHV0ZG93bigpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgdGhpcy52b3J0ZXhTdGF0dXNTZXJ2aWNlLmxvZ0RlYnVnKFxuICAgICAgICAgICAgXCJWb3J0ZXhDbGllbnRXZWJzb2NrZXQuc2h1dGRvd24gY2FsbGVkXCIsXG4gICAgICAgICk7XG4gICAgICAgIGF3YWl0IHRoaXMuY2xvc2VXZWJzb2NrZXQoKTtcbiAgICAgICAgdGhpcy52b3J0ZXhTdGF0dXNTZXJ2aWNlLmxvZ0RlYnVnKFxuICAgICAgICAgICAgXCJWb3J0ZXhDbGllbnRXZWJzb2NrZXQuc2h1dGRvd25cIiArIFwiIHJldHVybmVkXCIsXG4gICAgICAgICk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBhc3luYyBjbG9zZVdlYnNvY2tldCgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgdGhpcy52b3J0ZXhTdGF0dXNTZXJ2aWNlLmxvZ0RlYnVnKFxuICAgICAgICAgICAgXCJWb3J0ZXhDbGllbnRXZWJzb2NrZXQuY2xvc2VXZWJzb2NrZXQgY2FsbGVkXCIsXG4gICAgICAgICk7XG4gICAgICAgIC8vIElmIHdlJ3JlIG9wZW4gdGhlbiBjbG9zZVxuICAgICAgICBpZiAodGhpcy5zb2NrZXQgJiYgdGhpcy5zb2NrZXQucmVhZHlTdGF0ZSA9PT0gdGhpcy5Tb2NrZXQuT1BFTikge1xuICAgICAgICAgICAgYXdhaXQgbmV3IFByb21pc2U8dm9pZD4oKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgICAgICAgICAgIHRoaXMudm9ydGV4U3RhdHVzU2VydmljZS5sb2dEZWJ1ZyhcIldlYnNvY2tldCBjbG9zZSBzdGFydGluZ1wiKTtcbiAgICAgICAgICAgICAgICB0aGlzLnNldENsb3NpbmcoKTtcbiAgICAgICAgICAgICAgICB0aGlzLnNvY2tldC5hZGRFdmVudExpc3RlbmVyKFwiY2xvc2VcIiwgKCkgPT4ge1xuICAgICAgICAgICAgICAgICAgICB0aGlzLnZvcnRleFN0YXR1c1NlcnZpY2UubG9nSW5mbyhcbiAgICAgICAgICAgICAgICAgICAgICAgIFwiV2Vic29ja2V0IGNsb3NlIGNvbXBsZXRlXCIsXG4gICAgICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuc2V0Q2xvc2VkKCk7XG4gICAgICAgICAgICAgICAgICAgIHJlc29sdmUoKTtcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICB0aGlzLnNvY2tldC5jbG9zZSgpO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLnNvY2tldCA9IG51bGw7XG5cbiAgICAgICAgdGhpcy52b3J0ZXhTdGF0dXNTZXJ2aWNlLmxvZ0RlYnVnKFxuICAgICAgICAgICAgXCJWb3J0ZXhDbGllbnRXZWJzb2NrZXQuY2xvc2VXZWJzb2NrZXQgcmV0dXJuaW5nXCIsXG4gICAgICAgICk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBhc3luYyBjcmVhdGVTb2NrZXQoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIHRoaXMudm9ydGV4U3RhdHVzU2VydmljZS5sb2dEZWJ1ZyhcbiAgICAgICAgICAgIFwiVm9ydGV4Q2xpZW50V2Vic29ja2V0LmNyZWF0ZVNvY2tldCBjYWxsZWRcIixcbiAgICAgICAgKTtcbiAgICAgICAgLy8gSWYgd2UncmUgYWxyZWFkeSBjb25uZWN0aW5nLCB0aGVuIGRvIG5vdGhpbmdcbiAgICAgICAgaWYgKHRoaXMuc29ja2V0ICYmIHRoaXMuc29ja2V0LnJlYWR5U3RhdGUgPT09IHRoaXMuU29ja2V0LkNPTk5FQ1RJTkcpIHtcbiAgICAgICAgICAgIGlmICghdGhpcy5pc1NodXRkb3duKSB7XG4gICAgICAgICAgICAgICAgdGhpcy52b3J0ZXhTdGF0dXNTZXJ2aWNlLmxvZ0RlYnVnKFxuICAgICAgICAgICAgICAgICAgICBcIlZvcnRleENsaWVudFdlYnNvY2tldC5jcmVhdGVTb2NrZXRcIiArIFwiIHJldHVybmluZyAjMVwiLFxuICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmxvZyhcbiAgICAgICAgICAgICAgICAgICAgXCJBYm9ydGluZyBXZWJTb2NrZXQgY29ubmVjdGlvbiBhdHRlbXB0LFwiICtcbiAgICAgICAgICAgICAgICAgICAgICAgIFwiIHRoaXMgaXMgcHJvYmFibHkgYmVjYXVzZSBvZiBWb3J0ZXggcmVjb25uZWN0aW9uXCIsXG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgICB0aGlzLnNvY2tldC5jbG9zZSgpO1xuICAgICAgICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICAgICAgICAgIC8vIHBhc3NcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMudm9ydGV4U3RhdHVzU2VydmljZS5sb2dEZWJ1ZyhcbiAgICAgICAgICAgIFwiVm9ydGV4Q2xpZW50V2Vic29ja2V0LmNyZWF0ZVNvY2tldCBDbG9zaW5nIG9sZCB3ZWJzb2NrZXRcIixcbiAgICAgICAgKTtcbiAgICAgICAgYXdhaXQgdGhpcy5jbG9zZVdlYnNvY2tldCgpO1xuXG4gICAgICAgIC8vIERvbid0IGNvbnRpbnVhbGx5IHJlY29ubmVjdFxuICAgICAgICBsZXQgcmVjb25uZWN0RGlmZk1zID0gRGF0ZS5ub3coKSAtIHRoaXMubGFzdFJlY29ubmVjdERhdGU7XG4gICAgICAgIGlmIChyZWNvbm5lY3REaWZmTXMgPCB0aGlzLlJFQ09OTkVDVF9CQUNLT0ZGX1NFQ09ORFMgKiAxMDAwKSB7XG4gICAgICAgICAgICB0aGlzLnZvcnRleFN0YXR1c1NlcnZpY2UubG9nRGVidWcoXG4gICAgICAgICAgICAgICAgXCJWb3J0ZXhDbGllbnRXZWJzb2NrZXQuY3JlYXRlU29ja2V0IHJldHVybmluZyAjMiAtIGJhY2tvZmZcIiArXG4gICAgICAgICAgICAgICAgICAgIFwiIHRpbWVvdXRcIixcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgICBzZXRUaW1lb3V0KFxuICAgICAgICAgICAgICAgICgpID0+IHRoaXMuY3JlYXRlU29ja2V0KCksXG4gICAgICAgICAgICAgICAgdGhpcy5SRUNPTk5FQ1RfQkFDS09GRl9TRUNPTkRTICogMTAwMCAtIHJlY29ubmVjdERpZmZNcyArIDEwLFxuICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMubGFzdFJlY29ubmVjdERhdGUgPSBEYXRlLm5vdygpO1xuXG4gICAgICAgIC8vIFByZXBhcmUgdGhlIGFyZ3MgdG8gc2VuZFxuICAgICAgICBjb25zdCBhcmdzID0ge1xuICAgICAgICAgICAgdm9ydGV4VXVpZDogdGhpcy51dWlkLFxuICAgICAgICAgICAgdm9ydGV4TmFtZTogdGhpcy5uYW1lLFxuICAgICAgICB9O1xuXG4gICAgICAgIHRoaXMudm9ydGV4U3RhdHVzU2VydmljZS5sb2dDb25uZWN0aW9uSW5mbyhcbiAgICAgICAgICAgIGAke2RhdGVTdHIoKX0gV2ViU29ja2V0LCBjb25uZWN0aW5nIHRvICR7dGhpcy51cmx9YCxcbiAgICAgICAgKTtcblxuICAgICAgICBhd2FpdCBuZXcgUHJvbWlzZTx2b2lkPigocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICAgICAgICAvLyBDb25zdHJ1Y3QgKyBvcGVuIHRoZSBzb2NrZXRcbiAgICAgICAgICAgIGxldCBwcm9taXNlQ2FsbGVkID0gZmFsc2U7XG5cbiAgICAgICAgICAgIHRoaXMudm9ydGV4U3RhdHVzU2VydmljZS5sb2dEZWJ1ZyhcbiAgICAgICAgICAgICAgICBcIlZvcnRleENsaWVudFdlYnNvY2tldC5jcmVhdGVTb2NrZXQgQ3JlYXRpbmcgc29ja2V0XCIsXG4gICAgICAgICAgICApO1xuICAgICAgICAgICAgdGhpcy5zb2NrZXQgPSBuZXcgdGhpcy5Tb2NrZXQodGhpcy51cmwgKyBnZXRGaWx0U3RyKGFyZ3MpLCBbXSk7XG5cbiAgICAgICAgICAgIHRoaXMuc29ja2V0LmJpbmFyeVR5cGUgPSBcImFycmF5YnVmZmVyXCI7XG5cbiAgICAgICAgICAgIHRoaXMuc29ja2V0LmFkZEV2ZW50TGlzdGVuZXIoXCJtZXNzYWdlXCIsIChldmVudCkgPT4ge1xuICAgICAgICAgICAgICAgIHRoaXMub25NZXNzYWdlKGV2ZW50LCAoKSA9PiB7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMudm9ydGV4U3RhdHVzU2VydmljZS5sb2dEZWJ1ZyhcbiAgICAgICAgICAgICAgICAgICAgICAgIFwiVm9ydGV4Q2xpZW50V2Vic29ja2V0LmNyZWF0ZVNvY2tldCBjYWxsaW5nIHJlc29sdmVcIiArXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgXCIgZnJvbSBtZXNzYWdlXCIsXG4gICAgICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgICAgICAgIHJlc29sdmUoKTtcbiAgICAgICAgICAgICAgICAgICAgcHJvbWlzZUNhbGxlZCA9IHRydWU7XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIHRoaXMuc29ja2V0LmFkZEV2ZW50TGlzdGVuZXIoXCJjbG9zZVwiLCAoZXZlbnQpID0+IHtcbiAgICAgICAgICAgICAgICB0aGlzLnZvcnRleFN0YXR1c1NlcnZpY2UubG9nRGVidWcoXG4gICAgICAgICAgICAgICAgICAgIFwiVm9ydGV4Q2xpZW50V2Vic29ja2V0LmNyZWF0ZVNvY2tldCBhZGRFdmVudExpc3RlbmVyIGNsb3NlXCIsXG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgICB0aGlzLm9uQ2xvc2UoZXZlbnQpO1xuICAgICAgICAgICAgICAgIGlmICghcHJvbWlzZUNhbGxlZCkge1xuICAgICAgICAgICAgICAgICAgICB0aGlzLnZvcnRleFN0YXR1c1NlcnZpY2UubG9nRGVidWcoXG4gICAgICAgICAgICAgICAgICAgICAgICBcIlZvcnRleENsaWVudFdlYnNvY2tldC5jcmVhdGVTb2NrZXQgY2FsbGluZyByZWplY3RcIiArXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgXCIgZnJvbSBjbG9zZVwiLFxuICAgICAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICAgICAgICBwcm9taXNlQ2FsbGVkID0gdHJ1ZTtcbiAgICAgICAgICAgICAgICAgICAgcmVqZWN0KCk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB0aGlzLnNvY2tldC5hZGRFdmVudExpc3RlbmVyKFwiZXJyb3JcIiwgKGV2ZW50KSA9PiB7XG4gICAgICAgICAgICAgICAgdGhpcy52b3J0ZXhTdGF0dXNTZXJ2aWNlLmxvZ0RlYnVnKFxuICAgICAgICAgICAgICAgICAgICBgVm9ydGV4Q2xpZW50V2Vic29ja2V0LmNyZWF0ZVNvY2tldCBhZGRFdmVudExpc3RlbmVyIGVycm9yOiAke2V2ZW50fWAsXG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgICB0aGlzLm9uRXJyb3IoZXZlbnQsICgpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy52b3J0ZXhTdGF0dXNTZXJ2aWNlLmxvZ0RlYnVnKFxuICAgICAgICAgICAgICAgICAgICAgICAgXCJWb3J0ZXhDbGllbnRXZWJzb2NrZXQuY3JlYXRlU29ja2V0IGNhbGxpbmcgcmVqZWN0XCIgK1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIFwiIGZyb20gZXJyb3JcIixcbiAgICAgICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgICAgICAgcmVqZWN0KCk7XG4gICAgICAgICAgICAgICAgICAgIHByb21pc2VDYWxsZWQgPSB0cnVlO1xuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB0aGlzLnNvY2tldC5hZGRFdmVudExpc3RlbmVyKFwib3BlblwiLCAoZXZlbnQpID0+IHtcbiAgICAgICAgICAgICAgICB0aGlzLnZvcnRleFN0YXR1c1NlcnZpY2UubG9nRGVidWcoXG4gICAgICAgICAgICAgICAgICAgIFwiVm9ydGV4Q2xpZW50V2Vic29ja2V0LmNyZWF0ZVNvY2tldCBhZGRFdmVudExpc3RlbmVyIG9wZW5cIixcbiAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICAgIHRoaXMub25PcGVuKGV2ZW50KTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9KTtcblxuICAgICAgICB0aGlzLnZvcnRleFN0YXR1c1NlcnZpY2UubG9nRGVidWcoXG4gICAgICAgICAgICBcIlZvcnRleENsaWVudFdlYnNvY2tldC5jcmVhdGVTb2NrZXQgcmV0dXJuaW5nICMzXCIsXG4gICAgICAgICk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBvbk1lc3NhZ2UoZXZlbnQsIHByb21pc2VSZXNvbHZlcikge1xuICAgICAgICBpZiAoIXRoaXMuaXNPbmxpbmUpIHtcbiAgICAgICAgICAgIHRoaXMuc2V0T25saW5lKCk7XG4gICAgICAgICAgICBwcm9taXNlUmVzb2x2ZXIoKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMuYmVhdCgpO1xuXG4gICAgICAgIGlmIChldmVudC5kYXRhLmxlbmd0aCA9PSBudWxsKSB7XG4gICAgICAgICAgICB0aGlzLnZvcnRleFN0YXR1c1NlcnZpY2UubG9nQ29ubmVjdGlvbkVycm9yKFxuICAgICAgICAgICAgICAgIFwiV2ViU29ja2V0LCBXZSd2ZSByZWNlaXZlZCBhIHdlYnNvY2tldCBiaW5hcnkgbWVzc2FnZSxcIiArXG4gICAgICAgICAgICAgICAgICAgIFwiIHdlIGV4cGVjdCBhIHVuaWNvZGVcIixcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICAvLyBJZiB0aGUgc2VydmVyIHNlbmRzIHVzIGEgJy4nLCB0aGF0J3MgYSBoZWFydCBiZWF0LCByZXR1cm4gaXQuXG4gICAgICAgIGlmIChldmVudC5kYXRhID09PSBcIi5cIikge1xuICAgICAgICAgICAgdGhpcy5zb2NrZXQgIT0gbnVsbCAmJiB0aGlzLnNvY2tldC5zZW5kKFwiLlwiKTtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChldmVudC5kYXRhWzBdICE9PSBcIntcIikge1xuICAgICAgICAgICAgdGhpcy5fZGF0YSArPSBldmVudC5kYXRhO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgLy8gSXQgc3RhcnRzIHdpdGggeywgZW5zdXJlIGl0IGVuZHMgd2l0aCB9XG4gICAgICAgICAgICBpZiAoZXZlbnQuZGF0YVtldmVudC5kYXRhLmxlbmd0aCAtIDFdICE9PSBcIn1cIikge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKFxuICAgICAgICAgICAgICAgICAgICBkYXRlU3RyKCkgK1xuICAgICAgICAgICAgICAgICAgICAgICAgXCIgRVJST1IsIFBheWxvYWQgc2hvdWxkIGVuZCB3aXRoIH0sXCIgK1xuICAgICAgICAgICAgICAgICAgICAgICAgXCIgYnV0IGl0IGRvZXNuJ3Q6IFwiICtcbiAgICAgICAgICAgICAgICAgICAgICAgIGV2ZW50LmRhdGEsXG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB0aGlzLl92b3J0ZXhNc2dzUXVldWUucHVzaChldmVudC5kYXRhKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMuX3Byb2Nlc3NEYXRhKCk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBvbk9wZW4oZXZlbnQpIHtcbiAgICAgICAgc3VwZXJcbiAgICAgICAgICAgIC5zZW5kKG5ldyBQYXlsb2FkRW52ZWxvcGUoKSlcbiAgICAgICAgICAgIC5jYXRjaCgoZSkgPT5cbiAgICAgICAgICAgICAgICBjb25zb2xlLmxvZyhgRVJST1IgVm9ydGV4Q2xpZW50V2Vic29ja2V0IG9uT3BlbiBTZW5kOiAke2V9YCksXG4gICAgICAgICAgICApO1xuICAgICAgICB0aGlzLnZvcnRleFN0YXR1c1NlcnZpY2UubG9nQ29ubmVjdGlvbkluZm8oXCJXZWJTb2NrZXQsIGNvbm5lY3RpbmdcIik7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBvbkNsb3NlKGV2ZW50KSB7XG4gICAgICAgIHRoaXMudm9ydGV4U3RhdHVzU2VydmljZS5sb2dDb25uZWN0aW9uSW5mbyhcIldlYlNvY2tldCwgY2xvc2VkXCIpO1xuICAgICAgICBpZiAoISh0aGlzLnNvY2tldCAmJiB0aGlzLnNvY2tldC5yZWFkeVN0YXRlID09PSB0aGlzLlNvY2tldC5PUEVOKSkge1xuICAgICAgICAgICAgdGhpcy5zZXRDbG9zZWQoKTtcbiAgICAgICAgfVxuICAgICAgICAvLyBUaGUgYmFzZSBjbGFzcyB3aWxsIHJlY29ubmVjdFxuICAgIH1cblxuICAgIHByaXZhdGUgb25FcnJvcihldmVudCwgcmVqZWN0KSB7XG4gICAgICAgIHJlamVjdChldmVudC5lcnJvcj8udG9TdHJpbmcoKSB8fCBcIldlYnNvY2tldCBmYWlsZWQgdG8gY29ubmVjdFwiKTtcbiAgICAgICAgLy8gb25DbG9zZSB3aWxsIGdldCBjYWxsZWQgYXMgd2VsbFxuXG4gICAgICAgIC8vIENoZWNrIGlmIHRoZSBzZXJ2ZXIgaXMgc3RpbGwgdXAsIGlmIGl0IGlzIHdlJ3ZlIGJlZW4gbG9nZ2VkIG91dFxuICAgICAgICB0aGlzLnRlc3RJZk9ubGluZUFuZExvZ2dlZE91dCgpXG4gICAgICAgICAgICAudGhlbigoKSA9PiBjb25zb2xlLmxvZyhcIlNlcnZlciBpcyB1bnJlYWNoYWJsZVwiKSlcbiAgICAgICAgICAgIC5jYXRjaCgoKSA9PiB7XG4gICAgICAgICAgICAgICAgdGhpcy52b3J0ZXhTdGF0dXNTZXJ2aWNlLmxvZ0Nvbm5lY3Rpb25FcnJvcihcbiAgICAgICAgICAgICAgICAgICAgXCJWb3J0ZXggV2Vic29ja2V0IGRvZXNuJ3QgY29ubmVjdCBidXQgc2VydmVyIGlzIG9ubGluZSxcIiArXG4gICAgICAgICAgICAgICAgICAgICAgICBcIiBNYXJraW5nIHZvcnRleCBhcyBsb2dnZWQgb3V0XCIsXG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgICB0aGlzLnZvcnRleFN0YXR1c1NlcnZpY2Uuc2V0SHR0cFNlc3Npb25Mb2dnZWRPdXQoKTtcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9KTtcbiAgICB9XG5cbiAgICBwcml2YXRlIHRlc3RJZk9ubGluZUFuZExvZ2dlZE91dCgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgcmV0dXJuIG5ldyBQcm9taXNlPHZvaWQ+KChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IGh0dHBVcmwgPSBcImh0dHBcIiArIHRoaXMuX3VybC5zdWJzdHJpbmcoMik7XG4gICAgICAgICAgICBsZXQgeG1sSHR0cCA9IG5ldyBYTUxIdHRwUmVxdWVzdCgpO1xuICAgICAgICAgICAgeG1sSHR0cC50aW1lb3V0ID0gMjAwMDtcbiAgICAgICAgICAgIHhtbEh0dHAucmVzcG9uc2VUeXBlID0gXCJkb2N1bWVudFwiO1xuICAgICAgICAgICAgeG1sSHR0cC5vbmxvYWQgPSBmdW5jdGlvbiAoZXZlbnQpIHtcbiAgICAgICAgICAgICAgICByZWplY3QoKTtcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgICB4bWxIdHRwLm9uZXJyb3IgPSBmdW5jdGlvbiAoZXZlbnQpIHtcbiAgICAgICAgICAgICAgICByZXNvbHZlKCk7XG4gICAgICAgICAgICB9O1xuICAgICAgICAgICAgeG1sSHR0cC5vcGVuKFwiR0VUXCIsIGh0dHBVcmwsIHRydWUpOyAvLyB0cnVlIGZvciBhc3luY2hyb25vdXNcbiAgICAgICAgICAgIHhtbEh0dHAuc2VuZChudWxsKTtcbiAgICAgICAgfSk7XG4gICAgfVxufVxuIl19