diff --git a/wasm/web/relay.mjs b/wasm/web/relay.mjs index bc319530..9d38cb8f 100644 --- a/wasm/web/relay.mjs +++ b/wasm/web/relay.mjs @@ -1473,9 +1473,14 @@ export function relay ({ port = 8787, host = '0.0.0.0', maxPayload: DOC_FRAME_MAX }); /* Nothing a client sends may throw out of here: an exception in an - upgrade listener is the whole process. */ + upgrade listener is the whole process. So is an error on a socket + refused here, a reset after its 403, say: the server stops + listening for its errors at the upgrade, and ws does only for the + sockets it takes. */ server.on('upgrade', (req, socket, head) => { + socket.on('error', () => socket.destroy()); + try { upgrade(req, socket, head); diff --git a/wasm/web/relaytest.mjs b/wasm/web/relaytest.mjs index 7c4bd531..50b003ed 100644 --- a/wasm/web/relaytest.mjs +++ b/wasm/web/relaytest.mjs @@ -40,6 +40,7 @@ import { spawnSync } from 'node:child_process'; import fs from 'node:fs'; +import net from 'node:net'; import os from 'node:os'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; @@ -352,6 +353,24 @@ async function accountsInRooms () 'a document socket for a room that is not there is refused, ' + 'and the relay lives'); + /* And one reset as soon as it is refused. */ + { + const [host, port] = at.split(':'); + const s = net.connect(Number(port), host); + + await new Promise((r) => s.on('connect', r)); + s.write('GET /doc/nosuchroom HTTP/1.1\r\nHost: x\r\n' + + 'Upgrade: websocket\r\nConnection: Upgrade\r\n' + + 'Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n' + + 'Sec-WebSocket-Version: 13\r\n\r\n'); + await new Promise((r) => s.once('data', r)); + s.resetAndDestroy(); + await new Promise((r) => setTimeout(r, 200)); + check((await fetch(`http://${at}/`)).ok, + 'and one its client resets once refused, and the relay ' + + 'lives'); + } + /* A field made to throw when read as a string or a number, in each message that reads one: that socket is cut, and the relay lives. */ {