Skip to content

Commit 74ce54e

Browse files
committed
[major] Change WebSocket#{p{i,o}ng,send}() behavior
- If the `readyState` attribute is `CONNECTING`, throw an exception. - If the `readyState` attribute is `CLOSING` or `CLOSED` - Increase the `bufferedAmount` attribute by the length of the `data` argument in bytes. - If specified, call the `callback` function with an error. Fixes #1515
1 parent 40734d8 commit 74ce54e

File tree

2 files changed

+325
-100
lines changed

2 files changed

+325
-100
lines changed

lib/websocket.js

Lines changed: 61 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ const {
2121
kWebSocket,
2222
NOOP
2323
} = require('./constants');
24+
const { toBuffer } = require('./buffer-util');
2425

2526
const readyStates = ['CONNECTING', 'OPEN', 'CLOSING', 'CLOSED'];
2627
const protocolVersions = [8, 13];
@@ -57,6 +58,7 @@ class WebSocket extends EventEmitter {
5758
this._socket = null;
5859

5960
if (address !== null) {
61+
this._bufferedAmount = 0;
6062
this._isServer = false;
6163
this._redirects = 0;
6264

@@ -112,7 +114,7 @@ class WebSocket extends EventEmitter {
112114
* @type {Number}
113115
*/
114116
get bufferedAmount() {
115-
if (!this._socket) return 0;
117+
if (!this._socket) return this._bufferedAmount;
116118

117119
//
118120
// `socket.bufferSize` is `undefined` if the socket is closed.
@@ -252,6 +254,10 @@ class WebSocket extends EventEmitter {
252254
* @public
253255
*/
254256
ping(data, mask, cb) {
257+
if (this.readyState === WebSocket.CONNECTING) {
258+
throw new Error('WebSocket is not open: readyState 0 (CONNECTING)');
259+
}
260+
255261
if (typeof data === 'function') {
256262
cb = data;
257263
data = mask = undefined;
@@ -260,17 +266,13 @@ class WebSocket extends EventEmitter {
260266
mask = undefined;
261267
}
262268

263-
if (this.readyState !== WebSocket.OPEN) {
264-
const err = new Error(
265-
`WebSocket is not open: readyState ${this.readyState} ` +
266-
`(${readyStates[this.readyState]})`
267-
);
269+
if (typeof data === 'number') data = data.toString();
268270

269-
if (cb) return cb(err);
270-
throw err;
271+
if (this.readyState !== WebSocket.OPEN) {
272+
sendAfterClose(this, data, cb);
273+
return;
271274
}
272275

273-
if (typeof data === 'number') data = data.toString();
274276
if (mask === undefined) mask = !this._isServer;
275277
this._sender.ping(data || EMPTY_BUFFER, mask, cb);
276278
}
@@ -284,6 +286,10 @@ class WebSocket extends EventEmitter {
284286
* @public
285287
*/
286288
pong(data, mask, cb) {
289+
if (this.readyState === WebSocket.CONNECTING) {
290+
throw new Error('WebSocket is not open: readyState 0 (CONNECTING)');
291+
}
292+
287293
if (typeof data === 'function') {
288294
cb = data;
289295
data = mask = undefined;
@@ -292,17 +298,13 @@ class WebSocket extends EventEmitter {
292298
mask = undefined;
293299
}
294300

295-
if (this.readyState !== WebSocket.OPEN) {
296-
const err = new Error(
297-
`WebSocket is not open: readyState ${this.readyState} ` +
298-
`(${readyStates[this.readyState]})`
299-
);
301+
if (typeof data === 'number') data = data.toString();
300302

301-
if (cb) return cb(err);
302-
throw err;
303+
if (this.readyState !== WebSocket.OPEN) {
304+
sendAfterClose(this, data, cb);
305+
return;
303306
}
304307

305-
if (typeof data === 'number') data = data.toString();
306308
if (mask === undefined) mask = !this._isServer;
307309
this._sender.pong(data || EMPTY_BUFFER, mask, cb);
308310
}
@@ -312,31 +314,31 @@ class WebSocket extends EventEmitter {
312314
*
313315
* @param {*} data The message to send
314316
* @param {Object} options Options object
315-
* @param {Boolean} options.compress Specifies whether or not to compress `data`
317+
* @param {Boolean} options.compress Specifies whether or not to compress
318+
* `data`
316319
* @param {Boolean} options.binary Specifies whether `data` is binary or text
317320
* @param {Boolean} options.fin Specifies whether the fragment is the last one
318321
* @param {Boolean} options.mask Specifies whether or not to mask `data`
319322
* @param {Function} cb Callback which is executed when data is written out
320323
* @public
321324
*/
322325
send(data, options, cb) {
326+
if (this.readyState === WebSocket.CONNECTING) {
327+
throw new Error('WebSocket is not open: readyState 0 (CONNECTING)');
328+
}
329+
323330
if (typeof options === 'function') {
324331
cb = options;
325332
options = {};
326333
}
327334

328-
if (this.readyState !== WebSocket.OPEN) {
329-
const err = new Error(
330-
`WebSocket is not open: readyState ${this.readyState} ` +
331-
`(${readyStates[this.readyState]})`
332-
);
335+
if (typeof data === 'number') data = data.toString();
333336

334-
if (cb) return cb(err);
335-
throw err;
337+
if (this.readyState !== WebSocket.OPEN) {
338+
sendAfterClose(this, data, cb);
339+
return;
336340
}
337341

338-
if (typeof data === 'number') data = data.toString();
339-
340342
const opts = Object.assign(
341343
{
342344
binary: typeof data !== 'string',
@@ -723,6 +725,38 @@ function abortHandshake(websocket, stream, message) {
723725
}
724726
}
725727

728+
/**
729+
* Handle cases where the `ping()`, `pong()`, or `send()` methods are called
730+
* when the `readyState` attribute is `CLOSING` or `CLOSED`.
731+
*
732+
* @param {WebSocket} websocket The WebSocket instance
733+
* @param {*} data The data to send
734+
* @param {Function} cb Callback
735+
* @private
736+
*/
737+
function sendAfterClose(websocket, data, cb) {
738+
if (data) {
739+
const length = toBuffer(data).length;
740+
741+
//
742+
// The `_bufferedAmount` property is used only when the peer is a client and
743+
// the opening handshake fails. Under these circumstances, in fact, the
744+
// `setSocket()` method is not called, so the `_socket` and `_sender`
745+
// properties are set to `null`.
746+
//
747+
if (websocket._socket) websocket._sender._bufferedBytes += length;
748+
else websocket._bufferedAmount += length;
749+
}
750+
751+
if (cb) {
752+
const err = new Error(
753+
`WebSocket is not open: readyState ${websocket.readyState} ` +
754+
`(${readyStates[websocket.readyState]})`
755+
);
756+
cb(err);
757+
}
758+
}
759+
726760
/**
727761
* The listener of the `Receiver` `'conclude'` event.
728762
*

0 commit comments

Comments
 (0)