isomorfeus-transport 2.0.7 → 2.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,1144 +1,1225 @@
1
- /* eslint no-unused-vars: ["error", { "varsIgnorePattern": "^Readable$" }] */
2
-
3
- 'use strict';
4
-
5
- const EventEmitter = require('events');
6
- const https = require('https');
7
- const http = require('http');
8
- const net = require('net');
9
- const tls = require('tls');
10
- const { randomBytes, createHash } = require('crypto');
11
- const { Readable } = require('stream');
12
- const { URL } = require('url');
13
-
14
- const PerMessageDeflate = require('./permessage-deflate');
15
- const Receiver = require('./receiver');
16
- const Sender = require('./sender');
17
- const {
18
- BINARY_TYPES,
19
- EMPTY_BUFFER,
20
- GUID,
21
- kForOnEventAttribute,
22
- kListener,
23
- kStatusCode,
24
- kWebSocket,
25
- NOOP
26
- } = require('./constants');
27
- const {
28
- EventTarget: { addEventListener, removeEventListener }
29
- } = require('./event-target');
30
- const { format, parse } = require('./extension');
31
- const { toBuffer } = require('./buffer-util');
32
-
33
- const readyStates = ['CONNECTING', 'OPEN', 'CLOSING', 'CLOSED'];
34
- const subprotocolRegex = /^[!#$%&'*+\-.0-9A-Z^_`|a-z~]+$/;
35
- const protocolVersions = [8, 13];
36
- const closeTimeout = 30 * 1000;
37
-
38
- /**
39
- * Class representing a WebSocket.
40
- *
41
- * @extends EventEmitter
42
- */
43
- class WebSocket extends EventEmitter {
44
- /**
45
- * Create a new `WebSocket`.
46
- *
47
- * @param {(String|URL)} address The URL to which to connect
48
- * @param {(String|String[])} [protocols] The subprotocols
49
- * @param {Object} [options] Connection options
50
- */
51
- constructor(address, protocols, options) {
52
- super();
53
-
54
- this._binaryType = BINARY_TYPES[0];
55
- this._closeCode = 1006;
56
- this._closeFrameReceived = false;
57
- this._closeFrameSent = false;
58
- this._closeMessage = EMPTY_BUFFER;
59
- this._closeTimer = null;
60
- this._extensions = {};
61
- this._protocol = '';
62
- this._readyState = WebSocket.CONNECTING;
63
- this._receiver = null;
64
- this._sender = null;
65
- this._socket = null;
66
-
67
- if (address !== null) {
68
- this._bufferedAmount = 0;
69
- this._isServer = false;
70
- this._redirects = 0;
71
-
72
- if (protocols === undefined) {
73
- protocols = [];
74
- } else if (!Array.isArray(protocols)) {
75
- if (typeof protocols === 'object' && protocols !== null) {
76
- options = protocols;
77
- protocols = [];
78
- } else {
79
- protocols = [protocols];
80
- }
81
- }
82
-
83
- initAsClient(this, address, protocols, options);
84
- } else {
85
- this._isServer = true;
86
- }
87
- }
88
-
89
- /**
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).
93
- *
94
- * @type {String}
95
- */
96
- get binaryType() {
97
- return this._binaryType;
98
- }
99
-
100
- set binaryType(type) {
101
- if (!BINARY_TYPES.includes(type)) return;
102
-
103
- this._binaryType = type;
104
-
105
- //
106
- // Allow to change `binaryType` on the fly.
107
- //
108
- if (this._receiver) this._receiver._binaryType = type;
109
- }
110
-
111
- /**
112
- * @type {Number}
113
- */
114
- get bufferedAmount() {
115
- if (!this._socket) return this._bufferedAmount;
116
-
117
- return this._socket._writableState.length + this._sender._bufferedBytes;
118
- }
119
-
120
- /**
121
- * @type {String}
122
- */
123
- get extensions() {
124
- return Object.keys(this._extensions).join();
125
- }
126
-
127
- /**
128
- * @type {Function}
129
- */
130
- /* istanbul ignore next */
131
- get onclose() {
132
- return null;
133
- }
134
-
135
- /**
136
- * @type {Function}
137
- */
138
- /* istanbul ignore next */
139
- get onerror() {
140
- return null;
141
- }
142
-
143
- /**
144
- * @type {Function}
145
- */
146
- /* istanbul ignore next */
147
- get onopen() {
148
- return null;
149
- }
150
-
151
- /**
152
- * @type {Function}
153
- */
154
- /* istanbul ignore next */
155
- get onmessage() {
156
- return null;
157
- }
158
-
159
- /**
160
- * @type {String}
161
- */
162
- get protocol() {
163
- return this._protocol;
164
- }
165
-
166
- /**
167
- * @type {Number}
168
- */
169
- get readyState() {
170
- return this._readyState;
171
- }
172
-
173
- /**
174
- * @type {String}
175
- */
176
- get url() {
177
- return this._url;
178
- }
179
-
180
- /**
181
- * Set up the socket and the internal resources.
182
- *
183
- * @param {(net.Socket|tls.Socket)} socket The network socket between the
184
- * server and client
185
- * @param {Buffer} head The first packet of the upgraded stream
186
- * @param {Object} options Options object
187
- * @param {Number} [options.maxPayload=0] The maximum allowed message size
188
- * @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or
189
- * not to skip UTF-8 validation for text and close messages
190
- * @private
191
- */
192
- setSocket(socket, head, options) {
193
- const receiver = new Receiver({
194
- binaryType: this.binaryType,
195
- extensions: this._extensions,
196
- isServer: this._isServer,
197
- maxPayload: options.maxPayload,
198
- skipUTF8Validation: options.skipUTF8Validation
199
- });
200
-
201
- this._sender = new Sender(socket, this._extensions);
202
- this._receiver = receiver;
203
- this._socket = socket;
204
-
205
- receiver[kWebSocket] = this;
206
- socket[kWebSocket] = this;
207
-
208
- receiver.on('conclude', receiverOnConclude);
209
- receiver.on('drain', receiverOnDrain);
210
- receiver.on('error', receiverOnError);
211
- receiver.on('message', receiverOnMessage);
212
- receiver.on('ping', receiverOnPing);
213
- receiver.on('pong', receiverOnPong);
214
-
215
- socket.setTimeout(0);
216
- socket.setNoDelay();
217
-
218
- if (head.length > 0) socket.unshift(head);
219
-
220
- socket.on('close', socketOnClose);
221
- socket.on('data', socketOnData);
222
- socket.on('end', socketOnEnd);
223
- socket.on('error', socketOnError);
224
-
225
- this._readyState = WebSocket.OPEN;
226
- this.emit('open');
227
- }
228
-
229
- /**
230
- * Emit the `'close'` event.
231
- *
232
- * @private
233
- */
234
- emitClose() {
235
- if (!this._socket) {
236
- this._readyState = WebSocket.CLOSED;
237
- this.emit('close', this._closeCode, this._closeMessage);
238
- return;
239
- }
240
-
241
- if (this._extensions[PerMessageDeflate.extensionName]) {
242
- this._extensions[PerMessageDeflate.extensionName].cleanup();
243
- }
244
-
245
- this._receiver.removeAllListeners();
246
- this._readyState = WebSocket.CLOSED;
247
- this.emit('close', this._closeCode, this._closeMessage);
248
- }
249
-
250
- /**
251
- * Start a closing handshake.
252
- *
253
- * +----------+ +-----------+ +----------+
254
- * - - -|ws.close()|-->|close frame|-->|ws.close()|- - -
255
- * | +----------+ +-----------+ +----------+ |
256
- * +----------+ +-----------+ |
257
- * CLOSING |ws.close()|<--|close frame|<--+-----+ CLOSING
258
- * +----------+ +-----------+ |
259
- * | | | +---+ |
260
- * +------------------------+-->|fin| - - - -
261
- * | +---+ | +---+
262
- * - - - - -|fin|<---------------------+
263
- * +---+
264
- *
265
- * @param {Number} [code] Status code explaining why the connection is closing
266
- * @param {(String|Buffer)} [data] The reason why the connection is
267
- * closing
268
- * @public
269
- */
270
- close(code, data) {
271
- if (this.readyState === WebSocket.CLOSED) return;
272
- if (this.readyState === WebSocket.CONNECTING) {
273
- const msg = 'WebSocket was closed before the connection was established';
274
- return abortHandshake(this, this._req, msg);
275
- }
276
-
277
- if (this.readyState === WebSocket.CLOSING) {
278
- if (
279
- this._closeFrameSent &&
280
- (this._closeFrameReceived || this._receiver._writableState.errorEmitted)
281
- ) {
282
- this._socket.end();
283
- }
284
-
285
- return;
286
- }
287
-
288
- this._readyState = WebSocket.CLOSING;
289
- this._sender.close(code, data, !this._isServer, (err) => {
290
- //
291
- // This error is handled by the `'error'` listener on the socket. We only
292
- // want to know if the close frame has been sent here.
293
- //
294
- if (err) return;
295
-
296
- this._closeFrameSent = true;
297
-
298
- if (
299
- this._closeFrameReceived ||
300
- this._receiver._writableState.errorEmitted
301
- ) {
302
- this._socket.end();
303
- }
304
- });
305
-
306
- //
307
- // Specify a timeout for the closing handshake to complete.
308
- //
309
- this._closeTimer = setTimeout(
310
- this._socket.destroy.bind(this._socket),
311
- closeTimeout
312
- );
313
- }
314
-
315
- /**
316
- * Send a ping.
317
- *
318
- * @param {*} [data] The data to send
319
- * @param {Boolean} [mask] Indicates whether or not to mask `data`
320
- * @param {Function} [cb] Callback which is executed when the ping is sent
321
- * @public
322
- */
323
- ping(data, mask, cb) {
324
- if (this.readyState === WebSocket.CONNECTING) {
325
- throw new Error('WebSocket is not open: readyState 0 (CONNECTING)');
326
- }
327
-
328
- if (typeof data === 'function') {
329
- cb = data;
330
- data = mask = undefined;
331
- } else if (typeof mask === 'function') {
332
- cb = mask;
333
- mask = undefined;
334
- }
335
-
336
- if (typeof data === 'number') data = data.toString();
337
-
338
- if (this.readyState !== WebSocket.OPEN) {
339
- sendAfterClose(this, data, cb);
340
- return;
341
- }
342
-
343
- if (mask === undefined) mask = !this._isServer;
344
- this._sender.ping(data || EMPTY_BUFFER, mask, cb);
345
- }
346
-
347
- /**
348
- * Send a pong.
349
- *
350
- * @param {*} [data] The data to send
351
- * @param {Boolean} [mask] Indicates whether or not to mask `data`
352
- * @param {Function} [cb] Callback which is executed when the pong is sent
353
- * @public
354
- */
355
- pong(data, mask, cb) {
356
- if (this.readyState === WebSocket.CONNECTING) {
357
- throw new Error('WebSocket is not open: readyState 0 (CONNECTING)');
358
- }
359
-
360
- if (typeof data === 'function') {
361
- cb = data;
362
- data = mask = undefined;
363
- } else if (typeof mask === 'function') {
364
- cb = mask;
365
- mask = undefined;
366
- }
367
-
368
- if (typeof data === 'number') data = data.toString();
369
-
370
- if (this.readyState !== WebSocket.OPEN) {
371
- sendAfterClose(this, data, cb);
372
- return;
373
- }
374
-
375
- if (mask === undefined) mask = !this._isServer;
376
- this._sender.pong(data || EMPTY_BUFFER, mask, cb);
377
- }
378
-
379
- /**
380
- * Send a data message.
381
- *
382
- * @param {*} data The message to send
383
- * @param {Object} [options] Options object
384
- * @param {Boolean} [options.binary] Specifies whether `data` is binary or
385
- * text
386
- * @param {Boolean} [options.compress] Specifies whether or not to compress
387
- * `data`
388
- * @param {Boolean} [options.fin=true] Specifies whether the fragment is the
389
- * last one
390
- * @param {Boolean} [options.mask] Specifies whether or not to mask `data`
391
- * @param {Function} [cb] Callback which is executed when data is written out
392
- * @public
393
- */
394
- send(data, options, cb) {
395
- if (this.readyState === WebSocket.CONNECTING) {
396
- throw new Error('WebSocket is not open: readyState 0 (CONNECTING)');
397
- }
398
-
399
- if (typeof options === 'function') {
400
- cb = options;
401
- options = {};
402
- }
403
-
404
- if (typeof data === 'number') data = data.toString();
405
-
406
- if (this.readyState !== WebSocket.OPEN) {
407
- sendAfterClose(this, data, cb);
408
- return;
409
- }
410
-
411
- const opts = {
412
- binary: typeof data !== 'string',
413
- mask: !this._isServer,
414
- compress: true,
415
- fin: true,
416
- ...options
417
- };
418
-
419
- if (!this._extensions[PerMessageDeflate.extensionName]) {
420
- opts.compress = false;
421
- }
422
-
423
- this._sender.send(data || EMPTY_BUFFER, opts, cb);
424
- }
425
-
426
- /**
427
- * Forcibly close the connection.
428
- *
429
- * @public
430
- */
431
- terminate() {
432
- if (this.readyState === WebSocket.CLOSED) return;
433
- if (this.readyState === WebSocket.CONNECTING) {
434
- const msg = 'WebSocket was closed before the connection was established';
435
- return abortHandshake(this, this._req, msg);
436
- }
437
-
438
- if (this._socket) {
439
- this._readyState = WebSocket.CLOSING;
440
- this._socket.destroy();
441
- }
442
- }
443
- }
444
-
445
- /**
446
- * @constant {Number} CONNECTING
447
- * @memberof WebSocket
448
- */
449
- Object.defineProperty(WebSocket, 'CONNECTING', {
450
- enumerable: true,
451
- value: readyStates.indexOf('CONNECTING')
452
- });
453
-
454
- /**
455
- * @constant {Number} CONNECTING
456
- * @memberof WebSocket.prototype
457
- */
458
- Object.defineProperty(WebSocket.prototype, 'CONNECTING', {
459
- enumerable: true,
460
- value: readyStates.indexOf('CONNECTING')
461
- });
462
-
463
- /**
464
- * @constant {Number} OPEN
465
- * @memberof WebSocket
466
- */
467
- Object.defineProperty(WebSocket, 'OPEN', {
468
- enumerable: true,
469
- value: readyStates.indexOf('OPEN')
470
- });
471
-
472
- /**
473
- * @constant {Number} OPEN
474
- * @memberof WebSocket.prototype
475
- */
476
- Object.defineProperty(WebSocket.prototype, 'OPEN', {
477
- enumerable: true,
478
- value: readyStates.indexOf('OPEN')
479
- });
480
-
481
- /**
482
- * @constant {Number} CLOSING
483
- * @memberof WebSocket
484
- */
485
- Object.defineProperty(WebSocket, 'CLOSING', {
486
- enumerable: true,
487
- value: readyStates.indexOf('CLOSING')
488
- });
489
-
490
- /**
491
- * @constant {Number} CLOSING
492
- * @memberof WebSocket.prototype
493
- */
494
- Object.defineProperty(WebSocket.prototype, 'CLOSING', {
495
- enumerable: true,
496
- value: readyStates.indexOf('CLOSING')
497
- });
498
-
499
- /**
500
- * @constant {Number} CLOSED
501
- * @memberof WebSocket
502
- */
503
- Object.defineProperty(WebSocket, 'CLOSED', {
504
- enumerable: true,
505
- value: readyStates.indexOf('CLOSED')
506
- });
507
-
508
- /**
509
- * @constant {Number} CLOSED
510
- * @memberof WebSocket.prototype
511
- */
512
- Object.defineProperty(WebSocket.prototype, 'CLOSED', {
513
- enumerable: true,
514
- value: readyStates.indexOf('CLOSED')
515
- });
516
-
517
- [
518
- 'binaryType',
519
- 'bufferedAmount',
520
- 'extensions',
521
- 'protocol',
522
- 'readyState',
523
- 'url'
524
- ].forEach((property) => {
525
- Object.defineProperty(WebSocket.prototype, property, { enumerable: true });
526
- });
527
-
528
- //
529
- // Add the `onopen`, `onerror`, `onclose`, and `onmessage` attributes.
530
- // See https://html.spec.whatwg.org/multipage/comms.html#the-websocket-interface
531
- //
532
- ['open', 'error', 'close', 'message'].forEach((method) => {
533
- Object.defineProperty(WebSocket.prototype, `on${method}`, {
534
- enumerable: true,
535
- get() {
536
- for (const listener of this.listeners(method)) {
537
- if (listener[kForOnEventAttribute]) return listener[kListener];
538
- }
539
-
540
- return null;
541
- },
542
- set(handler) {
543
- for (const listener of this.listeners(method)) {
544
- if (listener[kForOnEventAttribute]) {
545
- this.removeListener(method, listener);
546
- break;
547
- }
548
- }
549
-
550
- if (typeof handler !== 'function') return;
551
-
552
- this.addEventListener(method, handler, {
553
- [kForOnEventAttribute]: true
554
- });
555
- }
556
- });
557
- });
558
-
559
- WebSocket.prototype.addEventListener = addEventListener;
560
- WebSocket.prototype.removeEventListener = removeEventListener;
561
-
562
- module.exports = WebSocket;
563
-
564
- /**
565
- * Initialize a WebSocket client.
566
- *
567
- * @param {WebSocket} websocket The client to initialize
568
- * @param {(String|URL)} address The URL to which to connect
569
- * @param {Array} protocols The subprotocols
570
- * @param {Object} [options] Connection options
571
- * @param {Boolean} [options.followRedirects=false] Whether or not to follow
572
- * redirects
573
- * @param {Number} [options.handshakeTimeout] Timeout in milliseconds for the
574
- * handshake request
575
- * @param {Number} [options.maxPayload=104857600] The maximum allowed message
576
- * size
577
- * @param {Number} [options.maxRedirects=10] The maximum number of redirects
578
- * allowed
579
- * @param {String} [options.origin] Value of the `Origin` or
580
- * `Sec-WebSocket-Origin` header
581
- * @param {(Boolean|Object)} [options.perMessageDeflate=true] Enable/disable
582
- * permessage-deflate
583
- * @param {Number} [options.protocolVersion=13] Value of the
584
- * `Sec-WebSocket-Version` header
585
- * @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or
586
- * not to skip UTF-8 validation for text and close messages
587
- * @private
588
- */
589
- function initAsClient(websocket, address, protocols, options) {
590
- const opts = {
591
- protocolVersion: protocolVersions[1],
592
- maxPayload: 100 * 1024 * 1024,
593
- skipUTF8Validation: false,
594
- perMessageDeflate: true,
595
- followRedirects: false,
596
- maxRedirects: 10,
597
- ...options,
598
- createConnection: undefined,
599
- socketPath: undefined,
600
- hostname: undefined,
601
- protocol: undefined,
602
- timeout: undefined,
603
- method: undefined,
604
- host: undefined,
605
- path: undefined,
606
- port: undefined
607
- };
608
-
609
- if (!protocolVersions.includes(opts.protocolVersion)) {
610
- throw new RangeError(
611
- `Unsupported protocol version: ${opts.protocolVersion} ` +
612
- `(supported versions: ${protocolVersions.join(', ')})`
613
- );
614
- }
615
-
616
- let parsedUrl;
617
-
618
- if (address instanceof URL) {
619
- parsedUrl = address;
620
- websocket._url = address.href;
621
- } else {
622
- try {
623
- parsedUrl = new URL(address);
624
- } catch (e) {
625
- throw new SyntaxError(`Invalid URL: ${address}`);
626
- }
627
-
628
- websocket._url = address;
629
- }
630
-
631
- const isSecure = parsedUrl.protocol === 'wss:';
632
- const isUnixSocket = parsedUrl.protocol === 'ws+unix:';
633
-
634
- if (parsedUrl.protocol !== 'ws:' && !isSecure && !isUnixSocket) {
635
- throw new SyntaxError(
636
- 'The URL\'s protocol must be one of "ws:", "wss:", or "ws+unix:"'
637
- );
638
- }
639
-
640
- if (isUnixSocket && !parsedUrl.pathname) {
641
- throw new SyntaxError("The URL's pathname is empty");
642
- }
643
-
644
- if (parsedUrl.hash) {
645
- throw new SyntaxError('The URL contains a fragment identifier');
646
- }
647
-
648
- const defaultPort = isSecure ? 443 : 80;
649
- const key = randomBytes(16).toString('base64');
650
- const get = isSecure ? https.get : http.get;
651
- const protocolSet = new Set();
652
- let perMessageDeflate;
653
-
654
- opts.createConnection = isSecure ? tlsConnect : netConnect;
655
- opts.defaultPort = opts.defaultPort || defaultPort;
656
- opts.port = parsedUrl.port || defaultPort;
657
- opts.host = parsedUrl.hostname.startsWith('[')
658
- ? parsedUrl.hostname.slice(1, -1)
659
- : parsedUrl.hostname;
660
- opts.headers = {
661
- 'Sec-WebSocket-Version': opts.protocolVersion,
662
- 'Sec-WebSocket-Key': key,
663
- Connection: 'Upgrade',
664
- Upgrade: 'websocket',
665
- ...opts.headers
666
- };
667
- opts.path = parsedUrl.pathname + parsedUrl.search;
668
- opts.timeout = opts.handshakeTimeout;
669
-
670
- if (opts.perMessageDeflate) {
671
- perMessageDeflate = new PerMessageDeflate(
672
- opts.perMessageDeflate !== true ? opts.perMessageDeflate : {},
673
- false,
674
- opts.maxPayload
675
- );
676
- opts.headers['Sec-WebSocket-Extensions'] = format({
677
- [PerMessageDeflate.extensionName]: perMessageDeflate.offer()
678
- });
679
- }
680
- if (protocols.length) {
681
- for (const protocol of protocols) {
682
- if (
683
- typeof protocol !== 'string' ||
684
- !subprotocolRegex.test(protocol) ||
685
- protocolSet.has(protocol)
686
- ) {
687
- throw new SyntaxError(
688
- 'An invalid or duplicated subprotocol was specified'
689
- );
690
- }
691
-
692
- protocolSet.add(protocol);
693
- }
694
-
695
- opts.headers['Sec-WebSocket-Protocol'] = protocols.join(',');
696
- }
697
- if (opts.origin) {
698
- if (opts.protocolVersion < 13) {
699
- opts.headers['Sec-WebSocket-Origin'] = opts.origin;
700
- } else {
701
- opts.headers.Origin = opts.origin;
702
- }
703
- }
704
- if (parsedUrl.username || parsedUrl.password) {
705
- opts.auth = `${parsedUrl.username}:${parsedUrl.password}`;
706
- }
707
-
708
- if (isUnixSocket) {
709
- const parts = opts.path.split(':');
710
-
711
- opts.socketPath = parts[0];
712
- opts.path = parts[1];
713
- }
714
-
715
- let req = (websocket._req = get(opts));
716
-
717
- if (opts.timeout) {
718
- req.on('timeout', () => {
719
- abortHandshake(websocket, req, 'Opening handshake has timed out');
720
- });
721
- }
722
-
723
- req.on('error', (err) => {
724
- if (req === null || req.aborted) return;
725
-
726
- req = websocket._req = null;
727
- websocket._readyState = WebSocket.CLOSING;
728
- websocket.emit('error', err);
729
- websocket.emitClose();
730
- });
731
-
732
- req.on('response', (res) => {
733
- const location = res.headers.location;
734
- const statusCode = res.statusCode;
735
-
736
- if (
737
- location &&
738
- opts.followRedirects &&
739
- statusCode >= 300 &&
740
- statusCode < 400
741
- ) {
742
- if (++websocket._redirects > opts.maxRedirects) {
743
- abortHandshake(websocket, req, 'Maximum redirects exceeded');
744
- return;
745
- }
746
-
747
- req.abort();
748
-
749
- const addr = new URL(location, address);
750
-
751
- initAsClient(websocket, addr, protocols, options);
752
- } else if (!websocket.emit('unexpected-response', req, res)) {
753
- abortHandshake(
754
- websocket,
755
- req,
756
- `Unexpected server response: ${res.statusCode}`
757
- );
758
- }
759
- });
760
-
761
- req.on('upgrade', (res, socket, head) => {
762
- websocket.emit('upgrade', res);
763
-
764
- //
765
- // The user may have closed the connection from a listener of the `upgrade`
766
- // event.
767
- //
768
- if (websocket.readyState !== WebSocket.CONNECTING) return;
769
-
770
- req = websocket._req = null;
771
-
772
- const digest = createHash('sha1')
773
- .update(key + GUID)
774
- .digest('base64');
775
-
776
- if (res.headers['sec-websocket-accept'] !== digest) {
777
- abortHandshake(websocket, socket, 'Invalid Sec-WebSocket-Accept header');
778
- return;
779
- }
780
-
781
- const serverProt = res.headers['sec-websocket-protocol'];
782
- let protError;
783
-
784
- if (serverProt !== undefined) {
785
- if (!protocolSet.size) {
786
- protError = 'Server sent a subprotocol but none was requested';
787
- } else if (!protocolSet.has(serverProt)) {
788
- protError = 'Server sent an invalid subprotocol';
789
- }
790
- } else if (protocolSet.size) {
791
- protError = 'Server sent no subprotocol';
792
- }
793
-
794
- if (protError) {
795
- abortHandshake(websocket, socket, protError);
796
- return;
797
- }
798
-
799
- if (serverProt) websocket._protocol = serverProt;
800
-
801
- const secWebSocketExtensions = res.headers['sec-websocket-extensions'];
802
-
803
- if (secWebSocketExtensions !== undefined) {
804
- if (!perMessageDeflate) {
805
- const message =
806
- 'Server sent a Sec-WebSocket-Extensions header but no extension ' +
807
- 'was requested';
808
- abortHandshake(websocket, socket, message);
809
- return;
810
- }
811
-
812
- let extensions;
813
-
814
- try {
815
- extensions = parse(secWebSocketExtensions);
816
- } catch (err) {
817
- const message = 'Invalid Sec-WebSocket-Extensions header';
818
- abortHandshake(websocket, socket, message);
819
- return;
820
- }
821
-
822
- const extensionNames = Object.keys(extensions);
823
-
824
- if (
825
- extensionNames.length !== 1 ||
826
- extensionNames[0] !== PerMessageDeflate.extensionName
827
- ) {
828
- const message = 'Server indicated an extension that was not requested';
829
- abortHandshake(websocket, socket, message);
830
- return;
831
- }
832
-
833
- try {
834
- perMessageDeflate.accept(extensions[PerMessageDeflate.extensionName]);
835
- } catch (err) {
836
- const message = 'Invalid Sec-WebSocket-Extensions header';
837
- abortHandshake(websocket, socket, message);
838
- return;
839
- }
840
-
841
- websocket._extensions[PerMessageDeflate.extensionName] =
842
- perMessageDeflate;
843
- }
844
-
845
- websocket.setSocket(socket, head, {
846
- maxPayload: opts.maxPayload,
847
- skipUTF8Validation: opts.skipUTF8Validation
848
- });
849
- });
850
- }
851
-
852
- /**
853
- * Create a `net.Socket` and initiate a connection.
854
- *
855
- * @param {Object} options Connection options
856
- * @return {net.Socket} The newly created socket used to start the connection
857
- * @private
858
- */
859
- function netConnect(options) {
860
- options.path = options.socketPath;
861
- return net.connect(options);
862
- }
863
-
864
- /**
865
- * Create a `tls.TLSSocket` and initiate a connection.
866
- *
867
- * @param {Object} options Connection options
868
- * @return {tls.TLSSocket} The newly created socket used to start the connection
869
- * @private
870
- */
871
- function tlsConnect(options) {
872
- options.path = undefined;
873
-
874
- if (!options.servername && options.servername !== '') {
875
- options.servername = net.isIP(options.host) ? '' : options.host;
876
- }
877
-
878
- return tls.connect(options);
879
- }
880
-
881
- /**
882
- * Abort the handshake and emit an error.
883
- *
884
- * @param {WebSocket} websocket The WebSocket instance
885
- * @param {(http.ClientRequest|net.Socket|tls.Socket)} stream The request to
886
- * abort or the socket to destroy
887
- * @param {String} message The error message
888
- * @private
889
- */
890
- function abortHandshake(websocket, stream, message) {
891
- websocket._readyState = WebSocket.CLOSING;
892
-
893
- const err = new Error(message);
894
- Error.captureStackTrace(err, abortHandshake);
895
-
896
- if (stream.setHeader) {
897
- stream.abort();
898
-
899
- if (stream.socket && !stream.socket.destroyed) {
900
- //
901
- // On Node.js >= 14.3.0 `request.abort()` does not destroy the socket if
902
- // called after the request completed. See
903
- // https://github.com/websockets/ws/issues/1869.
904
- //
905
- stream.socket.destroy();
906
- }
907
-
908
- stream.once('abort', websocket.emitClose.bind(websocket));
909
- websocket.emit('error', err);
910
- } else {
911
- stream.destroy(err);
912
- stream.once('error', websocket.emit.bind(websocket, 'error'));
913
- stream.once('close', websocket.emitClose.bind(websocket));
914
- }
915
- }
916
-
917
- /**
918
- * Handle cases where the `ping()`, `pong()`, or `send()` methods are called
919
- * when the `readyState` attribute is `CLOSING` or `CLOSED`.
920
- *
921
- * @param {WebSocket} websocket The WebSocket instance
922
- * @param {*} [data] The data to send
923
- * @param {Function} [cb] Callback
924
- * @private
925
- */
926
- function sendAfterClose(websocket, data, cb) {
927
- if (data) {
928
- const length = toBuffer(data).length;
929
-
930
- //
931
- // The `_bufferedAmount` property is used only when the peer is a client and
932
- // the opening handshake fails. Under these circumstances, in fact, the
933
- // `setSocket()` method is not called, so the `_socket` and `_sender`
934
- // properties are set to `null`.
935
- //
936
- if (websocket._socket) websocket._sender._bufferedBytes += length;
937
- else websocket._bufferedAmount += length;
938
- }
939
-
940
- if (cb) {
941
- const err = new Error(
942
- `WebSocket is not open: readyState ${websocket.readyState} ` +
943
- `(${readyStates[websocket.readyState]})`
944
- );
945
- cb(err);
946
- }
947
- }
948
-
949
- /**
950
- * The listener of the `Receiver` `'conclude'` event.
951
- *
952
- * @param {Number} code The status code
953
- * @param {Buffer} reason The reason for closing
954
- * @private
955
- */
956
- function receiverOnConclude(code, reason) {
957
- const websocket = this[kWebSocket];
958
-
959
- websocket._socket.removeListener('data', socketOnData);
960
- process.nextTick(resume, websocket._socket);
961
-
962
- websocket._closeFrameReceived = true;
963
- websocket._closeMessage = reason;
964
- websocket._closeCode = code;
965
-
966
- if (code === 1005) websocket.close();
967
- else websocket.close(code, reason);
968
- }
969
-
970
- /**
971
- * The listener of the `Receiver` `'drain'` event.
972
- *
973
- * @private
974
- */
975
- function receiverOnDrain() {
976
- this[kWebSocket]._socket.resume();
977
- }
978
-
979
- /**
980
- * The listener of the `Receiver` `'error'` event.
981
- *
982
- * @param {(RangeError|Error)} err The emitted error
983
- * @private
984
- */
985
- function receiverOnError(err) {
986
- const websocket = this[kWebSocket];
987
-
988
- websocket._socket.removeListener('data', socketOnData);
989
-
990
- //
991
- // On Node.js < 14.0.0 the `'error'` event is emitted synchronously. See
992
- // https://github.com/websockets/ws/issues/1940.
993
- //
994
- process.nextTick(resume, websocket._socket);
995
-
996
- websocket.close(err[kStatusCode]);
997
- websocket.emit('error', err);
998
- }
999
-
1000
- /**
1001
- * The listener of the `Receiver` `'finish'` event.
1002
- *
1003
- * @private
1004
- */
1005
- function receiverOnFinish() {
1006
- this[kWebSocket].emitClose();
1007
- }
1008
-
1009
- /**
1010
- * The listener of the `Receiver` `'message'` event.
1011
- *
1012
- * @param {Buffer|ArrayBuffer|Buffer[])} data The message
1013
- * @param {Boolean} isBinary Specifies whether the message is binary or not
1014
- * @private
1015
- */
1016
- function receiverOnMessage(data, isBinary) {
1017
- this[kWebSocket].emit('message', data, isBinary);
1018
- }
1019
-
1020
- /**
1021
- * The listener of the `Receiver` `'ping'` event.
1022
- *
1023
- * @param {Buffer} data The data included in the ping frame
1024
- * @private
1025
- */
1026
- function receiverOnPing(data) {
1027
- const websocket = this[kWebSocket];
1028
-
1029
- websocket.pong(data, !websocket._isServer, NOOP);
1030
- websocket.emit('ping', data);
1031
- }
1032
-
1033
- /**
1034
- * The listener of the `Receiver` `'pong'` event.
1035
- *
1036
- * @param {Buffer} data The data included in the pong frame
1037
- * @private
1038
- */
1039
- function receiverOnPong(data) {
1040
- this[kWebSocket].emit('pong', data);
1041
- }
1042
-
1043
- /**
1044
- * Resume a readable stream
1045
- *
1046
- * @param {Readable} stream The readable stream
1047
- * @private
1048
- */
1049
- function resume(stream) {
1050
- stream.resume();
1051
- }
1052
-
1053
- /**
1054
- * The listener of the `net.Socket` `'close'` event.
1055
- *
1056
- * @private
1057
- */
1058
- function socketOnClose() {
1059
- const websocket = this[kWebSocket];
1060
-
1061
- this.removeListener('close', socketOnClose);
1062
- this.removeListener('data', socketOnData);
1063
- this.removeListener('end', socketOnEnd);
1064
-
1065
- websocket._readyState = WebSocket.CLOSING;
1066
-
1067
- let chunk;
1068
-
1069
- //
1070
- // The close frame might not have been received or the `'end'` event emitted,
1071
- // for example, if the socket was destroyed due to an error. Ensure that the
1072
- // `receiver` stream is closed after writing any remaining buffered data to
1073
- // it. If the readable side of the socket is in flowing mode then there is no
1074
- // buffered data as everything has been already written and `readable.read()`
1075
- // will return `null`. If instead, the socket is paused, any possible buffered
1076
- // data will be read as a single chunk.
1077
- //
1078
- if (
1079
- !this._readableState.endEmitted &&
1080
- !websocket._closeFrameReceived &&
1081
- !websocket._receiver._writableState.errorEmitted &&
1082
- (chunk = websocket._socket.read()) !== null
1083
- ) {
1084
- websocket._receiver.write(chunk);
1085
- }
1086
-
1087
- websocket._receiver.end();
1088
-
1089
- this[kWebSocket] = undefined;
1090
-
1091
- clearTimeout(websocket._closeTimer);
1092
-
1093
- if (
1094
- websocket._receiver._writableState.finished ||
1095
- websocket._receiver._writableState.errorEmitted
1096
- ) {
1097
- websocket.emitClose();
1098
- } else {
1099
- websocket._receiver.on('error', receiverOnFinish);
1100
- websocket._receiver.on('finish', receiverOnFinish);
1101
- }
1102
- }
1103
-
1104
- /**
1105
- * The listener of the `net.Socket` `'data'` event.
1106
- *
1107
- * @param {Buffer} chunk A chunk of data
1108
- * @private
1109
- */
1110
- function socketOnData(chunk) {
1111
- if (!this[kWebSocket]._receiver.write(chunk)) {
1112
- this.pause();
1113
- }
1114
- }
1115
-
1116
- /**
1117
- * The listener of the `net.Socket` `'end'` event.
1118
- *
1119
- * @private
1120
- */
1121
- function socketOnEnd() {
1122
- const websocket = this[kWebSocket];
1123
-
1124
- websocket._readyState = WebSocket.CLOSING;
1125
- websocket._receiver.end();
1126
- this.end();
1127
- }
1128
-
1129
- /**
1130
- * The listener of the `net.Socket` `'error'` event.
1131
- *
1132
- * @private
1133
- */
1134
- function socketOnError() {
1135
- const websocket = this[kWebSocket];
1136
-
1137
- this.removeListener('error', socketOnError);
1138
- this.on('error', NOOP);
1139
-
1140
- if (websocket) {
1141
- websocket._readyState = WebSocket.CLOSING;
1142
- this.destroy();
1143
- }
1144
- }
1
+ /* eslint no-unused-vars: ["error", { "varsIgnorePattern": "^Readable$" }] */
2
+
3
+ 'use strict';
4
+
5
+ const EventEmitter = require('events');
6
+ const https = require('https');
7
+ const http = require('http');
8
+ const net = require('net');
9
+ const tls = require('tls');
10
+ const { randomBytes, createHash } = require('crypto');
11
+ const { Readable } = require('stream');
12
+ const { URL } = require('url');
13
+
14
+ const PerMessageDeflate = require('./permessage-deflate');
15
+ const Receiver = require('./receiver');
16
+ const Sender = require('./sender');
17
+ const {
18
+ BINARY_TYPES,
19
+ EMPTY_BUFFER,
20
+ GUID,
21
+ kForOnEventAttribute,
22
+ kListener,
23
+ kStatusCode,
24
+ kWebSocket,
25
+ NOOP
26
+ } = require('./constants');
27
+ const {
28
+ EventTarget: { addEventListener, removeEventListener }
29
+ } = require('./event-target');
30
+ const { format, parse } = require('./extension');
31
+ const { toBuffer } = require('./buffer-util');
32
+
33
+ const readyStates = ['CONNECTING', 'OPEN', 'CLOSING', 'CLOSED'];
34
+ const subprotocolRegex = /^[!#$%&'*+\-.0-9A-Z^_`|a-z~]+$/;
35
+ const protocolVersions = [8, 13];
36
+ const closeTimeout = 30 * 1000;
37
+
38
+ /**
39
+ * Class representing a WebSocket.
40
+ *
41
+ * @extends EventEmitter
42
+ */
43
+ class WebSocket extends EventEmitter {
44
+ /**
45
+ * Create a new `WebSocket`.
46
+ *
47
+ * @param {(String|URL)} address The URL to which to connect
48
+ * @param {(String|String[])} [protocols] The subprotocols
49
+ * @param {Object} [options] Connection options
50
+ */
51
+ constructor(address, protocols, options) {
52
+ super();
53
+
54
+ this._binaryType = BINARY_TYPES[0];
55
+ this._closeCode = 1006;
56
+ this._closeFrameReceived = false;
57
+ this._closeFrameSent = false;
58
+ this._closeMessage = EMPTY_BUFFER;
59
+ this._closeTimer = null;
60
+ this._extensions = {};
61
+ this._paused = false;
62
+ this._protocol = '';
63
+ this._readyState = WebSocket.CONNECTING;
64
+ this._receiver = null;
65
+ this._sender = null;
66
+ this._socket = null;
67
+
68
+ if (address !== null) {
69
+ this._bufferedAmount = 0;
70
+ this._isServer = false;
71
+ this._redirects = 0;
72
+
73
+ if (protocols === undefined) {
74
+ protocols = [];
75
+ } else if (!Array.isArray(protocols)) {
76
+ if (typeof protocols === 'object' && protocols !== null) {
77
+ options = protocols;
78
+ protocols = [];
79
+ } else {
80
+ protocols = [protocols];
81
+ }
82
+ }
83
+
84
+ initAsClient(this, address, protocols, options);
85
+ } else {
86
+ this._isServer = true;
87
+ }
88
+ }
89
+
90
+ /**
91
+ * This deviates from the WHATWG interface since ws doesn't support the
92
+ * required default "blob" type (instead we define a custom "nodebuffer"
93
+ * type).
94
+ *
95
+ * @type {String}
96
+ */
97
+ get binaryType() {
98
+ return this._binaryType;
99
+ }
100
+
101
+ set binaryType(type) {
102
+ if (!BINARY_TYPES.includes(type)) return;
103
+
104
+ this._binaryType = type;
105
+
106
+ //
107
+ // Allow to change `binaryType` on the fly.
108
+ //
109
+ if (this._receiver) this._receiver._binaryType = type;
110
+ }
111
+
112
+ /**
113
+ * @type {Number}
114
+ */
115
+ get bufferedAmount() {
116
+ if (!this._socket) return this._bufferedAmount;
117
+
118
+ return this._socket._writableState.length + this._sender._bufferedBytes;
119
+ }
120
+
121
+ /**
122
+ * @type {String}
123
+ */
124
+ get extensions() {
125
+ return Object.keys(this._extensions).join();
126
+ }
127
+
128
+ /**
129
+ * @type {Boolean}
130
+ */
131
+ get isPaused() {
132
+ return this._paused;
133
+ }
134
+
135
+ /**
136
+ * @type {Function}
137
+ */
138
+ /* istanbul ignore next */
139
+ get onclose() {
140
+ return null;
141
+ }
142
+
143
+ /**
144
+ * @type {Function}
145
+ */
146
+ /* istanbul ignore next */
147
+ get onerror() {
148
+ return null;
149
+ }
150
+
151
+ /**
152
+ * @type {Function}
153
+ */
154
+ /* istanbul ignore next */
155
+ get onopen() {
156
+ return null;
157
+ }
158
+
159
+ /**
160
+ * @type {Function}
161
+ */
162
+ /* istanbul ignore next */
163
+ get onmessage() {
164
+ return null;
165
+ }
166
+
167
+ /**
168
+ * @type {String}
169
+ */
170
+ get protocol() {
171
+ return this._protocol;
172
+ }
173
+
174
+ /**
175
+ * @type {Number}
176
+ */
177
+ get readyState() {
178
+ return this._readyState;
179
+ }
180
+
181
+ /**
182
+ * @type {String}
183
+ */
184
+ get url() {
185
+ return this._url;
186
+ }
187
+
188
+ /**
189
+ * Set up the socket and the internal resources.
190
+ *
191
+ * @param {(net.Socket|tls.Socket)} socket The network socket between the
192
+ * server and client
193
+ * @param {Buffer} head The first packet of the upgraded stream
194
+ * @param {Object} options Options object
195
+ * @param {Function} [options.generateMask] The function used to generate the
196
+ * masking key
197
+ * @param {Number} [options.maxPayload=0] The maximum allowed message size
198
+ * @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or
199
+ * not to skip UTF-8 validation for text and close messages
200
+ * @private
201
+ */
202
+ setSocket(socket, head, options) {
203
+ const receiver = new Receiver({
204
+ binaryType: this.binaryType,
205
+ extensions: this._extensions,
206
+ isServer: this._isServer,
207
+ maxPayload: options.maxPayload,
208
+ skipUTF8Validation: options.skipUTF8Validation
209
+ });
210
+
211
+ this._sender = new Sender(socket, this._extensions, options.generateMask);
212
+ this._receiver = receiver;
213
+ this._socket = socket;
214
+
215
+ receiver[kWebSocket] = this;
216
+ socket[kWebSocket] = this;
217
+
218
+ receiver.on('conclude', receiverOnConclude);
219
+ receiver.on('drain', receiverOnDrain);
220
+ receiver.on('error', receiverOnError);
221
+ receiver.on('message', receiverOnMessage);
222
+ receiver.on('ping', receiverOnPing);
223
+ receiver.on('pong', receiverOnPong);
224
+
225
+ socket.setTimeout(0);
226
+ socket.setNoDelay();
227
+
228
+ if (head.length > 0) socket.unshift(head);
229
+
230
+ socket.on('close', socketOnClose);
231
+ socket.on('data', socketOnData);
232
+ socket.on('end', socketOnEnd);
233
+ socket.on('error', socketOnError);
234
+
235
+ this._readyState = WebSocket.OPEN;
236
+ this.emit('open');
237
+ }
238
+
239
+ /**
240
+ * Emit the `'close'` event.
241
+ *
242
+ * @private
243
+ */
244
+ emitClose() {
245
+ if (!this._socket) {
246
+ this._readyState = WebSocket.CLOSED;
247
+ this.emit('close', this._closeCode, this._closeMessage);
248
+ return;
249
+ }
250
+
251
+ if (this._extensions[PerMessageDeflate.extensionName]) {
252
+ this._extensions[PerMessageDeflate.extensionName].cleanup();
253
+ }
254
+
255
+ this._receiver.removeAllListeners();
256
+ this._readyState = WebSocket.CLOSED;
257
+ this.emit('close', this._closeCode, this._closeMessage);
258
+ }
259
+
260
+ /**
261
+ * Start a closing handshake.
262
+ *
263
+ * +----------+ +-----------+ +----------+
264
+ * - - -|ws.close()|-->|close frame|-->|ws.close()|- - -
265
+ * | +----------+ +-----------+ +----------+ |
266
+ * +----------+ +-----------+ |
267
+ * CLOSING |ws.close()|<--|close frame|<--+-----+ CLOSING
268
+ * +----------+ +-----------+ |
269
+ * | | | +---+ |
270
+ * +------------------------+-->|fin| - - - -
271
+ * | +---+ | +---+
272
+ * - - - - -|fin|<---------------------+
273
+ * +---+
274
+ *
275
+ * @param {Number} [code] Status code explaining why the connection is closing
276
+ * @param {(String|Buffer)} [data] The reason why the connection is
277
+ * closing
278
+ * @public
279
+ */
280
+ close(code, data) {
281
+ if (this.readyState === WebSocket.CLOSED) return;
282
+ if (this.readyState === WebSocket.CONNECTING) {
283
+ const msg = 'WebSocket was closed before the connection was established';
284
+ return abortHandshake(this, this._req, msg);
285
+ }
286
+
287
+ if (this.readyState === WebSocket.CLOSING) {
288
+ if (
289
+ this._closeFrameSent &&
290
+ (this._closeFrameReceived || this._receiver._writableState.errorEmitted)
291
+ ) {
292
+ this._socket.end();
293
+ }
294
+
295
+ return;
296
+ }
297
+
298
+ this._readyState = WebSocket.CLOSING;
299
+ this._sender.close(code, data, !this._isServer, (err) => {
300
+ //
301
+ // This error is handled by the `'error'` listener on the socket. We only
302
+ // want to know if the close frame has been sent here.
303
+ //
304
+ if (err) return;
305
+
306
+ this._closeFrameSent = true;
307
+
308
+ if (
309
+ this._closeFrameReceived ||
310
+ this._receiver._writableState.errorEmitted
311
+ ) {
312
+ this._socket.end();
313
+ }
314
+ });
315
+
316
+ //
317
+ // Specify a timeout for the closing handshake to complete.
318
+ //
319
+ this._closeTimer = setTimeout(
320
+ this._socket.destroy.bind(this._socket),
321
+ closeTimeout
322
+ );
323
+ }
324
+
325
+ /**
326
+ * Pause the socket.
327
+ *
328
+ * @public
329
+ */
330
+ pause() {
331
+ if (
332
+ this.readyState === WebSocket.CONNECTING ||
333
+ this.readyState === WebSocket.CLOSED
334
+ ) {
335
+ return;
336
+ }
337
+
338
+ this._paused = true;
339
+ this._socket.pause();
340
+ }
341
+
342
+ /**
343
+ * Send a ping.
344
+ *
345
+ * @param {*} [data] The data to send
346
+ * @param {Boolean} [mask] Indicates whether or not to mask `data`
347
+ * @param {Function} [cb] Callback which is executed when the ping is sent
348
+ * @public
349
+ */
350
+ ping(data, mask, cb) {
351
+ if (this.readyState === WebSocket.CONNECTING) {
352
+ throw new Error('WebSocket is not open: readyState 0 (CONNECTING)');
353
+ }
354
+
355
+ if (typeof data === 'function') {
356
+ cb = data;
357
+ data = mask = undefined;
358
+ } else if (typeof mask === 'function') {
359
+ cb = mask;
360
+ mask = undefined;
361
+ }
362
+
363
+ if (typeof data === 'number') data = data.toString();
364
+
365
+ if (this.readyState !== WebSocket.OPEN) {
366
+ sendAfterClose(this, data, cb);
367
+ return;
368
+ }
369
+
370
+ if (mask === undefined) mask = !this._isServer;
371
+ this._sender.ping(data || EMPTY_BUFFER, mask, cb);
372
+ }
373
+
374
+ /**
375
+ * Send a pong.
376
+ *
377
+ * @param {*} [data] The data to send
378
+ * @param {Boolean} [mask] Indicates whether or not to mask `data`
379
+ * @param {Function} [cb] Callback which is executed when the pong is sent
380
+ * @public
381
+ */
382
+ pong(data, mask, cb) {
383
+ if (this.readyState === WebSocket.CONNECTING) {
384
+ throw new Error('WebSocket is not open: readyState 0 (CONNECTING)');
385
+ }
386
+
387
+ if (typeof data === 'function') {
388
+ cb = data;
389
+ data = mask = undefined;
390
+ } else if (typeof mask === 'function') {
391
+ cb = mask;
392
+ mask = undefined;
393
+ }
394
+
395
+ if (typeof data === 'number') data = data.toString();
396
+
397
+ if (this.readyState !== WebSocket.OPEN) {
398
+ sendAfterClose(this, data, cb);
399
+ return;
400
+ }
401
+
402
+ if (mask === undefined) mask = !this._isServer;
403
+ this._sender.pong(data || EMPTY_BUFFER, mask, cb);
404
+ }
405
+
406
+ /**
407
+ * Resume the socket.
408
+ *
409
+ * @public
410
+ */
411
+ resume() {
412
+ if (
413
+ this.readyState === WebSocket.CONNECTING ||
414
+ this.readyState === WebSocket.CLOSED
415
+ ) {
416
+ return;
417
+ }
418
+
419
+ this._paused = false;
420
+ if (!this._receiver._writableState.needDrain) this._socket.resume();
421
+ }
422
+
423
+ /**
424
+ * Send a data message.
425
+ *
426
+ * @param {*} data The message to send
427
+ * @param {Object} [options] Options object
428
+ * @param {Boolean} [options.binary] Specifies whether `data` is binary or
429
+ * text
430
+ * @param {Boolean} [options.compress] Specifies whether or not to compress
431
+ * `data`
432
+ * @param {Boolean} [options.fin=true] Specifies whether the fragment is the
433
+ * last one
434
+ * @param {Boolean} [options.mask] Specifies whether or not to mask `data`
435
+ * @param {Function} [cb] Callback which is executed when data is written out
436
+ * @public
437
+ */
438
+ send(data, options, cb) {
439
+ if (this.readyState === WebSocket.CONNECTING) {
440
+ throw new Error('WebSocket is not open: readyState 0 (CONNECTING)');
441
+ }
442
+
443
+ if (typeof options === 'function') {
444
+ cb = options;
445
+ options = {};
446
+ }
447
+
448
+ if (typeof data === 'number') data = data.toString();
449
+
450
+ if (this.readyState !== WebSocket.OPEN) {
451
+ sendAfterClose(this, data, cb);
452
+ return;
453
+ }
454
+
455
+ const opts = {
456
+ binary: typeof data !== 'string',
457
+ mask: !this._isServer,
458
+ compress: true,
459
+ fin: true,
460
+ ...options
461
+ };
462
+
463
+ if (!this._extensions[PerMessageDeflate.extensionName]) {
464
+ opts.compress = false;
465
+ }
466
+
467
+ this._sender.send(data || EMPTY_BUFFER, opts, cb);
468
+ }
469
+
470
+ /**
471
+ * Forcibly close the connection.
472
+ *
473
+ * @public
474
+ */
475
+ terminate() {
476
+ if (this.readyState === WebSocket.CLOSED) return;
477
+ if (this.readyState === WebSocket.CONNECTING) {
478
+ const msg = 'WebSocket was closed before the connection was established';
479
+ return abortHandshake(this, this._req, msg);
480
+ }
481
+
482
+ if (this._socket) {
483
+ this._readyState = WebSocket.CLOSING;
484
+ this._socket.destroy();
485
+ }
486
+ }
487
+ }
488
+
489
+ /**
490
+ * @constant {Number} CONNECTING
491
+ * @memberof WebSocket
492
+ */
493
+ Object.defineProperty(WebSocket, 'CONNECTING', {
494
+ enumerable: true,
495
+ value: readyStates.indexOf('CONNECTING')
496
+ });
497
+
498
+ /**
499
+ * @constant {Number} CONNECTING
500
+ * @memberof WebSocket.prototype
501
+ */
502
+ Object.defineProperty(WebSocket.prototype, 'CONNECTING', {
503
+ enumerable: true,
504
+ value: readyStates.indexOf('CONNECTING')
505
+ });
506
+
507
+ /**
508
+ * @constant {Number} OPEN
509
+ * @memberof WebSocket
510
+ */
511
+ Object.defineProperty(WebSocket, 'OPEN', {
512
+ enumerable: true,
513
+ value: readyStates.indexOf('OPEN')
514
+ });
515
+
516
+ /**
517
+ * @constant {Number} OPEN
518
+ * @memberof WebSocket.prototype
519
+ */
520
+ Object.defineProperty(WebSocket.prototype, 'OPEN', {
521
+ enumerable: true,
522
+ value: readyStates.indexOf('OPEN')
523
+ });
524
+
525
+ /**
526
+ * @constant {Number} CLOSING
527
+ * @memberof WebSocket
528
+ */
529
+ Object.defineProperty(WebSocket, 'CLOSING', {
530
+ enumerable: true,
531
+ value: readyStates.indexOf('CLOSING')
532
+ });
533
+
534
+ /**
535
+ * @constant {Number} CLOSING
536
+ * @memberof WebSocket.prototype
537
+ */
538
+ Object.defineProperty(WebSocket.prototype, 'CLOSING', {
539
+ enumerable: true,
540
+ value: readyStates.indexOf('CLOSING')
541
+ });
542
+
543
+ /**
544
+ * @constant {Number} CLOSED
545
+ * @memberof WebSocket
546
+ */
547
+ Object.defineProperty(WebSocket, 'CLOSED', {
548
+ enumerable: true,
549
+ value: readyStates.indexOf('CLOSED')
550
+ });
551
+
552
+ /**
553
+ * @constant {Number} CLOSED
554
+ * @memberof WebSocket.prototype
555
+ */
556
+ Object.defineProperty(WebSocket.prototype, 'CLOSED', {
557
+ enumerable: true,
558
+ value: readyStates.indexOf('CLOSED')
559
+ });
560
+
561
+ [
562
+ 'binaryType',
563
+ 'bufferedAmount',
564
+ 'extensions',
565
+ 'isPaused',
566
+ 'protocol',
567
+ 'readyState',
568
+ 'url'
569
+ ].forEach((property) => {
570
+ Object.defineProperty(WebSocket.prototype, property, { enumerable: true });
571
+ });
572
+
573
+ //
574
+ // Add the `onopen`, `onerror`, `onclose`, and `onmessage` attributes.
575
+ // See https://html.spec.whatwg.org/multipage/comms.html#the-websocket-interface
576
+ //
577
+ ['open', 'error', 'close', 'message'].forEach((method) => {
578
+ Object.defineProperty(WebSocket.prototype, `on${method}`, {
579
+ enumerable: true,
580
+ get() {
581
+ for (const listener of this.listeners(method)) {
582
+ if (listener[kForOnEventAttribute]) return listener[kListener];
583
+ }
584
+
585
+ return null;
586
+ },
587
+ set(handler) {
588
+ for (const listener of this.listeners(method)) {
589
+ if (listener[kForOnEventAttribute]) {
590
+ this.removeListener(method, listener);
591
+ break;
592
+ }
593
+ }
594
+
595
+ if (typeof handler !== 'function') return;
596
+
597
+ this.addEventListener(method, handler, {
598
+ [kForOnEventAttribute]: true
599
+ });
600
+ }
601
+ });
602
+ });
603
+
604
+ WebSocket.prototype.addEventListener = addEventListener;
605
+ WebSocket.prototype.removeEventListener = removeEventListener;
606
+
607
+ module.exports = WebSocket;
608
+
609
+ /**
610
+ * Initialize a WebSocket client.
611
+ *
612
+ * @param {WebSocket} websocket The client to initialize
613
+ * @param {(String|URL)} address The URL to which to connect
614
+ * @param {Array} protocols The subprotocols
615
+ * @param {Object} [options] Connection options
616
+ * @param {Boolean} [options.followRedirects=false] Whether or not to follow
617
+ * redirects
618
+ * @param {Function} [options.generateMask] The function used to generate the
619
+ * masking key
620
+ * @param {Number} [options.handshakeTimeout] Timeout in milliseconds for the
621
+ * handshake request
622
+ * @param {Number} [options.maxPayload=104857600] The maximum allowed message
623
+ * size
624
+ * @param {Number} [options.maxRedirects=10] The maximum number of redirects
625
+ * allowed
626
+ * @param {String} [options.origin] Value of the `Origin` or
627
+ * `Sec-WebSocket-Origin` header
628
+ * @param {(Boolean|Object)} [options.perMessageDeflate=true] Enable/disable
629
+ * permessage-deflate
630
+ * @param {Number} [options.protocolVersion=13] Value of the
631
+ * `Sec-WebSocket-Version` header
632
+ * @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or
633
+ * not to skip UTF-8 validation for text and close messages
634
+ * @private
635
+ */
636
+ function initAsClient(websocket, address, protocols, options) {
637
+ const opts = {
638
+ protocolVersion: protocolVersions[1],
639
+ maxPayload: 100 * 1024 * 1024,
640
+ skipUTF8Validation: false,
641
+ perMessageDeflate: true,
642
+ followRedirects: false,
643
+ maxRedirects: 10,
644
+ ...options,
645
+ createConnection: undefined,
646
+ socketPath: undefined,
647
+ hostname: undefined,
648
+ protocol: undefined,
649
+ timeout: undefined,
650
+ method: undefined,
651
+ host: undefined,
652
+ path: undefined,
653
+ port: undefined
654
+ };
655
+
656
+ if (!protocolVersions.includes(opts.protocolVersion)) {
657
+ throw new RangeError(
658
+ `Unsupported protocol version: ${opts.protocolVersion} ` +
659
+ `(supported versions: ${protocolVersions.join(', ')})`
660
+ );
661
+ }
662
+
663
+ let parsedUrl;
664
+
665
+ if (address instanceof URL) {
666
+ parsedUrl = address;
667
+ websocket._url = address.href;
668
+ } else {
669
+ try {
670
+ parsedUrl = new URL(address);
671
+ } catch (e) {
672
+ throw new SyntaxError(`Invalid URL: ${address}`);
673
+ }
674
+
675
+ websocket._url = address;
676
+ }
677
+
678
+ const isSecure = parsedUrl.protocol === 'wss:';
679
+ const isUnixSocket = parsedUrl.protocol === 'ws+unix:';
680
+ let invalidURLMessage;
681
+
682
+ if (parsedUrl.protocol !== 'ws:' && !isSecure && !isUnixSocket) {
683
+ invalidURLMessage =
684
+ 'The URL\'s protocol must be one of "ws:", "wss:", or "ws+unix:"';
685
+ } else if (isUnixSocket && !parsedUrl.pathname) {
686
+ invalidURLMessage = "The URL's pathname is empty";
687
+ } else if (parsedUrl.hash) {
688
+ invalidURLMessage = 'The URL contains a fragment identifier';
689
+ }
690
+
691
+ if (invalidURLMessage) {
692
+ const err = new SyntaxError(invalidURLMessage);
693
+
694
+ if (websocket._redirects === 0) {
695
+ throw err;
696
+ } else {
697
+ emitErrorAndClose(websocket, err);
698
+ return;
699
+ }
700
+ }
701
+
702
+ const defaultPort = isSecure ? 443 : 80;
703
+ const key = randomBytes(16).toString('base64');
704
+ const get = isSecure ? https.get : http.get;
705
+ const protocolSet = new Set();
706
+ let perMessageDeflate;
707
+
708
+ opts.createConnection = isSecure ? tlsConnect : netConnect;
709
+ opts.defaultPort = opts.defaultPort || defaultPort;
710
+ opts.port = parsedUrl.port || defaultPort;
711
+ opts.host = parsedUrl.hostname.startsWith('[')
712
+ ? parsedUrl.hostname.slice(1, -1)
713
+ : parsedUrl.hostname;
714
+ opts.headers = {
715
+ 'Sec-WebSocket-Version': opts.protocolVersion,
716
+ 'Sec-WebSocket-Key': key,
717
+ Connection: 'Upgrade',
718
+ Upgrade: 'websocket',
719
+ ...opts.headers
720
+ };
721
+ opts.path = parsedUrl.pathname + parsedUrl.search;
722
+ opts.timeout = opts.handshakeTimeout;
723
+
724
+ if (opts.perMessageDeflate) {
725
+ perMessageDeflate = new PerMessageDeflate(
726
+ opts.perMessageDeflate !== true ? opts.perMessageDeflate : {},
727
+ false,
728
+ opts.maxPayload
729
+ );
730
+ opts.headers['Sec-WebSocket-Extensions'] = format({
731
+ [PerMessageDeflate.extensionName]: perMessageDeflate.offer()
732
+ });
733
+ }
734
+ if (protocols.length) {
735
+ for (const protocol of protocols) {
736
+ if (
737
+ typeof protocol !== 'string' ||
738
+ !subprotocolRegex.test(protocol) ||
739
+ protocolSet.has(protocol)
740
+ ) {
741
+ throw new SyntaxError(
742
+ 'An invalid or duplicated subprotocol was specified'
743
+ );
744
+ }
745
+
746
+ protocolSet.add(protocol);
747
+ }
748
+
749
+ opts.headers['Sec-WebSocket-Protocol'] = protocols.join(',');
750
+ }
751
+ if (opts.origin) {
752
+ if (opts.protocolVersion < 13) {
753
+ opts.headers['Sec-WebSocket-Origin'] = opts.origin;
754
+ } else {
755
+ opts.headers.Origin = opts.origin;
756
+ }
757
+ }
758
+ if (parsedUrl.username || parsedUrl.password) {
759
+ opts.auth = `${parsedUrl.username}:${parsedUrl.password}`;
760
+ }
761
+
762
+ if (isUnixSocket) {
763
+ const parts = opts.path.split(':');
764
+
765
+ opts.socketPath = parts[0];
766
+ opts.path = parts[1];
767
+ }
768
+
769
+ let req = (websocket._req = get(opts));
770
+
771
+ if (opts.timeout) {
772
+ req.on('timeout', () => {
773
+ abortHandshake(websocket, req, 'Opening handshake has timed out');
774
+ });
775
+ }
776
+
777
+ req.on('error', (err) => {
778
+ if (req === null || req.aborted) return;
779
+
780
+ req = websocket._req = null;
781
+ emitErrorAndClose(websocket, err);
782
+ });
783
+
784
+ req.on('response', (res) => {
785
+ const location = res.headers.location;
786
+ const statusCode = res.statusCode;
787
+
788
+ if (
789
+ location &&
790
+ opts.followRedirects &&
791
+ statusCode >= 300 &&
792
+ statusCode < 400
793
+ ) {
794
+ if (++websocket._redirects > opts.maxRedirects) {
795
+ abortHandshake(websocket, req, 'Maximum redirects exceeded');
796
+ return;
797
+ }
798
+
799
+ req.abort();
800
+
801
+ let addr;
802
+
803
+ try {
804
+ addr = new URL(location, address);
805
+ } catch (e) {
806
+ const err = new SyntaxError(`Invalid URL: ${location}`);
807
+ emitErrorAndClose(websocket, err);
808
+ return;
809
+ }
810
+
811
+ initAsClient(websocket, addr, protocols, options);
812
+ } else if (!websocket.emit('unexpected-response', req, res)) {
813
+ abortHandshake(
814
+ websocket,
815
+ req,
816
+ `Unexpected server response: ${res.statusCode}`
817
+ );
818
+ }
819
+ });
820
+
821
+ req.on('upgrade', (res, socket, head) => {
822
+ websocket.emit('upgrade', res);
823
+
824
+ //
825
+ // The user may have closed the connection from a listener of the `upgrade`
826
+ // event.
827
+ //
828
+ if (websocket.readyState !== WebSocket.CONNECTING) return;
829
+
830
+ req = websocket._req = null;
831
+
832
+ const digest = createHash('sha1')
833
+ .update(key + GUID)
834
+ .digest('base64');
835
+
836
+ if (res.headers['sec-websocket-accept'] !== digest) {
837
+ abortHandshake(websocket, socket, 'Invalid Sec-WebSocket-Accept header');
838
+ return;
839
+ }
840
+
841
+ const serverProt = res.headers['sec-websocket-protocol'];
842
+ let protError;
843
+
844
+ if (serverProt !== undefined) {
845
+ if (!protocolSet.size) {
846
+ protError = 'Server sent a subprotocol but none was requested';
847
+ } else if (!protocolSet.has(serverProt)) {
848
+ protError = 'Server sent an invalid subprotocol';
849
+ }
850
+ } else if (protocolSet.size) {
851
+ protError = 'Server sent no subprotocol';
852
+ }
853
+
854
+ if (protError) {
855
+ abortHandshake(websocket, socket, protError);
856
+ return;
857
+ }
858
+
859
+ if (serverProt) websocket._protocol = serverProt;
860
+
861
+ const secWebSocketExtensions = res.headers['sec-websocket-extensions'];
862
+
863
+ if (secWebSocketExtensions !== undefined) {
864
+ if (!perMessageDeflate) {
865
+ const message =
866
+ 'Server sent a Sec-WebSocket-Extensions header but no extension ' +
867
+ 'was requested';
868
+ abortHandshake(websocket, socket, message);
869
+ return;
870
+ }
871
+
872
+ let extensions;
873
+
874
+ try {
875
+ extensions = parse(secWebSocketExtensions);
876
+ } catch (err) {
877
+ const message = 'Invalid Sec-WebSocket-Extensions header';
878
+ abortHandshake(websocket, socket, message);
879
+ return;
880
+ }
881
+
882
+ const extensionNames = Object.keys(extensions);
883
+
884
+ if (
885
+ extensionNames.length !== 1 ||
886
+ extensionNames[0] !== PerMessageDeflate.extensionName
887
+ ) {
888
+ const message = 'Server indicated an extension that was not requested';
889
+ abortHandshake(websocket, socket, message);
890
+ return;
891
+ }
892
+
893
+ try {
894
+ perMessageDeflate.accept(extensions[PerMessageDeflate.extensionName]);
895
+ } catch (err) {
896
+ const message = 'Invalid Sec-WebSocket-Extensions header';
897
+ abortHandshake(websocket, socket, message);
898
+ return;
899
+ }
900
+
901
+ websocket._extensions[PerMessageDeflate.extensionName] =
902
+ perMessageDeflate;
903
+ }
904
+
905
+ websocket.setSocket(socket, head, {
906
+ generateMask: opts.generateMask,
907
+ maxPayload: opts.maxPayload,
908
+ skipUTF8Validation: opts.skipUTF8Validation
909
+ });
910
+ });
911
+ }
912
+
913
+ /**
914
+ * Emit the `'error'` and `'close'` event.
915
+ *
916
+ * @param {WebSocket} websocket The WebSocket instance
917
+ * @param {Error} The error to emit
918
+ * @private
919
+ */
920
+ function emitErrorAndClose(websocket, err) {
921
+ websocket._readyState = WebSocket.CLOSING;
922
+ websocket.emit('error', err);
923
+ websocket.emitClose();
924
+ }
925
+
926
+ /**
927
+ * Create a `net.Socket` and initiate a connection.
928
+ *
929
+ * @param {Object} options Connection options
930
+ * @return {net.Socket} The newly created socket used to start the connection
931
+ * @private
932
+ */
933
+ function netConnect(options) {
934
+ options.path = options.socketPath;
935
+ return net.connect(options);
936
+ }
937
+
938
+ /**
939
+ * Create a `tls.TLSSocket` and initiate a connection.
940
+ *
941
+ * @param {Object} options Connection options
942
+ * @return {tls.TLSSocket} The newly created socket used to start the connection
943
+ * @private
944
+ */
945
+ function tlsConnect(options) {
946
+ options.path = undefined;
947
+
948
+ if (!options.servername && options.servername !== '') {
949
+ options.servername = net.isIP(options.host) ? '' : options.host;
950
+ }
951
+
952
+ return tls.connect(options);
953
+ }
954
+
955
+ /**
956
+ * Abort the handshake and emit an error.
957
+ *
958
+ * @param {WebSocket} websocket The WebSocket instance
959
+ * @param {(http.ClientRequest|net.Socket|tls.Socket)} stream The request to
960
+ * abort or the socket to destroy
961
+ * @param {String} message The error message
962
+ * @private
963
+ */
964
+ function abortHandshake(websocket, stream, message) {
965
+ websocket._readyState = WebSocket.CLOSING;
966
+
967
+ const err = new Error(message);
968
+ Error.captureStackTrace(err, abortHandshake);
969
+
970
+ if (stream.setHeader) {
971
+ stream.abort();
972
+
973
+ if (stream.socket && !stream.socket.destroyed) {
974
+ //
975
+ // On Node.js >= 14.3.0 `request.abort()` does not destroy the socket if
976
+ // called after the request completed. See
977
+ // https://github.com/websockets/ws/issues/1869.
978
+ //
979
+ stream.socket.destroy();
980
+ }
981
+
982
+ stream.once('abort', websocket.emitClose.bind(websocket));
983
+ websocket.emit('error', err);
984
+ } else {
985
+ stream.destroy(err);
986
+ stream.once('error', websocket.emit.bind(websocket, 'error'));
987
+ stream.once('close', websocket.emitClose.bind(websocket));
988
+ }
989
+ }
990
+
991
+ /**
992
+ * Handle cases where the `ping()`, `pong()`, or `send()` methods are called
993
+ * when the `readyState` attribute is `CLOSING` or `CLOSED`.
994
+ *
995
+ * @param {WebSocket} websocket The WebSocket instance
996
+ * @param {*} [data] The data to send
997
+ * @param {Function} [cb] Callback
998
+ * @private
999
+ */
1000
+ function sendAfterClose(websocket, data, cb) {
1001
+ if (data) {
1002
+ const length = toBuffer(data).length;
1003
+
1004
+ //
1005
+ // The `_bufferedAmount` property is used only when the peer is a client and
1006
+ // the opening handshake fails. Under these circumstances, in fact, the
1007
+ // `setSocket()` method is not called, so the `_socket` and `_sender`
1008
+ // properties are set to `null`.
1009
+ //
1010
+ if (websocket._socket) websocket._sender._bufferedBytes += length;
1011
+ else websocket._bufferedAmount += length;
1012
+ }
1013
+
1014
+ if (cb) {
1015
+ const err = new Error(
1016
+ `WebSocket is not open: readyState ${websocket.readyState} ` +
1017
+ `(${readyStates[websocket.readyState]})`
1018
+ );
1019
+ cb(err);
1020
+ }
1021
+ }
1022
+
1023
+ /**
1024
+ * The listener of the `Receiver` `'conclude'` event.
1025
+ *
1026
+ * @param {Number} code The status code
1027
+ * @param {Buffer} reason The reason for closing
1028
+ * @private
1029
+ */
1030
+ function receiverOnConclude(code, reason) {
1031
+ const websocket = this[kWebSocket];
1032
+
1033
+ websocket._closeFrameReceived = true;
1034
+ websocket._closeMessage = reason;
1035
+ websocket._closeCode = code;
1036
+
1037
+ if (websocket._socket[kWebSocket] === undefined) return;
1038
+
1039
+ websocket._socket.removeListener('data', socketOnData);
1040
+ process.nextTick(resume, websocket._socket);
1041
+
1042
+ if (code === 1005) websocket.close();
1043
+ else websocket.close(code, reason);
1044
+ }
1045
+
1046
+ /**
1047
+ * The listener of the `Receiver` `'drain'` event.
1048
+ *
1049
+ * @private
1050
+ */
1051
+ function receiverOnDrain() {
1052
+ const websocket = this[kWebSocket];
1053
+
1054
+ if (!websocket.isPaused) websocket._socket.resume();
1055
+ }
1056
+
1057
+ /**
1058
+ * The listener of the `Receiver` `'error'` event.
1059
+ *
1060
+ * @param {(RangeError|Error)} err The emitted error
1061
+ * @private
1062
+ */
1063
+ function receiverOnError(err) {
1064
+ const websocket = this[kWebSocket];
1065
+
1066
+ if (websocket._socket[kWebSocket] !== undefined) {
1067
+ websocket._socket.removeListener('data', socketOnData);
1068
+
1069
+ //
1070
+ // On Node.js < 14.0.0 the `'error'` event is emitted synchronously. See
1071
+ // https://github.com/websockets/ws/issues/1940.
1072
+ //
1073
+ process.nextTick(resume, websocket._socket);
1074
+
1075
+ websocket.close(err[kStatusCode]);
1076
+ }
1077
+
1078
+ websocket.emit('error', err);
1079
+ }
1080
+
1081
+ /**
1082
+ * The listener of the `Receiver` `'finish'` event.
1083
+ *
1084
+ * @private
1085
+ */
1086
+ function receiverOnFinish() {
1087
+ this[kWebSocket].emitClose();
1088
+ }
1089
+
1090
+ /**
1091
+ * The listener of the `Receiver` `'message'` event.
1092
+ *
1093
+ * @param {Buffer|ArrayBuffer|Buffer[])} data The message
1094
+ * @param {Boolean} isBinary Specifies whether the message is binary or not
1095
+ * @private
1096
+ */
1097
+ function receiverOnMessage(data, isBinary) {
1098
+ this[kWebSocket].emit('message', data, isBinary);
1099
+ }
1100
+
1101
+ /**
1102
+ * The listener of the `Receiver` `'ping'` event.
1103
+ *
1104
+ * @param {Buffer} data The data included in the ping frame
1105
+ * @private
1106
+ */
1107
+ function receiverOnPing(data) {
1108
+ const websocket = this[kWebSocket];
1109
+
1110
+ websocket.pong(data, !websocket._isServer, NOOP);
1111
+ websocket.emit('ping', data);
1112
+ }
1113
+
1114
+ /**
1115
+ * The listener of the `Receiver` `'pong'` event.
1116
+ *
1117
+ * @param {Buffer} data The data included in the pong frame
1118
+ * @private
1119
+ */
1120
+ function receiverOnPong(data) {
1121
+ this[kWebSocket].emit('pong', data);
1122
+ }
1123
+
1124
+ /**
1125
+ * Resume a readable stream
1126
+ *
1127
+ * @param {Readable} stream The readable stream
1128
+ * @private
1129
+ */
1130
+ function resume(stream) {
1131
+ stream.resume();
1132
+ }
1133
+
1134
+ /**
1135
+ * The listener of the `net.Socket` `'close'` event.
1136
+ *
1137
+ * @private
1138
+ */
1139
+ function socketOnClose() {
1140
+ const websocket = this[kWebSocket];
1141
+
1142
+ this.removeListener('close', socketOnClose);
1143
+ this.removeListener('data', socketOnData);
1144
+ this.removeListener('end', socketOnEnd);
1145
+
1146
+ websocket._readyState = WebSocket.CLOSING;
1147
+
1148
+ let chunk;
1149
+
1150
+ //
1151
+ // The close frame might not have been received or the `'end'` event emitted,
1152
+ // for example, if the socket was destroyed due to an error. Ensure that the
1153
+ // `receiver` stream is closed after writing any remaining buffered data to
1154
+ // it. If the readable side of the socket is in flowing mode then there is no
1155
+ // buffered data as everything has been already written and `readable.read()`
1156
+ // will return `null`. If instead, the socket is paused, any possible buffered
1157
+ // data will be read as a single chunk.
1158
+ //
1159
+ if (
1160
+ !this._readableState.endEmitted &&
1161
+ !websocket._closeFrameReceived &&
1162
+ !websocket._receiver._writableState.errorEmitted &&
1163
+ (chunk = websocket._socket.read()) !== null
1164
+ ) {
1165
+ websocket._receiver.write(chunk);
1166
+ }
1167
+
1168
+ websocket._receiver.end();
1169
+
1170
+ this[kWebSocket] = undefined;
1171
+
1172
+ clearTimeout(websocket._closeTimer);
1173
+
1174
+ if (
1175
+ websocket._receiver._writableState.finished ||
1176
+ websocket._receiver._writableState.errorEmitted
1177
+ ) {
1178
+ websocket.emitClose();
1179
+ } else {
1180
+ websocket._receiver.on('error', receiverOnFinish);
1181
+ websocket._receiver.on('finish', receiverOnFinish);
1182
+ }
1183
+ }
1184
+
1185
+ /**
1186
+ * The listener of the `net.Socket` `'data'` event.
1187
+ *
1188
+ * @param {Buffer} chunk A chunk of data
1189
+ * @private
1190
+ */
1191
+ function socketOnData(chunk) {
1192
+ if (!this[kWebSocket]._receiver.write(chunk)) {
1193
+ this.pause();
1194
+ }
1195
+ }
1196
+
1197
+ /**
1198
+ * The listener of the `net.Socket` `'end'` event.
1199
+ *
1200
+ * @private
1201
+ */
1202
+ function socketOnEnd() {
1203
+ const websocket = this[kWebSocket];
1204
+
1205
+ websocket._readyState = WebSocket.CLOSING;
1206
+ websocket._receiver.end();
1207
+ this.end();
1208
+ }
1209
+
1210
+ /**
1211
+ * The listener of the `net.Socket` `'error'` event.
1212
+ *
1213
+ * @private
1214
+ */
1215
+ function socketOnError() {
1216
+ const websocket = this[kWebSocket];
1217
+
1218
+ this.removeListener('error', socketOnError);
1219
+ this.on('error', NOOP);
1220
+
1221
+ if (websocket) {
1222
+ websocket._readyState = WebSocket.CLOSING;
1223
+ this.destroy();
1224
+ }
1225
+ }