Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions doc/ws.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@ This class represents a WebSocket server. It extends the `EventEmitter`.
in response to a ping. Defaults to `true`.
- `backlog` {Number} The maximum length of the queue of pending connections.
- `clientTracking` {Boolean} Specifies whether or not to track clients.
- `closeTimeout` {Number} Duration in milliseconds to wait for a graceful
close after [`websocket.close()`][] is called. If the limit is reached, the
connection is forcibly terminated. Defaults to 30000.
- `handleProtocols` {Function} A function which can be used to handle the
WebSocket subprotocols. See description below.
- `host` {String} The hostname where to bind the server.
Expand Down Expand Up @@ -304,6 +307,9 @@ This class represents a WebSocket. It extends the `EventEmitter`.
the WHATWG standardbut may negatively impact performance.
- `autoPong` {Boolean} Specifies whether or not to automatically send a pong
in response to a ping. Defaults to `true`.
- `closeTimeout` {Number} Duration in milliseconds to wait for a graceful
close after [`websocket.close()`][] is called. If the limit is reached, the
connection is forcibly terminated. Defaults to 30000.
- `finishRequest` {Function} A function which can be used to customize the
headers of each HTTP request before it is sent. See description below.
- `followRedirects` {Boolean} Whether or not to follow redirects. Defaults to
Expand Down Expand Up @@ -709,4 +715,5 @@ as configured by the `maxPayload` option.
[`request.removeheader()`]:
https://nodejs.org/api/http.html#requestremoveheadername
[`socket.destroy()`]: https://nodejs.org/api/net.html#net_socket_destroy_error
[`websocket.close()`]: #websocketclosecode-reason
[zlib-options]: https://nodejs.org/api/zlib.html#zlib_class_options
1 change: 1 addition & 0 deletions lib/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ if (hasBlob) BINARY_TYPES.push('blob');

module.exports = {
BINARY_TYPES,
CLOSE_TIMEOUT: 30000,
EMPTY_BUFFER: Buffer.alloc(0),
GUID: '258EAFA5-E914-47DA-95CA-C5AB0DC85B11',
hasBlob,
Expand Down
6 changes: 5 additions & 1 deletion lib/websocket-server.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const extension = require('./extension');
const PerMessageDeflate = require('./permessage-deflate');
const subprotocol = require('./subprotocol');
const WebSocket = require('./websocket');
const { GUID, kWebSocket } = require('./constants');
const { CLOSE_TIMEOUT, GUID, kWebSocket } = require('./constants');

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

Expand All @@ -38,6 +38,9 @@ class WebSocketServer extends EventEmitter {
* pending connections
* @param {Boolean} [options.clientTracking=true] Specifies whether or not to
* track clients
* @param {Number} [options.closeTimeout=30000] Duration in milliseconds to
* wait for the closing handshake to finish after `websocket.close()` is
* called
* @param {Function} [options.handleProtocols] A hook to handle protocols
* @param {String} [options.host] The hostname where to bind the server
* @param {Number} [options.maxPayload=104857600] The maximum allowed message
Expand Down Expand Up @@ -67,6 +70,7 @@ class WebSocketServer extends EventEmitter {
perMessageDeflate: false,
handleProtocols: null,
clientTracking: true,
closeTimeout: CLOSE_TIMEOUT,
verifyClient: null,
noServer: false,
backlog: null, // use default (511 as implemented in net.js)
Expand Down
9 changes: 7 additions & 2 deletions lib/websocket.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const { isBlob } = require('./validation');

const {
BINARY_TYPES,
CLOSE_TIMEOUT,
EMPTY_BUFFER,
GUID,
kForOnEventAttribute,
Expand All @@ -32,7 +33,6 @@ const {
const { format, parse } = require('./extension');
const { toBuffer } = require('./buffer-util');

const closeTimeout = 30 * 1000;
const kAborted = Symbol('kAborted');
const protocolVersions = [8, 13];
const readyStates = ['CONNECTING', 'OPEN', 'CLOSING', 'CLOSED'];
Expand Down Expand Up @@ -88,6 +88,7 @@ class WebSocket extends EventEmitter {
initAsClient(this, address, protocols, options);
} else {
this._autoPong = options.autoPong;
this._closeTimeout = options.closeTimeout;
this._isServer = true;
}
}
Expand Down Expand Up @@ -629,6 +630,8 @@ module.exports = WebSocket;
* times in the same tick
* @param {Boolean} [options.autoPong=true] Specifies whether or not to
* automatically send a pong in response to a ping
* @param {Number} [options.closeTimeout=30000] Duration in milliseconds to wait
* for the closing handshake to finish after `websocket.close()` is called
* @param {Function} [options.finishRequest] A function which can be used to
* customize the headers of each http request before it is sent
* @param {Boolean} [options.followRedirects=false] Whether or not to follow
Expand All @@ -655,6 +658,7 @@ function initAsClient(websocket, address, protocols, options) {
const opts = {
allowSynchronousEvents: true,
autoPong: true,
closeTimeout: CLOSE_TIMEOUT,
protocolVersion: protocolVersions[1],
maxPayload: 100 * 1024 * 1024,
skipUTF8Validation: false,
Expand All @@ -673,6 +677,7 @@ function initAsClient(websocket, address, protocols, options) {
};

websocket._autoPong = opts.autoPong;
websocket._closeTimeout = opts.closeTimeout;

if (!protocolVersions.includes(opts.protocolVersion)) {
throw new RangeError(
Expand Down Expand Up @@ -1290,7 +1295,7 @@ function senderOnError(err) {
function setCloseTimer(websocket) {
websocket._closeTimer = setTimeout(
websocket._socket.destroy.bind(websocket._socket),
closeTimeout
websocket._closeTimeout
);
}

Expand Down
16 changes: 16 additions & 0 deletions test/websocket-server.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,22 @@ describe('WebSocketServer', () => {
});
});
});

it('honors the `closeTimeout` option', (done) => {
const closeTimeout = 1000;
const wss = new WebSocket.Server({ closeTimeout, port: 0 }, () => {
const ws = new WebSocket(`ws://localhost:${wss.address().port}`);
});

wss.on('connection', (ws) => {
ws.on('close', () => {
wss.close(done);
});

ws.close();
assert.strictEqual(ws._closeTimer._idleTimeout, closeTimeout);
});
});
});

it('emits an error if http server bind fails', (done) => {
Expand Down
18 changes: 18 additions & 0 deletions test/websocket.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,24 @@ describe('WebSocket', () => {
ws.ping();
});
});

it('honors the `closeTimeout` option', (done) => {
const wss = new WebSocket.Server({ port: 0 }, () => {
const closeTimeout = 1000;
const ws = new WebSocket(`ws://localhost:${wss.address().port}`, {
closeTimeout
});

ws.on('open', () => {
ws.close();
assert.strictEqual(ws._closeTimer._idleTimeout, closeTimeout);
});

ws.on('close', () => {
wss.close(done);
});
});
});
});
});

Expand Down