Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(server): Distribute server error to all clients even if one error listener throws #56

Merged
merged 2 commits into from
Nov 4, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
15 changes: 13 additions & 2 deletions src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -443,9 +443,20 @@ export function createServer(
}
webSocketServer.on('connection', handleConnection);
webSocketServer.on('error', (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 webSocketServer.clients) {
// report server errors by erroring out all clients with the same error
client.emit('error', err);
try {
client.emit('error', err);
} catch (err) {
firstErr = firstErr ?? err;
}
}

if (firstErr) {
throw firstErr;
}
});

Expand Down
35 changes: 35 additions & 0 deletions src/tests/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,41 @@ it('should prefer the `onSubscribe` context value even if `context` option is se
);
});

it('should handle errors thrown from client error listeners', async () => {
const { server, url } = await startTServer();

const client = await createTClient(url);
client.ws.send(
stringifyMessage<MessageType.ConnectionInit>({
type: MessageType.ConnectionInit,
}),
);
await client.waitForMessage(({ data }) => {
expect(parseMessage(data).type).toBe(MessageType.ConnectionAck);
});

const surpriseErr1 = new Error('Well hello there!');
const surpriseErr2 = new Error('I wont be thrown!'); // first to throw stops emission
for (const client of server.webSocketServer.clients) {
client.on('error', () => {
throw surpriseErr1;
});
client.on('error', () => {
throw surpriseErr2;
});
}

expect(() => {
server.webSocketServer.emit('error', new Error('I am a nice error'));
}).toThrowError(surpriseErr1);

await client.waitForClose((event) => {
expect(event.code).toBe(1011);
expect(event.reason).toBe('I am a nice error');
expect(event.wasClean).toBeTruthy();
});
});

describe('Connect', () => {
it('should refuse connection and close socket if returning `false`', async () => {
const { url } = await startTServer({
Expand Down