From df7de574a07115e2321fdb5fc9b2d0fea55d27e8 Mon Sep 17 00:00:00 2001 From: Luigi Pinca Date: Mon, 12 Jul 2021 15:52:59 +0200 Subject: [PATCH] [major] Do not close existing connections When `WebSocketServer.prototype.close()` is called, stop accepting new connections but do not close the existing ones. If the HTTP/S server was created internally, then close it and emit the `'close'` event when it closes. Otherwise, if client tracking is enabled, then emit the `'close'` event when the number of connections goes down to zero. Otherwise, emit it in the next tick. Refs: https://github.com/websockets/ws/pull/1902 --- doc/ws.md | 11 +- lib/websocket-server.js | 55 +++++--- test/sender.test.js | 25 ++++ test/websocket-server.test.js | 100 +++++++++------ test/websocket.test.js | 234 ++++++++++++++++++++++------------ 5 files changed, 284 insertions(+), 141 deletions(-) diff --git a/doc/ws.md b/doc/ws.md index 6e0c6ef9f..390596270 100644 --- a/doc/ws.md +++ b/doc/ws.md @@ -207,9 +207,14 @@ added when the `clientTracking` is truthy. ### server.close([callback]) -Close the HTTP server if created internally, terminate all clients and call -callback when done. If an external HTTP server is used via the `server` or -`noServer` constructor options, it must be closed manually. +Prevent the server from accepting new connections and close the HTTP server if +created internally. If an external HTTP server is used via the `server` or +`noServer` constructor options, it must be closed manually. Existing connections +are not closed automatically. The server emits a `'close'` event when all +connections are closed unless an external HTTP server is used and client +tracking is disabled. In this case the `'close'` event is emitted in the next +tick. The optional callback is called when the `'close'` event occurs and +receives an `Error` if the server is already closed. ### server.handleUpgrade(request, socket, head, callback) diff --git a/lib/websocket-server.js b/lib/websocket-server.js index e2f1af616..b0cba8315 100644 --- a/lib/websocket-server.js +++ b/lib/websocket-server.js @@ -111,7 +111,11 @@ class WebSocketServer extends EventEmitter { } if (options.perMessageDeflate === true) options.perMessageDeflate = {}; - if (options.clientTracking) this.clients = new Set(); + if (options.clientTracking) { + this.clients = new Set(); + this._shouldEmitClose = false; + } + this.options = options; this._state = RUNNING; } @@ -135,9 +139,10 @@ class WebSocketServer extends EventEmitter { } /** - * Close the server. + * Stop the server from accepting new connections and emit the `'close'` event + * when all existing connections are closed. * - * @param {Function} [cb] Callback + * @param {Function} [cb] A one-time listener for the `'close'` event * @public */ close(cb) { @@ -151,29 +156,35 @@ class WebSocketServer extends EventEmitter { if (this._state === CLOSING) return; this._state = CLOSING; - // - // Terminate all associated clients. - // - if (this.clients) { - for (const client of this.clients) client.terminate(); - } + if (this.options.noServer || this.options.server) { + if (this._server) { + this._removeListeners(); + this._removeListeners = this._server = null; + } - const server = this._server; + if (this.clients) { + if (!this.clients.size) { + process.nextTick(emitClose, this); + } else { + this._shouldEmitClose = true; + } + } else { + process.nextTick(emitClose, this); + } + } else { + const server = this._server; - if (server) { this._removeListeners(); this._removeListeners = this._server = null; // - // Close the http server if it was internally created. + // The HTTP/S server was created internally. Close it, and rely on its + // `'close'` event. // - if (this.options.port != null) { - server.close(emitClose.bind(undefined, this)); - return; - } + server.close(() => { + emitClose(this); + }); } - - process.nextTick(emitClose, this); } /** @@ -373,7 +384,13 @@ class WebSocketServer extends EventEmitter { if (this.clients) { this.clients.add(ws); - ws.on('close', () => this.clients.delete(ws)); + ws.on('close', () => { + this.clients.delete(ws); + + if (this._shouldEmitClose && !this.clients.size) { + process.nextTick(emitClose, this); + } + }); } cb(ws, req); diff --git a/test/sender.test.js b/test/sender.test.js index 58eca8fbf..1c43dc5a5 100644 --- a/test/sender.test.js +++ b/test/sender.test.js @@ -266,6 +266,31 @@ describe('Sender', () => { }); describe('#close', () => { + it('throws an error if the first argument is invalid', () => { + const mockSocket = new MockSocket(); + const sender = new Sender(mockSocket); + + assert.throws( + () => sender.close('error'), + /^TypeError: First argument must be a valid error code number$/ + ); + + assert.throws( + () => sender.close(1004), + /^TypeError: First argument must be a valid error code number$/ + ); + }); + + it('throws an error if the message is greater than 123 bytes', () => { + const mockSocket = new MockSocket(); + const sender = new Sender(mockSocket); + + assert.throws( + () => sender.close(1000, 'a'.repeat(124)), + /^RangeError: The message must not be greater than 123 bytes$/ + ); + }); + it('should consume all data before closing', (done) => { const perMessageDeflate = new PerMessageDeflate({ threshold: 0 }); diff --git a/test/websocket-server.test.js b/test/websocket-server.test.js index 5fc26780b..ece58cbea 100644 --- a/test/websocket-server.test.js +++ b/test/websocket-server.test.js @@ -75,6 +75,8 @@ describe('WebSocketServer', () => { }, () => { const ws = new WebSocket(`ws://localhost:${wss.address().port}`); + + ws.on('open', ws.close); } ); @@ -103,6 +105,8 @@ describe('WebSocketServer', () => { const port = 1337; const wss = new WebSocket.Server({ port }, () => { const ws = new WebSocket(`ws://localhost:${port}`); + + ws.on('open', ws.close); }); wss.on('connection', () => wss.close(done)); @@ -120,12 +124,14 @@ describe('WebSocketServer', () => { server.listen(0, () => { const wss = new WebSocket.Server({ server }); - const ws = new WebSocket(`ws://localhost:${server.address().port}`); wss.on('connection', () => { - wss.close(); server.close(done); }); + + const ws = new WebSocket(`ws://localhost:${server.address().port}`); + + ws.on('open', ws.close); }); }); @@ -169,7 +175,11 @@ describe('WebSocketServer', () => { assert.strictEqual(req.url, '/foo?bar=bar'); } else { assert.strictEqual(req.url, '/'); - wss.close(); + + for (const client of wss.clients) { + client.close(); + } + server.close(done); } }); @@ -209,30 +219,13 @@ describe('WebSocketServer', () => { }); describe('#close', () => { - it('does not throw when called twice', (done) => { + it('does not throw if called multiple times', (done) => { const wss = new WebSocket.Server({ port: 0 }, () => { + wss.on('close', done); + wss.close(); wss.close(); wss.close(); - - done(); - }); - }); - - it('closes all clients', (done) => { - let closes = 0; - const wss = new WebSocket.Server({ port: 0 }, () => { - const ws = new WebSocket(`ws://localhost:${wss.address().port}`); - ws.on('close', () => { - if (++closes === 2) done(); - }); - }); - - wss.on('connection', (ws) => { - ws.on('close', () => { - if (++closes === 2) done(); - }); - wss.close(); }); }); @@ -254,6 +247,8 @@ describe('WebSocketServer', () => { server.listen(0, () => { const ws = new WebSocket(`ws://localhost:${server.address().port}`); + + ws.on('open', ws.close); }); }); @@ -309,6 +304,16 @@ describe('WebSocketServer', () => { }); }); + it("emits the 'close' event if client tracking is disabled", (done) => { + const wss = new WebSocket.Server({ + noServer: true, + clientTracking: false + }); + + wss.on('close', done); + wss.close(); + }); + it("emits the 'close' event if the server is already closed", (done) => { const wss = new WebSocket.Server({ port: 0 }, () => { wss.close(() => { @@ -324,7 +329,10 @@ describe('WebSocketServer', () => { it('returns a list of connected clients', (done) => { const wss = new WebSocket.Server({ port: 0 }, () => { assert.strictEqual(wss.clients.size, 0); + const ws = new WebSocket(`ws://localhost:${wss.address().port}`); + + ws.on('open', ws.close); }); wss.on('connection', () => { @@ -404,9 +412,10 @@ describe('WebSocketServer', () => { const wss = new WebSocket.Server({ noServer: true }); server.on('upgrade', (req, socket, head) => { - wss.handleUpgrade(req, socket, head, (client) => - client.send('hello') - ); + wss.handleUpgrade(req, socket, head, (ws) => { + ws.send('hello'); + ws.close(); + }); }); const ws = new WebSocket(`ws://localhost:${server.address().port}`); @@ -414,7 +423,6 @@ describe('WebSocketServer', () => { ws.on('message', (message, isBinary) => { assert.deepStrictEqual(message, Buffer.from('hello')); assert.ok(!isBinary); - wss.close(); server.close(done); }); }); @@ -683,6 +691,7 @@ describe('WebSocketServer', () => { socket.once('data', (chunk) => { assert.strictEqual(chunk[0], 0x88); + socket.destroy(); wss.close(done); }); }); @@ -742,7 +751,6 @@ describe('WebSocketServer', () => { }); wss.on('connection', () => { - wss.close(); server.close(done); }); @@ -751,6 +759,8 @@ describe('WebSocketServer', () => { headers: { Origin: 'https://example.com', foo: 'bar' }, rejectUnauthorized: false }); + + ws.on('open', ws.close); }); }); @@ -762,6 +772,8 @@ describe('WebSocketServer', () => { }, () => { const ws = new WebSocket(`ws://localhost:${wss.address().port}`); + + ws.on('open', ws.close); } ); @@ -959,6 +971,10 @@ describe('WebSocketServer', () => { wss.close(done); }); }); + + wss.on('connection', (ws) => { + ws.close(); + }); }); }); @@ -966,17 +982,19 @@ describe('WebSocketServer', () => { const wss = new WebSocket.Server({ port: 0 }, () => { const ws = new WebSocket(`ws://localhost:${wss.address().port}`); - wss.on('headers', (headers, request) => { - assert.deepStrictEqual(headers.slice(0, 3), [ - 'HTTP/1.1 101 Switching Protocols', - 'Upgrade: websocket', - 'Connection: Upgrade' - ]); - assert.ok(request instanceof http.IncomingMessage); - assert.strictEqual(request.url, '/'); + ws.on('open', ws.close); + }); - wss.on('connection', () => wss.close(done)); - }); + wss.on('headers', (headers, request) => { + assert.deepStrictEqual(headers.slice(0, 3), [ + 'HTTP/1.1 101 Switching Protocols', + 'Upgrade: websocket', + 'Connection: Upgrade' + ]); + assert.ok(request instanceof http.IncomingMessage); + assert.strictEqual(request.url, '/'); + + wss.on('connection', () => wss.close(done)); }); }); }); @@ -985,6 +1003,8 @@ describe('WebSocketServer', () => { it('is disabled by default', (done) => { const wss = new WebSocket.Server({ port: 0 }, () => { const ws = new WebSocket(`ws://localhost:${wss.address().port}`); + + ws.on('open', ws.close); }); wss.on('connection', (ws, req) => { @@ -1016,6 +1036,10 @@ describe('WebSocketServer', () => { }); } ); + + wss.on('connection', (ws) => { + ws.close(); + }); }); }); }); diff --git a/test/websocket.test.js b/test/websocket.test.js index 37768772e..feb1d8c50 100644 --- a/test/websocket.test.js +++ b/test/websocket.test.js @@ -105,6 +105,10 @@ describe('WebSocket', () => { }); } ); + + wss.on('connection', (ws) => { + ws.close(); + }); }); it('throws an error when using an invalid `protocolVersion`', () => { @@ -230,6 +234,10 @@ describe('WebSocket', () => { wss.close(done); }; }); + + wss.on('connection', (ws) => { + ws.close(); + }); }); it('takes into account the data in the sender queue', (done) => { @@ -258,6 +266,10 @@ describe('WebSocket', () => { }); } ); + + wss.on('connection', (ws) => { + ws.close(); + }); }); it('takes into account the data in the socket queue', (done) => { @@ -526,6 +538,10 @@ describe('WebSocket', () => { wss.close(done); }); }); + + wss.on('connection', (ws) => { + ws.close(); + }); }); it("emits a 'ping' event", (done) => { @@ -534,7 +550,10 @@ describe('WebSocket', () => { ws.on('ping', () => wss.close(done)); }); - wss.on('connection', (ws) => ws.ping()); + wss.on('connection', (ws) => { + ws.ping(); + ws.close(); + }); }); it("emits a 'pong' event", (done) => { @@ -543,7 +562,10 @@ describe('WebSocket', () => { ws.on('pong', () => wss.close(done)); }); - wss.on('connection', (ws) => ws.pong()); + wss.on('connection', (ws) => { + ws.pong(); + ws.close(); + }); }); }); @@ -977,7 +999,13 @@ describe('WebSocket', () => { const port = wss.address().port; const ws = new WebSocket(`ws://localhost:${port}/?token=qwerty`); - ws.on('open', () => wss.close(done)); + ws.on('open', () => { + wss.close(done); + }); + }); + + wss.on('connection', (ws) => { + ws.close(); }); }); @@ -986,7 +1014,13 @@ describe('WebSocket', () => { const port = wss.address().port; const ws = new WebSocket(`ws://localhost:${port}?token=qwerty`); - ws.on('open', () => wss.close(done)); + ws.on('open', () => { + wss.close(done); + }); + }); + + wss.on('connection', (ws) => { + ws.close(); }); }); }); @@ -1084,7 +1118,10 @@ describe('WebSocket', () => { const ws = new WebSocket(`ws://localhost:${wss.address().port}`); ws.on('open', () => { - ws.ping(() => ws.ping()); + ws.ping(() => { + ws.ping(); + ws.close(); + }); }); }); @@ -1103,7 +1140,10 @@ describe('WebSocket', () => { const ws = new WebSocket(`ws://localhost:${wss.address().port}`); ws.on('open', () => { - ws.ping('hi', () => ws.ping('hi', true)); + ws.ping('hi', () => { + ws.ping('hi', true); + ws.close(); + }); }); }); @@ -1120,7 +1160,10 @@ describe('WebSocket', () => { const wss = new WebSocket.Server({ port: 0 }, () => { const ws = new WebSocket(`ws://localhost:${wss.address().port}`); - ws.on('open', () => ws.ping(0)); + ws.on('open', () => { + ws.ping(0); + ws.close(); + }); }); wss.on('connection', (ws) => { @@ -1144,6 +1187,10 @@ describe('WebSocket', () => { wss.close(done); }); }); + + wss.on('connection', (ws) => { + ws.close(); + }); }); }); @@ -1240,7 +1287,10 @@ describe('WebSocket', () => { const ws = new WebSocket(`ws://localhost:${wss.address().port}`); ws.on('open', () => { - ws.pong(() => ws.pong()); + ws.pong(() => { + ws.pong(); + ws.close(); + }); }); }); @@ -1259,7 +1309,10 @@ describe('WebSocket', () => { const ws = new WebSocket(`ws://localhost:${wss.address().port}`); ws.on('open', () => { - ws.pong('hi', () => ws.pong('hi', true)); + ws.pong('hi', () => { + ws.pong('hi', true); + ws.close(); + }); }); }); @@ -1276,7 +1329,10 @@ describe('WebSocket', () => { const wss = new WebSocket.Server({ port: 0 }, () => { const ws = new WebSocket(`ws://localhost:${wss.address().port}`); - ws.on('open', () => ws.pong(0)); + ws.on('open', () => { + ws.pong(0); + ws.close(); + }); }); wss.on('connection', (ws) => { @@ -1300,6 +1356,10 @@ describe('WebSocket', () => { wss.close(done); }); }); + + wss.on('connection', (ws) => { + ws.close(); + }); }); }); @@ -1413,6 +1473,7 @@ describe('WebSocket', () => { ws.on('message', (msg, isBinary) => { assert.ok(isBinary); ws.send(msg); + ws.close(); }); }); }); @@ -1432,6 +1493,7 @@ describe('WebSocket', () => { wss.on('connection', (ws) => { ws.on('message', (msg, isBinary) => { ws.send(msg, { binary: isBinary }); + ws.close(); }); }); }); @@ -1443,6 +1505,7 @@ describe('WebSocket', () => { ws.on('open', () => { ws.send('fragment', { fin: false }); ws.send('fragment', { fin: true }); + ws.close(); }); }); @@ -1459,7 +1522,10 @@ describe('WebSocket', () => { const wss = new WebSocket.Server({ port: 0 }, () => { const ws = new WebSocket(`ws://localhost:${wss.address().port}`); - ws.on('open', () => ws.send(0)); + ws.on('open', () => { + ws.send(0); + ws.close(); + }); }); wss.on('connection', (ws) => { @@ -1488,7 +1554,11 @@ describe('WebSocket', () => { const ws = new WebSocket(`ws://localhost:${wss.address().port}`); - ws.on('open', () => ws.send(partial)); + ws.on('open', () => { + ws.send(partial); + ws.close(); + }); + ws.on('message', (message, isBinary) => { assert.deepStrictEqual(message, buf); assert.ok(isBinary); @@ -1514,7 +1584,11 @@ describe('WebSocket', () => { const ws = new WebSocket(`ws://localhost:${wss.address().port}`); - ws.on('open', () => ws.send(array.buffer)); + ws.on('open', () => { + ws.send(array.buffer); + ws.close(); + }); + ws.onmessage = (event) => { assert.ok(event.data.equals(Buffer.from(array.buffer))); wss.close(done); @@ -1534,7 +1608,10 @@ describe('WebSocket', () => { const buf = Buffer.from('foobar'); const ws = new WebSocket(`ws://localhost:${wss.address().port}`); - ws.on('open', () => ws.send(buf)); + ws.on('open', () => { + ws.send(buf); + ws.close(); + }); ws.onmessage = (event) => { assert.deepStrictEqual(event.data, buf); @@ -1561,13 +1638,20 @@ describe('WebSocket', () => { }); }); }); + + wss.on('connection', (ws) => { + ws.close(); + }); }); it('works when the `data` argument is falsy', (done) => { const wss = new WebSocket.Server({ port: 0 }, () => { const ws = new WebSocket(`ws://localhost:${wss.address().port}`); - ws.on('open', () => ws.send()); + ws.on('open', () => { + ws.send(); + ws.close(); + }); }); wss.on('connection', (ws) => { @@ -1743,51 +1827,6 @@ describe('WebSocket', () => { }); }); - it('throws an error if the first argument is invalid (1/2)', (done) => { - const wss = new WebSocket.Server({ port: 0 }, () => { - const ws = new WebSocket(`ws://localhost:${wss.address().port}`); - - ws.on('open', () => { - assert.throws( - () => ws.close('error'), - /^TypeError: First argument must be a valid error code number$/ - ); - - wss.close(done); - }); - }); - }); - - it('throws an error if the first argument is invalid (2/2)', (done) => { - const wss = new WebSocket.Server({ port: 0 }, () => { - const ws = new WebSocket(`ws://localhost:${wss.address().port}`); - - ws.on('open', () => { - assert.throws( - () => ws.close(1004), - /^TypeError: First argument must be a valid error code number$/ - ); - - wss.close(done); - }); - }); - }); - - it('throws an error if the message is greater than 123 bytes', (done) => { - const wss = new WebSocket.Server({ port: 0 }, () => { - const ws = new WebSocket(`ws://localhost:${wss.address().port}`); - - ws.on('open', () => { - assert.throws( - () => ws.close(1000, 'a'.repeat(124)), - /^RangeError: The message must not be greater than 123 bytes$/ - ); - - wss.close(done); - }); - }); - }); - it('sends the close status code only when necessary', (done) => { let sent; const wss = new WebSocket.Server({ port: 0 }, () => { @@ -2206,7 +2245,11 @@ describe('WebSocket', () => { const wss = new WebSocket.Server({ port: 0 }, () => { const ws = new WebSocket(`ws://localhost:${wss.address().port}`); - ws.addEventListener('open', () => ws.send('hi')); + ws.addEventListener('open', () => { + ws.send('hi'); + ws.close(); + }); + ws.addEventListener('message', (messageEvent) => { assert.strictEqual(messageEvent.data, 'hi'); wss.close(done); @@ -2262,7 +2305,7 @@ describe('WebSocket', () => { ws.addEventListener('message', (messageEvent) => { assert.strictEqual(messageEvent.type, 'message'); assert.strictEqual(messageEvent.target, ws); - wss.close(); + ws.close(); }); ws.addEventListener('close', (closeEvent) => { assert.strictEqual(closeEvent.type, 'close'); @@ -2275,7 +2318,7 @@ describe('WebSocket', () => { assert.strictEqual(errorEvent.target, ws); assert.strictEqual(errorEvent.error, err); - done(); + wss.close(done); }); }); @@ -2292,7 +2335,10 @@ describe('WebSocket', () => { }; }); - wss.on('connection', (ws) => ws.send(new Uint8Array(4096))); + wss.on('connection', (ws) => { + ws.send(new Uint8Array(4096)); + ws.close(); + }); }); it('ignores `binaryType` for text messages', (done) => { @@ -2307,7 +2353,10 @@ describe('WebSocket', () => { }; }); - wss.on('connection', (ws) => ws.send('foo')); + wss.on('connection', (ws) => { + ws.send('foo'); + ws.close(); + }); }); it('allows to update `binaryType` on the fly', (done) => { @@ -2337,7 +2386,10 @@ describe('WebSocket', () => { ws.onopen = () => { testType('nodebuffer', () => { testType('arraybuffer', () => { - testType('fragments', () => wss.close(done)); + testType('fragments', () => { + ws.close(); + wss.close(done); + }); }); }); }; @@ -2361,7 +2413,6 @@ describe('WebSocket', () => { const wss = new WebSocket.Server({ server }); wss.on('connection', () => { - wss.close(); server.close(done); }); @@ -2369,6 +2420,8 @@ describe('WebSocket', () => { const ws = new WebSocket(`wss://127.0.0.1:${server.address().port}`, { rejectUnauthorized: false }); + + ws.on('open', ws.close); }); }); @@ -2392,7 +2445,6 @@ describe('WebSocket', () => { wss.on('connection', () => { assert.ok(success); server.close(done); - wss.close(); }); server.listen(0, () => { @@ -2401,6 +2453,8 @@ describe('WebSocket', () => { key: fs.readFileSync('test/fixtures/client-key.pem'), rejectUnauthorized: false }); + + ws.on('open', ws.close); }); }); @@ -2435,7 +2489,6 @@ describe('WebSocket', () => { assert.deepStrictEqual(message, Buffer.from('foobar')); assert.ok(!isBinary); server.close(done); - wss.close(); }); }); @@ -2444,7 +2497,10 @@ describe('WebSocket', () => { rejectUnauthorized: false }); - ws.on('open', () => ws.send('foobar')); + ws.on('open', () => { + ws.send('foobar'); + ws.close(); + }); }); }); @@ -2460,6 +2516,7 @@ describe('WebSocket', () => { ws.on('message', (message, isBinary) => { assert.ok(isBinary); ws.send(message); + ws.close(); }); }); @@ -2474,7 +2531,6 @@ describe('WebSocket', () => { assert.ok(isBinary); server.close(done); - wss.close(); }); }); }).timeout(4000); @@ -2712,7 +2768,11 @@ describe('WebSocket', () => { perMessageDeflate: { threshold: 0 } }); - ws.on('open', () => ws.send('hi', { compress: true })); + ws.on('open', () => { + ws.send('hi', { compress: true }); + ws.close(); + }); + ws.on('message', (message, isBinary) => { assert.deepStrictEqual(message, Buffer.from('hi')); assert.ok(!isBinary); @@ -2745,7 +2805,11 @@ describe('WebSocket', () => { perMessageDeflate: { threshold: 0 } }); - ws.on('open', () => ws.send(array, { compress: true })); + ws.on('open', () => { + ws.send(array, { compress: true }); + ws.close(); + }); + ws.on('message', (message, isBinary) => { assert.deepStrictEqual(message, Buffer.from(array.buffer)); assert.ok(isBinary); @@ -2779,7 +2843,11 @@ describe('WebSocket', () => { perMessageDeflate: { threshold: 0 } }); - ws.on('open', () => ws.send(array.buffer, { compress: true })); + ws.on('open', () => { + ws.send(array.buffer, { compress: true }); + ws.close(); + }); + ws.on('message', (message, isBinary) => { assert.deepStrictEqual(message, Buffer.from(array.buffer)); assert.ok(isBinary); @@ -2918,7 +2986,11 @@ describe('WebSocket', () => { perMessageDeflate: false }); - ws.on('open', () => ws.send('hi', { compress: true })); + ws.on('open', () => { + ws.send('hi', { compress: true }); + ws.close(); + }); + ws.on('message', (message, isBinary) => { assert.deepStrictEqual(message, Buffer.from('hi')); assert.ok(!isBinary); @@ -2934,10 +3006,10 @@ describe('WebSocket', () => { }); it('calls the callback if the socket is closed prematurely', (done) => { + const called = []; const wss = new WebSocket.Server( { perMessageDeflate: true, port: 0 }, () => { - const called = []; const ws = new WebSocket(`ws://localhost:${wss.address().port}`, { perMessageDeflate: { threshold: 0 } }); @@ -2966,15 +3038,15 @@ describe('WebSocket', () => { ); }); }); - - ws.on('close', () => { - assert.deepStrictEqual(called, [1, 2]); - wss.close(done); - }); } ); wss.on('connection', (ws) => { + ws.on('close', () => { + assert.deepStrictEqual(called, [1, 2]); + wss.close(done); + }); + ws._socket.end(); }); });