diff --git a/src/__tests__/use.ts b/src/__tests__/use.ts index 7c7f04e0..abd80a3e 100644 --- a/src/__tests__/use.ts +++ b/src/__tests__/use.ts @@ -355,8 +355,7 @@ for (const { tServer, itForWS, skipUWS, startTServer } of tServers) { }); // uWebSocket.js cannot have errors emitted on the server instance - // TODO-db-211027 fastify-websocket - itForWS( + skipUWS( 'should report server emitted errors to clients by closing the connection', async () => { const { url, server } = await startTServer(); @@ -376,8 +375,7 @@ for (const { tServer, itForWS, skipUWS, startTServer } of tServers) { ); // uWebSocket.js cannot have errors emitted on the server instance - // TODO-db-211027 fastify-websocket - itForWS('should limit the server emitted error message size', async () => { + skipUWS('should limit the server emitted error message size', async () => { const { url, server, waitForClient } = await startTServer(); const client = await createTClient(url); diff --git a/src/use/fastify-websocket.ts b/src/use/fastify-websocket.ts index d028bd95..f93bbdf6 100644 --- a/src/use/fastify-websocket.ts +++ b/src/use/fastify-websocket.ts @@ -43,9 +43,45 @@ export function makeHandler< const isProd = process.env.NODE_ENV === 'production'; const server = makeServer(options); - return (connection, request) => { + // we dont have access to the fastify-websocket server instance yet, + // register an error handler on first connection ONCE only + let handlingServerEmittedErrors = false; + + return function handler(connection, request) { const { socket } = connection; + // handle server emitted errors only if not already handling + if (!handlingServerEmittedErrors) { + handlingServerEmittedErrors = true; + this.websocketServer.once('error', (err) => { + console.error( + 'Internal error emitted on the WebSocket server. ' + + 'Please check your implementation.', + err, + ); + + // catch the first thrown error and re-throw it once all clients have been notified + let firstErr: Error | null = null; + + // report server errors by erroring out all clients with the same error + for (const client of this.websocketServer.clients) { + try { + client.close( + CloseCode.InternalServerError, + // close reason should fit in one frame https://datatracker.ietf.org/doc/html/rfc6455#section-5.2 + isProd || err.message.length > 123 + ? 'Internal server error' + : err.message, + ); + } catch (err) { + firstErr = firstErr ?? err; + } + } + + if (firstErr) throw firstErr; + }); + } + // used as listener on two streams, prevent superfluous calls on close let emittedErrorHandled = false; function handleEmittedError(err: Error) {