From 76a1c8fa1d1bf97d93fc84b44b6d5bff6270c7cb Mon Sep 17 00:00:00 2001 From: Radon Rosborough Date: Sun, 7 Jun 2020 16:24:45 -0600 Subject: [PATCH] Automatic reconnect to server with exp backoff --- frontend/src/app.ts | 109 +++++++++++++++++++++++++------------------- 1 file changed, 63 insertions(+), 46 deletions(-) diff --git a/frontend/src/app.ts b/frontend/src/app.ts index b4c693e..206180b 100644 --- a/frontend/src/app.ts +++ b/frontend/src/app.ts @@ -14,56 +14,73 @@ term.open(document.getElementById("terminal")); fitAddon.fit(); window.addEventListener("resize", () => fitAddon.fit()); -const socket = new WebSocket( - (document.location.protocol === "http:" ? "ws://" : "wss://") + - document.location.host + - `/api/v1/ws?lang=${lang}` -); +const initialRetryDelayMs = 200; +let retryDelayMs = initialRetryDelayMs; -socket.addEventListener("open", () => - console.log("Successfully connected to server") -); -socket.addEventListener("message", (event) => { - let message: any; - try { - message = JSON.parse(event.data); - } catch (err) { - console.error("Malformed message from server:", event.data); - return; - } - switch (message?.event) { - case "terminalClear": - term.reset(); +function tryConnect() { + console.log("Connecting to server..."); + socket = new WebSocket( + (document.location.protocol === "http:" ? "ws://" : "wss://") + + document.location.host + + `/api/v1/ws?lang=${lang}` + ); + socket.addEventListener("open", () => { + retryDelayMs = initialRetryDelayMs; + console.log("Successfully connected to server"); + }); + socket.addEventListener("message", (event: MessageEvent) => { + let message: any; + try { + message = JSON.parse(event.data); + } catch (err) { + console.error("Malformed message from server:", event.data); return; - case "terminalOutput": - if (typeof message.output !== "string") { + } + switch (message?.event) { + case "terminalClear": + term.reset(); + return; + case "terminalOutput": + if (typeof message.output !== "string") { + console.error("Unexpected message from server:", message); + return; + } + term.write(message.output); + return; + case "setMonacoLanguage": + if (typeof message.monacoLanguage !== "string") { + console.error("Unexpected message from server:", message); + return; + } + monaco.editor.setModelLanguage( + editor.getModel(), + message.monacoLanguage + ); + return; + default: console.error("Unexpected message from server:", message); return; - } - term.write(message.output); - return; - case "setMonacoLanguage": - if (typeof message.monacoLanguage !== "string") { - console.error("Unexpected message from server:", message); - return; - } - monaco.editor.setModelLanguage(editor.getModel(), message.monacoLanguage); - return; - default: - console.error("Unexpected message from server:", message); - return; - } -}); -socket.addEventListener("close", (event) => { - if (event.wasClean) { - console.log("Connection closed cleanly"); - } else { - console.error("Connection died"); - } -}); -socket.addEventListener("onerror", (event) => - console.error("Connection error:", event) -); + } + }); + socket.addEventListener("close", (event: CloseEvent) => { + if (event.wasClean) { + console.log("Connection closed cleanly"); + } else { + console.error("Connection died"); + } + scheduleConnect(); + }); +} + +function scheduleConnect() { + const delay = retryDelayMs * Math.random(); + console.log(`Trying to reconnect in ${Math.floor(delay)}ms`); + setTimeout(tryConnect, delay); + retryDelayMs *= 2; +} + +let socket = null; +tryConnect(); term.onData((data) => socket.send(JSON.stringify({ event: "terminalInput", input: data }))