From 43f26e8ea9c5132b163ea0094d07405a4f19dbaa Mon Sep 17 00:00:00 2001 From: Lorenz Stechauner Date: Fri, 4 Jun 2021 15:19:06 +0200 Subject: [PATCH] WebSockets working --- src/usimp.ts | 149 +++++++++++++++++++++++++++++++++++++------------ www/index.html | 10 ++-- 2 files changed, 117 insertions(+), 42 deletions(-) diff --git a/src/usimp.ts b/src/usimp.ts index eac50c1..e25b5e6 100644 --- a/src/usimp.ts +++ b/src/usimp.ts @@ -162,20 +162,23 @@ export class Session { const host = this.server.host; const protocols = this.server.protocols; - /* - if ("ws" in protocols) { - this.websocket = new WebSocket(`ws://${host}:${protocols.ws}/_usimp/websocket`, ["usimp"]); + if ("wss" in protocols) { + this.websocket = new WebSocket(`wss://${host}:${protocols.wss}/_usimp/websocket`, ["usimp"]); + this.websocket.addEventListener("error", (error) => { + console.error(error); + this.websocket = null; + }); } - */ - // TODO http -> https - if (protocols) { - this.httpBaseUrl = `http://${host}:${protocols.http}/_usimp`; + if ("https" in protocols) { + this.httpBaseUrl = `https://${host}:${protocols.https}/_usimp`; try { return await this.ping(); } catch { - this.domain.invalidServers.push(this.server); - this.server = null; + if (this.server !== null) { + this.domain.invalidServers.push(this.server); + this.server = null; + } } } else { console.warn(`Domain server ${this.server} does not support 'https' transport protocol`); @@ -186,39 +189,111 @@ export class Session { return undefined; } + waitForWebSocket() { + if (this.websocket === null) throw Error("websocket not initialized"); + return new Promise((resolve, reject) => { + if (this.websocket?.readyState === this.websocket?.OPEN) { + resolve(); + } else { + const handlerOpen = () => { + this.websocket?.removeEventListener("open", handlerOpen); + resolve(); + }; + const handlerError = (error: any) => { + this.websocket?.removeEventListener("error", handlerError); + reject(error); + } + this.websocket?.addEventListener("open", handlerOpen); + this.websocket?.addEventListener("error", handlerError); + } + }); + } + + waitForWebSocketResponse(req_nr: number, timeout: number) { + if (this.websocket === null) throw Error("websocket not initialized"); + return new Promise((resolve, reject) => { + const handlerMsg = (msg: any) => { + const data = JSON.parse(msg.data); + if (data['request_nr'] === req_nr) { + clearTimeout(timer); + this.websocket?.removeEventListener("message", handlerMsg); + this.websocket?.removeEventListener("error", handlerError); + resolve(data); + } + } + const handlerError = (error: any) => { + clearTimeout(timer); + this.websocket?.removeEventListener("message", handlerMsg); + this.websocket?.removeEventListener("error", handlerError); + reject(error); + } + const handlerTimeout = () => { + this.websocket?.removeEventListener("message", handlerMsg); + this.websocket?.removeEventListener("error", handlerError); + reject(Error("timeout")); + } + const timer = setTimeout(handlerTimeout, timeout); + this.websocket?.addEventListener("message", handlerMsg); + this.websocket?.addEventListener("error", handlerError); + }); + } + async send(endpoint: string, data: object, timeout: number = 2000, forceHttp: boolean = false) { this.numRequests++; - if (!forceHttp && this.websocket) { - this.websocket.send(JSON.stringify({ - 'request_num': this.numRequests, - 'data': data - })); - } else { - const controller = new AbortController(); - const timer = setTimeout(() => controller.abort(), timeout); + while (true) { + try { + if (!forceHttp && this.websocket) { + const req_nr = this.numRequests; + const response = this.waitForWebSocketResponse(req_nr, timeout); - let headers: Record = { - 'Content-Type': 'application/json', - 'To-Domain': this.domain.id, - }; - if (this.token) { - headers['Authorization'] = `usimp ${this.token}`; + const startTime = performance.now(); + await this.waitForWebSocket(); + await this.websocket.send(JSON.stringify({ + 'request_nr': req_nr, + 'endpoint': endpoint, + 'to_domain': this.domain.id, + 'data': data + })); + const responseData = await response; + + const endTime = performance.now(); + responseData.duration = endTime - startTime; + return responseData; + } else { + const controller = new AbortController(); + const timer = setTimeout(() => controller.abort(), timeout); + + let headers: Record = { + 'Content-Type': 'application/json', + 'To-Domain': this.domain.id, + }; + if (this.token) { + headers['Authorization'] = `usimp ${this.token}`; + } + + const startTime = performance.now(); + const response = await fetch(`${this.httpBaseUrl}/${endpoint}`, { + method: "POST", + headers: headers, + body: JSON.stringify(data), + signal: controller.signal, + }); + clearTimeout(timer); + const responseData = await response.json(); + + const endTime = performance.now(); + responseData.duration = endTime - startTime; + return responseData; + } + } catch (error) { + console.error(error); + if (this.server !== null) { + this.domain.invalidServers.push(this.server); + this.server = null; + } + await this.chooseDomainServer(); } - - const startTime = new Date(); - const response = await fetch(`${this.httpBaseUrl}/${endpoint}`, { - method: "POST", - headers: headers, - body: JSON.stringify(data), - signal: controller.signal, - }); - const endTime = new Date(); - clearTimeout(timer); - - const responseData = await response.json(); - responseData.duration = endTime.getUTCMilliseconds() - startTime.getUTCMilliseconds(); - return responseData; } } diff --git a/www/index.html b/www/index.html index 4f2bf23..e823cd0 100644 --- a/www/index.html +++ b/www/index.html @@ -19,17 +19,17 @@ Distributed, end-to-end encrypted instant messaging."/> - - - - + + + +