@@ -21,6 +21,7 @@ const {
21
21
kWebSocket,
22
22
NOOP
23
23
} = require ( './constants' ) ;
24
+ const { toBuffer } = require ( './buffer-util' ) ;
24
25
25
26
const readyStates = [ 'CONNECTING' , 'OPEN' , 'CLOSING' , 'CLOSED' ] ;
26
27
const protocolVersions = [ 8 , 13 ] ;
@@ -57,6 +58,7 @@ class WebSocket extends EventEmitter {
57
58
this . _socket = null ;
58
59
59
60
if ( address !== null ) {
61
+ this . _bufferedAmount = 0 ;
60
62
this . _isServer = false ;
61
63
this . _redirects = 0 ;
62
64
@@ -112,7 +114,7 @@ class WebSocket extends EventEmitter {
112
114
* @type {Number }
113
115
*/
114
116
get bufferedAmount ( ) {
115
- if ( ! this . _socket ) return 0 ;
117
+ if ( ! this . _socket ) return this . _bufferedAmount ;
116
118
117
119
//
118
120
// `socket.bufferSize` is `undefined` if the socket is closed.
@@ -252,6 +254,10 @@ class WebSocket extends EventEmitter {
252
254
* @public
253
255
*/
254
256
ping ( data , mask , cb ) {
257
+ if ( this . readyState === WebSocket . CONNECTING ) {
258
+ throw new Error ( 'WebSocket is not open: readyState 0 (CONNECTING)' ) ;
259
+ }
260
+
255
261
if ( typeof data === 'function' ) {
256
262
cb = data ;
257
263
data = mask = undefined ;
@@ -260,17 +266,13 @@ class WebSocket extends EventEmitter {
260
266
mask = undefined ;
261
267
}
262
268
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 ( ) ;
268
270
269
- if ( cb ) return cb ( err ) ;
270
- throw err ;
271
+ if ( this . readyState !== WebSocket . OPEN ) {
272
+ sendAfterClose ( this , data , cb ) ;
273
+ return ;
271
274
}
272
275
273
- if ( typeof data === 'number' ) data = data . toString ( ) ;
274
276
if ( mask === undefined ) mask = ! this . _isServer ;
275
277
this . _sender . ping ( data || EMPTY_BUFFER , mask , cb ) ;
276
278
}
@@ -284,6 +286,10 @@ class WebSocket extends EventEmitter {
284
286
* @public
285
287
*/
286
288
pong ( data , mask , cb ) {
289
+ if ( this . readyState === WebSocket . CONNECTING ) {
290
+ throw new Error ( 'WebSocket is not open: readyState 0 (CONNECTING)' ) ;
291
+ }
292
+
287
293
if ( typeof data === 'function' ) {
288
294
cb = data ;
289
295
data = mask = undefined ;
@@ -292,17 +298,13 @@ class WebSocket extends EventEmitter {
292
298
mask = undefined ;
293
299
}
294
300
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 ( ) ;
300
302
301
- if ( cb ) return cb ( err ) ;
302
- throw err ;
303
+ if ( this . readyState !== WebSocket . OPEN ) {
304
+ sendAfterClose ( this , data , cb ) ;
305
+ return ;
303
306
}
304
307
305
- if ( typeof data === 'number' ) data = data . toString ( ) ;
306
308
if ( mask === undefined ) mask = ! this . _isServer ;
307
309
this . _sender . pong ( data || EMPTY_BUFFER , mask , cb ) ;
308
310
}
@@ -312,31 +314,31 @@ class WebSocket extends EventEmitter {
312
314
*
313
315
* @param {* } data The message to send
314
316
* @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`
316
319
* @param {Boolean } options.binary Specifies whether `data` is binary or text
317
320
* @param {Boolean } options.fin Specifies whether the fragment is the last one
318
321
* @param {Boolean } options.mask Specifies whether or not to mask `data`
319
322
* @param {Function } cb Callback which is executed when data is written out
320
323
* @public
321
324
*/
322
325
send ( data , options , cb ) {
326
+ if ( this . readyState === WebSocket . CONNECTING ) {
327
+ throw new Error ( 'WebSocket is not open: readyState 0 (CONNECTING)' ) ;
328
+ }
329
+
323
330
if ( typeof options === 'function' ) {
324
331
cb = options ;
325
332
options = { } ;
326
333
}
327
334
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 ( ) ;
333
336
334
- if ( cb ) return cb ( err ) ;
335
- throw err ;
337
+ if ( this . readyState !== WebSocket . OPEN ) {
338
+ sendAfterClose ( this , data , cb ) ;
339
+ return ;
336
340
}
337
341
338
- if ( typeof data === 'number' ) data = data . toString ( ) ;
339
-
340
342
const opts = Object . assign (
341
343
{
342
344
binary : typeof data !== 'string' ,
@@ -723,6 +725,38 @@ function abortHandshake(websocket, stream, message) {
723
725
}
724
726
}
725
727
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
+
726
760
/**
727
761
* The listener of the `Receiver` `'conclude'` event.
728
762
*
0 commit comments