Skip to content

Commit

Permalink
[test] Add tests for various close edge cases
Browse files Browse the repository at this point in the history
  • Loading branch information
pimterry authored and lpinca committed Jun 24, 2021
1 parent e350b86 commit 29c7f29
Showing 1 changed file with 218 additions and 0 deletions.
218 changes: 218 additions & 0 deletions test/websocket.test.js
Expand Up @@ -2641,6 +2641,168 @@ describe('WebSocket', () => {
});
});

it('handles a close frame received while compressing data', (done) => {
const wss = new WebSocket.Server(
{
perMessageDeflate: true,
port: 0
},
() => {
const ws = new WebSocket(`ws://localhost:${wss.address().port}`, {
perMessageDeflate: { threshold: 0 }
});

ws.on('open', () => {
ws._receiver.on('conclude', () => {
assert.ok(ws._sender._deflating);
});

ws.send('foo');
ws.send('bar');
ws.send('baz');
ws.send('qux');
});
}
);

wss.on('connection', (ws) => {
const messages = [];

ws.on('message', (message) => {
messages.push(message);
});

ws.on('close', (code, reason) => {
assert.deepStrictEqual(messages, ['foo', 'bar', 'baz', 'qux']);
assert.strictEqual(code, 1000);
assert.strictEqual(reason, '');
wss.close(done);
});

ws.close(1000);
});
});

it("handles the socket 'end' event while compressing data", (done) => {
const wss = new WebSocket.Server(
{
perMessageDeflate: true,
port: 0
},
() => {
const ws = new WebSocket(`ws://localhost:${wss.address().port}`, {
perMessageDeflate: { threshold: 0 }
});

ws.on('open', () => {
ws._socket.on('end', () => {
assert.ok(ws._sender._deflating);
});

ws.send('foo');
ws.send('bar');
ws.send('baz');
ws.send('qux');
});
}
);

wss.on('connection', (ws) => {
const messages = [];

ws.on('message', (message) => {
messages.push(message);
});

ws.on('close', (code, reason) => {
assert.deepStrictEqual(messages, ['foo', 'bar', 'baz', 'qux']);
assert.strictEqual(code, 1000);
assert.strictEqual(reason, '');
wss.close(done);
});

ws.close(1000);
ws._socket.end(); // Ensure the socket is immediately half-closed.
});
});

describe('#close', () => {
it('can be used while data is being decompressed (1/2)', (done) => {
const wss = new WebSocket.Server(
{
perMessageDeflate: true,
port: 0
},
() => {
const messages = [];
const ws = new WebSocket(`ws://localhost:${wss.address().port}`);

ws.on('open', () => {
ws._socket.on('end', () => {
assert.strictEqual(ws._receiver._state, 5);
});
});

ws.on('message', (message) => {
if (messages.push(message) > 1) return;

ws.close(1000);
});

ws.on('close', (code, reason) => {
assert.deepStrictEqual(messages, ['', '', '', '']);
assert.strictEqual(code, 1000);
assert.strictEqual(reason, '');
wss.close(done);
});
}
);

wss.on('connection', (ws) => {
const buf = Buffer.from('c10100c10100c10100c10100', 'hex');
ws._socket.write(buf);
});
});

it('can be used while data is being decompressed (2/2)', (done) => {
const wss = new WebSocket.Server(
{
perMessageDeflate: true,
port: 0
},
() => {
const messages = [];
const ws = new WebSocket(`ws://localhost:${wss.address().port}`);

ws.on('open', () => {
ws._socket.on('end', () => {
assert.strictEqual(ws._receiver._state, 5);
});
});

ws.on('message', (message) => {
if (messages.push(message) > 1) return;

ws.close(1000);
ws._socket.end(); // Ensure the socket is immediately half-closed.
});

ws.on('close', (code, reason) => {
assert.deepStrictEqual(messages, ['', '', '', '']);
assert.strictEqual(code, 1000);
assert.strictEqual(reason, '');
wss.close(done);
});
}
);

wss.on('connection', (ws) => {
const buf = Buffer.from('c10100c10100c10100c10100', 'hex');
ws._socket.write(buf);
});
});
});

describe('#send', () => {
it('ignores the `compress` option if the extension is disabled', (done) => {
const wss = new WebSocket.Server({ port: 0 }, () => {
Expand Down Expand Up @@ -2771,4 +2933,60 @@ describe('WebSocket', () => {
});
});
});

describe('Connection close edge cases', () => {
it('cleanly shuts down after simultaneous errors', (done) => {
let clientCloseEventEmitted = false;
let serverClientCloseEventEmitted = false;

const wss = new WebSocket.Server({ port: 0 }, () => {
const ws = new WebSocket(`ws://localhost:${wss.address().port}`);

ws.on('error', (err) => {
assert.ok(err instanceof RangeError);
assert.strictEqual(err.code, 'WS_ERR_INVALID_OPCODE');
assert.strictEqual(
err.message,
'Invalid WebSocket frame: invalid opcode 5'
);

ws.on('close', (code, reason) => {
assert.strictEqual(code, 1006);
assert.strictEqual(reason, '');

clientCloseEventEmitted = true;
if (serverClientCloseEventEmitted) wss.close(done);
});
});

ws.on('open', () => {
// Write an invalid frame in both directions to trigger simultaneous
// failure.
const chunk = Buffer.from([0x85, 0x00]);

wss.clients.values().next().value._socket.write(chunk);
ws._socket.write(chunk);
});
});

wss.on('connection', (ws) => {
ws.on('error', (err) => {
assert.ok(err instanceof RangeError);
assert.strictEqual(err.code, 'WS_ERR_INVALID_OPCODE');
assert.strictEqual(
err.message,
'Invalid WebSocket frame: invalid opcode 5'
);

ws.on('close', (code, reason) => {
assert.strictEqual(code, 1006);
assert.strictEqual(reason, '');

serverClientCloseEventEmitted = true;
if (clientCloseEventEmitted) wss.close(done);
});
});
});
});
});
});

0 comments on commit 29c7f29

Please sign in to comment.