undici 7.15.0 → 7.17.0

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.
Files changed (93) hide show
  1. package/README.md +48 -2
  2. package/docs/docs/api/Agent.md +1 -0
  3. package/docs/docs/api/Client.md +1 -0
  4. package/docs/docs/api/DiagnosticsChannel.md +57 -0
  5. package/docs/docs/api/Dispatcher.md +86 -0
  6. package/docs/docs/api/Errors.md +0 -1
  7. package/docs/docs/api/RoundRobinPool.md +145 -0
  8. package/docs/docs/api/WebSocket.md +21 -0
  9. package/docs/docs/best-practices/crawling.md +58 -0
  10. package/index-fetch.js +2 -2
  11. package/index.js +8 -9
  12. package/lib/api/api-request.js +22 -8
  13. package/lib/api/api-upgrade.js +2 -1
  14. package/lib/api/readable.js +7 -5
  15. package/lib/core/connect.js +4 -1
  16. package/lib/core/diagnostics.js +28 -1
  17. package/lib/core/errors.js +217 -13
  18. package/lib/core/request.js +5 -1
  19. package/lib/core/symbols.js +3 -0
  20. package/lib/core/util.js +61 -41
  21. package/lib/dispatcher/agent.js +19 -7
  22. package/lib/dispatcher/balanced-pool.js +10 -0
  23. package/lib/dispatcher/client-h1.js +18 -23
  24. package/lib/dispatcher/client-h2.js +166 -26
  25. package/lib/dispatcher/client.js +64 -59
  26. package/lib/dispatcher/dispatcher-base.js +20 -16
  27. package/lib/dispatcher/env-http-proxy-agent.js +12 -16
  28. package/lib/dispatcher/fixed-queue.js +15 -39
  29. package/lib/dispatcher/h2c-client.js +7 -78
  30. package/lib/dispatcher/pool-base.js +60 -43
  31. package/lib/dispatcher/pool.js +2 -2
  32. package/lib/dispatcher/proxy-agent.js +27 -11
  33. package/lib/dispatcher/round-robin-pool.js +137 -0
  34. package/lib/encoding/index.js +33 -0
  35. package/lib/global.js +19 -1
  36. package/lib/handler/cache-handler.js +84 -27
  37. package/lib/handler/deduplication-handler.js +216 -0
  38. package/lib/handler/retry-handler.js +0 -2
  39. package/lib/interceptor/cache.js +94 -15
  40. package/lib/interceptor/decompress.js +2 -1
  41. package/lib/interceptor/deduplicate.js +109 -0
  42. package/lib/interceptor/dns.js +55 -13
  43. package/lib/mock/mock-agent.js +4 -4
  44. package/lib/mock/mock-errors.js +10 -0
  45. package/lib/mock/mock-utils.js +13 -12
  46. package/lib/mock/snapshot-agent.js +11 -5
  47. package/lib/mock/snapshot-recorder.js +12 -4
  48. package/lib/mock/snapshot-utils.js +4 -4
  49. package/lib/util/cache.js +29 -1
  50. package/lib/util/date.js +534 -140
  51. package/lib/util/runtime-features.js +124 -0
  52. package/lib/web/cookies/index.js +1 -1
  53. package/lib/web/cookies/parse.js +1 -1
  54. package/lib/web/eventsource/eventsource-stream.js +2 -2
  55. package/lib/web/eventsource/eventsource.js +34 -29
  56. package/lib/web/eventsource/util.js +1 -9
  57. package/lib/web/fetch/body.js +45 -61
  58. package/lib/web/fetch/data-url.js +12 -160
  59. package/lib/web/fetch/formdata-parser.js +204 -127
  60. package/lib/web/fetch/index.js +21 -19
  61. package/lib/web/fetch/request.js +6 -0
  62. package/lib/web/fetch/response.js +4 -7
  63. package/lib/web/fetch/util.js +10 -79
  64. package/lib/web/infra/index.js +229 -0
  65. package/lib/web/subresource-integrity/subresource-integrity.js +6 -5
  66. package/lib/web/webidl/index.js +207 -44
  67. package/lib/web/websocket/connection.js +33 -22
  68. package/lib/web/websocket/events.js +1 -1
  69. package/lib/web/websocket/frame.js +9 -15
  70. package/lib/web/websocket/stream/websocketerror.js +22 -1
  71. package/lib/web/websocket/stream/websocketstream.js +17 -8
  72. package/lib/web/websocket/util.js +2 -1
  73. package/lib/web/websocket/websocket.js +32 -42
  74. package/package.json +9 -7
  75. package/types/agent.d.ts +2 -1
  76. package/types/api.d.ts +2 -2
  77. package/types/balanced-pool.d.ts +2 -1
  78. package/types/cache-interceptor.d.ts +1 -0
  79. package/types/client.d.ts +1 -1
  80. package/types/connector.d.ts +2 -2
  81. package/types/diagnostics-channel.d.ts +2 -2
  82. package/types/dispatcher.d.ts +12 -12
  83. package/types/errors.d.ts +5 -15
  84. package/types/fetch.d.ts +4 -4
  85. package/types/formdata.d.ts +1 -1
  86. package/types/h2c-client.d.ts +1 -1
  87. package/types/index.d.ts +9 -1
  88. package/types/interceptors.d.ts +36 -2
  89. package/types/pool.d.ts +1 -1
  90. package/types/readable.d.ts +2 -2
  91. package/types/round-robin-pool.d.ts +41 -0
  92. package/types/webidl.d.ts +82 -21
  93. package/types/websocket.d.ts +9 -9
@@ -1,7 +1,7 @@
1
1
  'use strict'
2
2
 
3
3
  const { types, inspect } = require('node:util')
4
- const { markAsUncloneable } = require('node:worker_threads')
4
+ const { runtimeFeatures } = require('../../util/runtime-features')
5
5
 
6
6
  const UNDEFINED = 1
7
7
  const BOOLEAN = 2
@@ -157,10 +157,12 @@ webidl.util.TypeValueToString = function (o) {
157
157
  }
158
158
  }
159
159
 
160
- webidl.util.markAsUncloneable = markAsUncloneable || (() => {})
160
+ webidl.util.markAsUncloneable = runtimeFeatures.has('markAsUncloneable')
161
+ ? require('node:worker_threads').markAsUncloneable
162
+ : () => {}
161
163
 
162
164
  // https://webidl.spec.whatwg.org/#abstract-opdef-converttoint
163
- webidl.util.ConvertToInt = function (V, bitLength, signedness, opts) {
165
+ webidl.util.ConvertToInt = function (V, bitLength, signedness, flags) {
164
166
  let upperBound
165
167
  let lowerBound
166
168
 
@@ -204,7 +206,7 @@ webidl.util.ConvertToInt = function (V, bitLength, signedness, opts) {
204
206
 
205
207
  // 6. If the conversion is to an IDL type associated
206
208
  // with the [EnforceRange] extended attribute, then:
207
- if (opts?.enforceRange === true) {
209
+ if (webidl.util.HasFlag(flags, webidl.attributes.EnforceRange)) {
208
210
  // 1. If x is NaN, +∞, or −∞, then throw a TypeError.
209
211
  if (
210
212
  Number.isNaN(x) ||
@@ -236,7 +238,7 @@ webidl.util.ConvertToInt = function (V, bitLength, signedness, opts) {
236
238
  // 7. If x is not NaN and the conversion is to an IDL
237
239
  // type associated with the [Clamp] extended
238
240
  // attribute, then:
239
- if (!Number.isNaN(x) && opts?.clamp === true) {
241
+ if (!Number.isNaN(x) && webidl.util.HasFlag(flags, webidl.attributes.Clamp)) {
240
242
  // 1. Set x to min(max(x, lowerBound), upperBound).
241
243
  x = Math.min(Math.max(x, lowerBound), upperBound)
242
244
 
@@ -310,6 +312,25 @@ webidl.util.Stringify = function (V) {
310
312
  }
311
313
  }
312
314
 
315
+ webidl.util.IsResizableArrayBuffer = function (V) {
316
+ if (types.isArrayBuffer(V)) {
317
+ return V.resizable
318
+ }
319
+
320
+ if (types.isSharedArrayBuffer(V)) {
321
+ return V.growable
322
+ }
323
+
324
+ throw webidl.errors.exception({
325
+ header: 'IsResizableArrayBuffer',
326
+ message: `"${webidl.util.Stringify(V)}" is not an array buffer.`
327
+ })
328
+ }
329
+
330
+ webidl.util.HasFlag = function (flags, attributes) {
331
+ return typeof flags === 'number' && (flags & attributes) === attributes
332
+ }
333
+
313
334
  // https://webidl.spec.whatwg.org/#es-sequence
314
335
  webidl.sequenceConverter = function (converter) {
315
336
  return (V, prefix, argument, Iterable) => {
@@ -514,13 +535,20 @@ webidl.is.URL = webidl.util.MakeTypeAssertion(URL)
514
535
  webidl.is.AbortSignal = webidl.util.MakeTypeAssertion(AbortSignal)
515
536
  webidl.is.MessagePort = webidl.util.MakeTypeAssertion(MessagePort)
516
537
 
538
+ webidl.is.BufferSource = function (V) {
539
+ return types.isArrayBuffer(V) || (
540
+ ArrayBuffer.isView(V) &&
541
+ types.isArrayBuffer(V.buffer)
542
+ )
543
+ }
544
+
517
545
  // https://webidl.spec.whatwg.org/#es-DOMString
518
- webidl.converters.DOMString = function (V, prefix, argument, opts) {
546
+ webidl.converters.DOMString = function (V, prefix, argument, flags) {
519
547
  // 1. If V is null and the conversion is to an IDL type
520
548
  // associated with the [LegacyNullToEmptyString]
521
549
  // extended attribute, then return the DOMString value
522
550
  // that represents the empty string.
523
- if (V === null && opts?.legacyNullToEmptyString) {
551
+ if (V === null && webidl.util.HasFlag(flags, webidl.attributes.LegacyNullToEmptyString)) {
524
552
  return ''
525
553
  }
526
554
 
@@ -599,7 +627,7 @@ webidl.converters.any = function (V) {
599
627
  // https://webidl.spec.whatwg.org/#es-long-long
600
628
  webidl.converters['long long'] = function (V, prefix, argument) {
601
629
  // 1. Let x be ? ConvertToInt(V, 64, "signed").
602
- const x = webidl.util.ConvertToInt(V, 64, 'signed', undefined, prefix, argument)
630
+ const x = webidl.util.ConvertToInt(V, 64, 'signed', 0, prefix, argument)
603
631
 
604
632
  // 2. Return the IDL long long value that represents
605
633
  // the same numeric value as x.
@@ -609,7 +637,7 @@ webidl.converters['long long'] = function (V, prefix, argument) {
609
637
  // https://webidl.spec.whatwg.org/#es-unsigned-long-long
610
638
  webidl.converters['unsigned long long'] = function (V, prefix, argument) {
611
639
  // 1. Let x be ? ConvertToInt(V, 64, "unsigned").
612
- const x = webidl.util.ConvertToInt(V, 64, 'unsigned', undefined, prefix, argument)
640
+ const x = webidl.util.ConvertToInt(V, 64, 'unsigned', 0, prefix, argument)
613
641
 
614
642
  // 2. Return the IDL unsigned long long value that
615
643
  // represents the same numeric value as x.
@@ -619,7 +647,7 @@ webidl.converters['unsigned long long'] = function (V, prefix, argument) {
619
647
  // https://webidl.spec.whatwg.org/#es-unsigned-long
620
648
  webidl.converters['unsigned long'] = function (V, prefix, argument) {
621
649
  // 1. Let x be ? ConvertToInt(V, 32, "unsigned").
622
- const x = webidl.util.ConvertToInt(V, 32, 'unsigned', undefined, prefix, argument)
650
+ const x = webidl.util.ConvertToInt(V, 32, 'unsigned', 0, prefix, argument)
623
651
 
624
652
  // 2. Return the IDL unsigned long value that
625
653
  // represents the same numeric value as x.
@@ -627,9 +655,9 @@ webidl.converters['unsigned long'] = function (V, prefix, argument) {
627
655
  }
628
656
 
629
657
  // https://webidl.spec.whatwg.org/#es-unsigned-short
630
- webidl.converters['unsigned short'] = function (V, prefix, argument, opts) {
658
+ webidl.converters['unsigned short'] = function (V, prefix, argument, flags) {
631
659
  // 1. Let x be ? ConvertToInt(V, 16, "unsigned").
632
- const x = webidl.util.ConvertToInt(V, 16, 'unsigned', opts, prefix, argument)
660
+ const x = webidl.util.ConvertToInt(V, 16, 'unsigned', flags, prefix, argument)
633
661
 
634
662
  // 2. Return the IDL unsigned short value that represents
635
663
  // the same numeric value as x.
@@ -637,15 +665,16 @@ webidl.converters['unsigned short'] = function (V, prefix, argument, opts) {
637
665
  }
638
666
 
639
667
  // https://webidl.spec.whatwg.org/#idl-ArrayBuffer
640
- webidl.converters.ArrayBuffer = function (V, prefix, argument, opts) {
641
- // 1. If Type(V) is not Object, or V does not have an
668
+ webidl.converters.ArrayBuffer = function (V, prefix, argument, flags) {
669
+ // 1. If V is not an Object, or V does not have an
642
670
  // [[ArrayBufferData]] internal slot, then throw a
643
671
  // TypeError.
672
+ // 2. If IsSharedArrayBuffer(V) is true, then throw a
673
+ // TypeError.
644
674
  // see: https://tc39.es/ecma262/#sec-properties-of-the-arraybuffer-instances
645
- // see: https://tc39.es/ecma262/#sec-properties-of-the-sharedarraybuffer-instances
646
675
  if (
647
676
  webidl.util.Type(V) !== OBJECT ||
648
- !types.isAnyArrayBuffer(V)
677
+ !types.isArrayBuffer(V)
649
678
  ) {
650
679
  throw webidl.errors.conversionFailed({
651
680
  prefix,
@@ -654,14 +683,38 @@ webidl.converters.ArrayBuffer = function (V, prefix, argument, opts) {
654
683
  })
655
684
  }
656
685
 
657
- // 2. If the conversion is not to an IDL type associated
658
- // with the [AllowShared] extended attribute, and
659
- // IsSharedArrayBuffer(V) is true, then throw a
686
+ // 3. If the conversion is not to an IDL type associated
687
+ // with the [AllowResizable] extended attribute, and
688
+ // IsResizableArrayBuffer(V) is true, then throw a
660
689
  // TypeError.
661
- if (opts?.allowShared === false && types.isSharedArrayBuffer(V)) {
690
+ if (!webidl.util.HasFlag(flags, webidl.attributes.AllowResizable) && webidl.util.IsResizableArrayBuffer(V)) {
662
691
  throw webidl.errors.exception({
663
- header: 'ArrayBuffer',
664
- message: 'SharedArrayBuffer is not allowed.'
692
+ header: prefix,
693
+ message: `${argument} cannot be a resizable ArrayBuffer.`
694
+ })
695
+ }
696
+
697
+ // 4. Return the IDL ArrayBuffer value that is a
698
+ // reference to the same object as V.
699
+ return V
700
+ }
701
+
702
+ // https://webidl.spec.whatwg.org/#idl-SharedArrayBuffer
703
+ webidl.converters.SharedArrayBuffer = function (V, prefix, argument, flags) {
704
+ // 1. If V is not an Object, or V does not have an
705
+ // [[ArrayBufferData]] internal slot, then throw a
706
+ // TypeError.
707
+ // 2. If IsSharedArrayBuffer(V) is false, then throw a
708
+ // TypeError.
709
+ // see: https://tc39.es/ecma262/#sec-properties-of-the-sharedarraybuffer-instances
710
+ if (
711
+ webidl.util.Type(V) !== OBJECT ||
712
+ !types.isSharedArrayBuffer(V)
713
+ ) {
714
+ throw webidl.errors.conversionFailed({
715
+ prefix,
716
+ argument: `${argument} ("${webidl.util.Stringify(V)}")`,
717
+ types: ['SharedArrayBuffer']
665
718
  })
666
719
  }
667
720
 
@@ -669,19 +722,20 @@ webidl.converters.ArrayBuffer = function (V, prefix, argument, opts) {
669
722
  // with the [AllowResizable] extended attribute, and
670
723
  // IsResizableArrayBuffer(V) is true, then throw a
671
724
  // TypeError.
672
- if (V.resizable || V.growable) {
725
+ if (!webidl.util.HasFlag(flags, webidl.attributes.AllowResizable) && webidl.util.IsResizableArrayBuffer(V)) {
673
726
  throw webidl.errors.exception({
674
- header: 'ArrayBuffer',
675
- message: 'Received a resizable ArrayBuffer.'
727
+ header: prefix,
728
+ message: `${argument} cannot be a resizable SharedArrayBuffer.`
676
729
  })
677
730
  }
678
731
 
679
- // 4. Return the IDL ArrayBuffer value that is a
732
+ // 4. Return the IDL SharedArrayBuffer value that is a
680
733
  // reference to the same object as V.
681
734
  return V
682
735
  }
683
736
 
684
- webidl.converters.TypedArray = function (V, T, prefix, name, opts) {
737
+ // https://webidl.spec.whatwg.org/#dfn-typed-array-type
738
+ webidl.converters.TypedArray = function (V, T, prefix, argument, flags) {
685
739
  // 1. Let T be the IDL type V is being converted to.
686
740
 
687
741
  // 2. If Type(V) is not Object, or V does not have a
@@ -694,7 +748,7 @@ webidl.converters.TypedArray = function (V, T, prefix, name, opts) {
694
748
  ) {
695
749
  throw webidl.errors.conversionFailed({
696
750
  prefix,
697
- argument: `${name} ("${webidl.util.Stringify(V)}")`,
751
+ argument: `${argument} ("${webidl.util.Stringify(V)}")`,
698
752
  types: [T.name]
699
753
  })
700
754
  }
@@ -703,10 +757,10 @@ webidl.converters.TypedArray = function (V, T, prefix, name, opts) {
703
757
  // with the [AllowShared] extended attribute, and
704
758
  // IsSharedArrayBuffer(V.[[ViewedArrayBuffer]]) is
705
759
  // true, then throw a TypeError.
706
- if (opts?.allowShared === false && types.isSharedArrayBuffer(V.buffer)) {
760
+ if (!webidl.util.HasFlag(flags, webidl.attributes.AllowShared) && types.isSharedArrayBuffer(V.buffer)) {
707
761
  throw webidl.errors.exception({
708
- header: 'ArrayBuffer',
709
- message: 'SharedArrayBuffer is not allowed.'
762
+ header: prefix,
763
+ message: `${argument} cannot be a view on a shared array buffer.`
710
764
  })
711
765
  }
712
766
 
@@ -714,10 +768,10 @@ webidl.converters.TypedArray = function (V, T, prefix, name, opts) {
714
768
  // with the [AllowResizable] extended attribute, and
715
769
  // IsResizableArrayBuffer(V.[[ViewedArrayBuffer]]) is
716
770
  // true, then throw a TypeError.
717
- if (V.buffer.resizable || V.buffer.growable) {
771
+ if (!webidl.util.HasFlag(flags, webidl.attributes.AllowResizable) && webidl.util.IsResizableArrayBuffer(V.buffer)) {
718
772
  throw webidl.errors.exception({
719
- header: 'ArrayBuffer',
720
- message: 'Received a resizable ArrayBuffer.'
773
+ header: prefix,
774
+ message: `${argument} cannot be a view on a resizable array buffer.`
721
775
  })
722
776
  }
723
777
 
@@ -726,13 +780,15 @@ webidl.converters.TypedArray = function (V, T, prefix, name, opts) {
726
780
  return V
727
781
  }
728
782
 
729
- webidl.converters.DataView = function (V, prefix, name, opts) {
783
+ // https://webidl.spec.whatwg.org/#idl-DataView
784
+ webidl.converters.DataView = function (V, prefix, argument, flags) {
730
785
  // 1. If Type(V) is not Object, or V does not have a
731
786
  // [[DataView]] internal slot, then throw a TypeError.
732
787
  if (webidl.util.Type(V) !== OBJECT || !types.isDataView(V)) {
733
- throw webidl.errors.exception({
734
- header: prefix,
735
- message: `${name} is not a DataView.`
788
+ throw webidl.errors.conversionFailed({
789
+ prefix,
790
+ argument: `${argument} ("${webidl.util.Stringify(V)}")`,
791
+ types: ['DataView']
736
792
  })
737
793
  }
738
794
 
@@ -740,10 +796,10 @@ webidl.converters.DataView = function (V, prefix, name, opts) {
740
796
  // with the [AllowShared] extended attribute, and
741
797
  // IsSharedArrayBuffer(V.[[ViewedArrayBuffer]]) is true,
742
798
  // then throw a TypeError.
743
- if (opts?.allowShared === false && types.isSharedArrayBuffer(V.buffer)) {
799
+ if (!webidl.util.HasFlag(flags, webidl.attributes.AllowShared) && types.isSharedArrayBuffer(V.buffer)) {
744
800
  throw webidl.errors.exception({
745
- header: 'ArrayBuffer',
746
- message: 'SharedArrayBuffer is not allowed.'
801
+ header: prefix,
802
+ message: `${argument} cannot be a view on a shared array buffer.`
747
803
  })
748
804
  }
749
805
 
@@ -751,10 +807,10 @@ webidl.converters.DataView = function (V, prefix, name, opts) {
751
807
  // with the [AllowResizable] extended attribute, and
752
808
  // IsResizableArrayBuffer(V.[[ViewedArrayBuffer]]) is
753
809
  // true, then throw a TypeError.
754
- if (V.buffer.resizable || V.buffer.growable) {
810
+ if (!webidl.util.HasFlag(flags, webidl.attributes.AllowResizable) && webidl.util.IsResizableArrayBuffer(V.buffer)) {
755
811
  throw webidl.errors.exception({
756
- header: 'ArrayBuffer',
757
- message: 'Received a resizable ArrayBuffer.'
812
+ header: prefix,
813
+ message: `${argument} cannot be a view on a resizable array buffer.`
758
814
  })
759
815
  }
760
816
 
@@ -763,6 +819,85 @@ webidl.converters.DataView = function (V, prefix, name, opts) {
763
819
  return V
764
820
  }
765
821
 
822
+ // https://webidl.spec.whatwg.org/#ArrayBufferView
823
+ webidl.converters.ArrayBufferView = function (V, prefix, argument, flags) {
824
+ if (
825
+ webidl.util.Type(V) !== OBJECT ||
826
+ !types.isArrayBufferView(V)
827
+ ) {
828
+ throw webidl.errors.conversionFailed({
829
+ prefix,
830
+ argument: `${argument} ("${webidl.util.Stringify(V)}")`,
831
+ types: ['ArrayBufferView']
832
+ })
833
+ }
834
+
835
+ if (!webidl.util.HasFlag(flags, webidl.attributes.AllowShared) && types.isSharedArrayBuffer(V.buffer)) {
836
+ throw webidl.errors.exception({
837
+ header: prefix,
838
+ message: `${argument} cannot be a view on a shared array buffer.`
839
+ })
840
+ }
841
+
842
+ if (!webidl.util.HasFlag(flags, webidl.attributes.AllowResizable) && webidl.util.IsResizableArrayBuffer(V.buffer)) {
843
+ throw webidl.errors.exception({
844
+ header: prefix,
845
+ message: `${argument} cannot be a view on a resizable array buffer.`
846
+ })
847
+ }
848
+
849
+ return V
850
+ }
851
+
852
+ // https://webidl.spec.whatwg.org/#BufferSource
853
+ webidl.converters.BufferSource = function (V, prefix, argument, flags) {
854
+ if (types.isArrayBuffer(V)) {
855
+ return webidl.converters.ArrayBuffer(V, prefix, argument, flags)
856
+ }
857
+
858
+ if (types.isArrayBufferView(V)) {
859
+ flags &= ~webidl.attributes.AllowShared
860
+
861
+ return webidl.converters.ArrayBufferView(V, prefix, argument, flags)
862
+ }
863
+
864
+ // Make this explicit for easier debugging
865
+ if (types.isSharedArrayBuffer(V)) {
866
+ throw webidl.errors.exception({
867
+ header: prefix,
868
+ message: `${argument} cannot be a SharedArrayBuffer.`
869
+ })
870
+ }
871
+
872
+ throw webidl.errors.conversionFailed({
873
+ prefix,
874
+ argument: `${argument} ("${webidl.util.Stringify(V)}")`,
875
+ types: ['ArrayBuffer', 'ArrayBufferView']
876
+ })
877
+ }
878
+
879
+ // https://webidl.spec.whatwg.org/#AllowSharedBufferSource
880
+ webidl.converters.AllowSharedBufferSource = function (V, prefix, argument, flags) {
881
+ if (types.isArrayBuffer(V)) {
882
+ return webidl.converters.ArrayBuffer(V, prefix, argument, flags)
883
+ }
884
+
885
+ if (types.isSharedArrayBuffer(V)) {
886
+ return webidl.converters.SharedArrayBuffer(V, prefix, argument, flags)
887
+ }
888
+
889
+ if (types.isArrayBufferView(V)) {
890
+ flags |= webidl.attributes.AllowShared
891
+ return webidl.converters.ArrayBufferView(V, prefix, argument, flags)
892
+ }
893
+
894
+ throw webidl.errors.conversionFailed({
895
+ prefix,
896
+ argument: `${argument} ("${webidl.util.Stringify(V)}")`,
897
+ types: ['ArrayBuffer', 'SharedArrayBuffer', 'ArrayBufferView']
898
+ })
899
+ }
900
+
766
901
  webidl.converters['sequence<ByteString>'] = webidl.sequenceConverter(
767
902
  webidl.converters.ByteString
768
903
  )
@@ -783,6 +918,34 @@ webidl.converters.AbortSignal = webidl.interfaceConverter(
783
918
  'AbortSignal'
784
919
  )
785
920
 
921
+ /**
922
+ * [LegacyTreatNonObjectAsNull]
923
+ * callback EventHandlerNonNull = any (Event event);
924
+ * typedef EventHandlerNonNull? EventHandler;
925
+ * @param {*} V
926
+ */
927
+ webidl.converters.EventHandlerNonNull = function (V) {
928
+ if (webidl.util.Type(V) !== OBJECT) {
929
+ return null
930
+ }
931
+
932
+ // [I]f the value is not an object, it will be converted to null, and if the value is not callable,
933
+ // it will be converted to a callback function value that does nothing when called.
934
+ if (typeof V === 'function') {
935
+ return V
936
+ }
937
+
938
+ return () => {}
939
+ }
940
+
941
+ webidl.attributes = {
942
+ Clamp: 1 << 0,
943
+ EnforceRange: 1 << 1,
944
+ AllowShared: 1 << 2,
945
+ AllowResizable: 1 << 3,
946
+ LegacyNullToEmptyString: 1 << 4
947
+ }
948
+
786
949
  module.exports = {
787
950
  webidl
788
951
  }
@@ -1,22 +1,20 @@
1
1
  'use strict'
2
2
 
3
3
  const { uid, states, sentCloseFrameState, emptyBuffer, opcodes } = require('./constants')
4
- const { parseExtensions, isClosed, isClosing, isEstablished, validateCloseCodeAndReason } = require('./util')
4
+ const { parseExtensions, isClosed, isClosing, isEstablished, isConnecting, validateCloseCodeAndReason } = require('./util')
5
5
  const { makeRequest } = require('../fetch/request')
6
6
  const { fetching } = require('../fetch/index')
7
7
  const { Headers, getHeadersList } = require('../fetch/headers')
8
8
  const { getDecodeSplit } = require('../fetch/util')
9
9
  const { WebsocketFrameSend } = require('./frame')
10
10
  const assert = require('node:assert')
11
+ const { runtimeFeatures } = require('../../util/runtime-features')
11
12
 
12
- /** @type {import('crypto')} */
13
- let crypto
14
- try {
15
- crypto = require('node:crypto')
16
- /* c8 ignore next 3 */
17
- } catch {
13
+ const crypto = runtimeFeatures.has('crypto')
14
+ ? require('node:crypto')
15
+ : null
18
16
 
19
- }
17
+ let warningEmitted = false
20
18
 
21
19
  /**
22
20
  * @see https://websockets.spec.whatwg.org/#concept-websocket-establish
@@ -95,17 +93,27 @@ function establishWebSocketConnection (url, protocols, client, handler, options)
95
93
  useParallelQueue: true,
96
94
  dispatcher: options.dispatcher,
97
95
  processResponse (response) {
98
- if (response.type === 'error') {
99
- // If the WebSocket connection could not be established, it is also said
100
- // that _The WebSocket Connection is Closed_, but not _cleanly_.
101
- handler.readyState = states.CLOSED
102
- }
103
-
104
96
  // 1. If response is a network error or its status is not 101,
105
97
  // fail the WebSocket connection.
98
+ // if (response.type === 'error' || ((response.socket?.session != null && response.status !== 200) && response.status !== 101)) {
106
99
  if (response.type === 'error' || response.status !== 101) {
107
- failWebsocketConnection(handler, 1002, 'Received network error or non-101 status code.', response.error)
108
- return
100
+ // The presence of a session property on the socket indicates HTTP2
101
+ // HTTP1
102
+ if (response.socket?.session == null) {
103
+ failWebsocketConnection(handler, 1002, 'Received network error or non-101 status code.', response.error)
104
+ return
105
+ }
106
+
107
+ // HTTP2
108
+ if (response.status !== 200) {
109
+ failWebsocketConnection(handler, 1002, 'Received network error or non-200 status code.', response.error)
110
+ return
111
+ }
112
+ }
113
+
114
+ if (warningEmitted === false && response.socket?.session != null) {
115
+ process.emitWarning('WebSocket over HTTP2 is experimental, and subject to change.', 'ExperimentalWarning')
116
+ warningEmitted = true
109
117
  }
110
118
 
111
119
  // 2. If protocols is not the empty list and extracting header
@@ -127,7 +135,8 @@ function establishWebSocketConnection (url, protocols, client, handler, options)
127
135
  // header field contains a value that is not an ASCII case-
128
136
  // insensitive match for the value "websocket", the client MUST
129
137
  // _Fail the WebSocket Connection_.
130
- if (response.headersList.get('Upgrade')?.toLowerCase() !== 'websocket') {
138
+ // For H2, no upgrade header is expected.
139
+ if (response.socket.session == null && response.headersList.get('Upgrade')?.toLowerCase() !== 'websocket') {
131
140
  failWebsocketConnection(handler, 1002, 'Server did not set Upgrade header to "websocket".')
132
141
  return
133
142
  }
@@ -136,7 +145,8 @@ function establishWebSocketConnection (url, protocols, client, handler, options)
136
145
  // |Connection| header field doesn't contain a token that is an
137
146
  // ASCII case-insensitive match for the value "Upgrade", the client
138
147
  // MUST _Fail the WebSocket Connection_.
139
- if (response.headersList.get('Connection')?.toLowerCase() !== 'upgrade') {
148
+ // For H2, no connection header is expected.
149
+ if (response.socket.session == null && response.headersList.get('Connection')?.toLowerCase() !== 'upgrade') {
140
150
  failWebsocketConnection(handler, 1002, 'Server did not set Connection header to "upgrade".')
141
151
  return
142
152
  }
@@ -149,7 +159,7 @@ function establishWebSocketConnection (url, protocols, client, handler, options)
149
159
  // trailing whitespace, the client MUST _Fail the WebSocket
150
160
  // Connection_.
151
161
  const secWSAccept = response.headersList.get('Sec-WebSocket-Accept')
152
- const digest = crypto.createHash('sha1').update(keyValue + uid).digest('base64')
162
+ const digest = crypto.hash('sha1', keyValue + uid, 'base64')
153
163
  if (secWSAccept !== digest) {
154
164
  failWebsocketConnection(handler, 1002, 'Incorrect hash received in Sec-WebSocket-Accept header.')
155
165
  return
@@ -303,11 +313,12 @@ function failWebsocketConnection (handler, code, reason, cause) {
303
313
 
304
314
  handler.controller.abort()
305
315
 
306
- if (handler.socket?.destroyed === false) {
316
+ if (isConnecting(handler.readyState)) {
317
+ // If the connection was not established, we must still emit an 'error' and 'close' events
318
+ handler.onSocketClose()
319
+ } else if (handler.socket?.destroyed === false) {
307
320
  handler.socket.destroy()
308
321
  }
309
-
310
- handler.onFail(code, reason, cause)
311
322
  }
312
323
 
313
324
  module.exports = {
@@ -272,7 +272,7 @@ webidl.converters.MessageEventInit = webidl.dictionaryConverter([
272
272
  {
273
273
  key: 'ports',
274
274
  converter: webidl.converters['sequence<MessagePort>'],
275
- defaultValue: () => new Array(0)
275
+ defaultValue: () => []
276
276
  }
277
277
  ])
278
278
 
@@ -1,33 +1,27 @@
1
1
  'use strict'
2
2
 
3
+ const { runtimeFeatures } = require('../../util/runtime-features')
3
4
  const { maxUnsigned16Bit, opcodes } = require('./constants')
4
5
 
5
6
  const BUFFER_SIZE = 8 * 1024
6
7
 
7
- /** @type {import('crypto')} */
8
- let crypto
9
8
  let buffer = null
10
9
  let bufIdx = BUFFER_SIZE
11
10
 
12
- try {
13
- crypto = require('node:crypto')
14
- /* c8 ignore next 3 */
15
- } catch {
16
- crypto = {
17
- // not full compatibility, but minimum.
18
- randomFillSync: function randomFillSync (buffer, _offset, _size) {
19
- for (let i = 0; i < buffer.length; ++i) {
20
- buffer[i] = Math.random() * 255 | 0
21
- }
22
- return buffer
11
+ const randomFillSync = runtimeFeatures.has('crypto')
12
+ ? require('node:crypto').randomFillSync
13
+ // not full compatibility, but minimum.
14
+ : function randomFillSync (buffer, _offset, _size) {
15
+ for (let i = 0; i < buffer.length; ++i) {
16
+ buffer[i] = Math.random() * 255 | 0
23
17
  }
18
+ return buffer
24
19
  }
25
- }
26
20
 
27
21
  function generateMask () {
28
22
  if (bufIdx === BUFFER_SIZE) {
29
23
  bufIdx = 0
30
- crypto.randomFillSync((buffer ??= Buffer.allocUnsafeSlow(BUFFER_SIZE)), 0, BUFFER_SIZE)
24
+ randomFillSync((buffer ??= Buffer.allocUnsafeSlow(BUFFER_SIZE)), 0, BUFFER_SIZE)
31
25
  }
32
26
  return [buffer[bufIdx++], buffer[bufIdx++], buffer[bufIdx++], buffer[bufIdx++]]
33
27
  }
@@ -5,7 +5,28 @@ const { validateCloseCodeAndReason } = require('../util')
5
5
  const { kConstruct } = require('../../../core/symbols')
6
6
  const { kEnumerableProperty } = require('../../../core/util')
7
7
 
8
- class WebSocketError extends DOMException {
8
+ function createInheritableDOMException () {
9
+ // https://github.com/nodejs/node/issues/59677
10
+ class Test extends DOMException {
11
+ get reason () {
12
+ return ''
13
+ }
14
+ }
15
+
16
+ if (new Test().reason !== undefined) {
17
+ return DOMException
18
+ }
19
+
20
+ return new Proxy(DOMException, {
21
+ construct (target, args, newTarget) {
22
+ const instance = Reflect.construct(target, args, target)
23
+ Object.setPrototypeOf(instance, newTarget.prototype)
24
+ return instance
25
+ }
26
+ })
27
+ }
28
+
29
+ class WebSocketError extends createInheritableDOMException() {
9
30
  #closeCode
10
31
  #reason
11
32