Skip to content

Commit aa1dcd5

Browse files
committed
[fix] Make WebSocket#close() set the close timer immediately
If there is buffered data to write and the stream is not flowing, it is possible that the callback of `Sender.prototype.close()` is never called.
1 parent 297f56d commit aa1dcd5

File tree

2 files changed

+44
-37
lines changed

2 files changed

+44
-37
lines changed

lib/websocket.js

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ const {
2424

2525
const readyStates = ['CONNECTING', 'OPEN', 'CLOSING', 'CLOSED'];
2626
const protocolVersions = [8, 13];
27-
const closeTimeout = 30 * 1000; // Allow 30 seconds to terminate the connection cleanly.
27+
const closeTimeout = 30 * 1000;
2828

2929
/**
3030
* Class representing a WebSocket.
@@ -87,8 +87,9 @@ class WebSocket extends EventEmitter {
8787
}
8888

8989
/**
90-
* This deviates from the WHATWG interface since ws doesn't support the required
91-
* default "blob" type (instead we define a custom "nodebuffer" type).
90+
* This deviates from the WHATWG interface since ws doesn't support the
91+
* required default "blob" type (instead we define a custom "nodebuffer"
92+
* type).
9293
*
9394
* @type {String}
9495
*/
@@ -230,20 +231,16 @@ class WebSocket extends EventEmitter {
230231
if (err) return;
231232

232233
this._closeFrameSent = true;
233-
234-
if (this._socket.writable) {
235-
if (this._closeFrameReceived) this._socket.end();
236-
237-
//
238-
// Ensure that the connection is closed even if the closing handshake
239-
// fails.
240-
//
241-
this._closeTimer = setTimeout(
242-
this._socket.destroy.bind(this._socket),
243-
closeTimeout
244-
);
245-
}
234+
if (this._closeFrameReceived) this._socket.end();
246235
});
236+
237+
//
238+
// Specify a timeout for the closing handshake to complete.
239+
//
240+
this._closeTimer = setTimeout(
241+
this._socket.destroy.bind(this._socket),
242+
closeTimeout
243+
);
247244
}
248245

249246
/**

test/websocket.test.js

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1305,27 +1305,6 @@ describe('WebSocket', () => {
13051305
});
13061306
});
13071307

1308-
it('ends connection to the server', (done) => {
1309-
const wss = new WebSocket.Server(
1310-
{
1311-
clientTracking: false,
1312-
port: 0
1313-
},
1314-
() => {
1315-
const ws = new WebSocket(`ws://localhost:${wss.address().port}`);
1316-
1317-
ws.on('open', () => {
1318-
ws.on('close', (code, reason) => {
1319-
assert.strictEqual(reason, 'some reason');
1320-
assert.strictEqual(code, 1000);
1321-
wss.close(done);
1322-
});
1323-
ws.close(1000, 'some reason');
1324-
});
1325-
}
1326-
);
1327-
});
1328-
13291308
it('permits all buffered data to be delivered', (done) => {
13301309
const wss = new WebSocket.Server(
13311310
{
@@ -1383,6 +1362,37 @@ describe('WebSocket', () => {
13831362

13841363
wss.on('connection', (ws) => ws.close());
13851364
});
1365+
1366+
it('sets a timer for the closing handshake to complete', (done) => {
1367+
const wss = new WebSocket.Server({ port: 0 }, () => {
1368+
const ws = new WebSocket(`ws://localhost:${wss.address().port}`);
1369+
1370+
ws.on('close', (code, reason) => {
1371+
assert.strictEqual(code, 1000);
1372+
assert.strictEqual(reason, 'some reason');
1373+
wss.close(done);
1374+
});
1375+
1376+
ws.on('open', () => {
1377+
let callbackCalled = false;
1378+
1379+
assert.strictEqual(ws._closeTimer, null);
1380+
1381+
ws.send('foo', () => {
1382+
callbackCalled = true;
1383+
});
1384+
1385+
ws.close(1000, 'some reason');
1386+
1387+
//
1388+
// Check that the close timer is set even if the `Sender.close()`
1389+
// callback is not called.
1390+
//
1391+
assert.strictEqual(callbackCalled, false);
1392+
assert.strictEqual(ws._closeTimer._idleTimeout, 30000);
1393+
});
1394+
});
1395+
});
13861396
});
13871397

13881398
describe('#terminate', () => {

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy