Compare commits

..

2 Commits

Author SHA1 Message Date
Techno Tim
0e5fc534c8
Merge a27d783451 into d7aa9f99f5 2025-04-05 16:23:30 +02:00
Adam Shiervani
d7aa9f99f5
refactor: improve WebSocket handling in CreateSession function (#30) 2025-04-03 19:29:11 +02:00

View File

@ -54,11 +54,10 @@ export const CreateSession = async (req: express.Request, res: express.Response)
// extract the websocket and ip from the tuple // extract the websocket and ip from the tuple
const [ws, ip] = wsTuple; const [ws, ip] = wsTuple;
let wsRes: ((value: unknown) => void) | null = null,
wsRej: ((value: unknown) => void) | null = null;
let timeout: NodeJS.Timeout | undefined; let timeout: NodeJS.Timeout | undefined;
let httpClose: (() => void) | null = null;
try { try {
inFlight.add(id); inFlight.add(id);
const resp: any = await new Promise((res, rej) => { const resp: any = await new Promise((res, rej) => {
@ -66,43 +65,49 @@ export const CreateSession = async (req: express.Request, res: express.Response)
rej(new Error("Timeout waiting for response from ws")); rej(new Error("Timeout waiting for response from ws"));
}, 15000); }, 15000);
// Hoist the res and rej functions to be used in the finally block for cleanup ws.onerror = rej;
wsRes = res; ws.onclose = rej;
wsRej = rej; ws.onmessage = res;
ws.addEventListener("message", wsRes); httpClose = () => {
ws.addEventListener("error", wsRej); rej(new Error("HTTP client closed the connection"));
ws.addEventListener("close", wsRej); };
// If the HTTP client closes the connection before the websocket response is received, reject the promise // If the HTTP client closes the connection before the websocket response is received, reject the promise
req.socket.on("close", wsRej); req.socket.on("close", httpClose);
ws.send(JSON.stringify({ ws.send(
sd, JSON.stringify({
ip, sd,
iceServers, ip,
OidcGoogle: idToken iceServers,
})); OidcGoogle: idToken,
}),
);
}); });
return res.json(JSON.parse(resp.data)); return res.json(JSON.parse(resp.data));
} catch (e) { } catch (e) {
console.error(`Error sending data to kvm with ${id}`, e); console.log(`Error sending data to kvm with ${id}`, e);
// If there was an error, remove the socket from the map
ws.close(); // Most likely there is no-one on the other end to close the connection
activeConnections.delete(id);
return res return res
.status(500) .status(500)
.json({ error: "There was an error sending and receiving data to the KVM" }); .json({ error: "There was an error sending and receiving data to the KVM" });
} finally { } finally {
if (timeout) clearTimeout(timeout); if (timeout) clearTimeout(timeout);
console.log("Removing in flight", id);
inFlight.delete(id); inFlight.delete(id);
if (wsRes && wsRej) {
ws.removeEventListener("message", wsRes); if (httpClose) {
ws.removeEventListener("error", wsRej); console.log("Removing http close listener", id);
ws.removeEventListener("close", wsRej); req.socket.off("close", httpClose);
}
if (ws) {
console.log("Removing ws listeners", id);
ws.onerror = null;
ws.onclose = null;
ws.onmessage = null;
} }
} }
}; };
@ -246,9 +251,9 @@ export const registerWebsocketServer = (server: any) => {
return ws.close(); return ws.close();
} }
const ip = ( const ip =
process.env.REAL_IP_HEADER && req.headers[process.env.REAL_IP_HEADER] (process.env.REAL_IP_HEADER && req.headers[process.env.REAL_IP_HEADER]) ||
) || req.socket.remoteAddress; req.socket.remoteAddress;
activeConnections.set(id, [ws, `${ip}`]); activeConnections.set(id, [ws, `${ip}`]);
console.log("New socket for id", id); console.log("New socket for id", id);