Skip to content

Commit

Permalink
fix(ws): Handle socket emitted errors
Browse files Browse the repository at this point in the history
  • Loading branch information
enisdenjo committed Oct 27, 2021
1 parent 0826b0a commit a22c00f
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 0 deletions.
53 changes: 53 additions & 0 deletions src/__tests__/use.ts
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,59 @@ for (const { tServer, itForWS, startTServer } of tServers) {
});
});

// uWebSocket.js cannot have errors emitted on the socket
// TODO-db-211027 fastify-websocket
itForWS(
'should report socket emitted errors to clients by closing the connection',
async () => {
const { url, waitForClient } = await startTServer();

const client = await createTClient(url);

const emittedError = new Error("I'm a teapot");

await waitForClient((client) => {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
client.socket!.emit('error', emittedError);
});

await client.waitForClose((event) => {
expect(event.code).toBe(CloseCode.InternalServerError); // CloseCode.InternalServerError: Internal server error
expect(event.reason).toBe(emittedError.message);
expect(event.wasClean).toBeTruthy(); // because the server reported the error
});
},
);

// uWebSocket.js cannot have errors emitted on the socket
// TODO-db-211027 fastify-websocket
itForWS('should limit the socket emitted error message size', async () => {
const { url, waitForClient } = await startTServer();

const client = await createTClient(url);
client.ws.send(
stringifyMessage<MessageType.ConnectionInit>({
type: MessageType.ConnectionInit,
}),
);

await waitForClient((client) => {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
client.socket!.emit(
'error',
new Error(
'i am exactly 124 characters long i am exactly 124 characters long i am exactly 124 characters long i am exactly 124 characte',
),
);
});

await client.waitForClose((event) => {
expect(event.code).toBe(CloseCode.InternalServerError);
expect(event.reason).toBe('Internal server error');
expect(event.wasClean).toBeTruthy(); // because the server reported the error
});
});

it('should limit the internal server error message size', async () => {
const { url } = await startTServer({
onConnect: () => {
Expand Down
3 changes: 3 additions & 0 deletions src/__tests__/utils/tservers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ afterEach(async () => {
});

export interface TServerClient {
socket: ws | null; // null when uWS
send(data: string): void;
onMessage(cb: (message: string) => void): () => void;
close(code?: number, data?: string): void;
Expand Down Expand Up @@ -216,6 +217,7 @@ export async function startWSTServer(

function toClient(socket: ws): TServerClient {
return {
socket,
send: (data) => socket.send(data),
onMessage: (cb) => {
const listener = (data: unknown) => cb(String(data));
Expand Down Expand Up @@ -479,6 +481,7 @@ export async function startFastifyWSTServer(

function toClient(socket: ws): TServerClient {
return {
socket,
send: (data) => socket.send(data),
onMessage: (cb) => {
const listener = (data: unknown) => cb(String(data));
Expand Down
15 changes: 15 additions & 0 deletions src/use/ws.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,21 @@ export function useServer<
});

ws.on('connection', (socket, request) => {
socket.on('error', (err) => {
console.error(
'Internal error emitted on a WebSocket socket. ' +
'Please check your implementation.',
err,
);
socket.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,
);
});

// keep alive through ping-pong messages
let pongWait: NodeJS.Timeout | null = null;
const pingInterval =
Expand Down

0 comments on commit a22c00f

Please sign in to comment.