diff --git a/scripts/e2e-static-server.mjs b/scripts/e2e-static-server.mjs index 6ea4365..45fb0b3 100644 --- a/scripts/e2e-static-server.mjs +++ b/scripts/e2e-static-server.mjs @@ -80,6 +80,11 @@ server.on('upgrade', (req, socket, head) => { socket.destroy(); return; } + // A WebSocket that is dropped abruptly (e.g. a test toggling the browser + // offline, or a context closing) emits 'error' on these sockets. Without a + // listener that becomes an uncaught exception that would crash this server — + // so always handle it and just tear the pair down. + socket.on('error', () => socket.destroy()); const upstream = httpRequest({ hostname: COLLAB_TARGET.hostname, port: COLLAB_TARGET.port, @@ -88,6 +93,7 @@ server.on('upgrade', (req, socket, head) => { headers: { ...req.headers, host: `${COLLAB_TARGET.hostname}:${COLLAB_TARGET.port}` }, }); upstream.on('upgrade', (upstreamRes, upstreamSocket, upstreamHead) => { + upstreamSocket.on('error', () => socket.destroy()); const statusLine = `HTTP/1.1 ${upstreamRes.statusCode} ${upstreamRes.statusMessage}\r\n`; const headerLines = Object.entries(upstreamRes.headers) .map(([key, value]) => `${key}: ${value}`) @@ -96,13 +102,18 @@ server.on('upgrade', (req, socket, head) => { if (upstreamHead?.length) socket.write(upstreamHead); upstreamSocket.pipe(socket); socket.pipe(upstreamSocket); - upstreamSocket.on('error', () => socket.destroy()); }); upstream.on('error', () => socket.destroy()); if (head?.length) upstream.write(head); upstream.end(); }); +// Last-resort guard: this is a throwaway test server, so a stray socket error +// must never take it down mid-run and fail an unrelated later test pack. +process.on('uncaughtException', (error) => { + console.error('e2e static server: ignored uncaught error:', error?.message ?? error); +}); + server.listen(PORT, () => console.log( `e2e static server on :${PORT} → api ${API_TARGET.href} collab ${COLLAB_TARGET.href}`,