Skip to content

Commit f713a6e

Browse files
pimterrylpinca
authored andcommitted
[test] Add tests for various close edge cases
1 parent e350b86 commit f713a6e

File tree

1 file changed

+218
-0
lines changed

1 file changed

+218
-0
lines changed

test/websocket.test.js

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2641,6 +2641,168 @@ describe('WebSocket', () => {
26412641
});
26422642
});
26432643

2644+
it('handles a close frame received while compressing data', (done) => {
2645+
const wss = new WebSocket.Server(
2646+
{
2647+
perMessageDeflate: true,
2648+
port: 0
2649+
},
2650+
() => {
2651+
const ws = new WebSocket(`ws://localhost:${wss.address().port}`, {
2652+
perMessageDeflate: { threshold: 0 }
2653+
});
2654+
2655+
ws.on('open', () => {
2656+
ws._receiver.on('conclude', () => {
2657+
assert.ok(ws._sender._deflating);
2658+
});
2659+
2660+
ws.send('foo');
2661+
ws.send('bar');
2662+
ws.send('baz');
2663+
ws.send('qux');
2664+
});
2665+
}
2666+
);
2667+
2668+
wss.on('connection', (ws) => {
2669+
const messages = [];
2670+
2671+
ws.on('message', (message) => {
2672+
messages.push(message);
2673+
});
2674+
2675+
ws.on('close', (code, reason) => {
2676+
assert.strictEqual(code, 1000);
2677+
assert.strictEqual(reason, '');
2678+
assert.deepStrictEqual(messages, ['foo', 'bar', 'baz', 'qux']);
2679+
wss.close(done);
2680+
});
2681+
2682+
ws.close(1000);
2683+
});
2684+
});
2685+
2686+
it("handles the socket 'end' event while compressing data", (done) => {
2687+
const wss = new WebSocket.Server(
2688+
{
2689+
perMessageDeflate: true,
2690+
port: 0
2691+
},
2692+
() => {
2693+
const ws = new WebSocket(`ws://localhost:${wss.address().port}`, {
2694+
perMessageDeflate: { threshold: 0 }
2695+
});
2696+
2697+
ws.on('open', () => {
2698+
ws._socket.on('end', () => {
2699+
assert.ok(ws._sender._deflating);
2700+
});
2701+
2702+
ws.send('foo');
2703+
ws.send('bar');
2704+
ws.send('baz');
2705+
ws.send('qux');
2706+
});
2707+
}
2708+
);
2709+
2710+
wss.on('connection', (ws) => {
2711+
const messages = [];
2712+
2713+
ws.on('message', (message) => {
2714+
messages.push(message);
2715+
});
2716+
2717+
ws.on('close', (code, reason) => {
2718+
assert.strictEqual(code, 1000);
2719+
assert.strictEqual(reason, '');
2720+
assert.deepStrictEqual(messages, ['foo', 'bar', 'baz', 'qux']);
2721+
wss.close(done);
2722+
});
2723+
2724+
ws.close(1000);
2725+
ws._socket.end(); // Ensure the socket is immediately half-closed.
2726+
});
2727+
});
2728+
2729+
describe('#close', () => {
2730+
it('can be used while data is being decompressed (1/2)', (done) => {
2731+
const wss = new WebSocket.Server(
2732+
{
2733+
perMessageDeflate: true,
2734+
port: 0
2735+
},
2736+
() => {
2737+
const messages = [];
2738+
const ws = new WebSocket(`ws://localhost:${wss.address().port}`);
2739+
2740+
ws.on('open', () => {
2741+
ws._socket.on('end', () => {
2742+
assert.strictEqual(ws._receiver._state, 5);
2743+
});
2744+
});
2745+
2746+
ws.on('message', (message) => {
2747+
if (messages.push(message) > 1) return;
2748+
2749+
ws.close(1000);
2750+
});
2751+
2752+
ws.on('close', (code, reason) => {
2753+
assert.strictEqual(code, 1000);
2754+
assert.strictEqual(reason, '');
2755+
assert.deepStrictEqual(messages, ['', '', '', '']);
2756+
wss.close(done);
2757+
});
2758+
}
2759+
);
2760+
2761+
wss.on('connection', (ws) => {
2762+
const buf = Buffer.from('c10100c10100c10100c10100', 'hex');
2763+
ws._socket.write(buf);
2764+
});
2765+
});
2766+
2767+
it('can be used while data is being decompressed (2/2)', (done) => {
2768+
const wss = new WebSocket.Server(
2769+
{
2770+
perMessageDeflate: true,
2771+
port: 0
2772+
},
2773+
() => {
2774+
const messages = [];
2775+
const ws = new WebSocket(`ws://localhost:${wss.address().port}`);
2776+
2777+
ws.on('open', () => {
2778+
ws._socket.on('end', () => {
2779+
assert.strictEqual(ws._receiver._state, 5);
2780+
});
2781+
});
2782+
2783+
ws.on('message', (message) => {
2784+
if (messages.push(message) > 1) return;
2785+
2786+
ws.close(1000);
2787+
ws._socket.end(); // Ensure the socket is immediately half-closed.
2788+
});
2789+
2790+
ws.on('close', (code, reason) => {
2791+
assert.strictEqual(code, 1000);
2792+
assert.strictEqual(reason, '');
2793+
assert.deepStrictEqual(messages, ['', '', '', '']);
2794+
wss.close(done);
2795+
});
2796+
}
2797+
);
2798+
2799+
wss.on('connection', (ws) => {
2800+
const buf = Buffer.from('c10100c10100c10100c10100', 'hex');
2801+
ws._socket.write(buf);
2802+
});
2803+
});
2804+
});
2805+
26442806
describe('#send', () => {
26452807
it('ignores the `compress` option if the extension is disabled', (done) => {
26462808
const wss = new WebSocket.Server({ port: 0 }, () => {
@@ -2771,4 +2933,60 @@ describe('WebSocket', () => {
27712933
});
27722934
});
27732935
});
2936+
2937+
describe('Connection close edge cases', () => {
2938+
it('cleanly shuts down after simultaneous errors', (done) => {
2939+
let clientCloseEventEmitted = false;
2940+
let serverClientCloseEventEmitted = false;
2941+
2942+
const wss = new WebSocket.Server({ port: 0 }, () => {
2943+
const ws = new WebSocket(`ws://localhost:${wss.address().port}`);
2944+
2945+
ws.on('error', (err) => {
2946+
assert.ok(err instanceof RangeError);
2947+
assert.strictEqual(err.code, 'WS_ERR_INVALID_OPCODE');
2948+
assert.strictEqual(
2949+
err.message,
2950+
'Invalid WebSocket frame: invalid opcode 5'
2951+
);
2952+
2953+
ws.on('close', (code, reason) => {
2954+
assert.strictEqual(code, 1006);
2955+
assert.strictEqual(reason, '');
2956+
2957+
clientCloseEventEmitted = true;
2958+
if (serverClientCloseEventEmitted) wss.close(done);
2959+
});
2960+
});
2961+
2962+
ws.on('open', () => {
2963+
// Write an invalid frame in both directions to trigger simultaneous
2964+
// failure.
2965+
const chunk = Buffer.from([0x85, 0x00]);
2966+
2967+
wss.clients.values().next().value._socket.write(chunk);
2968+
ws._socket.write(chunk);
2969+
});
2970+
});
2971+
2972+
wss.on('connection', (ws) => {
2973+
ws.on('error', (err) => {
2974+
assert.ok(err instanceof RangeError);
2975+
assert.strictEqual(err.code, 'WS_ERR_INVALID_OPCODE');
2976+
assert.strictEqual(
2977+
err.message,
2978+
'Invalid WebSocket frame: invalid opcode 5'
2979+
);
2980+
2981+
ws.on('close', (code, reason) => {
2982+
assert.strictEqual(code, 1006);
2983+
assert.strictEqual(reason, '');
2984+
2985+
serverClientCloseEventEmitted = true;
2986+
if (clientCloseEventEmitted) wss.close(done);
2987+
});
2988+
});
2989+
});
2990+
});
2991+
});
27742992
});

0 commit comments

Comments
 (0)