@babylonjs/inspector 9.5.0 → 9.5.1

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,15 +1,16 @@
1
1
  import require$$7, { fileURLToPath } from 'url';
2
- import require$$0$3 from 'events';
3
- import require$$1$1 from 'https';
2
+ import require$$0$4 from 'events';
3
+ import require$$1$2 from 'https';
4
4
  import require$$2$1 from 'http';
5
5
  import require$$3 from 'net';
6
6
  import require$$4 from 'tls';
7
- import require$$5 from 'crypto';
8
- import require$$0$2 from 'stream';
7
+ import require$$1$1 from 'crypto';
8
+ import require$$0$3 from 'stream';
9
9
  import require$$0$1 from 'zlib';
10
10
  import require$$0, { readFileSync, existsSync } from 'fs';
11
11
  import require$$1, { join, dirname } from 'path';
12
12
  import require$$2 from 'os';
13
+ import require$$0$2 from 'buffer';
13
14
 
14
15
  function getDefaultExportFromCjs (x) {
15
16
  return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
@@ -17,12 +18,21 @@ function getDefaultExportFromCjs (x) {
17
18
 
18
19
  var bufferUtil$1 = {exports: {}};
19
20
 
21
+ const BINARY_TYPES$2 = ['nodebuffer', 'arraybuffer', 'fragments'];
22
+ const hasBlob$1 = typeof Blob !== 'undefined';
23
+
24
+ if (hasBlob$1) BINARY_TYPES$2.push('blob');
25
+
20
26
  var constants = {
21
- BINARY_TYPES: ['nodebuffer', 'arraybuffer', 'fragments'],
27
+ BINARY_TYPES: BINARY_TYPES$2,
28
+ CLOSE_TIMEOUT: 30000,
29
+ EMPTY_BUFFER: Buffer.alloc(0),
22
30
  GUID: '258EAFA5-E914-47DA-95CA-C5AB0DC85B11',
31
+ hasBlob: hasBlob$1,
32
+ kForOnEventAttribute: Symbol('kIsForOnEventAttribute'),
33
+ kListener: Symbol('kListener'),
23
34
  kStatusCode: Symbol('status-code'),
24
35
  kWebSocket: Symbol('websocket'),
25
- EMPTY_BUFFER: Buffer.alloc(0),
26
36
  NOOP: () => {}
27
37
  };
28
38
 
@@ -320,8 +330,13 @@ function requireBufferutil () {
320
330
  return bufferutil.exports;
321
331
  }
322
332
 
333
+ var unmask$1;
334
+ var mask;
335
+
323
336
  const { EMPTY_BUFFER: EMPTY_BUFFER$3 } = constants;
324
337
 
338
+ const FastBuffer$2 = Buffer[Symbol.species];
339
+
325
340
  /**
326
341
  * Merges an array of buffers into a new buffer.
327
342
  *
@@ -343,7 +358,9 @@ function concat$1(list, totalLength) {
343
358
  offset += buf.length;
344
359
  }
345
360
 
346
- if (offset < totalLength) return target.slice(0, offset);
361
+ if (offset < totalLength) {
362
+ return new FastBuffer$2(target.buffer, target.byteOffset, offset);
363
+ }
347
364
 
348
365
  return target;
349
366
  }
@@ -372,9 +389,7 @@ function _mask(source, mask, output, offset, length) {
372
389
  * @public
373
390
  */
374
391
  function _unmask(buffer, mask) {
375
- // Required until https://github.com/nodejs/node/issues/9006 is resolved.
376
- const length = buffer.length;
377
- for (let i = 0; i < length; i++) {
392
+ for (let i = 0; i < buffer.length; i++) {
378
393
  buffer[i] ^= mask[i & 3];
379
394
  }
380
395
  }
@@ -387,11 +402,11 @@ function _unmask(buffer, mask) {
387
402
  * @public
388
403
  */
389
404
  function toArrayBuffer$1(buf) {
390
- if (buf.byteLength === buf.buffer.byteLength) {
405
+ if (buf.length === buf.buffer.byteLength) {
391
406
  return buf.buffer;
392
407
  }
393
408
 
394
- return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
409
+ return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.length);
395
410
  }
396
411
 
397
412
  /**
@@ -410,9 +425,9 @@ function toBuffer$2(data) {
410
425
  let buf;
411
426
 
412
427
  if (data instanceof ArrayBuffer) {
413
- buf = Buffer.from(data);
428
+ buf = new FastBuffer$2(data);
414
429
  } else if (ArrayBuffer.isView(data)) {
415
- buf = Buffer.from(data.buffer, data.byteOffset, data.byteLength);
430
+ buf = new FastBuffer$2(data.buffer, data.byteOffset, data.byteLength);
416
431
  } else {
417
432
  buf = Buffer.from(data);
418
433
  toBuffer$2.readOnly = false;
@@ -421,31 +436,31 @@ function toBuffer$2(data) {
421
436
  return buf;
422
437
  }
423
438
 
424
- try {
425
- const bufferUtil = requireBufferutil();
426
- const bu = bufferUtil.BufferUtil || bufferUtil;
439
+ bufferUtil$1.exports = {
440
+ concat: concat$1,
441
+ mask: _mask,
442
+ toArrayBuffer: toArrayBuffer$1,
443
+ toBuffer: toBuffer$2,
444
+ unmask: _unmask
445
+ };
446
+
447
+ /* istanbul ignore else */
448
+ if (!process.env.WS_NO_BUFFER_UTIL) {
449
+ try {
450
+ const bufferUtil = requireBufferutil();
427
451
 
428
- bufferUtil$1.exports = {
429
- concat: concat$1,
430
- mask(source, mask, output, offset, length) {
452
+ mask = bufferUtil$1.exports.mask = function (source, mask, output, offset, length) {
431
453
  if (length < 48) _mask(source, mask, output, offset, length);
432
- else bu.mask(source, mask, output, offset, length);
433
- },
434
- toArrayBuffer: toArrayBuffer$1,
435
- toBuffer: toBuffer$2,
436
- unmask(buffer, mask) {
454
+ else bufferUtil.mask(source, mask, output, offset, length);
455
+ };
456
+
457
+ unmask$1 = bufferUtil$1.exports.unmask = function (buffer, mask) {
437
458
  if (buffer.length < 32) _unmask(buffer, mask);
438
- else bu.unmask(buffer, mask);
439
- }
440
- };
441
- } catch (e) /* istanbul ignore next */ {
442
- bufferUtil$1.exports = {
443
- concat: concat$1,
444
- mask: _mask,
445
- toArrayBuffer: toArrayBuffer$1,
446
- toBuffer: toBuffer$2,
447
- unmask: _unmask
448
- };
459
+ else bufferUtil.unmask(buffer, mask);
460
+ };
461
+ } catch (e) {
462
+ // Continue regardless of the error.
463
+ }
449
464
  }
450
465
 
451
466
  var bufferUtilExports = bufferUtil$1.exports;
@@ -508,14 +523,15 @@ const zlib = require$$0$1;
508
523
 
509
524
  const bufferUtil = bufferUtilExports;
510
525
  const Limiter = limiter;
511
- const { kStatusCode: kStatusCode$2, NOOP: NOOP$1 } = constants;
526
+ const { kStatusCode: kStatusCode$2 } = constants;
512
527
 
528
+ const FastBuffer$1 = Buffer[Symbol.species];
513
529
  const TRAILER = Buffer.from([0x00, 0x00, 0xff, 0xff]);
514
530
  const kPerMessageDeflate = Symbol('permessage-deflate');
515
531
  const kTotalLength = Symbol('total-length');
516
532
  const kCallback = Symbol('callback');
517
533
  const kBuffers = Symbol('buffers');
518
- const kError = Symbol('error');
534
+ const kError$1 = Symbol('error');
519
535
 
520
536
  //
521
537
  // We limit zlib concurrency, which prevents severe memory fragmentation
@@ -534,32 +550,32 @@ let PerMessageDeflate$4 = class PerMessageDeflate {
534
550
  * Creates a PerMessageDeflate instance.
535
551
  *
536
552
  * @param {Object} [options] Configuration options
537
- * @param {Boolean} [options.serverNoContextTakeover=false] Request/accept
538
- * disabling of server context takeover
553
+ * @param {(Boolean|Number)} [options.clientMaxWindowBits] Advertise support
554
+ * for, or request, a custom client window size
539
555
  * @param {Boolean} [options.clientNoContextTakeover=false] Advertise/
540
556
  * acknowledge disabling of client context takeover
557
+ * @param {Number} [options.concurrencyLimit=10] The number of concurrent
558
+ * calls to zlib
559
+ * @param {Boolean} [options.isServer=false] Create the instance in either
560
+ * server or client mode
561
+ * @param {Number} [options.maxPayload=0] The maximum allowed message length
541
562
  * @param {(Boolean|Number)} [options.serverMaxWindowBits] Request/confirm the
542
563
  * use of a custom server window size
543
- * @param {(Boolean|Number)} [options.clientMaxWindowBits] Advertise support
544
- * for, or request, a custom client window size
564
+ * @param {Boolean} [options.serverNoContextTakeover=false] Request/accept
565
+ * disabling of server context takeover
566
+ * @param {Number} [options.threshold=1024] Size (in bytes) below which
567
+ * messages should not be compressed if context takeover is disabled
545
568
  * @param {Object} [options.zlibDeflateOptions] Options to pass to zlib on
546
569
  * deflate
547
570
  * @param {Object} [options.zlibInflateOptions] Options to pass to zlib on
548
571
  * inflate
549
- * @param {Number} [options.threshold=1024] Size (in bytes) below which
550
- * messages should not be compressed
551
- * @param {Number} [options.concurrencyLimit=10] The number of concurrent
552
- * calls to zlib
553
- * @param {Boolean} [isServer=false] Create the instance in either server or
554
- * client mode
555
- * @param {Number} [maxPayload=0] The maximum allowed message length
556
572
  */
557
- constructor(options, isServer, maxPayload) {
558
- this._maxPayload = maxPayload | 0;
573
+ constructor(options) {
559
574
  this._options = options || {};
560
575
  this._threshold =
561
576
  this._options.threshold !== undefined ? this._options.threshold : 1024;
562
- this._isServer = !!isServer;
577
+ this._maxPayload = this._options.maxPayload | 0;
578
+ this._isServer = !!this._options.isServer;
563
579
  this._deflate = null;
564
580
  this._inflate = null;
565
581
 
@@ -817,7 +833,7 @@ let PerMessageDeflate$4 = class PerMessageDeflate {
817
833
  /**
818
834
  * Compress data. Concurrency limited.
819
835
  *
820
- * @param {Buffer} data Data to compress
836
+ * @param {(Buffer|String)} data Data to compress
821
837
  * @param {Boolean} fin Specifies whether or not this is the last fragment
822
838
  * @param {Function} callback Callback
823
839
  * @public
@@ -866,7 +882,7 @@ let PerMessageDeflate$4 = class PerMessageDeflate {
866
882
  if (fin) this._inflate.write(TRAILER);
867
883
 
868
884
  this._inflate.flush(() => {
869
- const err = this._inflate[kError];
885
+ const err = this._inflate[kError$1];
870
886
 
871
887
  if (err) {
872
888
  this._inflate.close();
@@ -899,7 +915,7 @@ let PerMessageDeflate$4 = class PerMessageDeflate {
899
915
  /**
900
916
  * Compress data.
901
917
  *
902
- * @param {Buffer} data Data to compress
918
+ * @param {(Buffer|String)} data Data to compress
903
919
  * @param {Boolean} fin Specifies whether or not this is the last fragment
904
920
  * @param {Function} callback Callback
905
921
  * @private
@@ -922,13 +938,6 @@ let PerMessageDeflate$4 = class PerMessageDeflate {
922
938
  this._deflate[kTotalLength] = 0;
923
939
  this._deflate[kBuffers] = [];
924
940
 
925
- //
926
- // An `'error'` event is emitted, only on Node.js < 10.0.0, if the
927
- // `zlib.DeflateRaw` instance is closed while data is being processed.
928
- // This can happen if `PerMessageDeflate#cleanup()` is called at the wrong
929
- // time due to an abnormal WebSocket closure.
930
- //
931
- this._deflate.on('error', NOOP$1);
932
941
  this._deflate.on('data', deflateOnData);
933
942
  }
934
943
 
@@ -948,7 +957,9 @@ let PerMessageDeflate$4 = class PerMessageDeflate {
948
957
  this._deflate[kTotalLength]
949
958
  );
950
959
 
951
- if (fin) data = data.slice(0, data.length - 4);
960
+ if (fin) {
961
+ data = new FastBuffer$1(data.buffer, data.byteOffset, data.length - 4);
962
+ }
952
963
 
953
964
  //
954
965
  // Ensure that the callback will not be called again in
@@ -998,10 +1009,18 @@ function inflateOnData(chunk) {
998
1009
  return;
999
1010
  }
1000
1011
 
1001
- this[kError] = new RangeError('Max payload size exceeded');
1002
- this[kError].code = 'WS_ERR_UNSUPPORTED_MESSAGE_LENGTH';
1003
- this[kError][kStatusCode$2] = 1009;
1012
+ this[kError$1] = new RangeError('Max payload size exceeded');
1013
+ this[kError$1].code = 'WS_ERR_UNSUPPORTED_MESSAGE_LENGTH';
1014
+ this[kError$1][kStatusCode$2] = 1009;
1004
1015
  this.removeListener('data', inflateOnData);
1016
+
1017
+ //
1018
+ // The choice to employ `zlib.reset()` over `zlib.close()` is dictated by the
1019
+ // fact that in Node.js versions prior to 13.10.0, the callback for
1020
+ // `zlib.flush()` is not called if `zlib.close()` is used. Utilizing
1021
+ // `zlib.reset()` ensures that either the callback is invoked or an error is
1022
+ // emitted.
1023
+ //
1005
1024
  this.reset();
1006
1025
  }
1007
1026
 
@@ -1017,6 +1036,12 @@ function inflateOnError(err) {
1017
1036
  // closed when an error is emitted.
1018
1037
  //
1019
1038
  this[kPerMessageDeflate]._inflate = null;
1039
+
1040
+ if (this[kError$1]) {
1041
+ this[kCallback](this[kError$1]);
1042
+ return;
1043
+ }
1044
+
1020
1045
  err[kStatusCode$2] = 1007;
1021
1046
  this[kCallback](err);
1022
1047
  }
@@ -1109,6 +1134,35 @@ function requireUtf8Validate () {
1109
1134
  return utf8Validate.exports;
1110
1135
  }
1111
1136
 
1137
+ var isValidUTF8_1;
1138
+
1139
+ const { isUtf8 } = require$$0$2;
1140
+
1141
+ const { hasBlob } = constants;
1142
+
1143
+ //
1144
+ // Allowed token characters:
1145
+ //
1146
+ // '!', '#', '$', '%', '&', ''', '*', '+', '-',
1147
+ // '.', 0-9, A-Z, '^', '_', '`', a-z, '|', '~'
1148
+ //
1149
+ // tokenChars[32] === 0 // ' '
1150
+ // tokenChars[33] === 1 // '!'
1151
+ // tokenChars[34] === 0 // '"'
1152
+ // ...
1153
+ //
1154
+ // prettier-ignore
1155
+ const tokenChars$2 = [
1156
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 - 15
1157
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 - 31
1158
+ 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, // 32 - 47
1159
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 48 - 63
1160
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64 - 79
1161
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, // 80 - 95
1162
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96 - 111
1163
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0 // 112 - 127
1164
+ ];
1165
+
1112
1166
  /**
1113
1167
  * Checks if a status code is allowed in a close frame.
1114
1168
  *
@@ -1191,47 +1245,71 @@ function _isValidUTF8(buf) {
1191
1245
  return true;
1192
1246
  }
1193
1247
 
1194
- try {
1195
- let isValidUTF8 = requireUtf8Validate();
1248
+ /**
1249
+ * Determines whether a value is a `Blob`.
1250
+ *
1251
+ * @param {*} value The value to be tested
1252
+ * @return {Boolean} `true` if `value` is a `Blob`, else `false`
1253
+ * @private
1254
+ */
1255
+ function isBlob$2(value) {
1256
+ return (
1257
+ hasBlob &&
1258
+ typeof value === 'object' &&
1259
+ typeof value.arrayBuffer === 'function' &&
1260
+ typeof value.type === 'string' &&
1261
+ typeof value.stream === 'function' &&
1262
+ (value[Symbol.toStringTag] === 'Blob' ||
1263
+ value[Symbol.toStringTag] === 'File')
1264
+ );
1265
+ }
1196
1266
 
1197
- /* istanbul ignore if */
1198
- if (typeof isValidUTF8 === 'object') {
1199
- isValidUTF8 = isValidUTF8.Validation.isValidUTF8; // utf-8-validate@<3.0.0
1200
- }
1267
+ validation.exports = {
1268
+ isBlob: isBlob$2,
1269
+ isValidStatusCode: isValidStatusCode$2,
1270
+ isValidUTF8: _isValidUTF8,
1271
+ tokenChars: tokenChars$2
1272
+ };
1201
1273
 
1202
- validation.exports = {
1203
- isValidStatusCode: isValidStatusCode$2,
1204
- isValidUTF8(buf) {
1205
- return buf.length < 150 ? _isValidUTF8(buf) : isValidUTF8(buf);
1206
- }
1207
- };
1208
- } catch (e) /* istanbul ignore next */ {
1209
- validation.exports = {
1210
- isValidStatusCode: isValidStatusCode$2,
1211
- isValidUTF8: _isValidUTF8
1274
+ if (isUtf8) {
1275
+ isValidUTF8_1 = validation.exports.isValidUTF8 = function (buf) {
1276
+ return buf.length < 24 ? _isValidUTF8(buf) : isUtf8(buf);
1212
1277
  };
1278
+ } /* istanbul ignore else */ else if (!process.env.WS_NO_UTF_8_VALIDATE) {
1279
+ try {
1280
+ const isValidUTF8 = requireUtf8Validate();
1281
+
1282
+ isValidUTF8_1 = validation.exports.isValidUTF8 = function (buf) {
1283
+ return buf.length < 32 ? _isValidUTF8(buf) : isValidUTF8(buf);
1284
+ };
1285
+ } catch (e) {
1286
+ // Continue regardless of the error.
1287
+ }
1213
1288
  }
1214
1289
 
1215
1290
  var validationExports = validation.exports;
1216
1291
 
1217
- const { Writable } = require$$0$2;
1292
+ const { Writable } = require$$0$3;
1218
1293
 
1219
1294
  const PerMessageDeflate$3 = permessageDeflate;
1220
1295
  const {
1221
1296
  BINARY_TYPES: BINARY_TYPES$1,
1222
1297
  EMPTY_BUFFER: EMPTY_BUFFER$2,
1223
1298
  kStatusCode: kStatusCode$1,
1224
- kWebSocket: kWebSocket$2
1299
+ kWebSocket: kWebSocket$3
1225
1300
  } = constants;
1226
1301
  const { concat, toArrayBuffer, unmask } = bufferUtilExports;
1227
1302
  const { isValidStatusCode: isValidStatusCode$1, isValidUTF8 } = validationExports;
1228
1303
 
1304
+ const FastBuffer = Buffer[Symbol.species];
1305
+
1229
1306
  const GET_INFO = 0;
1230
1307
  const GET_PAYLOAD_LENGTH_16 = 1;
1231
1308
  const GET_PAYLOAD_LENGTH_64 = 2;
1232
1309
  const GET_MASK = 3;
1233
1310
  const GET_DATA = 4;
1234
1311
  const INFLATING = 5;
1312
+ const DEFER_EVENT = 6;
1235
1313
 
1236
1314
  /**
1237
1315
  * HyBi Receiver implementation.
@@ -1242,20 +1320,32 @@ let Receiver$1 = class Receiver extends Writable {
1242
1320
  /**
1243
1321
  * Creates a Receiver instance.
1244
1322
  *
1245
- * @param {String} [binaryType=nodebuffer] The type for binary data
1246
- * @param {Object} [extensions] An object containing the negotiated extensions
1247
- * @param {Boolean} [isServer=false] Specifies whether to operate in client or
1248
- * server mode
1249
- * @param {Number} [maxPayload=0] The maximum allowed message length
1323
+ * @param {Object} [options] Options object
1324
+ * @param {Boolean} [options.allowSynchronousEvents=true] Specifies whether
1325
+ * any of the `'message'`, `'ping'`, and `'pong'` events can be emitted
1326
+ * multiple times in the same tick
1327
+ * @param {String} [options.binaryType=nodebuffer] The type for binary data
1328
+ * @param {Object} [options.extensions] An object containing the negotiated
1329
+ * extensions
1330
+ * @param {Boolean} [options.isServer=false] Specifies whether to operate in
1331
+ * client or server mode
1332
+ * @param {Number} [options.maxPayload=0] The maximum allowed message length
1333
+ * @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or
1334
+ * not to skip UTF-8 validation for text and close messages
1250
1335
  */
1251
- constructor(binaryType, extensions, isServer, maxPayload) {
1336
+ constructor(options = {}) {
1252
1337
  super();
1253
1338
 
1254
- this._binaryType = binaryType || BINARY_TYPES$1[0];
1255
- this[kWebSocket$2] = undefined;
1256
- this._extensions = extensions || {};
1257
- this._isServer = !!isServer;
1258
- this._maxPayload = maxPayload | 0;
1339
+ this._allowSynchronousEvents =
1340
+ options.allowSynchronousEvents !== undefined
1341
+ ? options.allowSynchronousEvents
1342
+ : true;
1343
+ this._binaryType = options.binaryType || BINARY_TYPES$1[0];
1344
+ this._extensions = options.extensions || {};
1345
+ this._isServer = !!options.isServer;
1346
+ this._maxPayload = options.maxPayload | 0;
1347
+ this._skipUTF8Validation = !!options.skipUTF8Validation;
1348
+ this[kWebSocket$3] = undefined;
1259
1349
 
1260
1350
  this._bufferedBytes = 0;
1261
1351
  this._buffers = [];
@@ -1272,8 +1362,9 @@ let Receiver$1 = class Receiver extends Writable {
1272
1362
  this._messageLength = 0;
1273
1363
  this._fragments = [];
1274
1364
 
1275
- this._state = GET_INFO;
1365
+ this._errored = false;
1276
1366
  this._loop = false;
1367
+ this._state = GET_INFO;
1277
1368
  }
1278
1369
 
1279
1370
  /**
@@ -1306,8 +1397,13 @@ let Receiver$1 = class Receiver extends Writable {
1306
1397
 
1307
1398
  if (n < this._buffers[0].length) {
1308
1399
  const buf = this._buffers[0];
1309
- this._buffers[0] = buf.slice(n);
1310
- return buf.slice(0, n);
1400
+ this._buffers[0] = new FastBuffer(
1401
+ buf.buffer,
1402
+ buf.byteOffset + n,
1403
+ buf.length - n
1404
+ );
1405
+
1406
+ return new FastBuffer(buf.buffer, buf.byteOffset, n);
1311
1407
  }
1312
1408
 
1313
1409
  const dst = Buffer.allocUnsafe(n);
@@ -1320,7 +1416,11 @@ let Receiver$1 = class Receiver extends Writable {
1320
1416
  dst.set(this._buffers.shift(), offset);
1321
1417
  } else {
1322
1418
  dst.set(new Uint8Array(buf.buffer, buf.byteOffset, n), offset);
1323
- this._buffers[0] = buf.slice(n);
1419
+ this._buffers[0] = new FastBuffer(
1420
+ buf.buffer,
1421
+ buf.byteOffset + n,
1422
+ buf.length - n
1423
+ );
1324
1424
  }
1325
1425
 
1326
1426
  n -= buf.length;
@@ -1336,43 +1436,42 @@ let Receiver$1 = class Receiver extends Writable {
1336
1436
  * @private
1337
1437
  */
1338
1438
  startLoop(cb) {
1339
- let err;
1340
1439
  this._loop = true;
1341
1440
 
1342
1441
  do {
1343
1442
  switch (this._state) {
1344
1443
  case GET_INFO:
1345
- err = this.getInfo();
1444
+ this.getInfo(cb);
1346
1445
  break;
1347
1446
  case GET_PAYLOAD_LENGTH_16:
1348
- err = this.getPayloadLength16();
1447
+ this.getPayloadLength16(cb);
1349
1448
  break;
1350
1449
  case GET_PAYLOAD_LENGTH_64:
1351
- err = this.getPayloadLength64();
1450
+ this.getPayloadLength64(cb);
1352
1451
  break;
1353
1452
  case GET_MASK:
1354
1453
  this.getMask();
1355
1454
  break;
1356
1455
  case GET_DATA:
1357
- err = this.getData(cb);
1456
+ this.getData(cb);
1358
1457
  break;
1359
- default:
1360
- // `INFLATING`
1458
+ case INFLATING:
1459
+ case DEFER_EVENT:
1361
1460
  this._loop = false;
1362
1461
  return;
1363
1462
  }
1364
1463
  } while (this._loop);
1365
1464
 
1366
- cb(err);
1465
+ if (!this._errored) cb();
1367
1466
  }
1368
1467
 
1369
1468
  /**
1370
1469
  * Reads the first two bytes of a frame.
1371
1470
  *
1372
- * @return {(RangeError|undefined)} A possible error
1471
+ * @param {Function} cb Callback
1373
1472
  * @private
1374
1473
  */
1375
- getInfo() {
1474
+ getInfo(cb) {
1376
1475
  if (this._bufferedBytes < 2) {
1377
1476
  this._loop = false;
1378
1477
  return;
@@ -1381,27 +1480,31 @@ let Receiver$1 = class Receiver extends Writable {
1381
1480
  const buf = this.consume(2);
1382
1481
 
1383
1482
  if ((buf[0] & 0x30) !== 0x00) {
1384
- this._loop = false;
1385
- return error(
1483
+ const error = this.createError(
1386
1484
  RangeError,
1387
1485
  'RSV2 and RSV3 must be clear',
1388
1486
  true,
1389
1487
  1002,
1390
1488
  'WS_ERR_UNEXPECTED_RSV_2_3'
1391
1489
  );
1490
+
1491
+ cb(error);
1492
+ return;
1392
1493
  }
1393
1494
 
1394
1495
  const compressed = (buf[0] & 0x40) === 0x40;
1395
1496
 
1396
1497
  if (compressed && !this._extensions[PerMessageDeflate$3.extensionName]) {
1397
- this._loop = false;
1398
- return error(
1498
+ const error = this.createError(
1399
1499
  RangeError,
1400
1500
  'RSV1 must be clear',
1401
1501
  true,
1402
1502
  1002,
1403
1503
  'WS_ERR_UNEXPECTED_RSV_1'
1404
1504
  );
1505
+
1506
+ cb(error);
1507
+ return;
1405
1508
  }
1406
1509
 
1407
1510
  this._fin = (buf[0] & 0x80) === 0x80;
@@ -1410,83 +1513,100 @@ let Receiver$1 = class Receiver extends Writable {
1410
1513
 
1411
1514
  if (this._opcode === 0x00) {
1412
1515
  if (compressed) {
1413
- this._loop = false;
1414
- return error(
1516
+ const error = this.createError(
1415
1517
  RangeError,
1416
1518
  'RSV1 must be clear',
1417
1519
  true,
1418
1520
  1002,
1419
1521
  'WS_ERR_UNEXPECTED_RSV_1'
1420
1522
  );
1523
+
1524
+ cb(error);
1525
+ return;
1421
1526
  }
1422
1527
 
1423
1528
  if (!this._fragmented) {
1424
- this._loop = false;
1425
- return error(
1529
+ const error = this.createError(
1426
1530
  RangeError,
1427
1531
  'invalid opcode 0',
1428
1532
  true,
1429
1533
  1002,
1430
1534
  'WS_ERR_INVALID_OPCODE'
1431
1535
  );
1536
+
1537
+ cb(error);
1538
+ return;
1432
1539
  }
1433
1540
 
1434
1541
  this._opcode = this._fragmented;
1435
1542
  } else if (this._opcode === 0x01 || this._opcode === 0x02) {
1436
1543
  if (this._fragmented) {
1437
- this._loop = false;
1438
- return error(
1544
+ const error = this.createError(
1439
1545
  RangeError,
1440
1546
  `invalid opcode ${this._opcode}`,
1441
1547
  true,
1442
1548
  1002,
1443
1549
  'WS_ERR_INVALID_OPCODE'
1444
1550
  );
1551
+
1552
+ cb(error);
1553
+ return;
1445
1554
  }
1446
1555
 
1447
1556
  this._compressed = compressed;
1448
1557
  } else if (this._opcode > 0x07 && this._opcode < 0x0b) {
1449
1558
  if (!this._fin) {
1450
- this._loop = false;
1451
- return error(
1559
+ const error = this.createError(
1452
1560
  RangeError,
1453
1561
  'FIN must be set',
1454
1562
  true,
1455
1563
  1002,
1456
1564
  'WS_ERR_EXPECTED_FIN'
1457
1565
  );
1566
+
1567
+ cb(error);
1568
+ return;
1458
1569
  }
1459
1570
 
1460
1571
  if (compressed) {
1461
- this._loop = false;
1462
- return error(
1572
+ const error = this.createError(
1463
1573
  RangeError,
1464
1574
  'RSV1 must be clear',
1465
1575
  true,
1466
1576
  1002,
1467
1577
  'WS_ERR_UNEXPECTED_RSV_1'
1468
1578
  );
1579
+
1580
+ cb(error);
1581
+ return;
1469
1582
  }
1470
1583
 
1471
- if (this._payloadLength > 0x7d) {
1472
- this._loop = false;
1473
- return error(
1584
+ if (
1585
+ this._payloadLength > 0x7d ||
1586
+ (this._opcode === 0x08 && this._payloadLength === 1)
1587
+ ) {
1588
+ const error = this.createError(
1474
1589
  RangeError,
1475
1590
  `invalid payload length ${this._payloadLength}`,
1476
1591
  true,
1477
1592
  1002,
1478
1593
  'WS_ERR_INVALID_CONTROL_PAYLOAD_LENGTH'
1479
1594
  );
1595
+
1596
+ cb(error);
1597
+ return;
1480
1598
  }
1481
1599
  } else {
1482
- this._loop = false;
1483
- return error(
1600
+ const error = this.createError(
1484
1601
  RangeError,
1485
1602
  `invalid opcode ${this._opcode}`,
1486
1603
  true,
1487
1604
  1002,
1488
1605
  'WS_ERR_INVALID_OPCODE'
1489
1606
  );
1607
+
1608
+ cb(error);
1609
+ return;
1490
1610
  }
1491
1611
 
1492
1612
  if (!this._fin && !this._fragmented) this._fragmented = this._opcode;
@@ -1494,54 +1614,58 @@ let Receiver$1 = class Receiver extends Writable {
1494
1614
 
1495
1615
  if (this._isServer) {
1496
1616
  if (!this._masked) {
1497
- this._loop = false;
1498
- return error(
1617
+ const error = this.createError(
1499
1618
  RangeError,
1500
1619
  'MASK must be set',
1501
1620
  true,
1502
1621
  1002,
1503
1622
  'WS_ERR_EXPECTED_MASK'
1504
1623
  );
1624
+
1625
+ cb(error);
1626
+ return;
1505
1627
  }
1506
1628
  } else if (this._masked) {
1507
- this._loop = false;
1508
- return error(
1629
+ const error = this.createError(
1509
1630
  RangeError,
1510
1631
  'MASK must be clear',
1511
1632
  true,
1512
1633
  1002,
1513
1634
  'WS_ERR_UNEXPECTED_MASK'
1514
1635
  );
1636
+
1637
+ cb(error);
1638
+ return;
1515
1639
  }
1516
1640
 
1517
1641
  if (this._payloadLength === 126) this._state = GET_PAYLOAD_LENGTH_16;
1518
1642
  else if (this._payloadLength === 127) this._state = GET_PAYLOAD_LENGTH_64;
1519
- else return this.haveLength();
1643
+ else this.haveLength(cb);
1520
1644
  }
1521
1645
 
1522
1646
  /**
1523
1647
  * Gets extended payload length (7+16).
1524
1648
  *
1525
- * @return {(RangeError|undefined)} A possible error
1649
+ * @param {Function} cb Callback
1526
1650
  * @private
1527
1651
  */
1528
- getPayloadLength16() {
1652
+ getPayloadLength16(cb) {
1529
1653
  if (this._bufferedBytes < 2) {
1530
1654
  this._loop = false;
1531
1655
  return;
1532
1656
  }
1533
1657
 
1534
1658
  this._payloadLength = this.consume(2).readUInt16BE(0);
1535
- return this.haveLength();
1659
+ this.haveLength(cb);
1536
1660
  }
1537
1661
 
1538
1662
  /**
1539
1663
  * Gets extended payload length (7+64).
1540
1664
  *
1541
- * @return {(RangeError|undefined)} A possible error
1665
+ * @param {Function} cb Callback
1542
1666
  * @private
1543
1667
  */
1544
- getPayloadLength64() {
1668
+ getPayloadLength64(cb) {
1545
1669
  if (this._bufferedBytes < 8) {
1546
1670
  this._loop = false;
1547
1671
  return;
@@ -1555,38 +1679,42 @@ let Receiver$1 = class Receiver extends Writable {
1555
1679
  // if payload length is greater than this number.
1556
1680
  //
1557
1681
  if (num > Math.pow(2, 53 - 32) - 1) {
1558
- this._loop = false;
1559
- return error(
1682
+ const error = this.createError(
1560
1683
  RangeError,
1561
1684
  'Unsupported WebSocket frame: payload length > 2^53 - 1',
1562
1685
  false,
1563
1686
  1009,
1564
1687
  'WS_ERR_UNSUPPORTED_DATA_PAYLOAD_LENGTH'
1565
1688
  );
1689
+
1690
+ cb(error);
1691
+ return;
1566
1692
  }
1567
1693
 
1568
1694
  this._payloadLength = num * Math.pow(2, 32) + buf.readUInt32BE(4);
1569
- return this.haveLength();
1695
+ this.haveLength(cb);
1570
1696
  }
1571
1697
 
1572
1698
  /**
1573
1699
  * Payload length has been read.
1574
1700
  *
1575
- * @return {(RangeError|undefined)} A possible error
1701
+ * @param {Function} cb Callback
1576
1702
  * @private
1577
1703
  */
1578
- haveLength() {
1704
+ haveLength(cb) {
1579
1705
  if (this._payloadLength && this._opcode < 0x08) {
1580
1706
  this._totalPayloadLength += this._payloadLength;
1581
1707
  if (this._totalPayloadLength > this._maxPayload && this._maxPayload > 0) {
1582
- this._loop = false;
1583
- return error(
1708
+ const error = this.createError(
1584
1709
  RangeError,
1585
1710
  'Max payload size exceeded',
1586
1711
  false,
1587
1712
  1009,
1588
1713
  'WS_ERR_UNSUPPORTED_MESSAGE_LENGTH'
1589
1714
  );
1715
+
1716
+ cb(error);
1717
+ return;
1590
1718
  }
1591
1719
  }
1592
1720
 
@@ -1613,7 +1741,6 @@ let Receiver$1 = class Receiver extends Writable {
1613
1741
  * Reads data bytes.
1614
1742
  *
1615
1743
  * @param {Function} cb Callback
1616
- * @return {(Error|RangeError|undefined)} A possible error
1617
1744
  * @private
1618
1745
  */
1619
1746
  getData(cb) {
@@ -1626,10 +1753,19 @@ let Receiver$1 = class Receiver extends Writable {
1626
1753
  }
1627
1754
 
1628
1755
  data = this.consume(this._payloadLength);
1629
- if (this._masked) unmask(data, this._mask);
1756
+
1757
+ if (
1758
+ this._masked &&
1759
+ (this._mask[0] | this._mask[1] | this._mask[2] | this._mask[3]) !== 0
1760
+ ) {
1761
+ unmask(data, this._mask);
1762
+ }
1630
1763
  }
1631
1764
 
1632
- if (this._opcode > 0x07) return this.controlMessage(data);
1765
+ if (this._opcode > 0x07) {
1766
+ this.controlMessage(data, cb);
1767
+ return;
1768
+ }
1633
1769
 
1634
1770
  if (this._compressed) {
1635
1771
  this._state = INFLATING;
@@ -1639,14 +1775,14 @@ let Receiver$1 = class Receiver extends Writable {
1639
1775
 
1640
1776
  if (data.length) {
1641
1777
  //
1642
- // This message is not compressed so its lenght is the sum of the payload
1778
+ // This message is not compressed so its length is the sum of the payload
1643
1779
  // length of all fragments.
1644
1780
  //
1645
1781
  this._messageLength = this._totalPayloadLength;
1646
1782
  this._fragments.push(data);
1647
1783
  }
1648
1784
 
1649
- return this.dataMessage();
1785
+ this.dataMessage(cb);
1650
1786
  }
1651
1787
 
1652
1788
  /**
@@ -1665,74 +1801,98 @@ let Receiver$1 = class Receiver extends Writable {
1665
1801
  if (buf.length) {
1666
1802
  this._messageLength += buf.length;
1667
1803
  if (this._messageLength > this._maxPayload && this._maxPayload > 0) {
1668
- return cb(
1669
- error(
1670
- RangeError,
1671
- 'Max payload size exceeded',
1672
- false,
1673
- 1009,
1674
- 'WS_ERR_UNSUPPORTED_MESSAGE_LENGTH'
1675
- )
1804
+ const error = this.createError(
1805
+ RangeError,
1806
+ 'Max payload size exceeded',
1807
+ false,
1808
+ 1009,
1809
+ 'WS_ERR_UNSUPPORTED_MESSAGE_LENGTH'
1676
1810
  );
1811
+
1812
+ cb(error);
1813
+ return;
1677
1814
  }
1678
1815
 
1679
1816
  this._fragments.push(buf);
1680
1817
  }
1681
1818
 
1682
- const er = this.dataMessage();
1683
- if (er) return cb(er);
1684
-
1685
- this.startLoop(cb);
1819
+ this.dataMessage(cb);
1820
+ if (this._state === GET_INFO) this.startLoop(cb);
1686
1821
  });
1687
1822
  }
1688
1823
 
1689
1824
  /**
1690
1825
  * Handles a data message.
1691
1826
  *
1692
- * @return {(Error|undefined)} A possible error
1827
+ * @param {Function} cb Callback
1693
1828
  * @private
1694
1829
  */
1695
- dataMessage() {
1696
- if (this._fin) {
1697
- const messageLength = this._messageLength;
1698
- const fragments = this._fragments;
1699
-
1700
- this._totalPayloadLength = 0;
1701
- this._messageLength = 0;
1702
- this._fragmented = 0;
1703
- this._fragments = [];
1704
-
1705
- if (this._opcode === 2) {
1706
- let data;
1707
-
1708
- if (this._binaryType === 'nodebuffer') {
1709
- data = concat(fragments, messageLength);
1710
- } else if (this._binaryType === 'arraybuffer') {
1711
- data = toArrayBuffer(concat(fragments, messageLength));
1712
- } else {
1713
- data = fragments;
1714
- }
1830
+ dataMessage(cb) {
1831
+ if (!this._fin) {
1832
+ this._state = GET_INFO;
1833
+ return;
1834
+ }
1835
+
1836
+ const messageLength = this._messageLength;
1837
+ const fragments = this._fragments;
1838
+
1839
+ this._totalPayloadLength = 0;
1840
+ this._messageLength = 0;
1841
+ this._fragmented = 0;
1842
+ this._fragments = [];
1715
1843
 
1716
- this.emit('message', data);
1844
+ if (this._opcode === 2) {
1845
+ let data;
1846
+
1847
+ if (this._binaryType === 'nodebuffer') {
1848
+ data = concat(fragments, messageLength);
1849
+ } else if (this._binaryType === 'arraybuffer') {
1850
+ data = toArrayBuffer(concat(fragments, messageLength));
1851
+ } else if (this._binaryType === 'blob') {
1852
+ data = new Blob(fragments);
1717
1853
  } else {
1718
- const buf = concat(fragments, messageLength);
1854
+ data = fragments;
1855
+ }
1719
1856
 
1720
- if (!isValidUTF8(buf)) {
1721
- this._loop = false;
1722
- return error(
1723
- Error,
1724
- 'invalid UTF-8 sequence',
1725
- true,
1726
- 1007,
1727
- 'WS_ERR_INVALID_UTF8'
1728
- );
1729
- }
1857
+ if (this._allowSynchronousEvents) {
1858
+ this.emit('message', data, true);
1859
+ this._state = GET_INFO;
1860
+ } else {
1861
+ this._state = DEFER_EVENT;
1862
+ setImmediate(() => {
1863
+ this.emit('message', data, true);
1864
+ this._state = GET_INFO;
1865
+ this.startLoop(cb);
1866
+ });
1867
+ }
1868
+ } else {
1869
+ const buf = concat(fragments, messageLength);
1870
+
1871
+ if (!this._skipUTF8Validation && !isValidUTF8(buf)) {
1872
+ const error = this.createError(
1873
+ Error,
1874
+ 'invalid UTF-8 sequence',
1875
+ true,
1876
+ 1007,
1877
+ 'WS_ERR_INVALID_UTF8'
1878
+ );
1730
1879
 
1731
- this.emit('message', buf.toString());
1880
+ cb(error);
1881
+ return;
1732
1882
  }
1733
- }
1734
1883
 
1735
- this._state = GET_INFO;
1884
+ if (this._state === INFLATING || this._allowSynchronousEvents) {
1885
+ this.emit('message', buf, false);
1886
+ this._state = GET_INFO;
1887
+ } else {
1888
+ this._state = DEFER_EVENT;
1889
+ setImmediate(() => {
1890
+ this.emit('message', buf, false);
1891
+ this._state = GET_INFO;
1892
+ this.startLoop(cb);
1893
+ });
1894
+ }
1895
+ }
1736
1896
  }
1737
1897
 
1738
1898
  /**
@@ -1742,93 +1902,117 @@ let Receiver$1 = class Receiver extends Writable {
1742
1902
  * @return {(Error|RangeError|undefined)} A possible error
1743
1903
  * @private
1744
1904
  */
1745
- controlMessage(data) {
1905
+ controlMessage(data, cb) {
1746
1906
  if (this._opcode === 0x08) {
1747
- this._loop = false;
1748
-
1749
1907
  if (data.length === 0) {
1750
- this.emit('conclude', 1005, '');
1908
+ this._loop = false;
1909
+ this.emit('conclude', 1005, EMPTY_BUFFER$2);
1751
1910
  this.end();
1752
- } else if (data.length === 1) {
1753
- return error(
1754
- RangeError,
1755
- 'invalid payload length 1',
1756
- true,
1757
- 1002,
1758
- 'WS_ERR_INVALID_CONTROL_PAYLOAD_LENGTH'
1759
- );
1760
1911
  } else {
1761
1912
  const code = data.readUInt16BE(0);
1762
1913
 
1763
1914
  if (!isValidStatusCode$1(code)) {
1764
- return error(
1915
+ const error = this.createError(
1765
1916
  RangeError,
1766
1917
  `invalid status code ${code}`,
1767
1918
  true,
1768
1919
  1002,
1769
1920
  'WS_ERR_INVALID_CLOSE_CODE'
1770
1921
  );
1922
+
1923
+ cb(error);
1924
+ return;
1771
1925
  }
1772
1926
 
1773
- const buf = data.slice(2);
1927
+ const buf = new FastBuffer(
1928
+ data.buffer,
1929
+ data.byteOffset + 2,
1930
+ data.length - 2
1931
+ );
1774
1932
 
1775
- if (!isValidUTF8(buf)) {
1776
- return error(
1933
+ if (!this._skipUTF8Validation && !isValidUTF8(buf)) {
1934
+ const error = this.createError(
1777
1935
  Error,
1778
1936
  'invalid UTF-8 sequence',
1779
1937
  true,
1780
1938
  1007,
1781
1939
  'WS_ERR_INVALID_UTF8'
1782
1940
  );
1941
+
1942
+ cb(error);
1943
+ return;
1783
1944
  }
1784
1945
 
1785
- this.emit('conclude', code, buf.toString());
1946
+ this._loop = false;
1947
+ this.emit('conclude', code, buf);
1786
1948
  this.end();
1787
1949
  }
1788
- } else if (this._opcode === 0x09) {
1789
- this.emit('ping', data);
1950
+
1951
+ this._state = GET_INFO;
1952
+ return;
1953
+ }
1954
+
1955
+ if (this._allowSynchronousEvents) {
1956
+ this.emit(this._opcode === 0x09 ? 'ping' : 'pong', data);
1957
+ this._state = GET_INFO;
1790
1958
  } else {
1791
- this.emit('pong', data);
1959
+ this._state = DEFER_EVENT;
1960
+ setImmediate(() => {
1961
+ this.emit(this._opcode === 0x09 ? 'ping' : 'pong', data);
1962
+ this._state = GET_INFO;
1963
+ this.startLoop(cb);
1964
+ });
1792
1965
  }
1966
+ }
1793
1967
 
1794
- this._state = GET_INFO;
1968
+ /**
1969
+ * Builds an error object.
1970
+ *
1971
+ * @param {function(new:Error|RangeError)} ErrorCtor The error constructor
1972
+ * @param {String} message The error message
1973
+ * @param {Boolean} prefix Specifies whether or not to add a default prefix to
1974
+ * `message`
1975
+ * @param {Number} statusCode The status code
1976
+ * @param {String} errorCode The exposed error code
1977
+ * @return {(Error|RangeError)} The error
1978
+ * @private
1979
+ */
1980
+ createError(ErrorCtor, message, prefix, statusCode, errorCode) {
1981
+ this._loop = false;
1982
+ this._errored = true;
1983
+
1984
+ const err = new ErrorCtor(
1985
+ prefix ? `Invalid WebSocket frame: ${message}` : message
1986
+ );
1987
+
1988
+ Error.captureStackTrace(err, this.createError);
1989
+ err.code = errorCode;
1990
+ err[kStatusCode$1] = statusCode;
1991
+ return err;
1795
1992
  }
1796
1993
  };
1797
1994
 
1798
1995
  var receiver = Receiver$1;
1799
1996
 
1800
- /**
1801
- * Builds an error object.
1802
- *
1803
- * @param {function(new:Error|RangeError)} ErrorCtor The error constructor
1804
- * @param {String} message The error message
1805
- * @param {Boolean} prefix Specifies whether or not to add a default prefix to
1806
- * `message`
1807
- * @param {Number} statusCode The status code
1808
- * @param {String} errorCode The exposed error code
1809
- * @return {(Error|RangeError)} The error
1810
- * @private
1811
- */
1812
- function error(ErrorCtor, message, prefix, statusCode, errorCode) {
1813
- const err = new ErrorCtor(
1814
- prefix ? `Invalid WebSocket frame: ${message}` : message
1815
- );
1816
-
1817
- Error.captureStackTrace(err, error);
1818
- err.code = errorCode;
1819
- err[kStatusCode$1] = statusCode;
1820
- return err;
1821
- }
1997
+ /* eslint no-unused-vars: ["error", { "varsIgnorePattern": "^Duplex" }] */
1822
1998
 
1823
- /* eslint no-unused-vars: ["error", { "varsIgnorePattern": "^net|tls$" }] */
1824
- const { randomFillSync } = require$$5;
1999
+ const { Duplex: Duplex$3 } = require$$0$3;
2000
+ const { randomFillSync } = require$$1$1;
1825
2001
 
1826
2002
  const PerMessageDeflate$2 = permessageDeflate;
1827
- const { EMPTY_BUFFER: EMPTY_BUFFER$1 } = constants;
1828
- const { isValidStatusCode } = validationExports;
2003
+ const { EMPTY_BUFFER: EMPTY_BUFFER$1, kWebSocket: kWebSocket$2, NOOP: NOOP$1 } = constants;
2004
+ const { isBlob: isBlob$1, isValidStatusCode } = validationExports;
1829
2005
  const { mask: applyMask, toBuffer: toBuffer$1 } = bufferUtilExports;
1830
2006
 
1831
- const mask = Buffer.alloc(4);
2007
+ const kByteLength = Symbol('kByteLength');
2008
+ const maskBuffer = Buffer.alloc(4);
2009
+ const RANDOM_POOL_SIZE = 8 * 1024;
2010
+ let randomPool;
2011
+ let randomPoolPointer = RANDOM_POOL_SIZE;
2012
+
2013
+ const DEFAULT = 0;
2014
+ const DEFLATING = 1;
2015
+ const GET_BLOB_DATA = 2;
1832
2016
 
1833
2017
  /**
1834
2018
  * HyBi Sender implementation.
@@ -1837,52 +2021,116 @@ let Sender$1 = class Sender {
1837
2021
  /**
1838
2022
  * Creates a Sender instance.
1839
2023
  *
1840
- * @param {(net.Socket|tls.Socket)} socket The connection socket
2024
+ * @param {Duplex} socket The connection socket
1841
2025
  * @param {Object} [extensions] An object containing the negotiated extensions
2026
+ * @param {Function} [generateMask] The function used to generate the masking
2027
+ * key
1842
2028
  */
1843
- constructor(socket, extensions) {
2029
+ constructor(socket, extensions, generateMask) {
1844
2030
  this._extensions = extensions || {};
2031
+
2032
+ if (generateMask) {
2033
+ this._generateMask = generateMask;
2034
+ this._maskBuffer = Buffer.alloc(4);
2035
+ }
2036
+
1845
2037
  this._socket = socket;
1846
2038
 
1847
2039
  this._firstFragment = true;
1848
2040
  this._compress = false;
1849
2041
 
1850
2042
  this._bufferedBytes = 0;
1851
- this._deflating = false;
1852
2043
  this._queue = [];
2044
+ this._state = DEFAULT;
2045
+ this.onerror = NOOP$1;
2046
+ this[kWebSocket$2] = undefined;
1853
2047
  }
1854
2048
 
1855
2049
  /**
1856
2050
  * Frames a piece of data according to the HyBi WebSocket protocol.
1857
2051
  *
1858
- * @param {Buffer} data The data to frame
2052
+ * @param {(Buffer|String)} data The data to frame
1859
2053
  * @param {Object} options Options object
1860
- * @param {Number} options.opcode The opcode
1861
- * @param {Boolean} [options.readOnly=false] Specifies whether `data` can be
1862
- * modified
1863
2054
  * @param {Boolean} [options.fin=false] Specifies whether or not to set the
1864
2055
  * FIN bit
2056
+ * @param {Function} [options.generateMask] The function used to generate the
2057
+ * masking key
1865
2058
  * @param {Boolean} [options.mask=false] Specifies whether or not to mask
1866
2059
  * `data`
2060
+ * @param {Buffer} [options.maskBuffer] The buffer used to store the masking
2061
+ * key
2062
+ * @param {Number} options.opcode The opcode
2063
+ * @param {Boolean} [options.readOnly=false] Specifies whether `data` can be
2064
+ * modified
1867
2065
  * @param {Boolean} [options.rsv1=false] Specifies whether or not to set the
1868
2066
  * RSV1 bit
1869
- * @return {Buffer[]} The framed data as a list of `Buffer` instances
2067
+ * @return {(Buffer|String)[]} The framed data
1870
2068
  * @public
1871
2069
  */
1872
2070
  static frame(data, options) {
1873
- const merge = options.mask && options.readOnly;
1874
- let offset = options.mask ? 6 : 2;
1875
- let payloadLength = data.length;
2071
+ let mask;
2072
+ let merge = false;
2073
+ let offset = 2;
2074
+ let skipMasking = false;
2075
+
2076
+ if (options.mask) {
2077
+ mask = options.maskBuffer || maskBuffer;
2078
+
2079
+ if (options.generateMask) {
2080
+ options.generateMask(mask);
2081
+ } else {
2082
+ if (randomPoolPointer === RANDOM_POOL_SIZE) {
2083
+ /* istanbul ignore else */
2084
+ if (randomPool === undefined) {
2085
+ //
2086
+ // This is lazily initialized because server-sent frames must not
2087
+ // be masked so it may never be used.
2088
+ //
2089
+ randomPool = Buffer.alloc(RANDOM_POOL_SIZE);
2090
+ }
2091
+
2092
+ randomFillSync(randomPool, 0, RANDOM_POOL_SIZE);
2093
+ randomPoolPointer = 0;
2094
+ }
2095
+
2096
+ mask[0] = randomPool[randomPoolPointer++];
2097
+ mask[1] = randomPool[randomPoolPointer++];
2098
+ mask[2] = randomPool[randomPoolPointer++];
2099
+ mask[3] = randomPool[randomPoolPointer++];
2100
+ }
1876
2101
 
1877
- if (data.length >= 65536) {
2102
+ skipMasking = (mask[0] | mask[1] | mask[2] | mask[3]) === 0;
2103
+ offset = 6;
2104
+ }
2105
+
2106
+ let dataLength;
2107
+
2108
+ if (typeof data === 'string') {
2109
+ if (
2110
+ (!options.mask || skipMasking) &&
2111
+ options[kByteLength] !== undefined
2112
+ ) {
2113
+ dataLength = options[kByteLength];
2114
+ } else {
2115
+ data = Buffer.from(data);
2116
+ dataLength = data.length;
2117
+ }
2118
+ } else {
2119
+ dataLength = data.length;
2120
+ merge = options.mask && options.readOnly && !skipMasking;
2121
+ }
2122
+
2123
+ let payloadLength = dataLength;
2124
+
2125
+ if (dataLength >= 65536) {
1878
2126
  offset += 8;
1879
2127
  payloadLength = 127;
1880
- } else if (data.length > 125) {
2128
+ } else if (dataLength > 125) {
1881
2129
  offset += 2;
1882
2130
  payloadLength = 126;
1883
2131
  }
1884
2132
 
1885
- const target = Buffer.allocUnsafe(merge ? data.length + offset : offset);
2133
+ const target = Buffer.allocUnsafe(merge ? dataLength + offset : offset);
1886
2134
 
1887
2135
  target[0] = options.fin ? options.opcode | 0x80 : options.opcode;
1888
2136
  if (options.rsv1) target[0] |= 0x40;
@@ -1890,28 +2138,28 @@ let Sender$1 = class Sender {
1890
2138
  target[1] = payloadLength;
1891
2139
 
1892
2140
  if (payloadLength === 126) {
1893
- target.writeUInt16BE(data.length, 2);
2141
+ target.writeUInt16BE(dataLength, 2);
1894
2142
  } else if (payloadLength === 127) {
1895
- target.writeUInt32BE(0, 2);
1896
- target.writeUInt32BE(data.length, 6);
2143
+ target[2] = target[3] = 0;
2144
+ target.writeUIntBE(dataLength, 4, 6);
1897
2145
  }
1898
2146
 
1899
2147
  if (!options.mask) return [target, data];
1900
2148
 
1901
- randomFillSync(mask, 0, 4);
1902
-
1903
2149
  target[1] |= 0x80;
1904
2150
  target[offset - 4] = mask[0];
1905
2151
  target[offset - 3] = mask[1];
1906
2152
  target[offset - 2] = mask[2];
1907
2153
  target[offset - 1] = mask[3];
1908
2154
 
2155
+ if (skipMasking) return [target, data];
2156
+
1909
2157
  if (merge) {
1910
- applyMask(data, mask, target, offset, data.length);
2158
+ applyMask(data, mask, target, offset, dataLength);
1911
2159
  return [target];
1912
2160
  }
1913
2161
 
1914
- applyMask(data, mask, data, 0, data.length);
2162
+ applyMask(data, mask, data, 0, dataLength);
1915
2163
  return [target, data];
1916
2164
  }
1917
2165
 
@@ -1919,7 +2167,7 @@ let Sender$1 = class Sender {
1919
2167
  * Sends a close message to the other peer.
1920
2168
  *
1921
2169
  * @param {Number} [code] The status code component of the body
1922
- * @param {String} [data] The message component of the body
2170
+ * @param {(String|Buffer)} [data] The message component of the body
1923
2171
  * @param {Boolean} [mask=false] Specifies whether or not to mask the message
1924
2172
  * @param {Function} [cb] Callback
1925
2173
  * @public
@@ -1931,7 +2179,7 @@ let Sender$1 = class Sender {
1931
2179
  buf = EMPTY_BUFFER$1;
1932
2180
  } else if (typeof code !== 'number' || !isValidStatusCode(code)) {
1933
2181
  throw new TypeError('First argument must be a valid error code number');
1934
- } else if (data === undefined || data === '') {
2182
+ } else if (data === undefined || !data.length) {
1935
2183
  buf = Buffer.allocUnsafe(2);
1936
2184
  buf.writeUInt16BE(code, 0);
1937
2185
  } else {
@@ -1943,37 +2191,32 @@ let Sender$1 = class Sender {
1943
2191
 
1944
2192
  buf = Buffer.allocUnsafe(2 + length);
1945
2193
  buf.writeUInt16BE(code, 0);
1946
- buf.write(data, 2);
2194
+
2195
+ if (typeof data === 'string') {
2196
+ buf.write(data, 2);
2197
+ } else {
2198
+ buf.set(data, 2);
2199
+ }
1947
2200
  }
1948
2201
 
1949
- if (this._deflating) {
1950
- this.enqueue([this.doClose, buf, mask, cb]);
2202
+ const options = {
2203
+ [kByteLength]: buf.length,
2204
+ fin: true,
2205
+ generateMask: this._generateMask,
2206
+ mask,
2207
+ maskBuffer: this._maskBuffer,
2208
+ opcode: 0x08,
2209
+ readOnly: false,
2210
+ rsv1: false
2211
+ };
2212
+
2213
+ if (this._state !== DEFAULT) {
2214
+ this.enqueue([this.dispatch, buf, false, options, cb]);
1951
2215
  } else {
1952
- this.doClose(buf, mask, cb);
2216
+ this.sendFrame(Sender.frame(buf, options), cb);
1953
2217
  }
1954
2218
  }
1955
2219
 
1956
- /**
1957
- * Frames and sends a close message.
1958
- *
1959
- * @param {Buffer} data The message to send
1960
- * @param {Boolean} [mask=false] Specifies whether or not to mask `data`
1961
- * @param {Function} [cb] Callback
1962
- * @private
1963
- */
1964
- doClose(data, mask, cb) {
1965
- this.sendFrame(
1966
- Sender.frame(data, {
1967
- fin: true,
1968
- rsv1: false,
1969
- opcode: 0x08,
1970
- mask,
1971
- readOnly: false
1972
- }),
1973
- cb
1974
- );
1975
- }
1976
-
1977
2220
  /**
1978
2221
  * Sends a ping message to the other peer.
1979
2222
  *
@@ -1983,41 +2226,49 @@ let Sender$1 = class Sender {
1983
2226
  * @public
1984
2227
  */
1985
2228
  ping(data, mask, cb) {
1986
- const buf = toBuffer$1(data);
2229
+ let byteLength;
2230
+ let readOnly;
2231
+
2232
+ if (typeof data === 'string') {
2233
+ byteLength = Buffer.byteLength(data);
2234
+ readOnly = false;
2235
+ } else if (isBlob$1(data)) {
2236
+ byteLength = data.size;
2237
+ readOnly = false;
2238
+ } else {
2239
+ data = toBuffer$1(data);
2240
+ byteLength = data.length;
2241
+ readOnly = toBuffer$1.readOnly;
2242
+ }
1987
2243
 
1988
- if (buf.length > 125) {
2244
+ if (byteLength > 125) {
1989
2245
  throw new RangeError('The data size must not be greater than 125 bytes');
1990
2246
  }
1991
2247
 
1992
- if (this._deflating) {
1993
- this.enqueue([this.doPing, buf, mask, toBuffer$1.readOnly, cb]);
2248
+ const options = {
2249
+ [kByteLength]: byteLength,
2250
+ fin: true,
2251
+ generateMask: this._generateMask,
2252
+ mask,
2253
+ maskBuffer: this._maskBuffer,
2254
+ opcode: 0x09,
2255
+ readOnly,
2256
+ rsv1: false
2257
+ };
2258
+
2259
+ if (isBlob$1(data)) {
2260
+ if (this._state !== DEFAULT) {
2261
+ this.enqueue([this.getBlobData, data, false, options, cb]);
2262
+ } else {
2263
+ this.getBlobData(data, false, options, cb);
2264
+ }
2265
+ } else if (this._state !== DEFAULT) {
2266
+ this.enqueue([this.dispatch, data, false, options, cb]);
1994
2267
  } else {
1995
- this.doPing(buf, mask, toBuffer$1.readOnly, cb);
2268
+ this.sendFrame(Sender.frame(data, options), cb);
1996
2269
  }
1997
2270
  }
1998
2271
 
1999
- /**
2000
- * Frames and sends a ping message.
2001
- *
2002
- * @param {Buffer} data The message to send
2003
- * @param {Boolean} [mask=false] Specifies whether or not to mask `data`
2004
- * @param {Boolean} [readOnly=false] Specifies whether `data` can be modified
2005
- * @param {Function} [cb] Callback
2006
- * @private
2007
- */
2008
- doPing(data, mask, readOnly, cb) {
2009
- this.sendFrame(
2010
- Sender.frame(data, {
2011
- fin: true,
2012
- rsv1: false,
2013
- opcode: 0x09,
2014
- mask,
2015
- readOnly
2016
- }),
2017
- cb
2018
- );
2019
- }
2020
-
2021
2272
  /**
2022
2273
  * Sends a pong message to the other peer.
2023
2274
  *
@@ -2027,50 +2278,58 @@ let Sender$1 = class Sender {
2027
2278
  * @public
2028
2279
  */
2029
2280
  pong(data, mask, cb) {
2030
- const buf = toBuffer$1(data);
2281
+ let byteLength;
2282
+ let readOnly;
2283
+
2284
+ if (typeof data === 'string') {
2285
+ byteLength = Buffer.byteLength(data);
2286
+ readOnly = false;
2287
+ } else if (isBlob$1(data)) {
2288
+ byteLength = data.size;
2289
+ readOnly = false;
2290
+ } else {
2291
+ data = toBuffer$1(data);
2292
+ byteLength = data.length;
2293
+ readOnly = toBuffer$1.readOnly;
2294
+ }
2031
2295
 
2032
- if (buf.length > 125) {
2296
+ if (byteLength > 125) {
2033
2297
  throw new RangeError('The data size must not be greater than 125 bytes');
2034
2298
  }
2035
2299
 
2036
- if (this._deflating) {
2037
- this.enqueue([this.doPong, buf, mask, toBuffer$1.readOnly, cb]);
2300
+ const options = {
2301
+ [kByteLength]: byteLength,
2302
+ fin: true,
2303
+ generateMask: this._generateMask,
2304
+ mask,
2305
+ maskBuffer: this._maskBuffer,
2306
+ opcode: 0x0a,
2307
+ readOnly,
2308
+ rsv1: false
2309
+ };
2310
+
2311
+ if (isBlob$1(data)) {
2312
+ if (this._state !== DEFAULT) {
2313
+ this.enqueue([this.getBlobData, data, false, options, cb]);
2314
+ } else {
2315
+ this.getBlobData(data, false, options, cb);
2316
+ }
2317
+ } else if (this._state !== DEFAULT) {
2318
+ this.enqueue([this.dispatch, data, false, options, cb]);
2038
2319
  } else {
2039
- this.doPong(buf, mask, toBuffer$1.readOnly, cb);
2320
+ this.sendFrame(Sender.frame(data, options), cb);
2040
2321
  }
2041
2322
  }
2042
2323
 
2043
- /**
2044
- * Frames and sends a pong message.
2045
- *
2046
- * @param {Buffer} data The message to send
2047
- * @param {Boolean} [mask=false] Specifies whether or not to mask `data`
2048
- * @param {Boolean} [readOnly=false] Specifies whether `data` can be modified
2049
- * @param {Function} [cb] Callback
2050
- * @private
2051
- */
2052
- doPong(data, mask, readOnly, cb) {
2053
- this.sendFrame(
2054
- Sender.frame(data, {
2055
- fin: true,
2056
- rsv1: false,
2057
- opcode: 0x0a,
2058
- mask,
2059
- readOnly
2060
- }),
2061
- cb
2062
- );
2063
- }
2064
-
2065
2324
  /**
2066
2325
  * Sends a data message to the other peer.
2067
2326
  *
2068
2327
  * @param {*} data The message to send
2069
2328
  * @param {Object} options Options object
2070
- * @param {Boolean} [options.compress=false] Specifies whether or not to
2071
- * compress `data`
2072
2329
  * @param {Boolean} [options.binary=false] Specifies whether `data` is binary
2073
2330
  * or text
2331
+ * @param {Boolean} [options.compress=false] Specifies whether or not to
2332
+ * compress `data`
2074
2333
  * @param {Boolean} [options.fin=false] Specifies whether the fragment is the
2075
2334
  * last one
2076
2335
  * @param {Boolean} [options.mask=false] Specifies whether or not to mask
@@ -2079,15 +2338,37 @@ let Sender$1 = class Sender {
2079
2338
  * @public
2080
2339
  */
2081
2340
  send(data, options, cb) {
2082
- const buf = toBuffer$1(data);
2083
2341
  const perMessageDeflate = this._extensions[PerMessageDeflate$2.extensionName];
2084
2342
  let opcode = options.binary ? 2 : 1;
2085
2343
  let rsv1 = options.compress;
2086
2344
 
2345
+ let byteLength;
2346
+ let readOnly;
2347
+
2348
+ if (typeof data === 'string') {
2349
+ byteLength = Buffer.byteLength(data);
2350
+ readOnly = false;
2351
+ } else if (isBlob$1(data)) {
2352
+ byteLength = data.size;
2353
+ readOnly = false;
2354
+ } else {
2355
+ data = toBuffer$1(data);
2356
+ byteLength = data.length;
2357
+ readOnly = toBuffer$1.readOnly;
2358
+ }
2359
+
2087
2360
  if (this._firstFragment) {
2088
2361
  this._firstFragment = false;
2089
- if (rsv1 && perMessageDeflate) {
2090
- rsv1 = buf.length >= perMessageDeflate._threshold;
2362
+ if (
2363
+ rsv1 &&
2364
+ perMessageDeflate &&
2365
+ perMessageDeflate.params[
2366
+ perMessageDeflate._isServer
2367
+ ? 'server_no_context_takeover'
2368
+ : 'client_no_context_takeover'
2369
+ ]
2370
+ ) {
2371
+ rsv1 = byteLength >= perMessageDeflate._threshold;
2091
2372
  }
2092
2373
  this._compress = rsv1;
2093
2374
  } else {
@@ -2097,48 +2378,112 @@ let Sender$1 = class Sender {
2097
2378
 
2098
2379
  if (options.fin) this._firstFragment = true;
2099
2380
 
2100
- if (perMessageDeflate) {
2101
- const opts = {
2102
- fin: options.fin,
2103
- rsv1,
2104
- opcode,
2105
- mask: options.mask,
2106
- readOnly: toBuffer$1.readOnly
2107
- };
2381
+ const opts = {
2382
+ [kByteLength]: byteLength,
2383
+ fin: options.fin,
2384
+ generateMask: this._generateMask,
2385
+ mask: options.mask,
2386
+ maskBuffer: this._maskBuffer,
2387
+ opcode,
2388
+ readOnly,
2389
+ rsv1
2390
+ };
2108
2391
 
2109
- if (this._deflating) {
2110
- this.enqueue([this.dispatch, buf, this._compress, opts, cb]);
2392
+ if (isBlob$1(data)) {
2393
+ if (this._state !== DEFAULT) {
2394
+ this.enqueue([this.getBlobData, data, this._compress, opts, cb]);
2111
2395
  } else {
2112
- this.dispatch(buf, this._compress, opts, cb);
2396
+ this.getBlobData(data, this._compress, opts, cb);
2113
2397
  }
2398
+ } else if (this._state !== DEFAULT) {
2399
+ this.enqueue([this.dispatch, data, this._compress, opts, cb]);
2114
2400
  } else {
2115
- this.sendFrame(
2116
- Sender.frame(buf, {
2117
- fin: options.fin,
2118
- rsv1: false,
2119
- opcode,
2120
- mask: options.mask,
2121
- readOnly: toBuffer$1.readOnly
2122
- }),
2123
- cb
2124
- );
2401
+ this.dispatch(data, this._compress, opts, cb);
2125
2402
  }
2126
2403
  }
2127
2404
 
2128
2405
  /**
2129
- * Dispatches a data message.
2406
+ * Gets the contents of a blob as binary data.
2130
2407
  *
2131
- * @param {Buffer} data The message to send
2408
+ * @param {Blob} blob The blob
2132
2409
  * @param {Boolean} [compress=false] Specifies whether or not to compress
2133
- * `data`
2410
+ * the data
2134
2411
  * @param {Object} options Options object
2412
+ * @param {Boolean} [options.fin=false] Specifies whether or not to set the
2413
+ * FIN bit
2414
+ * @param {Function} [options.generateMask] The function used to generate the
2415
+ * masking key
2416
+ * @param {Boolean} [options.mask=false] Specifies whether or not to mask
2417
+ * `data`
2418
+ * @param {Buffer} [options.maskBuffer] The buffer used to store the masking
2419
+ * key
2135
2420
  * @param {Number} options.opcode The opcode
2136
2421
  * @param {Boolean} [options.readOnly=false] Specifies whether `data` can be
2137
2422
  * modified
2423
+ * @param {Boolean} [options.rsv1=false] Specifies whether or not to set the
2424
+ * RSV1 bit
2425
+ * @param {Function} [cb] Callback
2426
+ * @private
2427
+ */
2428
+ getBlobData(blob, compress, options, cb) {
2429
+ this._bufferedBytes += options[kByteLength];
2430
+ this._state = GET_BLOB_DATA;
2431
+
2432
+ blob
2433
+ .arrayBuffer()
2434
+ .then((arrayBuffer) => {
2435
+ if (this._socket.destroyed) {
2436
+ const err = new Error(
2437
+ 'The socket was closed while the blob was being read'
2438
+ );
2439
+
2440
+ //
2441
+ // `callCallbacks` is called in the next tick to ensure that errors
2442
+ // that might be thrown in the callbacks behave like errors thrown
2443
+ // outside the promise chain.
2444
+ //
2445
+ process.nextTick(callCallbacks, this, err, cb);
2446
+ return;
2447
+ }
2448
+
2449
+ this._bufferedBytes -= options[kByteLength];
2450
+ const data = toBuffer$1(arrayBuffer);
2451
+
2452
+ if (!compress) {
2453
+ this._state = DEFAULT;
2454
+ this.sendFrame(Sender.frame(data, options), cb);
2455
+ this.dequeue();
2456
+ } else {
2457
+ this.dispatch(data, compress, options, cb);
2458
+ }
2459
+ })
2460
+ .catch((err) => {
2461
+ //
2462
+ // `onError` is called in the next tick for the same reason that
2463
+ // `callCallbacks` above is.
2464
+ //
2465
+ process.nextTick(onError, this, err, cb);
2466
+ });
2467
+ }
2468
+
2469
+ /**
2470
+ * Dispatches a message.
2471
+ *
2472
+ * @param {(Buffer|String)} data The message to send
2473
+ * @param {Boolean} [compress=false] Specifies whether or not to compress
2474
+ * `data`
2475
+ * @param {Object} options Options object
2138
2476
  * @param {Boolean} [options.fin=false] Specifies whether or not to set the
2139
2477
  * FIN bit
2478
+ * @param {Function} [options.generateMask] The function used to generate the
2479
+ * masking key
2140
2480
  * @param {Boolean} [options.mask=false] Specifies whether or not to mask
2141
2481
  * `data`
2482
+ * @param {Buffer} [options.maskBuffer] The buffer used to store the masking
2483
+ * key
2484
+ * @param {Number} options.opcode The opcode
2485
+ * @param {Boolean} [options.readOnly=false] Specifies whether `data` can be
2486
+ * modified
2142
2487
  * @param {Boolean} [options.rsv1=false] Specifies whether or not to set the
2143
2488
  * RSV1 bit
2144
2489
  * @param {Function} [cb] Callback
@@ -2152,27 +2497,20 @@ let Sender$1 = class Sender {
2152
2497
 
2153
2498
  const perMessageDeflate = this._extensions[PerMessageDeflate$2.extensionName];
2154
2499
 
2155
- this._bufferedBytes += data.length;
2156
- this._deflating = true;
2500
+ this._bufferedBytes += options[kByteLength];
2501
+ this._state = DEFLATING;
2157
2502
  perMessageDeflate.compress(data, options.fin, (_, buf) => {
2158
2503
  if (this._socket.destroyed) {
2159
2504
  const err = new Error(
2160
2505
  'The socket was closed while data was being compressed'
2161
2506
  );
2162
2507
 
2163
- if (typeof cb === 'function') cb(err);
2164
-
2165
- for (let i = 0; i < this._queue.length; i++) {
2166
- const callback = this._queue[i][4];
2167
-
2168
- if (typeof callback === 'function') callback(err);
2169
- }
2170
-
2508
+ callCallbacks(this, err, cb);
2171
2509
  return;
2172
2510
  }
2173
2511
 
2174
- this._bufferedBytes -= data.length;
2175
- this._deflating = false;
2512
+ this._bufferedBytes -= options[kByteLength];
2513
+ this._state = DEFAULT;
2176
2514
  options.readOnly = false;
2177
2515
  this.sendFrame(Sender.frame(buf, options), cb);
2178
2516
  this.dequeue();
@@ -2185,10 +2523,10 @@ let Sender$1 = class Sender {
2185
2523
  * @private
2186
2524
  */
2187
2525
  dequeue() {
2188
- while (!this._deflating && this._queue.length) {
2526
+ while (this._state === DEFAULT && this._queue.length) {
2189
2527
  const params = this._queue.shift();
2190
2528
 
2191
- this._bufferedBytes -= params[1].length;
2529
+ this._bufferedBytes -= params[3][kByteLength];
2192
2530
  Reflect.apply(params[0], this, params.slice(1));
2193
2531
  }
2194
2532
  }
@@ -2200,14 +2538,14 @@ let Sender$1 = class Sender {
2200
2538
  * @private
2201
2539
  */
2202
2540
  enqueue(params) {
2203
- this._bufferedBytes += params[1].length;
2541
+ this._bufferedBytes += params[3][kByteLength];
2204
2542
  this._queue.push(params);
2205
2543
  }
2206
2544
 
2207
2545
  /**
2208
2546
  * Sends a frame.
2209
2547
  *
2210
- * @param {Buffer[]} list The frame to send
2548
+ * @param {(Buffer | String)[]} list The frame to send
2211
2549
  * @param {Function} [cb] Callback
2212
2550
  * @private
2213
2551
  */
@@ -2226,111 +2564,204 @@ let Sender$1 = class Sender {
2226
2564
  var sender = Sender$1;
2227
2565
 
2228
2566
  /**
2229
- * Class representing an event.
2567
+ * Calls queued callbacks with an error.
2230
2568
  *
2569
+ * @param {Sender} sender The `Sender` instance
2570
+ * @param {Error} err The error to call the callbacks with
2571
+ * @param {Function} [cb] The first callback
2231
2572
  * @private
2232
2573
  */
2233
- class Event {
2234
- /**
2235
- * Create a new `Event`.
2236
- *
2237
- * @param {String} type The name of the event
2238
- * @param {Object} target A reference to the target to which the event was
2239
- * dispatched
2240
- */
2241
- constructor(type, target) {
2242
- this.target = target;
2243
- this.type = type;
2574
+ function callCallbacks(sender, err, cb) {
2575
+ if (typeof cb === 'function') cb(err);
2576
+
2577
+ for (let i = 0; i < sender._queue.length; i++) {
2578
+ const params = sender._queue[i];
2579
+ const callback = params[params.length - 1];
2580
+
2581
+ if (typeof callback === 'function') callback(err);
2244
2582
  }
2245
2583
  }
2246
2584
 
2247
2585
  /**
2248
- * Class representing a message event.
2586
+ * Handles a `Sender` error.
2249
2587
  *
2250
- * @extends Event
2588
+ * @param {Sender} sender The `Sender` instance
2589
+ * @param {Error} err The error
2590
+ * @param {Function} [cb] The first pending callback
2251
2591
  * @private
2252
2592
  */
2253
- class MessageEvent extends Event {
2593
+ function onError(sender, err, cb) {
2594
+ callCallbacks(sender, err, cb);
2595
+ sender.onerror(err);
2596
+ }
2597
+
2598
+ const { kForOnEventAttribute: kForOnEventAttribute$1, kListener: kListener$1 } = constants;
2599
+
2600
+ const kCode = Symbol('kCode');
2601
+ const kData = Symbol('kData');
2602
+ const kError = Symbol('kError');
2603
+ const kMessage = Symbol('kMessage');
2604
+ const kReason = Symbol('kReason');
2605
+ const kTarget = Symbol('kTarget');
2606
+ const kType = Symbol('kType');
2607
+ const kWasClean = Symbol('kWasClean');
2608
+
2609
+ /**
2610
+ * Class representing an event.
2611
+ */
2612
+ class Event {
2254
2613
  /**
2255
- * Create a new `MessageEvent`.
2614
+ * Create a new `Event`.
2256
2615
  *
2257
- * @param {(String|Buffer|ArrayBuffer|Buffer[])} data The received data
2258
- * @param {WebSocket} target A reference to the target to which the event was
2259
- * dispatched
2616
+ * @param {String} type The name of the event
2617
+ * @throws {TypeError} If the `type` argument is not specified
2260
2618
  */
2261
- constructor(data, target) {
2262
- super('message', target);
2619
+ constructor(type) {
2620
+ this[kTarget] = null;
2621
+ this[kType] = type;
2622
+ }
2263
2623
 
2264
- this.data = data;
2624
+ /**
2625
+ * @type {*}
2626
+ */
2627
+ get target() {
2628
+ return this[kTarget];
2629
+ }
2630
+
2631
+ /**
2632
+ * @type {String}
2633
+ */
2634
+ get type() {
2635
+ return this[kType];
2265
2636
  }
2266
2637
  }
2267
2638
 
2639
+ Object.defineProperty(Event.prototype, 'target', { enumerable: true });
2640
+ Object.defineProperty(Event.prototype, 'type', { enumerable: true });
2641
+
2268
2642
  /**
2269
2643
  * Class representing a close event.
2270
2644
  *
2271
2645
  * @extends Event
2272
- * @private
2273
2646
  */
2274
2647
  class CloseEvent extends Event {
2275
2648
  /**
2276
2649
  * Create a new `CloseEvent`.
2277
2650
  *
2278
- * @param {Number} code The status code explaining why the connection is being
2279
- * closed
2280
- * @param {String} reason A human-readable string explaining why the
2281
- * connection is closing
2282
- * @param {WebSocket} target A reference to the target to which the event was
2283
- * dispatched
2651
+ * @param {String} type The name of the event
2652
+ * @param {Object} [options] A dictionary object that allows for setting
2653
+ * attributes via object members of the same name
2654
+ * @param {Number} [options.code=0] The status code explaining why the
2655
+ * connection was closed
2656
+ * @param {String} [options.reason=''] A human-readable string explaining why
2657
+ * the connection was closed
2658
+ * @param {Boolean} [options.wasClean=false] Indicates whether or not the
2659
+ * connection was cleanly closed
2284
2660
  */
2285
- constructor(code, reason, target) {
2286
- super('close', target);
2661
+ constructor(type, options = {}) {
2662
+ super(type);
2287
2663
 
2288
- this.wasClean = target._closeFrameReceived && target._closeFrameSent;
2289
- this.reason = reason;
2290
- this.code = code;
2664
+ this[kCode] = options.code === undefined ? 0 : options.code;
2665
+ this[kReason] = options.reason === undefined ? '' : options.reason;
2666
+ this[kWasClean] = options.wasClean === undefined ? false : options.wasClean;
2667
+ }
2668
+
2669
+ /**
2670
+ * @type {Number}
2671
+ */
2672
+ get code() {
2673
+ return this[kCode];
2674
+ }
2675
+
2676
+ /**
2677
+ * @type {String}
2678
+ */
2679
+ get reason() {
2680
+ return this[kReason];
2681
+ }
2682
+
2683
+ /**
2684
+ * @type {Boolean}
2685
+ */
2686
+ get wasClean() {
2687
+ return this[kWasClean];
2291
2688
  }
2292
2689
  }
2293
2690
 
2691
+ Object.defineProperty(CloseEvent.prototype, 'code', { enumerable: true });
2692
+ Object.defineProperty(CloseEvent.prototype, 'reason', { enumerable: true });
2693
+ Object.defineProperty(CloseEvent.prototype, 'wasClean', { enumerable: true });
2694
+
2294
2695
  /**
2295
- * Class representing an open event.
2696
+ * Class representing an error event.
2296
2697
  *
2297
2698
  * @extends Event
2298
- * @private
2299
2699
  */
2300
- class OpenEvent extends Event {
2700
+ class ErrorEvent extends Event {
2301
2701
  /**
2302
- * Create a new `OpenEvent`.
2702
+ * Create a new `ErrorEvent`.
2303
2703
  *
2304
- * @param {WebSocket} target A reference to the target to which the event was
2305
- * dispatched
2704
+ * @param {String} type The name of the event
2705
+ * @param {Object} [options] A dictionary object that allows for setting
2706
+ * attributes via object members of the same name
2707
+ * @param {*} [options.error=null] The error that generated this event
2708
+ * @param {String} [options.message=''] The error message
2709
+ */
2710
+ constructor(type, options = {}) {
2711
+ super(type);
2712
+
2713
+ this[kError] = options.error === undefined ? null : options.error;
2714
+ this[kMessage] = options.message === undefined ? '' : options.message;
2715
+ }
2716
+
2717
+ /**
2718
+ * @type {*}
2306
2719
  */
2307
- constructor(target) {
2308
- super('open', target);
2720
+ get error() {
2721
+ return this[kError];
2722
+ }
2723
+
2724
+ /**
2725
+ * @type {String}
2726
+ */
2727
+ get message() {
2728
+ return this[kMessage];
2309
2729
  }
2310
2730
  }
2311
2731
 
2732
+ Object.defineProperty(ErrorEvent.prototype, 'error', { enumerable: true });
2733
+ Object.defineProperty(ErrorEvent.prototype, 'message', { enumerable: true });
2734
+
2312
2735
  /**
2313
- * Class representing an error event.
2736
+ * Class representing a message event.
2314
2737
  *
2315
2738
  * @extends Event
2316
- * @private
2317
2739
  */
2318
- class ErrorEvent extends Event {
2740
+ class MessageEvent extends Event {
2319
2741
  /**
2320
- * Create a new `ErrorEvent`.
2742
+ * Create a new `MessageEvent`.
2321
2743
  *
2322
- * @param {Object} error The error that generated this event
2323
- * @param {WebSocket} target A reference to the target to which the event was
2324
- * dispatched
2744
+ * @param {String} type The name of the event
2745
+ * @param {Object} [options] A dictionary object that allows for setting
2746
+ * attributes via object members of the same name
2747
+ * @param {*} [options.data=null] The message content
2325
2748
  */
2326
- constructor(error, target) {
2327
- super('error', target);
2749
+ constructor(type, options = {}) {
2750
+ super(type);
2328
2751
 
2329
- this.message = error.message;
2330
- this.error = error;
2752
+ this[kData] = options.data === undefined ? null : options.data;
2753
+ }
2754
+
2755
+ /**
2756
+ * @type {*}
2757
+ */
2758
+ get data() {
2759
+ return this[kData];
2331
2760
  }
2332
2761
  }
2333
2762
 
2763
+ Object.defineProperty(MessageEvent.prototype, 'data', { enumerable: true });
2764
+
2334
2765
  /**
2335
2766
  * This provides methods for emulating the `EventTarget` interface. It's not
2336
2767
  * meant to be used directly.
@@ -2342,49 +2773,75 @@ const EventTarget = {
2342
2773
  * Register an event listener.
2343
2774
  *
2344
2775
  * @param {String} type A string representing the event type to listen for
2345
- * @param {Function} listener The listener to add
2776
+ * @param {(Function|Object)} handler The listener to add
2346
2777
  * @param {Object} [options] An options object specifies characteristics about
2347
2778
  * the event listener
2348
- * @param {Boolean} [options.once=false] A `Boolean`` indicating that the
2779
+ * @param {Boolean} [options.once=false] A `Boolean` indicating that the
2349
2780
  * listener should be invoked at most once after being added. If `true`,
2350
2781
  * the listener would be automatically removed when invoked.
2351
2782
  * @public
2352
2783
  */
2353
- addEventListener(type, listener, options) {
2354
- if (typeof listener !== 'function') return;
2355
-
2356
- function onMessage(data) {
2357
- listener.call(this, new MessageEvent(data, this));
2358
- }
2359
-
2360
- function onClose(code, message) {
2361
- listener.call(this, new CloseEvent(code, message, this));
2362
- }
2363
-
2364
- function onError(error) {
2365
- listener.call(this, new ErrorEvent(error, this));
2366
- }
2367
-
2368
- function onOpen() {
2369
- listener.call(this, new OpenEvent(this));
2784
+ addEventListener(type, handler, options = {}) {
2785
+ for (const listener of this.listeners(type)) {
2786
+ if (
2787
+ !options[kForOnEventAttribute$1] &&
2788
+ listener[kListener$1] === handler &&
2789
+ !listener[kForOnEventAttribute$1]
2790
+ ) {
2791
+ return;
2792
+ }
2370
2793
  }
2371
2794
 
2372
- const method = options && options.once ? 'once' : 'on';
2795
+ let wrapper;
2373
2796
 
2374
2797
  if (type === 'message') {
2375
- onMessage._listener = listener;
2376
- this[method](type, onMessage);
2798
+ wrapper = function onMessage(data, isBinary) {
2799
+ const event = new MessageEvent('message', {
2800
+ data: isBinary ? data : data.toString()
2801
+ });
2802
+
2803
+ event[kTarget] = this;
2804
+ callListener(handler, this, event);
2805
+ };
2377
2806
  } else if (type === 'close') {
2378
- onClose._listener = listener;
2379
- this[method](type, onClose);
2807
+ wrapper = function onClose(code, message) {
2808
+ const event = new CloseEvent('close', {
2809
+ code,
2810
+ reason: message.toString(),
2811
+ wasClean: this._closeFrameReceived && this._closeFrameSent
2812
+ });
2813
+
2814
+ event[kTarget] = this;
2815
+ callListener(handler, this, event);
2816
+ };
2380
2817
  } else if (type === 'error') {
2381
- onError._listener = listener;
2382
- this[method](type, onError);
2818
+ wrapper = function onError(error) {
2819
+ const event = new ErrorEvent('error', {
2820
+ error,
2821
+ message: error.message
2822
+ });
2823
+
2824
+ event[kTarget] = this;
2825
+ callListener(handler, this, event);
2826
+ };
2383
2827
  } else if (type === 'open') {
2384
- onOpen._listener = listener;
2385
- this[method](type, onOpen);
2828
+ wrapper = function onOpen() {
2829
+ const event = new Event('open');
2830
+
2831
+ event[kTarget] = this;
2832
+ callListener(handler, this, event);
2833
+ };
2386
2834
  } else {
2387
- this[method](type, listener);
2835
+ return;
2836
+ }
2837
+
2838
+ wrapper[kForOnEventAttribute$1] = !!options[kForOnEventAttribute$1];
2839
+ wrapper[kListener$1] = handler;
2840
+
2841
+ if (options.once) {
2842
+ this.once(type, wrapper);
2843
+ } else {
2844
+ this.on(type, wrapper);
2388
2845
  }
2389
2846
  },
2390
2847
 
@@ -2392,44 +2849,39 @@ const EventTarget = {
2392
2849
  * Remove an event listener.
2393
2850
  *
2394
2851
  * @param {String} type A string representing the event type to remove
2395
- * @param {Function} listener The listener to remove
2852
+ * @param {(Function|Object)} handler The listener to remove
2396
2853
  * @public
2397
2854
  */
2398
- removeEventListener(type, listener) {
2399
- const listeners = this.listeners(type);
2400
-
2401
- for (let i = 0; i < listeners.length; i++) {
2402
- if (listeners[i] === listener || listeners[i]._listener === listener) {
2403
- this.removeListener(type, listeners[i]);
2855
+ removeEventListener(type, handler) {
2856
+ for (const listener of this.listeners(type)) {
2857
+ if (listener[kListener$1] === handler && !listener[kForOnEventAttribute$1]) {
2858
+ this.removeListener(type, listener);
2859
+ break;
2404
2860
  }
2405
2861
  }
2406
2862
  }
2407
2863
  };
2408
2864
 
2409
- var eventTarget = EventTarget;
2865
+ var eventTarget = {
2866
+ EventTarget};
2410
2867
 
2411
- //
2412
- // Allowed token characters:
2413
- //
2414
- // '!', '#', '$', '%', '&', ''', '*', '+', '-',
2415
- // '.', 0-9, A-Z, '^', '_', '`', a-z, '|', '~'
2416
- //
2417
- // tokenChars[32] === 0 // ' '
2418
- // tokenChars[33] === 1 // '!'
2419
- // tokenChars[34] === 0 // '"'
2420
- // ...
2421
- //
2422
- // prettier-ignore
2423
- const tokenChars = [
2424
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 - 15
2425
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 - 31
2426
- 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, // 32 - 47
2427
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 48 - 63
2428
- 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64 - 79
2429
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, // 80 - 95
2430
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96 - 111
2431
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0 // 112 - 127
2432
- ];
2868
+ /**
2869
+ * Call an event listener
2870
+ *
2871
+ * @param {(Function|Object)} listener The listener to call
2872
+ * @param {*} thisArg The value to use as `this`` when calling the listener
2873
+ * @param {Event} event The event to pass to the listener
2874
+ * @private
2875
+ */
2876
+ function callListener(listener, thisArg, event) {
2877
+ if (typeof listener === 'object' && listener.handleEvent) {
2878
+ listener.handleEvent.call(listener, event);
2879
+ } else {
2880
+ listener.call(thisArg, event);
2881
+ }
2882
+ }
2883
+
2884
+ const { tokenChars: tokenChars$1 } = validationExports;
2433
2885
 
2434
2886
  /**
2435
2887
  * Adds an offer to the map of extension offers or a parameter to the map of
@@ -2455,9 +2907,6 @@ function push(dest, name, elem) {
2455
2907
  */
2456
2908
  function parse$2(header) {
2457
2909
  const offers = Object.create(null);
2458
-
2459
- if (header === undefined || header === '') return offers;
2460
-
2461
2910
  let params = Object.create(null);
2462
2911
  let mustUnescape = false;
2463
2912
  let isEscaping = false;
@@ -2465,16 +2914,20 @@ function parse$2(header) {
2465
2914
  let extensionName;
2466
2915
  let paramName;
2467
2916
  let start = -1;
2917
+ let code = -1;
2468
2918
  let end = -1;
2469
2919
  let i = 0;
2470
2920
 
2471
2921
  for (; i < header.length; i++) {
2472
- const code = header.charCodeAt(i);
2922
+ code = header.charCodeAt(i);
2473
2923
 
2474
2924
  if (extensionName === undefined) {
2475
- if (end === -1 && tokenChars[code] === 1) {
2925
+ if (end === -1 && tokenChars$1[code] === 1) {
2476
2926
  if (start === -1) start = i;
2477
- } else if (code === 0x20 /* ' ' */ || code === 0x09 /* '\t' */) {
2927
+ } else if (
2928
+ i !== 0 &&
2929
+ (code === 0x20 /* ' ' */ || code === 0x09) /* '\t' */
2930
+ ) {
2478
2931
  if (end === -1 && start !== -1) end = i;
2479
2932
  } else if (code === 0x3b /* ';' */ || code === 0x2c /* ',' */) {
2480
2933
  if (start === -1) {
@@ -2495,7 +2948,7 @@ function parse$2(header) {
2495
2948
  throw new SyntaxError(`Unexpected character at index ${i}`);
2496
2949
  }
2497
2950
  } else if (paramName === undefined) {
2498
- if (end === -1 && tokenChars[code] === 1) {
2951
+ if (end === -1 && tokenChars$1[code] === 1) {
2499
2952
  if (start === -1) start = i;
2500
2953
  } else if (code === 0x20 || code === 0x09) {
2501
2954
  if (end === -1 && start !== -1) end = i;
@@ -2526,14 +2979,14 @@ function parse$2(header) {
2526
2979
  // Ref: https://tools.ietf.org/html/rfc6455#section-9.1
2527
2980
  //
2528
2981
  if (isEscaping) {
2529
- if (tokenChars[code] !== 1) {
2982
+ if (tokenChars$1[code] !== 1) {
2530
2983
  throw new SyntaxError(`Unexpected character at index ${i}`);
2531
2984
  }
2532
2985
  if (start === -1) start = i;
2533
2986
  else if (!mustUnescape) mustUnescape = true;
2534
2987
  isEscaping = false;
2535
2988
  } else if (inQuotes) {
2536
- if (tokenChars[code] === 1) {
2989
+ if (tokenChars$1[code] === 1) {
2537
2990
  if (start === -1) start = i;
2538
2991
  } else if (code === 0x22 /* '"' */ && start !== -1) {
2539
2992
  inQuotes = false;
@@ -2545,7 +2998,7 @@ function parse$2(header) {
2545
2998
  }
2546
2999
  } else if (code === 0x22 && header.charCodeAt(i - 1) === 0x3d) {
2547
3000
  inQuotes = true;
2548
- } else if (end === -1 && tokenChars[code] === 1) {
3001
+ } else if (end === -1 && tokenChars$1[code] === 1) {
2549
3002
  if (start === -1) start = i;
2550
3003
  } else if (start !== -1 && (code === 0x20 || code === 0x09)) {
2551
3004
  if (end === -1) end = i;
@@ -2575,7 +3028,7 @@ function parse$2(header) {
2575
3028
  }
2576
3029
  }
2577
3030
 
2578
- if (start === -1 || inQuotes) {
3031
+ if (start === -1 || inQuotes || code === 0x20 || code === 0x09) {
2579
3032
  throw new SyntaxError('Unexpected end of input');
2580
3033
  }
2581
3034
 
@@ -2604,7 +3057,7 @@ function parse$2(header) {
2604
3057
  * @return {String} A string representing the given object
2605
3058
  * @public
2606
3059
  */
2607
- function format$2(extensions) {
3060
+ function format$1(extensions) {
2608
3061
  return Object.keys(extensions)
2609
3062
  .map((extension) => {
2610
3063
  let configurations = extensions[extension];
@@ -2628,44 +3081,52 @@ function format$2(extensions) {
2628
3081
  .join(', ');
2629
3082
  }
2630
3083
 
2631
- var extension = { format: format$2, parse: parse$2 };
3084
+ var extension$1 = { format: format$1, parse: parse$2 };
2632
3085
 
2633
- /* eslint no-unused-vars: ["error", { "varsIgnorePattern": "^Readable$" }] */
3086
+ /* eslint no-unused-vars: ["error", { "varsIgnorePattern": "^Duplex|Readable$", "caughtErrors": "none" }] */
2634
3087
 
2635
- const EventEmitter$1 = require$$0$3;
2636
- const https = require$$1$1;
3088
+ const EventEmitter$1 = require$$0$4;
3089
+ const https = require$$1$2;
2637
3090
  const http$1 = require$$2$1;
2638
3091
  const net = require$$3;
2639
3092
  const tls = require$$4;
2640
- const { randomBytes, createHash: createHash$1 } = require$$5;
2641
- const { Readable } = require$$0$2;
3093
+ const { randomBytes, createHash: createHash$1 } = require$$1$1;
3094
+ const { Duplex: Duplex$2, Readable } = require$$0$3;
2642
3095
  const { URL } = require$$7;
2643
3096
 
2644
3097
  const PerMessageDeflate$1 = permessageDeflate;
2645
3098
  const Receiver = receiver;
2646
3099
  const Sender = sender;
3100
+ const { isBlob } = validationExports;
3101
+
2647
3102
  const {
2648
3103
  BINARY_TYPES,
3104
+ CLOSE_TIMEOUT: CLOSE_TIMEOUT$1,
2649
3105
  EMPTY_BUFFER,
2650
3106
  GUID: GUID$1,
3107
+ kForOnEventAttribute,
3108
+ kListener,
2651
3109
  kStatusCode,
2652
3110
  kWebSocket: kWebSocket$1,
2653
3111
  NOOP
2654
3112
  } = constants;
2655
- const { addEventListener, removeEventListener } = eventTarget;
2656
- const { format: format$1, parse: parse$1 } = extension;
3113
+ const {
3114
+ EventTarget: { addEventListener, removeEventListener }
3115
+ } = eventTarget;
3116
+ const { format, parse: parse$1 } = extension$1;
2657
3117
  const { toBuffer } = bufferUtilExports;
2658
3118
 
2659
- const readyStates = ['CONNECTING', 'OPEN', 'CLOSING', 'CLOSED'];
3119
+ const kAborted = Symbol('kAborted');
2660
3120
  const protocolVersions = [8, 13];
2661
- const closeTimeout = 30 * 1000;
3121
+ const readyStates = ['CONNECTING', 'OPEN', 'CLOSING', 'CLOSED'];
3122
+ const subprotocolRegex = /^[!#$%&'*+\-.0-9A-Z^_`|a-z~]+$/;
2662
3123
 
2663
3124
  /**
2664
3125
  * Class representing a WebSocket.
2665
3126
  *
2666
3127
  * @extends EventEmitter
2667
3128
  */
2668
- let WebSocket$2 = class WebSocket extends EventEmitter$1 {
3129
+ let WebSocket$1 = class WebSocket extends EventEmitter$1 {
2669
3130
  /**
2670
3131
  * Create a new `WebSocket`.
2671
3132
  *
@@ -2680,9 +3141,11 @@ let WebSocket$2 = class WebSocket extends EventEmitter$1 {
2680
3141
  this._closeCode = 1006;
2681
3142
  this._closeFrameReceived = false;
2682
3143
  this._closeFrameSent = false;
2683
- this._closeMessage = '';
3144
+ this._closeMessage = EMPTY_BUFFER;
2684
3145
  this._closeTimer = null;
3146
+ this._errorEmitted = false;
2685
3147
  this._extensions = {};
3148
+ this._paused = false;
2686
3149
  this._protocol = '';
2687
3150
  this._readyState = WebSocket.CONNECTING;
2688
3151
  this._receiver = null;
@@ -2694,23 +3157,28 @@ let WebSocket$2 = class WebSocket extends EventEmitter$1 {
2694
3157
  this._isServer = false;
2695
3158
  this._redirects = 0;
2696
3159
 
2697
- if (Array.isArray(protocols)) {
2698
- protocols = protocols.join(', ');
2699
- } else if (typeof protocols === 'object' && protocols !== null) {
2700
- options = protocols;
2701
- protocols = undefined;
3160
+ if (protocols === undefined) {
3161
+ protocols = [];
3162
+ } else if (!Array.isArray(protocols)) {
3163
+ if (typeof protocols === 'object' && protocols !== null) {
3164
+ options = protocols;
3165
+ protocols = [];
3166
+ } else {
3167
+ protocols = [protocols];
3168
+ }
2702
3169
  }
2703
3170
 
2704
3171
  initAsClient(this, address, protocols, options);
2705
3172
  } else {
3173
+ this._autoPong = options.autoPong;
3174
+ this._closeTimeout = options.closeTimeout;
2706
3175
  this._isServer = true;
2707
3176
  }
2708
3177
  }
2709
3178
 
2710
3179
  /**
2711
- * This deviates from the WHATWG interface since ws doesn't support the
2712
- * required default "blob" type (instead we define a custom "nodebuffer"
2713
- * type).
3180
+ * For historical reasons, the custom "nodebuffer" type is used by the default
3181
+ * instead of "blob".
2714
3182
  *
2715
3183
  * @type {String}
2716
3184
  */
@@ -2745,50 +3213,45 @@ let WebSocket$2 = class WebSocket extends EventEmitter$1 {
2745
3213
  return Object.keys(this._extensions).join();
2746
3214
  }
2747
3215
 
3216
+ /**
3217
+ * @type {Boolean}
3218
+ */
3219
+ get isPaused() {
3220
+ return this._paused;
3221
+ }
3222
+
2748
3223
  /**
2749
3224
  * @type {Function}
2750
3225
  */
2751
3226
  /* istanbul ignore next */
2752
3227
  get onclose() {
2753
- return undefined;
3228
+ return null;
2754
3229
  }
2755
3230
 
2756
- /* istanbul ignore next */
2757
- set onclose(listener) {}
2758
-
2759
3231
  /**
2760
3232
  * @type {Function}
2761
3233
  */
2762
3234
  /* istanbul ignore next */
2763
3235
  get onerror() {
2764
- return undefined;
3236
+ return null;
2765
3237
  }
2766
3238
 
2767
- /* istanbul ignore next */
2768
- set onerror(listener) {}
2769
-
2770
3239
  /**
2771
3240
  * @type {Function}
2772
3241
  */
2773
3242
  /* istanbul ignore next */
2774
3243
  get onopen() {
2775
- return undefined;
3244
+ return null;
2776
3245
  }
2777
3246
 
2778
- /* istanbul ignore next */
2779
- set onopen(listener) {}
2780
-
2781
3247
  /**
2782
3248
  * @type {Function}
2783
3249
  */
2784
3250
  /* istanbul ignore next */
2785
3251
  get onmessage() {
2786
- return undefined;
3252
+ return null;
2787
3253
  }
2788
3254
 
2789
- /* istanbul ignore next */
2790
- set onmessage(listener) {}
2791
-
2792
3255
  /**
2793
3256
  * @type {String}
2794
3257
  */
@@ -2813,25 +3276,37 @@ let WebSocket$2 = class WebSocket extends EventEmitter$1 {
2813
3276
  /**
2814
3277
  * Set up the socket and the internal resources.
2815
3278
  *
2816
- * @param {(net.Socket|tls.Socket)} socket The network socket between the
2817
- * server and client
3279
+ * @param {Duplex} socket The network socket between the server and client
2818
3280
  * @param {Buffer} head The first packet of the upgraded stream
2819
- * @param {Number} [maxPayload=0] The maximum allowed message size
3281
+ * @param {Object} options Options object
3282
+ * @param {Boolean} [options.allowSynchronousEvents=false] Specifies whether
3283
+ * any of the `'message'`, `'ping'`, and `'pong'` events can be emitted
3284
+ * multiple times in the same tick
3285
+ * @param {Function} [options.generateMask] The function used to generate the
3286
+ * masking key
3287
+ * @param {Number} [options.maxPayload=0] The maximum allowed message size
3288
+ * @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or
3289
+ * not to skip UTF-8 validation for text and close messages
2820
3290
  * @private
2821
3291
  */
2822
- setSocket(socket, head, maxPayload) {
2823
- const receiver = new Receiver(
2824
- this.binaryType,
2825
- this._extensions,
2826
- this._isServer,
2827
- maxPayload
2828
- );
3292
+ setSocket(socket, head, options) {
3293
+ const receiver = new Receiver({
3294
+ allowSynchronousEvents: options.allowSynchronousEvents,
3295
+ binaryType: this.binaryType,
3296
+ extensions: this._extensions,
3297
+ isServer: this._isServer,
3298
+ maxPayload: options.maxPayload,
3299
+ skipUTF8Validation: options.skipUTF8Validation
3300
+ });
3301
+
3302
+ const sender = new Sender(socket, this._extensions, options.generateMask);
2829
3303
 
2830
- this._sender = new Sender(socket, this._extensions);
2831
3304
  this._receiver = receiver;
3305
+ this._sender = sender;
2832
3306
  this._socket = socket;
2833
3307
 
2834
3308
  receiver[kWebSocket$1] = this;
3309
+ sender[kWebSocket$1] = this;
2835
3310
  socket[kWebSocket$1] = this;
2836
3311
 
2837
3312
  receiver.on('conclude', receiverOnConclude);
@@ -2841,8 +3316,13 @@ let WebSocket$2 = class WebSocket extends EventEmitter$1 {
2841
3316
  receiver.on('ping', receiverOnPing);
2842
3317
  receiver.on('pong', receiverOnPong);
2843
3318
 
2844
- socket.setTimeout(0);
2845
- socket.setNoDelay();
3319
+ sender.onerror = senderOnError;
3320
+
3321
+ //
3322
+ // These methods may not be available if `socket` is just a `Duplex`.
3323
+ //
3324
+ if (socket.setTimeout) socket.setTimeout(0);
3325
+ if (socket.setNoDelay) socket.setNoDelay();
2846
3326
 
2847
3327
  if (head.length > 0) socket.unshift(head);
2848
3328
 
@@ -2892,14 +3372,16 @@ let WebSocket$2 = class WebSocket extends EventEmitter$1 {
2892
3372
  * +---+
2893
3373
  *
2894
3374
  * @param {Number} [code] Status code explaining why the connection is closing
2895
- * @param {String} [data] A string explaining why the connection is closing
3375
+ * @param {(String|Buffer)} [data] The reason why the connection is
3376
+ * closing
2896
3377
  * @public
2897
3378
  */
2898
3379
  close(code, data) {
2899
3380
  if (this.readyState === WebSocket.CLOSED) return;
2900
3381
  if (this.readyState === WebSocket.CONNECTING) {
2901
3382
  const msg = 'WebSocket was closed before the connection was established';
2902
- return abortHandshake$1(this, this._req, msg);
3383
+ abortHandshake$1(this, this._req, msg);
3384
+ return;
2903
3385
  }
2904
3386
 
2905
3387
  if (this.readyState === WebSocket.CLOSING) {
@@ -2931,13 +3413,24 @@ let WebSocket$2 = class WebSocket extends EventEmitter$1 {
2931
3413
  }
2932
3414
  });
2933
3415
 
2934
- //
2935
- // Specify a timeout for the closing handshake to complete.
2936
- //
2937
- this._closeTimer = setTimeout(
2938
- this._socket.destroy.bind(this._socket),
2939
- closeTimeout
2940
- );
3416
+ setCloseTimer(this);
3417
+ }
3418
+
3419
+ /**
3420
+ * Pause the socket.
3421
+ *
3422
+ * @public
3423
+ */
3424
+ pause() {
3425
+ if (
3426
+ this.readyState === WebSocket.CONNECTING ||
3427
+ this.readyState === WebSocket.CLOSED
3428
+ ) {
3429
+ return;
3430
+ }
3431
+
3432
+ this._paused = true;
3433
+ this._socket.pause();
2941
3434
  }
2942
3435
 
2943
3436
  /**
@@ -3004,15 +3497,32 @@ let WebSocket$2 = class WebSocket extends EventEmitter$1 {
3004
3497
  this._sender.pong(data || EMPTY_BUFFER, mask, cb);
3005
3498
  }
3006
3499
 
3500
+ /**
3501
+ * Resume the socket.
3502
+ *
3503
+ * @public
3504
+ */
3505
+ resume() {
3506
+ if (
3507
+ this.readyState === WebSocket.CONNECTING ||
3508
+ this.readyState === WebSocket.CLOSED
3509
+ ) {
3510
+ return;
3511
+ }
3512
+
3513
+ this._paused = false;
3514
+ if (!this._receiver._writableState.needDrain) this._socket.resume();
3515
+ }
3516
+
3007
3517
  /**
3008
3518
  * Send a data message.
3009
3519
  *
3010
3520
  * @param {*} data The message to send
3011
3521
  * @param {Object} [options] Options object
3012
- * @param {Boolean} [options.compress] Specifies whether or not to compress
3013
- * `data`
3014
3522
  * @param {Boolean} [options.binary] Specifies whether `data` is binary or
3015
3523
  * text
3524
+ * @param {Boolean} [options.compress] Specifies whether or not to compress
3525
+ * `data`
3016
3526
  * @param {Boolean} [options.fin=true] Specifies whether the fragment is the
3017
3527
  * last one
3018
3528
  * @param {Boolean} [options.mask] Specifies whether or not to mask `data`
@@ -3060,7 +3570,8 @@ let WebSocket$2 = class WebSocket extends EventEmitter$1 {
3060
3570
  if (this.readyState === WebSocket.CLOSED) return;
3061
3571
  if (this.readyState === WebSocket.CONNECTING) {
3062
3572
  const msg = 'WebSocket was closed before the connection was established';
3063
- return abortHandshake$1(this, this._req, msg);
3573
+ abortHandshake$1(this, this._req, msg);
3574
+ return;
3064
3575
  }
3065
3576
 
3066
3577
  if (this._socket) {
@@ -3074,7 +3585,7 @@ let WebSocket$2 = class WebSocket extends EventEmitter$1 {
3074
3585
  * @constant {Number} CONNECTING
3075
3586
  * @memberof WebSocket
3076
3587
  */
3077
- Object.defineProperty(WebSocket$2, 'CONNECTING', {
3588
+ Object.defineProperty(WebSocket$1, 'CONNECTING', {
3078
3589
  enumerable: true,
3079
3590
  value: readyStates.indexOf('CONNECTING')
3080
3591
  });
@@ -3083,7 +3594,7 @@ Object.defineProperty(WebSocket$2, 'CONNECTING', {
3083
3594
  * @constant {Number} CONNECTING
3084
3595
  * @memberof WebSocket.prototype
3085
3596
  */
3086
- Object.defineProperty(WebSocket$2.prototype, 'CONNECTING', {
3597
+ Object.defineProperty(WebSocket$1.prototype, 'CONNECTING', {
3087
3598
  enumerable: true,
3088
3599
  value: readyStates.indexOf('CONNECTING')
3089
3600
  });
@@ -3092,7 +3603,7 @@ Object.defineProperty(WebSocket$2.prototype, 'CONNECTING', {
3092
3603
  * @constant {Number} OPEN
3093
3604
  * @memberof WebSocket
3094
3605
  */
3095
- Object.defineProperty(WebSocket$2, 'OPEN', {
3606
+ Object.defineProperty(WebSocket$1, 'OPEN', {
3096
3607
  enumerable: true,
3097
3608
  value: readyStates.indexOf('OPEN')
3098
3609
  });
@@ -3101,7 +3612,7 @@ Object.defineProperty(WebSocket$2, 'OPEN', {
3101
3612
  * @constant {Number} OPEN
3102
3613
  * @memberof WebSocket.prototype
3103
3614
  */
3104
- Object.defineProperty(WebSocket$2.prototype, 'OPEN', {
3615
+ Object.defineProperty(WebSocket$1.prototype, 'OPEN', {
3105
3616
  enumerable: true,
3106
3617
  value: readyStates.indexOf('OPEN')
3107
3618
  });
@@ -3110,7 +3621,7 @@ Object.defineProperty(WebSocket$2.prototype, 'OPEN', {
3110
3621
  * @constant {Number} CLOSING
3111
3622
  * @memberof WebSocket
3112
3623
  */
3113
- Object.defineProperty(WebSocket$2, 'CLOSING', {
3624
+ Object.defineProperty(WebSocket$1, 'CLOSING', {
3114
3625
  enumerable: true,
3115
3626
  value: readyStates.indexOf('CLOSING')
3116
3627
  });
@@ -3119,7 +3630,7 @@ Object.defineProperty(WebSocket$2, 'CLOSING', {
3119
3630
  * @constant {Number} CLOSING
3120
3631
  * @memberof WebSocket.prototype
3121
3632
  */
3122
- Object.defineProperty(WebSocket$2.prototype, 'CLOSING', {
3633
+ Object.defineProperty(WebSocket$1.prototype, 'CLOSING', {
3123
3634
  enumerable: true,
3124
3635
  value: readyStates.indexOf('CLOSING')
3125
3636
  });
@@ -3128,7 +3639,7 @@ Object.defineProperty(WebSocket$2.prototype, 'CLOSING', {
3128
3639
  * @constant {Number} CLOSED
3129
3640
  * @memberof WebSocket
3130
3641
  */
3131
- Object.defineProperty(WebSocket$2, 'CLOSED', {
3642
+ Object.defineProperty(WebSocket$1, 'CLOSED', {
3132
3643
  enumerable: true,
3133
3644
  value: readyStates.indexOf('CLOSED')
3134
3645
  });
@@ -3137,7 +3648,7 @@ Object.defineProperty(WebSocket$2, 'CLOSED', {
3137
3648
  * @constant {Number} CLOSED
3138
3649
  * @memberof WebSocket.prototype
3139
3650
  */
3140
- Object.defineProperty(WebSocket$2.prototype, 'CLOSED', {
3651
+ Object.defineProperty(WebSocket$1.prototype, 'CLOSED', {
3141
3652
  enumerable: true,
3142
3653
  value: readyStates.indexOf('CLOSED')
3143
3654
  });
@@ -3146,11 +3657,12 @@ Object.defineProperty(WebSocket$2.prototype, 'CLOSED', {
3146
3657
  'binaryType',
3147
3658
  'bufferedAmount',
3148
3659
  'extensions',
3660
+ 'isPaused',
3149
3661
  'protocol',
3150
3662
  'readyState',
3151
3663
  'url'
3152
3664
  ].forEach((property) => {
3153
- Object.defineProperty(WebSocket$2.prototype, property, { enumerable: true });
3665
+ Object.defineProperty(WebSocket$1.prototype, property, { enumerable: true });
3154
3666
  });
3155
3667
 
3156
3668
  //
@@ -3158,76 +3670,98 @@ Object.defineProperty(WebSocket$2.prototype, 'CLOSED', {
3158
3670
  // See https://html.spec.whatwg.org/multipage/comms.html#the-websocket-interface
3159
3671
  //
3160
3672
  ['open', 'error', 'close', 'message'].forEach((method) => {
3161
- Object.defineProperty(WebSocket$2.prototype, `on${method}`, {
3673
+ Object.defineProperty(WebSocket$1.prototype, `on${method}`, {
3162
3674
  enumerable: true,
3163
3675
  get() {
3164
- const listeners = this.listeners(method);
3165
- for (let i = 0; i < listeners.length; i++) {
3166
- if (listeners[i]._listener) return listeners[i]._listener;
3676
+ for (const listener of this.listeners(method)) {
3677
+ if (listener[kForOnEventAttribute]) return listener[kListener];
3167
3678
  }
3168
3679
 
3169
- return undefined;
3680
+ return null;
3170
3681
  },
3171
- set(listener) {
3172
- const listeners = this.listeners(method);
3173
- for (let i = 0; i < listeners.length; i++) {
3174
- //
3175
- // Remove only the listeners added via `addEventListener`.
3176
- //
3177
- if (listeners[i]._listener) this.removeListener(method, listeners[i]);
3682
+ set(handler) {
3683
+ for (const listener of this.listeners(method)) {
3684
+ if (listener[kForOnEventAttribute]) {
3685
+ this.removeListener(method, listener);
3686
+ break;
3687
+ }
3178
3688
  }
3179
- this.addEventListener(method, listener);
3689
+
3690
+ if (typeof handler !== 'function') return;
3691
+
3692
+ this.addEventListener(method, handler, {
3693
+ [kForOnEventAttribute]: true
3694
+ });
3180
3695
  }
3181
3696
  });
3182
3697
  });
3183
3698
 
3184
- WebSocket$2.prototype.addEventListener = addEventListener;
3185
- WebSocket$2.prototype.removeEventListener = removeEventListener;
3699
+ WebSocket$1.prototype.addEventListener = addEventListener;
3700
+ WebSocket$1.prototype.removeEventListener = removeEventListener;
3186
3701
 
3187
- var websocket = WebSocket$2;
3702
+ var websocket = WebSocket$1;
3188
3703
 
3189
3704
  /**
3190
3705
  * Initialize a WebSocket client.
3191
3706
  *
3192
3707
  * @param {WebSocket} websocket The client to initialize
3193
3708
  * @param {(String|URL)} address The URL to which to connect
3194
- * @param {String} [protocols] The subprotocols
3709
+ * @param {Array} protocols The subprotocols
3195
3710
  * @param {Object} [options] Connection options
3196
- * @param {(Boolean|Object)} [options.perMessageDeflate=true] Enable/disable
3197
- * permessage-deflate
3711
+ * @param {Boolean} [options.allowSynchronousEvents=true] Specifies whether any
3712
+ * of the `'message'`, `'ping'`, and `'pong'` events can be emitted multiple
3713
+ * times in the same tick
3714
+ * @param {Boolean} [options.autoPong=true] Specifies whether or not to
3715
+ * automatically send a pong in response to a ping
3716
+ * @param {Number} [options.closeTimeout=30000] Duration in milliseconds to wait
3717
+ * for the closing handshake to finish after `websocket.close()` is called
3718
+ * @param {Function} [options.finishRequest] A function which can be used to
3719
+ * customize the headers of each http request before it is sent
3720
+ * @param {Boolean} [options.followRedirects=false] Whether or not to follow
3721
+ * redirects
3722
+ * @param {Function} [options.generateMask] The function used to generate the
3723
+ * masking key
3198
3724
  * @param {Number} [options.handshakeTimeout] Timeout in milliseconds for the
3199
3725
  * handshake request
3200
- * @param {Number} [options.protocolVersion=13] Value of the
3201
- * `Sec-WebSocket-Version` header
3202
- * @param {String} [options.origin] Value of the `Origin` or
3203
- * `Sec-WebSocket-Origin` header
3204
3726
  * @param {Number} [options.maxPayload=104857600] The maximum allowed message
3205
3727
  * size
3206
- * @param {Boolean} [options.followRedirects=false] Whether or not to follow
3207
- * redirects
3208
3728
  * @param {Number} [options.maxRedirects=10] The maximum number of redirects
3209
3729
  * allowed
3730
+ * @param {String} [options.origin] Value of the `Origin` or
3731
+ * `Sec-WebSocket-Origin` header
3732
+ * @param {(Boolean|Object)} [options.perMessageDeflate=true] Enable/disable
3733
+ * permessage-deflate
3734
+ * @param {Number} [options.protocolVersion=13] Value of the
3735
+ * `Sec-WebSocket-Version` header
3736
+ * @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or
3737
+ * not to skip UTF-8 validation for text and close messages
3210
3738
  * @private
3211
3739
  */
3212
3740
  function initAsClient(websocket, address, protocols, options) {
3213
3741
  const opts = {
3742
+ allowSynchronousEvents: true,
3743
+ autoPong: true,
3744
+ closeTimeout: CLOSE_TIMEOUT$1,
3214
3745
  protocolVersion: protocolVersions[1],
3215
3746
  maxPayload: 100 * 1024 * 1024,
3747
+ skipUTF8Validation: false,
3216
3748
  perMessageDeflate: true,
3217
3749
  followRedirects: false,
3218
3750
  maxRedirects: 10,
3219
3751
  ...options,
3220
- createConnection: undefined,
3221
3752
  socketPath: undefined,
3222
3753
  hostname: undefined,
3223
3754
  protocol: undefined,
3224
3755
  timeout: undefined,
3225
- method: undefined,
3756
+ method: 'GET',
3226
3757
  host: undefined,
3227
3758
  path: undefined,
3228
3759
  port: undefined
3229
3760
  };
3230
3761
 
3762
+ websocket._autoPong = opts.autoPong;
3763
+ websocket._closeTimeout = opts.closeTimeout;
3764
+
3231
3765
  if (!protocolVersions.includes(opts.protocolVersion)) {
3232
3766
  throw new RangeError(
3233
3767
  `Unsupported protocol version: ${opts.protocolVersion} ` +
@@ -3239,16 +3773,38 @@ function initAsClient(websocket, address, protocols, options) {
3239
3773
 
3240
3774
  if (address instanceof URL) {
3241
3775
  parsedUrl = address;
3242
- websocket._url = address.href;
3243
3776
  } else {
3244
- parsedUrl = new URL(address);
3245
- websocket._url = address;
3777
+ try {
3778
+ parsedUrl = new URL(address);
3779
+ } catch {
3780
+ throw new SyntaxError(`Invalid URL: ${address}`);
3781
+ }
3782
+ }
3783
+
3784
+ if (parsedUrl.protocol === 'http:') {
3785
+ parsedUrl.protocol = 'ws:';
3786
+ } else if (parsedUrl.protocol === 'https:') {
3787
+ parsedUrl.protocol = 'wss:';
3246
3788
  }
3247
3789
 
3248
- const isUnixSocket = parsedUrl.protocol === 'ws+unix:';
3790
+ websocket._url = parsedUrl.href;
3791
+
3792
+ const isSecure = parsedUrl.protocol === 'wss:';
3793
+ const isIpcUrl = parsedUrl.protocol === 'ws+unix:';
3794
+ let invalidUrlMessage;
3249
3795
 
3250
- if (!parsedUrl.host && (!isUnixSocket || !parsedUrl.pathname)) {
3251
- const err = new Error(`Invalid URL: ${websocket.url}`);
3796
+ if (parsedUrl.protocol !== 'ws:' && !isSecure && !isIpcUrl) {
3797
+ invalidUrlMessage =
3798
+ 'The URL\'s protocol must be one of "ws:", "wss:", ' +
3799
+ '"http:", "https:", or "ws+unix:"';
3800
+ } else if (isIpcUrl && !parsedUrl.pathname) {
3801
+ invalidUrlMessage = "The URL's pathname is empty";
3802
+ } else if (parsedUrl.hash) {
3803
+ invalidUrlMessage = 'The URL contains a fragment identifier';
3804
+ }
3805
+
3806
+ if (invalidUrlMessage) {
3807
+ const err = new SyntaxError(invalidUrlMessage);
3252
3808
 
3253
3809
  if (websocket._redirects === 0) {
3254
3810
  throw err;
@@ -3258,41 +3814,55 @@ function initAsClient(websocket, address, protocols, options) {
3258
3814
  }
3259
3815
  }
3260
3816
 
3261
- const isSecure =
3262
- parsedUrl.protocol === 'wss:' || parsedUrl.protocol === 'https:';
3263
3817
  const defaultPort = isSecure ? 443 : 80;
3264
3818
  const key = randomBytes(16).toString('base64');
3265
- const get = isSecure ? https.get : http$1.get;
3819
+ const request = isSecure ? https.request : http$1.request;
3820
+ const protocolSet = new Set();
3266
3821
  let perMessageDeflate;
3267
3822
 
3268
- opts.createConnection = isSecure ? tlsConnect : netConnect;
3823
+ opts.createConnection =
3824
+ opts.createConnection || (isSecure ? tlsConnect : netConnect);
3269
3825
  opts.defaultPort = opts.defaultPort || defaultPort;
3270
3826
  opts.port = parsedUrl.port || defaultPort;
3271
3827
  opts.host = parsedUrl.hostname.startsWith('[')
3272
3828
  ? parsedUrl.hostname.slice(1, -1)
3273
3829
  : parsedUrl.hostname;
3274
3830
  opts.headers = {
3831
+ ...opts.headers,
3275
3832
  'Sec-WebSocket-Version': opts.protocolVersion,
3276
3833
  'Sec-WebSocket-Key': key,
3277
3834
  Connection: 'Upgrade',
3278
- Upgrade: 'websocket',
3279
- ...opts.headers
3835
+ Upgrade: 'websocket'
3280
3836
  };
3281
3837
  opts.path = parsedUrl.pathname + parsedUrl.search;
3282
3838
  opts.timeout = opts.handshakeTimeout;
3283
3839
 
3284
3840
  if (opts.perMessageDeflate) {
3285
- perMessageDeflate = new PerMessageDeflate$1(
3286
- opts.perMessageDeflate !== true ? opts.perMessageDeflate : {},
3287
- false,
3288
- opts.maxPayload
3289
- );
3290
- opts.headers['Sec-WebSocket-Extensions'] = format$1({
3841
+ perMessageDeflate = new PerMessageDeflate$1({
3842
+ ...opts.perMessageDeflate,
3843
+ isServer: false,
3844
+ maxPayload: opts.maxPayload
3845
+ });
3846
+ opts.headers['Sec-WebSocket-Extensions'] = format({
3291
3847
  [PerMessageDeflate$1.extensionName]: perMessageDeflate.offer()
3292
3848
  });
3293
3849
  }
3294
- if (protocols) {
3295
- opts.headers['Sec-WebSocket-Protocol'] = protocols;
3850
+ if (protocols.length) {
3851
+ for (const protocol of protocols) {
3852
+ if (
3853
+ typeof protocol !== 'string' ||
3854
+ !subprotocolRegex.test(protocol) ||
3855
+ protocolSet.has(protocol)
3856
+ ) {
3857
+ throw new SyntaxError(
3858
+ 'An invalid or duplicated subprotocol was specified'
3859
+ );
3860
+ }
3861
+
3862
+ protocolSet.add(protocol);
3863
+ }
3864
+
3865
+ opts.headers['Sec-WebSocket-Protocol'] = protocols.join(',');
3296
3866
  }
3297
3867
  if (opts.origin) {
3298
3868
  if (opts.protocolVersion < 13) {
@@ -3305,18 +3875,20 @@ function initAsClient(websocket, address, protocols, options) {
3305
3875
  opts.auth = `${parsedUrl.username}:${parsedUrl.password}`;
3306
3876
  }
3307
3877
 
3308
- if (isUnixSocket) {
3878
+ if (isIpcUrl) {
3309
3879
  const parts = opts.path.split(':');
3310
3880
 
3311
3881
  opts.socketPath = parts[0];
3312
3882
  opts.path = parts[1];
3313
3883
  }
3314
3884
 
3885
+ let req;
3886
+
3315
3887
  if (opts.followRedirects) {
3316
3888
  if (websocket._redirects === 0) {
3317
- websocket._originalUnixSocket = isUnixSocket;
3889
+ websocket._originalIpc = isIpcUrl;
3318
3890
  websocket._originalSecure = isSecure;
3319
- websocket._originalHostOrSocketPath = isUnixSocket
3891
+ websocket._originalHostOrSocketPath = isIpcUrl
3320
3892
  ? opts.socketPath
3321
3893
  : parsedUrl.host;
3322
3894
 
@@ -3333,14 +3905,14 @@ function initAsClient(websocket, address, protocols, options) {
3333
3905
  options.headers[key.toLowerCase()] = value;
3334
3906
  }
3335
3907
  }
3336
- } else {
3337
- const isSameHost = isUnixSocket
3338
- ? websocket._originalUnixSocket
3908
+ } else if (websocket.listenerCount('redirect') === 0) {
3909
+ const isSameHost = isIpcUrl
3910
+ ? websocket._originalIpc
3339
3911
  ? opts.socketPath === websocket._originalHostOrSocketPath
3340
3912
  : false
3341
- : websocket._originalUnixSocket
3342
- ? false
3343
- : parsedUrl.host === websocket._originalHostOrSocketPath;
3913
+ : websocket._originalIpc
3914
+ ? false
3915
+ : parsedUrl.host === websocket._originalHostOrSocketPath;
3344
3916
 
3345
3917
  if (!isSameHost || (websocket._originalSecure && !isSecure)) {
3346
3918
  //
@@ -3365,9 +3937,24 @@ function initAsClient(websocket, address, protocols, options) {
3365
3937
  options.headers.authorization =
3366
3938
  'Basic ' + Buffer.from(opts.auth).toString('base64');
3367
3939
  }
3368
- }
3369
3940
 
3370
- let req = (websocket._req = get(opts));
3941
+ req = websocket._req = request(opts);
3942
+
3943
+ if (websocket._redirects) {
3944
+ //
3945
+ // Unlike what is done for the `'upgrade'` event, no early exit is
3946
+ // triggered here if the user calls `websocket.close()` or
3947
+ // `websocket.terminate()` from a listener of the `'redirect'` event. This
3948
+ // is because the user can also call `request.destroy()` with an error
3949
+ // before calling `websocket.close()` or `websocket.terminate()` and this
3950
+ // would result in an error being emitted on the `request` object with no
3951
+ // `'error'` event listeners attached.
3952
+ //
3953
+ websocket.emit('redirect', websocket.url, req);
3954
+ }
3955
+ } else {
3956
+ req = websocket._req = request(opts);
3957
+ }
3371
3958
 
3372
3959
  if (opts.timeout) {
3373
3960
  req.on('timeout', () => {
@@ -3376,7 +3963,7 @@ function initAsClient(websocket, address, protocols, options) {
3376
3963
  }
3377
3964
 
3378
3965
  req.on('error', (err) => {
3379
- if (req === null || req.aborted) return;
3966
+ if (req === null || req[kAborted]) return;
3380
3967
 
3381
3968
  req = websocket._req = null;
3382
3969
  emitErrorAndClose(websocket, err);
@@ -3403,7 +3990,8 @@ function initAsClient(websocket, address, protocols, options) {
3403
3990
 
3404
3991
  try {
3405
3992
  addr = new URL(location, address);
3406
- } catch (err) {
3993
+ } catch (e) {
3994
+ const err = new SyntaxError(`Invalid URL: ${location}`);
3407
3995
  emitErrorAndClose(websocket, err);
3408
3996
  return;
3409
3997
  }
@@ -3422,10 +4010,10 @@ function initAsClient(websocket, address, protocols, options) {
3422
4010
  websocket.emit('upgrade', res);
3423
4011
 
3424
4012
  //
3425
- // The user may have closed the connection from a listener of the `upgrade`
3426
- // event.
4013
+ // The user may have closed the connection from a listener of the
4014
+ // `'upgrade'` event.
3427
4015
  //
3428
- if (websocket.readyState !== WebSocket$2.CONNECTING) return;
4016
+ if (websocket.readyState !== WebSocket$1.CONNECTING) return;
3429
4017
 
3430
4018
  req = websocket._req = null;
3431
4019
 
@@ -3446,15 +4034,16 @@ function initAsClient(websocket, address, protocols, options) {
3446
4034
  }
3447
4035
 
3448
4036
  const serverProt = res.headers['sec-websocket-protocol'];
3449
- const protList = (protocols || '').split(/, */);
3450
4037
  let protError;
3451
4038
 
3452
- if (!protocols && serverProt) {
3453
- protError = 'Server sent a subprotocol but none was requested';
3454
- } else if (protocols && !serverProt) {
4039
+ if (serverProt !== undefined) {
4040
+ if (!protocolSet.size) {
4041
+ protError = 'Server sent a subprotocol but none was requested';
4042
+ } else if (!protocolSet.has(serverProt)) {
4043
+ protError = 'Server sent an invalid subprotocol';
4044
+ }
4045
+ } else if (protocolSet.size) {
3455
4046
  protError = 'Server sent no subprotocol';
3456
- } else if (serverProt && !protList.includes(serverProt)) {
3457
- protError = 'Server sent an invalid subprotocol';
3458
4047
  }
3459
4048
 
3460
4049
  if (protError) {
@@ -3487,43 +4076,56 @@ function initAsClient(websocket, address, protocols, options) {
3487
4076
 
3488
4077
  const extensionNames = Object.keys(extensions);
3489
4078
 
3490
- if (extensionNames.length) {
3491
- if (
3492
- extensionNames.length !== 1 ||
3493
- extensionNames[0] !== PerMessageDeflate$1.extensionName
3494
- ) {
3495
- const message =
3496
- 'Server indicated an extension that was not requested';
3497
- abortHandshake$1(websocket, socket, message);
3498
- return;
3499
- }
3500
-
3501
- try {
3502
- perMessageDeflate.accept(extensions[PerMessageDeflate$1.extensionName]);
3503
- } catch (err) {
3504
- const message = 'Invalid Sec-WebSocket-Extensions header';
3505
- abortHandshake$1(websocket, socket, message);
3506
- return;
3507
- }
4079
+ if (
4080
+ extensionNames.length !== 1 ||
4081
+ extensionNames[0] !== PerMessageDeflate$1.extensionName
4082
+ ) {
4083
+ const message = 'Server indicated an extension that was not requested';
4084
+ abortHandshake$1(websocket, socket, message);
4085
+ return;
4086
+ }
3508
4087
 
3509
- websocket._extensions[PerMessageDeflate$1.extensionName] =
3510
- perMessageDeflate;
4088
+ try {
4089
+ perMessageDeflate.accept(extensions[PerMessageDeflate$1.extensionName]);
4090
+ } catch (err) {
4091
+ const message = 'Invalid Sec-WebSocket-Extensions header';
4092
+ abortHandshake$1(websocket, socket, message);
4093
+ return;
3511
4094
  }
4095
+
4096
+ websocket._extensions[PerMessageDeflate$1.extensionName] =
4097
+ perMessageDeflate;
3512
4098
  }
3513
4099
 
3514
- websocket.setSocket(socket, head, opts.maxPayload);
4100
+ websocket.setSocket(socket, head, {
4101
+ allowSynchronousEvents: opts.allowSynchronousEvents,
4102
+ generateMask: opts.generateMask,
4103
+ maxPayload: opts.maxPayload,
4104
+ skipUTF8Validation: opts.skipUTF8Validation
4105
+ });
3515
4106
  });
4107
+
4108
+ if (opts.finishRequest) {
4109
+ opts.finishRequest(req, websocket);
4110
+ } else {
4111
+ req.end();
4112
+ }
3516
4113
  }
3517
4114
 
3518
4115
  /**
3519
- * Emit the `'error'` and `'close'` event.
4116
+ * Emit the `'error'` and `'close'` events.
3520
4117
  *
3521
4118
  * @param {WebSocket} websocket The WebSocket instance
3522
4119
  * @param {Error} The error to emit
3523
4120
  * @private
3524
4121
  */
3525
4122
  function emitErrorAndClose(websocket, err) {
3526
- websocket._readyState = WebSocket$2.CLOSING;
4123
+ websocket._readyState = WebSocket$1.CLOSING;
4124
+ //
4125
+ // The following assignment is practically useless and is done only for
4126
+ // consistency.
4127
+ //
4128
+ websocket._errorEmitted = true;
3527
4129
  websocket.emit('error', err);
3528
4130
  websocket.emitClose();
3529
4131
  }
@@ -3567,12 +4169,13 @@ function tlsConnect(options) {
3567
4169
  * @private
3568
4170
  */
3569
4171
  function abortHandshake$1(websocket, stream, message) {
3570
- websocket._readyState = WebSocket$2.CLOSING;
4172
+ websocket._readyState = WebSocket$1.CLOSING;
3571
4173
 
3572
4174
  const err = new Error(message);
3573
4175
  Error.captureStackTrace(err, abortHandshake$1);
3574
4176
 
3575
4177
  if (stream.setHeader) {
4178
+ stream[kAborted] = true;
3576
4179
  stream.abort();
3577
4180
 
3578
4181
  if (stream.socket && !stream.socket.destroyed) {
@@ -3584,8 +4187,7 @@ function abortHandshake$1(websocket, stream, message) {
3584
4187
  stream.socket.destroy();
3585
4188
  }
3586
4189
 
3587
- stream.once('abort', websocket.emitClose.bind(websocket));
3588
- websocket.emit('error', err);
4190
+ process.nextTick(emitErrorAndClose, websocket, err);
3589
4191
  } else {
3590
4192
  stream.destroy(err);
3591
4193
  stream.once('error', websocket.emit.bind(websocket, 'error'));
@@ -3604,7 +4206,7 @@ function abortHandshake$1(websocket, stream, message) {
3604
4206
  */
3605
4207
  function sendAfterClose(websocket, data, cb) {
3606
4208
  if (data) {
3607
- const length = toBuffer(data).length;
4209
+ const length = isBlob(data) ? data.size : toBuffer(data).length;
3608
4210
 
3609
4211
  //
3610
4212
  // The `_bufferedAmount` property is used only when the peer is a client and
@@ -3621,7 +4223,7 @@ function sendAfterClose(websocket, data, cb) {
3621
4223
  `WebSocket is not open: readyState ${websocket.readyState} ` +
3622
4224
  `(${readyStates[websocket.readyState]})`
3623
4225
  );
3624
- cb(err);
4226
+ process.nextTick(cb, err);
3625
4227
  }
3626
4228
  }
3627
4229
 
@@ -3629,7 +4231,7 @@ function sendAfterClose(websocket, data, cb) {
3629
4231
  * The listener of the `Receiver` `'conclude'` event.
3630
4232
  *
3631
4233
  * @param {Number} code The status code
3632
- * @param {String} reason The reason for closing
4234
+ * @param {Buffer} reason The reason for closing
3633
4235
  * @private
3634
4236
  */
3635
4237
  function receiverOnConclude(code, reason) {
@@ -3654,7 +4256,9 @@ function receiverOnConclude(code, reason) {
3654
4256
  * @private
3655
4257
  */
3656
4258
  function receiverOnDrain() {
3657
- this[kWebSocket$1]._socket.resume();
4259
+ const websocket = this[kWebSocket$1];
4260
+
4261
+ if (!websocket.isPaused) websocket._socket.resume();
3658
4262
  }
3659
4263
 
3660
4264
  /**
@@ -3678,7 +4282,10 @@ function receiverOnError(err) {
3678
4282
  websocket.close(err[kStatusCode]);
3679
4283
  }
3680
4284
 
3681
- websocket.emit('error', err);
4285
+ if (!websocket._errorEmitted) {
4286
+ websocket._errorEmitted = true;
4287
+ websocket.emit('error', err);
4288
+ }
3682
4289
  }
3683
4290
 
3684
4291
  /**
@@ -3693,11 +4300,12 @@ function receiverOnFinish() {
3693
4300
  /**
3694
4301
  * The listener of the `Receiver` `'message'` event.
3695
4302
  *
3696
- * @param {(String|Buffer|ArrayBuffer|Buffer[])} data The message
4303
+ * @param {Buffer|ArrayBuffer|Buffer[])} data The message
4304
+ * @param {Boolean} isBinary Specifies whether the message is binary or not
3697
4305
  * @private
3698
4306
  */
3699
- function receiverOnMessage(data) {
3700
- this[kWebSocket$1].emit('message', data);
4307
+ function receiverOnMessage(data, isBinary) {
4308
+ this[kWebSocket$1].emit('message', data, isBinary);
3701
4309
  }
3702
4310
 
3703
4311
  /**
@@ -3709,7 +4317,7 @@ function receiverOnMessage(data) {
3709
4317
  function receiverOnPing(data) {
3710
4318
  const websocket = this[kWebSocket$1];
3711
4319
 
3712
- websocket.pong(data, !websocket._isServer, NOOP);
4320
+ if (websocket._autoPong) websocket.pong(data, !this._isServer, NOOP);
3713
4321
  websocket.emit('ping', data);
3714
4322
  }
3715
4323
 
@@ -3734,7 +4342,48 @@ function resume(stream) {
3734
4342
  }
3735
4343
 
3736
4344
  /**
3737
- * The listener of the `net.Socket` `'close'` event.
4345
+ * The `Sender` error event handler.
4346
+ *
4347
+ * @param {Error} The error
4348
+ * @private
4349
+ */
4350
+ function senderOnError(err) {
4351
+ const websocket = this[kWebSocket$1];
4352
+
4353
+ if (websocket.readyState === WebSocket$1.CLOSED) return;
4354
+ if (websocket.readyState === WebSocket$1.OPEN) {
4355
+ websocket._readyState = WebSocket$1.CLOSING;
4356
+ setCloseTimer(websocket);
4357
+ }
4358
+
4359
+ //
4360
+ // `socket.end()` is used instead of `socket.destroy()` to allow the other
4361
+ // peer to finish sending queued data. There is no need to set a timer here
4362
+ // because `CLOSING` means that it is already set or not needed.
4363
+ //
4364
+ this._socket.end();
4365
+
4366
+ if (!websocket._errorEmitted) {
4367
+ websocket._errorEmitted = true;
4368
+ websocket.emit('error', err);
4369
+ }
4370
+ }
4371
+
4372
+ /**
4373
+ * Set a timer to destroy the underlying raw socket of a WebSocket.
4374
+ *
4375
+ * @param {WebSocket} websocket The WebSocket instance
4376
+ * @private
4377
+ */
4378
+ function setCloseTimer(websocket) {
4379
+ websocket._closeTimer = setTimeout(
4380
+ websocket._socket.destroy.bind(websocket._socket),
4381
+ websocket._closeTimeout
4382
+ );
4383
+ }
4384
+
4385
+ /**
4386
+ * The listener of the socket `'close'` event.
3738
4387
  *
3739
4388
  * @private
3740
4389
  */
@@ -3745,25 +4394,25 @@ function socketOnClose() {
3745
4394
  this.removeListener('data', socketOnData);
3746
4395
  this.removeListener('end', socketOnEnd);
3747
4396
 
3748
- websocket._readyState = WebSocket$2.CLOSING;
3749
-
3750
- let chunk;
4397
+ websocket._readyState = WebSocket$1.CLOSING;
3751
4398
 
3752
4399
  //
3753
4400
  // The close frame might not have been received or the `'end'` event emitted,
3754
4401
  // for example, if the socket was destroyed due to an error. Ensure that the
3755
4402
  // `receiver` stream is closed after writing any remaining buffered data to
3756
4403
  // it. If the readable side of the socket is in flowing mode then there is no
3757
- // buffered data as everything has been already written and `readable.read()`
3758
- // will return `null`. If instead, the socket is paused, any possible buffered
3759
- // data will be read as a single chunk.
4404
+ // buffered data as everything has been already written. If instead, the
4405
+ // socket is paused, any possible buffered data will be read as a single
4406
+ // chunk.
3760
4407
  //
3761
4408
  if (
3762
4409
  !this._readableState.endEmitted &&
3763
4410
  !websocket._closeFrameReceived &&
3764
4411
  !websocket._receiver._writableState.errorEmitted &&
3765
- (chunk = websocket._socket.read()) !== null
4412
+ this._readableState.length !== 0
3766
4413
  ) {
4414
+ const chunk = this.read(this._readableState.length);
4415
+
3767
4416
  websocket._receiver.write(chunk);
3768
4417
  }
3769
4418
 
@@ -3785,7 +4434,7 @@ function socketOnClose() {
3785
4434
  }
3786
4435
 
3787
4436
  /**
3788
- * The listener of the `net.Socket` `'data'` event.
4437
+ * The listener of the socket `'data'` event.
3789
4438
  *
3790
4439
  * @param {Buffer} chunk A chunk of data
3791
4440
  * @private
@@ -3797,20 +4446,20 @@ function socketOnData(chunk) {
3797
4446
  }
3798
4447
 
3799
4448
  /**
3800
- * The listener of the `net.Socket` `'end'` event.
4449
+ * The listener of the socket `'end'` event.
3801
4450
  *
3802
4451
  * @private
3803
4452
  */
3804
4453
  function socketOnEnd() {
3805
4454
  const websocket = this[kWebSocket$1];
3806
4455
 
3807
- websocket._readyState = WebSocket$2.CLOSING;
4456
+ websocket._readyState = WebSocket$1.CLOSING;
3808
4457
  websocket._receiver.end();
3809
4458
  this.end();
3810
4459
  }
3811
4460
 
3812
4461
  /**
3813
- * The listener of the `net.Socket` `'error'` event.
4462
+ * The listener of the socket `'error'` event.
3814
4463
  *
3815
4464
  * @private
3816
4465
  */
@@ -3821,200 +4470,87 @@ function socketOnError$1() {
3821
4470
  this.on('error', NOOP);
3822
4471
 
3823
4472
  if (websocket) {
3824
- websocket._readyState = WebSocket$2.CLOSING;
4473
+ websocket._readyState = WebSocket$1.CLOSING;
3825
4474
  this.destroy();
3826
4475
  }
3827
4476
  }
3828
4477
 
3829
- const { Duplex } = require$$0$2;
3830
-
3831
- /**
3832
- * Emits the `'close'` event on a stream.
3833
- *
3834
- * @param {Duplex} stream The stream.
3835
- * @private
3836
- */
3837
- function emitClose$1(stream) {
3838
- stream.emit('close');
3839
- }
3840
-
3841
- /**
3842
- * The listener of the `'end'` event.
3843
- *
3844
- * @private
3845
- */
3846
- function duplexOnEnd() {
3847
- if (!this.destroyed && this._writableState.finished) {
3848
- this.destroy();
3849
- }
3850
- }
4478
+ /* eslint no-unused-vars: ["error", { "varsIgnorePattern": "^WebSocket$" }] */
4479
+ const { Duplex: Duplex$1 } = require$$0$3;
3851
4480
 
3852
- /**
3853
- * The listener of the `'error'` event.
3854
- *
3855
- * @param {Error} err The error
3856
- * @private
3857
- */
3858
- function duplexOnError(err) {
3859
- this.removeListener('error', duplexOnError);
3860
- this.destroy();
3861
- if (this.listenerCount('error') === 0) {
3862
- // Do not suppress the throwing behavior.
3863
- this.emit('error', err);
3864
- }
3865
- }
4481
+ const { tokenChars } = validationExports;
3866
4482
 
3867
4483
  /**
3868
- * Wraps a `WebSocket` in a duplex stream.
4484
+ * Parses the `Sec-WebSocket-Protocol` header into a set of subprotocol names.
3869
4485
  *
3870
- * @param {WebSocket} ws The `WebSocket` to wrap
3871
- * @param {Object} [options] The options for the `Duplex` constructor
3872
- * @return {Duplex} The duplex stream
4486
+ * @param {String} header The field value of the header
4487
+ * @return {Set} The subprotocol names
3873
4488
  * @public
3874
4489
  */
3875
- function createWebSocketStream(ws, options) {
3876
- let resumeOnReceiverDrain = true;
3877
- let terminateOnDestroy = true;
3878
-
3879
- function receiverOnDrain() {
3880
- if (resumeOnReceiverDrain) ws._socket.resume();
3881
- }
3882
-
3883
- if (ws.readyState === ws.CONNECTING) {
3884
- ws.once('open', function open() {
3885
- ws._receiver.removeAllListeners('drain');
3886
- ws._receiver.on('drain', receiverOnDrain);
3887
- });
3888
- } else {
3889
- ws._receiver.removeAllListeners('drain');
3890
- ws._receiver.on('drain', receiverOnDrain);
3891
- }
3892
-
3893
- const duplex = new Duplex({
3894
- ...options,
3895
- autoDestroy: false,
3896
- emitClose: false,
3897
- objectMode: false,
3898
- writableObjectMode: false
3899
- });
3900
-
3901
- ws.on('message', function message(msg) {
3902
- if (!duplex.push(msg)) {
3903
- resumeOnReceiverDrain = false;
3904
- ws._socket.pause();
3905
- }
3906
- });
3907
-
3908
- ws.once('error', function error(err) {
3909
- if (duplex.destroyed) return;
3910
-
3911
- // Prevent `ws.terminate()` from being called by `duplex._destroy()`.
3912
- //
3913
- // - If the `'error'` event is emitted before the `'open'` event, then
3914
- // `ws.terminate()` is a noop as no socket is assigned.
3915
- // - Otherwise, the error is re-emitted by the listener of the `'error'`
3916
- // event of the `Receiver` object. The listener already closes the
3917
- // connection by calling `ws.close()`. This allows a close frame to be
3918
- // sent to the other peer. If `ws.terminate()` is called right after this,
3919
- // then the close frame might not be sent.
3920
- terminateOnDestroy = false;
3921
- duplex.destroy(err);
3922
- });
3923
-
3924
- ws.once('close', function close() {
3925
- if (duplex.destroyed) return;
3926
-
3927
- duplex.push(null);
3928
- });
3929
-
3930
- duplex._destroy = function (err, callback) {
3931
- if (ws.readyState === ws.CLOSED) {
3932
- callback(err);
3933
- process.nextTick(emitClose$1, duplex);
3934
- return;
3935
- }
3936
-
3937
- let called = false;
4490
+ function parse(header) {
4491
+ const protocols = new Set();
4492
+ let start = -1;
4493
+ let end = -1;
4494
+ let i = 0;
3938
4495
 
3939
- ws.once('error', function error(err) {
3940
- called = true;
3941
- callback(err);
3942
- });
4496
+ for (i; i < header.length; i++) {
4497
+ const code = header.charCodeAt(i);
3943
4498
 
3944
- ws.once('close', function close() {
3945
- if (!called) callback(err);
3946
- process.nextTick(emitClose$1, duplex);
3947
- });
4499
+ if (end === -1 && tokenChars[code] === 1) {
4500
+ if (start === -1) start = i;
4501
+ } else if (
4502
+ i !== 0 &&
4503
+ (code === 0x20 /* ' ' */ || code === 0x09) /* '\t' */
4504
+ ) {
4505
+ if (end === -1 && start !== -1) end = i;
4506
+ } else if (code === 0x2c /* ',' */) {
4507
+ if (start === -1) {
4508
+ throw new SyntaxError(`Unexpected character at index ${i}`);
4509
+ }
3948
4510
 
3949
- if (terminateOnDestroy) ws.terminate();
3950
- };
4511
+ if (end === -1) end = i;
3951
4512
 
3952
- duplex._final = function (callback) {
3953
- if (ws.readyState === ws.CONNECTING) {
3954
- ws.once('open', function open() {
3955
- duplex._final(callback);
3956
- });
3957
- return;
3958
- }
4513
+ const protocol = header.slice(start, end);
3959
4514
 
3960
- // If the value of the `_socket` property is `null` it means that `ws` is a
3961
- // client websocket and the handshake failed. In fact, when this happens, a
3962
- // socket is never assigned to the websocket. Wait for the `'error'` event
3963
- // that will be emitted by the websocket.
3964
- if (ws._socket === null) return;
4515
+ if (protocols.has(protocol)) {
4516
+ throw new SyntaxError(`The "${protocol}" subprotocol is duplicated`);
4517
+ }
3965
4518
 
3966
- if (ws._socket._writableState.finished) {
3967
- callback();
3968
- if (duplex._readableState.endEmitted) duplex.destroy();
4519
+ protocols.add(protocol);
4520
+ start = end = -1;
3969
4521
  } else {
3970
- ws._socket.once('finish', function finish() {
3971
- // `duplex` is not destroyed here because the `'end'` event will be
3972
- // emitted on `duplex` after this `'finish'` event. The EOF signaling
3973
- // `null` chunk is, in fact, pushed when the websocket emits `'close'`.
3974
- callback();
3975
- });
3976
- ws.close();
4522
+ throw new SyntaxError(`Unexpected character at index ${i}`);
3977
4523
  }
3978
- };
4524
+ }
3979
4525
 
3980
- duplex._read = function () {
3981
- if (
3982
- (ws.readyState === ws.OPEN || ws.readyState === ws.CLOSING) &&
3983
- !resumeOnReceiverDrain
3984
- ) {
3985
- resumeOnReceiverDrain = true;
3986
- if (!ws._receiver._writableState.needDrain) ws._socket.resume();
3987
- }
3988
- };
4526
+ if (start === -1 || end !== -1) {
4527
+ throw new SyntaxError('Unexpected end of input');
4528
+ }
3989
4529
 
3990
- duplex._write = function (chunk, encoding, callback) {
3991
- if (ws.readyState === ws.CONNECTING) {
3992
- ws.once('open', function open() {
3993
- duplex._write(chunk, encoding, callback);
3994
- });
3995
- return;
3996
- }
4530
+ const protocol = header.slice(start, i);
3997
4531
 
3998
- ws.send(chunk, callback);
3999
- };
4532
+ if (protocols.has(protocol)) {
4533
+ throw new SyntaxError(`The "${protocol}" subprotocol is duplicated`);
4534
+ }
4000
4535
 
4001
- duplex.on('end', duplexOnEnd);
4002
- duplex.on('error', duplexOnError);
4003
- return duplex;
4536
+ protocols.add(protocol);
4537
+ return protocols;
4004
4538
  }
4005
4539
 
4006
- var stream = createWebSocketStream;
4540
+ var subprotocol$1 = { parse };
4007
4541
 
4008
- /* eslint no-unused-vars: ["error", { "varsIgnorePattern": "^net|tls|https$" }] */
4542
+ /* eslint no-unused-vars: ["error", { "varsIgnorePattern": "^Duplex$", "caughtErrors": "none" }] */
4009
4543
 
4010
- const EventEmitter = require$$0$3;
4544
+ const EventEmitter = require$$0$4;
4011
4545
  const http = require$$2$1;
4012
- const { createHash } = require$$5;
4546
+ const { Duplex } = require$$0$3;
4547
+ const { createHash } = require$$1$1;
4013
4548
 
4549
+ const extension = extension$1;
4014
4550
  const PerMessageDeflate = permessageDeflate;
4015
- const WebSocket$1 = websocket;
4016
- const { format, parse } = extension;
4017
- const { GUID, kWebSocket } = constants;
4551
+ const subprotocol = subprotocol$1;
4552
+ const WebSocket = websocket;
4553
+ const { CLOSE_TIMEOUT, GUID, kWebSocket } = constants;
4018
4554
 
4019
4555
  const keyRegex = /^[+/0-9A-Za-z]{22}==$/;
4020
4556
 
@@ -4032,10 +4568,18 @@ let WebSocketServer$1 = class WebSocketServer extends EventEmitter {
4032
4568
  * Create a `WebSocketServer` instance.
4033
4569
  *
4034
4570
  * @param {Object} options Configuration options
4571
+ * @param {Boolean} [options.allowSynchronousEvents=true] Specifies whether
4572
+ * any of the `'message'`, `'ping'`, and `'pong'` events can be emitted
4573
+ * multiple times in the same tick
4574
+ * @param {Boolean} [options.autoPong=true] Specifies whether or not to
4575
+ * automatically send a pong in response to a ping
4035
4576
  * @param {Number} [options.backlog=511] The maximum length of the queue of
4036
4577
  * pending connections
4037
4578
  * @param {Boolean} [options.clientTracking=true] Specifies whether or not to
4038
4579
  * track clients
4580
+ * @param {Number} [options.closeTimeout=30000] Duration in milliseconds to
4581
+ * wait for the closing handshake to finish after `websocket.close()` is
4582
+ * called
4039
4583
  * @param {Function} [options.handleProtocols] A hook to handle protocols
4040
4584
  * @param {String} [options.host] The hostname where to bind the server
4041
4585
  * @param {Number} [options.maxPayload=104857600] The maximum allowed message
@@ -4047,17 +4591,25 @@ let WebSocketServer$1 = class WebSocketServer extends EventEmitter {
4047
4591
  * @param {Number} [options.port] The port where to bind the server
4048
4592
  * @param {(http.Server|https.Server)} [options.server] A pre-created HTTP/S
4049
4593
  * server to use
4594
+ * @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or
4595
+ * not to skip UTF-8 validation for text and close messages
4050
4596
  * @param {Function} [options.verifyClient] A hook to reject connections
4597
+ * @param {Function} [options.WebSocket=WebSocket] Specifies the `WebSocket`
4598
+ * class to use. It must be the `WebSocket` class or class that extends it
4051
4599
  * @param {Function} [callback] A listener for the `listening` event
4052
4600
  */
4053
4601
  constructor(options, callback) {
4054
4602
  super();
4055
4603
 
4056
4604
  options = {
4605
+ allowSynchronousEvents: true,
4606
+ autoPong: true,
4057
4607
  maxPayload: 100 * 1024 * 1024,
4608
+ skipUTF8Validation: false,
4058
4609
  perMessageDeflate: false,
4059
4610
  handleProtocols: null,
4060
4611
  clientTracking: true,
4612
+ closeTimeout: CLOSE_TIMEOUT,
4061
4613
  verifyClient: null,
4062
4614
  noServer: false,
4063
4615
  backlog: null, // use default (511 as implemented in net.js)
@@ -4065,6 +4617,7 @@ let WebSocketServer$1 = class WebSocketServer extends EventEmitter {
4065
4617
  host: null,
4066
4618
  path: null,
4067
4619
  port: null,
4620
+ WebSocket,
4068
4621
  ...options
4069
4622
  };
4070
4623
 
@@ -4112,7 +4665,11 @@ let WebSocketServer$1 = class WebSocketServer extends EventEmitter {
4112
4665
  }
4113
4666
 
4114
4667
  if (options.perMessageDeflate === true) options.perMessageDeflate = {};
4115
- if (options.clientTracking) this.clients = new Set();
4668
+ if (options.clientTracking) {
4669
+ this.clients = new Set();
4670
+ this._shouldEmitClose = false;
4671
+ }
4672
+
4116
4673
  this.options = options;
4117
4674
  this._state = RUNNING;
4118
4675
  }
@@ -4136,45 +4693,58 @@ let WebSocketServer$1 = class WebSocketServer extends EventEmitter {
4136
4693
  }
4137
4694
 
4138
4695
  /**
4139
- * Close the server.
4696
+ * Stop the server from accepting new connections and emit the `'close'` event
4697
+ * when all existing connections are closed.
4140
4698
  *
4141
- * @param {Function} [cb] Callback
4699
+ * @param {Function} [cb] A one-time listener for the `'close'` event
4142
4700
  * @public
4143
4701
  */
4144
4702
  close(cb) {
4145
- if (cb) this.once('close', cb);
4146
-
4147
4703
  if (this._state === CLOSED) {
4704
+ if (cb) {
4705
+ this.once('close', () => {
4706
+ cb(new Error('The server is not running'));
4707
+ });
4708
+ }
4709
+
4148
4710
  process.nextTick(emitClose, this);
4149
4711
  return;
4150
4712
  }
4151
4713
 
4714
+ if (cb) this.once('close', cb);
4715
+
4152
4716
  if (this._state === CLOSING) return;
4153
4717
  this._state = CLOSING;
4154
4718
 
4155
- //
4156
- // Terminate all associated clients.
4157
- //
4158
- if (this.clients) {
4159
- for (const client of this.clients) client.terminate();
4160
- }
4719
+ if (this.options.noServer || this.options.server) {
4720
+ if (this._server) {
4721
+ this._removeListeners();
4722
+ this._removeListeners = this._server = null;
4723
+ }
4161
4724
 
4162
- const server = this._server;
4725
+ if (this.clients) {
4726
+ if (!this.clients.size) {
4727
+ process.nextTick(emitClose, this);
4728
+ } else {
4729
+ this._shouldEmitClose = true;
4730
+ }
4731
+ } else {
4732
+ process.nextTick(emitClose, this);
4733
+ }
4734
+ } else {
4735
+ const server = this._server;
4163
4736
 
4164
- if (server) {
4165
4737
  this._removeListeners();
4166
4738
  this._removeListeners = this._server = null;
4167
4739
 
4168
4740
  //
4169
- // Close the http server if it was internally created.
4741
+ // The HTTP/S server was created internally. Close it, and rely on its
4742
+ // `'close'` event.
4170
4743
  //
4171
- if (this.options.port != null) {
4172
- server.close(emitClose.bind(undefined, this));
4173
- return;
4174
- }
4744
+ server.close(() => {
4745
+ emitClose(this);
4746
+ });
4175
4747
  }
4176
-
4177
- process.nextTick(emitClose, this);
4178
4748
  }
4179
4749
 
4180
4750
  /**
@@ -4199,8 +4769,7 @@ let WebSocketServer$1 = class WebSocketServer extends EventEmitter {
4199
4769
  * Handle a HTTP Upgrade request.
4200
4770
  *
4201
4771
  * @param {http.IncomingMessage} req The request object
4202
- * @param {(net.Socket|tls.Socket)} socket The network socket between the
4203
- * server and client
4772
+ * @param {Duplex} socket The network socket between the server and client
4204
4773
  * @param {Buffer} head The first packet of the upgraded stream
4205
4774
  * @param {Function} cb Callback
4206
4775
  * @public
@@ -4208,42 +4777,79 @@ let WebSocketServer$1 = class WebSocketServer extends EventEmitter {
4208
4777
  handleUpgrade(req, socket, head, cb) {
4209
4778
  socket.on('error', socketOnError);
4210
4779
 
4211
- const key =
4212
- req.headers['sec-websocket-key'] !== undefined
4213
- ? req.headers['sec-websocket-key'].trim()
4214
- : false;
4780
+ const key = req.headers['sec-websocket-key'];
4215
4781
  const upgrade = req.headers.upgrade;
4216
4782
  const version = +req.headers['sec-websocket-version'];
4783
+
4784
+ if (req.method !== 'GET') {
4785
+ const message = 'Invalid HTTP method';
4786
+ abortHandshakeOrEmitwsClientError(this, req, socket, 405, message);
4787
+ return;
4788
+ }
4789
+
4790
+ if (upgrade === undefined || upgrade.toLowerCase() !== 'websocket') {
4791
+ const message = 'Invalid Upgrade header';
4792
+ abortHandshakeOrEmitwsClientError(this, req, socket, 400, message);
4793
+ return;
4794
+ }
4795
+
4796
+ if (key === undefined || !keyRegex.test(key)) {
4797
+ const message = 'Missing or invalid Sec-WebSocket-Key header';
4798
+ abortHandshakeOrEmitwsClientError(this, req, socket, 400, message);
4799
+ return;
4800
+ }
4801
+
4802
+ if (version !== 13 && version !== 8) {
4803
+ const message = 'Missing or invalid Sec-WebSocket-Version header';
4804
+ abortHandshakeOrEmitwsClientError(this, req, socket, 400, message, {
4805
+ 'Sec-WebSocket-Version': '13, 8'
4806
+ });
4807
+ return;
4808
+ }
4809
+
4810
+ if (!this.shouldHandle(req)) {
4811
+ abortHandshake(socket, 400);
4812
+ return;
4813
+ }
4814
+
4815
+ const secWebSocketProtocol = req.headers['sec-websocket-protocol'];
4816
+ let protocols = new Set();
4817
+
4818
+ if (secWebSocketProtocol !== undefined) {
4819
+ try {
4820
+ protocols = subprotocol.parse(secWebSocketProtocol);
4821
+ } catch (err) {
4822
+ const message = 'Invalid Sec-WebSocket-Protocol header';
4823
+ abortHandshakeOrEmitwsClientError(this, req, socket, 400, message);
4824
+ return;
4825
+ }
4826
+ }
4827
+
4828
+ const secWebSocketExtensions = req.headers['sec-websocket-extensions'];
4217
4829
  const extensions = {};
4218
4830
 
4219
4831
  if (
4220
- req.method !== 'GET' ||
4221
- upgrade === undefined ||
4222
- upgrade.toLowerCase() !== 'websocket' ||
4223
- !key ||
4224
- !keyRegex.test(key) ||
4225
- (version !== 8 && version !== 13) ||
4226
- !this.shouldHandle(req)
4832
+ this.options.perMessageDeflate &&
4833
+ secWebSocketExtensions !== undefined
4227
4834
  ) {
4228
- return abortHandshake(socket, 400);
4229
- }
4230
-
4231
- if (this.options.perMessageDeflate) {
4232
- const perMessageDeflate = new PerMessageDeflate(
4233
- this.options.perMessageDeflate,
4234
- true,
4235
- this.options.maxPayload
4236
- );
4835
+ const perMessageDeflate = new PerMessageDeflate({
4836
+ ...this.options.perMessageDeflate,
4837
+ isServer: true,
4838
+ maxPayload: this.options.maxPayload
4839
+ });
4237
4840
 
4238
4841
  try {
4239
- const offers = parse(req.headers['sec-websocket-extensions']);
4842
+ const offers = extension.parse(secWebSocketExtensions);
4240
4843
 
4241
4844
  if (offers[PerMessageDeflate.extensionName]) {
4242
4845
  perMessageDeflate.accept(offers[PerMessageDeflate.extensionName]);
4243
4846
  extensions[PerMessageDeflate.extensionName] = perMessageDeflate;
4244
4847
  }
4245
4848
  } catch (err) {
4246
- return abortHandshake(socket, 400);
4849
+ const message =
4850
+ 'Invalid or unacceptable Sec-WebSocket-Extensions header';
4851
+ abortHandshakeOrEmitwsClientError(this, req, socket, 400, message);
4852
+ return;
4247
4853
  }
4248
4854
  }
4249
4855
 
@@ -4264,7 +4870,15 @@ let WebSocketServer$1 = class WebSocketServer extends EventEmitter {
4264
4870
  return abortHandshake(socket, code || 401, message, headers);
4265
4871
  }
4266
4872
 
4267
- this.completeUpgrade(key, extensions, req, socket, head, cb);
4873
+ this.completeUpgrade(
4874
+ extensions,
4875
+ key,
4876
+ protocols,
4877
+ req,
4878
+ socket,
4879
+ head,
4880
+ cb
4881
+ );
4268
4882
  });
4269
4883
  return;
4270
4884
  }
@@ -4272,23 +4886,23 @@ let WebSocketServer$1 = class WebSocketServer extends EventEmitter {
4272
4886
  if (!this.options.verifyClient(info)) return abortHandshake(socket, 401);
4273
4887
  }
4274
4888
 
4275
- this.completeUpgrade(key, extensions, req, socket, head, cb);
4889
+ this.completeUpgrade(extensions, key, protocols, req, socket, head, cb);
4276
4890
  }
4277
4891
 
4278
4892
  /**
4279
4893
  * Upgrade the connection to WebSocket.
4280
4894
  *
4281
- * @param {String} key The value of the `Sec-WebSocket-Key` header
4282
4895
  * @param {Object} extensions The accepted extensions
4896
+ * @param {String} key The value of the `Sec-WebSocket-Key` header
4897
+ * @param {Set} protocols The subprotocols
4283
4898
  * @param {http.IncomingMessage} req The request object
4284
- * @param {(net.Socket|tls.Socket)} socket The network socket between the
4285
- * server and client
4899
+ * @param {Duplex} socket The network socket between the server and client
4286
4900
  * @param {Buffer} head The first packet of the upgraded stream
4287
4901
  * @param {Function} cb Callback
4288
4902
  * @throws {Error} If called more than once with the same socket
4289
4903
  * @private
4290
4904
  */
4291
- completeUpgrade(key, extensions, req, socket, head, cb) {
4905
+ completeUpgrade(extensions, key, protocols, req, socket, head, cb) {
4292
4906
  //
4293
4907
  // Destroy the socket if the client has already sent a FIN packet.
4294
4908
  //
@@ -4314,20 +4928,15 @@ let WebSocketServer$1 = class WebSocketServer extends EventEmitter {
4314
4928
  `Sec-WebSocket-Accept: ${digest}`
4315
4929
  ];
4316
4930
 
4317
- const ws = new WebSocket$1(null);
4318
- let protocol = req.headers['sec-websocket-protocol'];
4319
-
4320
- if (protocol) {
4321
- protocol = protocol.split(',').map(trim);
4931
+ const ws = new this.options.WebSocket(null, undefined, this.options);
4322
4932
 
4933
+ if (protocols.size) {
4323
4934
  //
4324
4935
  // Optionally call external protocol selection handler.
4325
4936
  //
4326
- if (this.options.handleProtocols) {
4327
- protocol = this.options.handleProtocols(protocol, req);
4328
- } else {
4329
- protocol = protocol[0];
4330
- }
4937
+ const protocol = this.options.handleProtocols
4938
+ ? this.options.handleProtocols(protocols, req)
4939
+ : protocols.values().next().value;
4331
4940
 
4332
4941
  if (protocol) {
4333
4942
  headers.push(`Sec-WebSocket-Protocol: ${protocol}`);
@@ -4337,7 +4946,7 @@ let WebSocketServer$1 = class WebSocketServer extends EventEmitter {
4337
4946
 
4338
4947
  if (extensions[PerMessageDeflate.extensionName]) {
4339
4948
  const params = extensions[PerMessageDeflate.extensionName].params;
4340
- const value = format({
4949
+ const value = extension.format({
4341
4950
  [PerMessageDeflate.extensionName]: [params]
4342
4951
  });
4343
4952
  headers.push(`Sec-WebSocket-Extensions: ${value}`);
@@ -4352,11 +4961,21 @@ let WebSocketServer$1 = class WebSocketServer extends EventEmitter {
4352
4961
  socket.write(headers.concat('\r\n').join('\r\n'));
4353
4962
  socket.removeListener('error', socketOnError);
4354
4963
 
4355
- ws.setSocket(socket, head, this.options.maxPayload);
4964
+ ws.setSocket(socket, head, {
4965
+ allowSynchronousEvents: this.options.allowSynchronousEvents,
4966
+ maxPayload: this.options.maxPayload,
4967
+ skipUTF8Validation: this.options.skipUTF8Validation
4968
+ });
4356
4969
 
4357
4970
  if (this.clients) {
4358
4971
  this.clients.add(ws);
4359
- ws.on('close', () => this.clients.delete(ws));
4972
+ ws.on('close', () => {
4973
+ this.clients.delete(ws);
4974
+
4975
+ if (this._shouldEmitClose && !this.clients.size) {
4976
+ process.nextTick(emitClose, this);
4977
+ }
4978
+ });
4360
4979
  }
4361
4980
 
4362
4981
  cb(ws, req);
@@ -4397,7 +5016,7 @@ function emitClose(server) {
4397
5016
  }
4398
5017
 
4399
5018
  /**
4400
- * Handle premature socket errors.
5019
+ * Handle socket errors.
4401
5020
  *
4402
5021
  * @private
4403
5022
  */
@@ -4408,68 +5027,80 @@ function socketOnError() {
4408
5027
  /**
4409
5028
  * Close the connection when preconditions are not fulfilled.
4410
5029
  *
4411
- * @param {(net.Socket|tls.Socket)} socket The socket of the upgrade request
5030
+ * @param {Duplex} socket The socket of the upgrade request
4412
5031
  * @param {Number} code The HTTP response status code
4413
5032
  * @param {String} [message] The HTTP response body
4414
5033
  * @param {Object} [headers] Additional HTTP response headers
4415
5034
  * @private
4416
5035
  */
4417
5036
  function abortHandshake(socket, code, message, headers) {
4418
- if (socket.writable) {
4419
- message = message || http.STATUS_CODES[code];
4420
- headers = {
4421
- Connection: 'close',
4422
- 'Content-Type': 'text/html',
4423
- 'Content-Length': Buffer.byteLength(message),
4424
- ...headers
4425
- };
5037
+ //
5038
+ // The socket is writable unless the user destroyed or ended it before calling
5039
+ // `server.handleUpgrade()` or in the `verifyClient` function, which is a user
5040
+ // error. Handling this does not make much sense as the worst that can happen
5041
+ // is that some of the data written by the user might be discarded due to the
5042
+ // call to `socket.end()` below, which triggers an `'error'` event that in
5043
+ // turn causes the socket to be destroyed.
5044
+ //
5045
+ message = message || http.STATUS_CODES[code];
5046
+ headers = {
5047
+ Connection: 'close',
5048
+ 'Content-Type': 'text/html',
5049
+ 'Content-Length': Buffer.byteLength(message),
5050
+ ...headers
5051
+ };
4426
5052
 
4427
- socket.write(
4428
- `HTTP/1.1 ${code} ${http.STATUS_CODES[code]}\r\n` +
4429
- Object.keys(headers)
4430
- .map((h) => `${h}: ${headers[h]}`)
4431
- .join('\r\n') +
4432
- '\r\n\r\n' +
4433
- message
4434
- );
4435
- }
5053
+ socket.once('finish', socket.destroy);
4436
5054
 
4437
- socket.removeListener('error', socketOnError);
4438
- socket.destroy();
5055
+ socket.end(
5056
+ `HTTP/1.1 ${code} ${http.STATUS_CODES[code]}\r\n` +
5057
+ Object.keys(headers)
5058
+ .map((h) => `${h}: ${headers[h]}`)
5059
+ .join('\r\n') +
5060
+ '\r\n\r\n' +
5061
+ message
5062
+ );
4439
5063
  }
4440
5064
 
4441
5065
  /**
4442
- * Remove whitespace characters from both ends of a string.
5066
+ * Emit a `'wsClientError'` event on a `WebSocketServer` if there is at least
5067
+ * one listener for it, otherwise call `abortHandshake()`.
4443
5068
  *
4444
- * @param {String} str The string
4445
- * @return {String} A new string representing `str` stripped of whitespace
4446
- * characters from both its beginning and end
5069
+ * @param {WebSocketServer} server The WebSocket server
5070
+ * @param {http.IncomingMessage} req The request object
5071
+ * @param {Duplex} socket The socket of the upgrade request
5072
+ * @param {Number} code The HTTP response status code
5073
+ * @param {String} message The HTTP response body
5074
+ * @param {Object} [headers] The HTTP response headers
4447
5075
  * @private
4448
5076
  */
4449
- function trim(str) {
4450
- return str.trim();
5077
+ function abortHandshakeOrEmitwsClientError(
5078
+ server,
5079
+ req,
5080
+ socket,
5081
+ code,
5082
+ message,
5083
+ headers
5084
+ ) {
5085
+ if (server.listenerCount('wsClientError')) {
5086
+ const err = new Error(message);
5087
+ Error.captureStackTrace(err, abortHandshakeOrEmitwsClientError);
5088
+
5089
+ server.emit('wsClientError', err, socket, req);
5090
+ } else {
5091
+ abortHandshake(socket, code, message, headers);
5092
+ }
4451
5093
  }
4452
5094
 
4453
- const WebSocket = websocket;
4454
-
4455
- WebSocket.createWebSocketStream = stream;
4456
- WebSocket.Server = websocketServer;
4457
- WebSocket.Receiver = receiver;
4458
- WebSocket.Sender = sender;
4459
-
4460
- var ws = WebSocket;
4461
-
4462
- var ws$1 = /*@__PURE__*/getDefaultExportFromCjs(ws);
5095
+ var websocketServer_default = /*@__PURE__*/getDefaultExportFromCjs(websocketServer);
4463
5096
 
4464
5097
  /**
4465
5098
  * Re-exports WebSocket and WebSocketServer from the ws package.
4466
5099
  *
4467
- * ws is a CJS module named exports like WebSocketServer aren't available
4468
- * at runtime when Node auto-detects ESM from tsc output. This module works
4469
- * around that by extracting them from the default export and re-exporting
4470
- * them as merged type+value pairs.
5100
+ * ws is a CJS module with different shapes for default and namespace imports.
5101
+ * This module normalizes those imports and re-exports them as merged type+value pairs.
4471
5102
  */
4472
- const WebSocketServer = ws$1.Server;
5103
+ const WebSocketServer = websocketServer_default;
4473
5104
 
4474
5105
  const CONFIG_FILENAME = ".babyloninspector";
4475
5106
  const DefaultBrowserPort = 4400;