Skip to content

Commit 5a58730

Browse files
committed
[fix] Emit the 'close' event after the server is closed
Ensure that `WebSocketServer.prototype.close()` does not emit a `'close'` event prematurely if called while the internal HTTP/S server is closing.
1 parent ea63b29 commit 5a58730

File tree

2 files changed

+53
-5
lines changed

2 files changed

+53
-5
lines changed

lib/websocket-server.js

+15-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ const { GUID, kWebSocket } = require('./constants');
1616

1717
const keyRegex = /^[+/0-9A-Za-z]{22}==$/;
1818

19+
const RUNNING = 0;
20+
const CLOSING = 1;
21+
const CLOSED = 2;
22+
1923
/**
2024
* Class representing a WebSocket server.
2125
*
@@ -108,6 +112,7 @@ class WebSocketServer extends EventEmitter {
108112
if (options.perMessageDeflate === true) options.perMessageDeflate = {};
109113
if (options.clientTracking) this.clients = new Set();
110114
this.options = options;
115+
this._state = RUNNING;
111116
}
112117

113118
/**
@@ -137,6 +142,14 @@ class WebSocketServer extends EventEmitter {
137142
close(cb) {
138143
if (cb) this.once('close', cb);
139144

145+
if (this._state === CLOSED) {
146+
process.nextTick(emitClose, this);
147+
return;
148+
}
149+
150+
if (this._state === CLOSING) return;
151+
this._state = CLOSING;
152+
140153
//
141154
// Terminate all associated clients.
142155
//
@@ -154,7 +167,7 @@ class WebSocketServer extends EventEmitter {
154167
// Close the http server if it was internally created.
155168
//
156169
if (this.options.port != null) {
157-
server.close(() => this.emit('close'));
170+
server.close(emitClose.bind(undefined, this));
158171
return;
159172
}
160173
}
@@ -373,6 +386,7 @@ function addListeners(server, map) {
373386
* @private
374387
*/
375388
function emitClose(server) {
389+
server._state = CLOSED;
376390
server.emit('close');
377391
}
378392

test/websocket-server.test.js

+38-4
Original file line numberDiff line numberDiff line change
@@ -278,11 +278,45 @@ describe('WebSocketServer', () => {
278278
});
279279
});
280280

281-
it("emits the 'close' event", (done) => {
282-
const wss = new WebSocket.Server({ noServer: true });
281+
it("emits the 'close' event after the server closes", (done) => {
282+
let serverCloseEventEmitted = false;
283+
284+
const wss = new WebSocket.Server({ port: 0 }, () => {
285+
net.createConnection({ port: wss.address().port });
286+
});
287+
288+
wss._server.on('connection', (socket) => {
289+
wss.close();
290+
291+
//
292+
// The server is closing. Ensure this does not emit a `'close'`
293+
// event before the server is actually closed.
294+
//
295+
wss.close();
296+
297+
process.nextTick(() => {
298+
socket.end();
299+
});
300+
});
283301

284-
wss.on('close', done);
285-
wss.close();
302+
wss._server.on('close', () => {
303+
serverCloseEventEmitted = true;
304+
});
305+
306+
wss.on('close', () => {
307+
assert.ok(serverCloseEventEmitted);
308+
done();
309+
});
310+
});
311+
312+
it("emits the 'close' event if the server is already closed", (done) => {
313+
const wss = new WebSocket.Server({ port: 0 }, () => {
314+
wss.close(() => {
315+
assert.strictEqual(wss._state, 2);
316+
wss.on('close', done);
317+
wss.close();
318+
});
319+
});
286320
});
287321
});
288322

0 commit comments

Comments
 (0)