undici 6.20.0 → 7.0.0-alpha.2

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 (137) hide show
  1. package/README.md +6 -10
  2. package/docs/docs/api/Agent.md +0 -3
  3. package/docs/docs/api/Client.md +1 -3
  4. package/docs/docs/api/Debug.md +1 -1
  5. package/docs/docs/api/Dispatcher.md +60 -8
  6. package/docs/docs/api/EnvHttpProxyAgent.md +0 -1
  7. package/docs/docs/api/Fetch.md +1 -0
  8. package/docs/docs/api/MockAgent.md +2 -0
  9. package/docs/docs/api/MockPool.md +2 -1
  10. package/docs/docs/api/Pool.md +0 -1
  11. package/docs/docs/api/RetryAgent.md +1 -1
  12. package/docs/docs/api/RetryHandler.md +1 -1
  13. package/docs/docs/api/WebSocket.md +45 -3
  14. package/index.js +6 -6
  15. package/lib/api/abort-signal.js +2 -0
  16. package/lib/api/api-connect.js +3 -1
  17. package/lib/api/api-pipeline.js +7 -6
  18. package/lib/api/api-request.js +32 -47
  19. package/lib/api/api-stream.js +39 -50
  20. package/lib/api/api-upgrade.js +5 -3
  21. package/lib/api/readable.js +261 -64
  22. package/lib/api/util.js +2 -0
  23. package/lib/core/constants.js +11 -9
  24. package/lib/core/diagnostics.js +122 -128
  25. package/lib/core/errors.js +4 -4
  26. package/lib/core/request.js +11 -9
  27. package/lib/core/symbols.js +2 -1
  28. package/lib/core/tree.js +9 -1
  29. package/lib/core/util.js +219 -48
  30. package/lib/dispatcher/agent.js +3 -17
  31. package/lib/dispatcher/balanced-pool.js +5 -8
  32. package/lib/dispatcher/client-h1.js +278 -54
  33. package/lib/dispatcher/client-h2.js +1 -1
  34. package/lib/dispatcher/client.js +23 -34
  35. package/lib/dispatcher/dispatcher-base.js +2 -34
  36. package/lib/dispatcher/dispatcher.js +3 -24
  37. package/lib/dispatcher/fixed-queue.js +91 -49
  38. package/lib/dispatcher/pool-stats.js +2 -0
  39. package/lib/dispatcher/pool.js +3 -6
  40. package/lib/dispatcher/proxy-agent.js +6 -7
  41. package/lib/handler/decorator-handler.js +24 -0
  42. package/lib/handler/redirect-handler.js +11 -2
  43. package/lib/handler/retry-handler.js +12 -3
  44. package/lib/interceptor/dns.js +346 -0
  45. package/lib/interceptor/dump.js +2 -2
  46. package/lib/interceptor/redirect.js +11 -14
  47. package/lib/interceptor/response-error.js +4 -1
  48. package/lib/llhttp/constants.d.ts +97 -0
  49. package/lib/llhttp/constants.js +412 -192
  50. package/lib/llhttp/constants.js.map +1 -0
  51. package/lib/llhttp/llhttp-wasm.js +11 -1
  52. package/lib/llhttp/llhttp_simd-wasm.js +11 -1
  53. package/lib/llhttp/utils.d.ts +2 -0
  54. package/lib/llhttp/utils.js +9 -9
  55. package/lib/llhttp/utils.js.map +1 -0
  56. package/lib/mock/mock-agent.js +5 -8
  57. package/lib/mock/mock-client.js +9 -4
  58. package/lib/mock/mock-errors.js +3 -1
  59. package/lib/mock/mock-interceptor.js +8 -6
  60. package/lib/mock/mock-pool.js +9 -4
  61. package/lib/mock/mock-symbols.js +3 -1
  62. package/lib/mock/mock-utils.js +29 -5
  63. package/lib/web/cache/cache.js +24 -21
  64. package/lib/web/cache/cachestorage.js +1 -1
  65. package/lib/web/cookies/index.js +17 -13
  66. package/lib/web/cookies/parse.js +2 -2
  67. package/lib/web/eventsource/eventsource-stream.js +9 -8
  68. package/lib/web/eventsource/eventsource.js +10 -6
  69. package/lib/web/fetch/body.js +42 -36
  70. package/lib/web/fetch/constants.js +35 -26
  71. package/lib/web/fetch/data-url.js +1 -1
  72. package/lib/web/fetch/formdata-parser.js +2 -2
  73. package/lib/web/fetch/formdata.js +65 -54
  74. package/lib/web/fetch/headers.js +117 -85
  75. package/lib/web/fetch/index.js +55 -62
  76. package/lib/web/fetch/request.js +135 -77
  77. package/lib/web/fetch/response.js +86 -56
  78. package/lib/web/fetch/util.js +90 -64
  79. package/lib/web/fetch/webidl.js +99 -64
  80. package/lib/web/websocket/connection.js +76 -147
  81. package/lib/web/websocket/constants.js +3 -4
  82. package/lib/web/websocket/events.js +4 -2
  83. package/lib/web/websocket/frame.js +45 -3
  84. package/lib/web/websocket/receiver.js +29 -33
  85. package/lib/web/websocket/sender.js +18 -13
  86. package/lib/web/websocket/stream/websocketerror.js +83 -0
  87. package/lib/web/websocket/stream/websocketstream.js +485 -0
  88. package/lib/web/websocket/util.js +128 -77
  89. package/lib/web/websocket/websocket.js +234 -135
  90. package/package.json +20 -33
  91. package/scripts/strip-comments.js +3 -1
  92. package/types/agent.d.ts +7 -7
  93. package/types/api.d.ts +24 -24
  94. package/types/balanced-pool.d.ts +11 -11
  95. package/types/client.d.ts +11 -12
  96. package/types/diagnostics-channel.d.ts +10 -10
  97. package/types/dispatcher.d.ts +96 -97
  98. package/types/env-http-proxy-agent.d.ts +2 -2
  99. package/types/errors.d.ts +53 -47
  100. package/types/fetch.d.ts +8 -8
  101. package/types/formdata.d.ts +7 -7
  102. package/types/global-dispatcher.d.ts +4 -4
  103. package/types/global-origin.d.ts +5 -5
  104. package/types/handlers.d.ts +4 -4
  105. package/types/header.d.ts +157 -1
  106. package/types/index.d.ts +42 -46
  107. package/types/interceptors.d.ts +22 -8
  108. package/types/mock-agent.d.ts +21 -18
  109. package/types/mock-client.d.ts +4 -4
  110. package/types/mock-errors.d.ts +3 -3
  111. package/types/mock-interceptor.d.ts +19 -19
  112. package/types/mock-pool.d.ts +4 -4
  113. package/types/patch.d.ts +0 -4
  114. package/types/pool-stats.d.ts +8 -8
  115. package/types/pool.d.ts +12 -12
  116. package/types/proxy-agent.d.ts +4 -4
  117. package/types/readable.d.ts +22 -14
  118. package/types/retry-agent.d.ts +1 -1
  119. package/types/retry-handler.d.ts +8 -8
  120. package/types/util.d.ts +3 -3
  121. package/types/utility.d.ts +7 -0
  122. package/types/webidl.d.ts +44 -6
  123. package/types/websocket.d.ts +34 -1
  124. package/docs/docs/api/DispatchInterceptor.md +0 -60
  125. package/lib/interceptor/redirect-interceptor.js +0 -21
  126. package/lib/mock/pluralizer.js +0 -29
  127. package/lib/web/cache/symbols.js +0 -5
  128. package/lib/web/fetch/file.js +0 -126
  129. package/lib/web/fetch/symbols.js +0 -9
  130. package/lib/web/fileapi/encoding.js +0 -290
  131. package/lib/web/fileapi/filereader.js +0 -344
  132. package/lib/web/fileapi/progressevent.js +0 -78
  133. package/lib/web/fileapi/symbols.js +0 -10
  134. package/lib/web/fileapi/util.js +0 -391
  135. package/lib/web/websocket/symbols.js +0 -12
  136. package/types/file.d.ts +0 -39
  137. package/types/filereader.d.ts +0 -54
@@ -13,19 +13,18 @@ const { webidl } = require('./webidl')
13
13
  const assert = require('node:assert')
14
14
  const util = require('node:util')
15
15
 
16
- const kHeadersMap = Symbol('headers map')
17
- const kHeadersSortedMap = Symbol('headers map sorted')
18
-
19
16
  /**
20
17
  * @param {number} code
18
+ * @returns {code is (0x0a | 0x0d | 0x09 | 0x20)}
21
19
  */
22
20
  function isHTTPWhiteSpaceCharCode (code) {
23
- return code === 0x00a || code === 0x00d || code === 0x009 || code === 0x020
21
+ return code === 0x0a || code === 0x0d || code === 0x09 || code === 0x20
24
22
  }
25
23
 
26
24
  /**
27
25
  * @see https://fetch.spec.whatwg.org/#concept-header-value-normalize
28
26
  * @param {string} potentialValue
27
+ * @returns {string}
29
28
  */
30
29
  function headerValueNormalize (potentialValue) {
31
30
  // To normalize a byte sequence potentialValue, remove
@@ -39,6 +38,10 @@ function headerValueNormalize (potentialValue) {
39
38
  return i === 0 && j === potentialValue.length ? potentialValue : potentialValue.substring(i, j)
40
39
  }
41
40
 
41
+ /**
42
+ * @param {Headers} headers
43
+ * @param {Array|Object} object
44
+ */
42
45
  function fill (headers, object) {
43
46
  // To fill a Headers object headers with a given object object, run these steps:
44
47
 
@@ -78,6 +81,9 @@ function fill (headers, object) {
78
81
 
79
82
  /**
80
83
  * @see https://fetch.spec.whatwg.org/#concept-headers-append
84
+ * @param {Headers} headers
85
+ * @param {string} name
86
+ * @param {string} value
81
87
  */
82
88
  function appendHeader (headers, name, value) {
83
89
  // 1. Normalize value.
@@ -119,6 +125,67 @@ function appendHeader (headers, name, value) {
119
125
  // privileged no-CORS request headers from headers
120
126
  }
121
127
 
128
+ // https://fetch.spec.whatwg.org/#concept-header-list-sort-and-combine
129
+ /**
130
+ * @param {Headers} target
131
+ */
132
+ function headersListSortAndCombine (target) {
133
+ const headersList = getHeadersList(target)
134
+
135
+ if (!headersList) {
136
+ return []
137
+ }
138
+
139
+ if (headersList.sortedMap) {
140
+ return headersList.sortedMap
141
+ }
142
+
143
+ // 1. Let headers be an empty list of headers with the key being the name
144
+ // and value the value.
145
+ const headers = []
146
+
147
+ // 2. Let names be the result of convert header names to a sorted-lowercase
148
+ // set with all the names of the headers in list.
149
+ const names = headersList.toSortedArray()
150
+
151
+ const cookies = headersList.cookies
152
+
153
+ // fast-path
154
+ if (cookies === null || cookies.length === 1) {
155
+ // Note: The non-null assertion of value has already been done by `HeadersList#toSortedArray`
156
+ return (headersList.sortedMap = names)
157
+ }
158
+
159
+ // 3. For each name of names:
160
+ for (let i = 0; i < names.length; ++i) {
161
+ const { 0: name, 1: value } = names[i]
162
+ // 1. If name is `set-cookie`, then:
163
+ if (name === 'set-cookie') {
164
+ // 1. Let values be a list of all values of headers in list whose name
165
+ // is a byte-case-insensitive match for name, in order.
166
+
167
+ // 2. For each value of values:
168
+ // 1. Append (name, value) to headers.
169
+ for (let j = 0; j < cookies.length; ++j) {
170
+ headers.push([name, cookies[j]])
171
+ }
172
+ } else {
173
+ // 2. Otherwise:
174
+
175
+ // 1. Let value be the result of getting name from list.
176
+
177
+ // 2. Assert: value is non-null.
178
+ // Note: This operation was done by `HeadersList#toSortedArray`.
179
+
180
+ // 3. Append (name, value) to headers.
181
+ headers.push([name, value])
182
+ }
183
+ }
184
+
185
+ // 4. Return headers.
186
+ return (headersList.sortedMap = headers)
187
+ }
188
+
122
189
  function compareHeaderName (a, b) {
123
190
  return a[0] < b[0] ? -1 : 1
124
191
  }
@@ -127,14 +194,17 @@ class HeadersList {
127
194
  /** @type {[string, string][]|null} */
128
195
  cookies = null
129
196
 
197
+ sortedMap
198
+ headersMap
199
+
130
200
  constructor (init) {
131
201
  if (init instanceof HeadersList) {
132
- this[kHeadersMap] = new Map(init[kHeadersMap])
133
- this[kHeadersSortedMap] = init[kHeadersSortedMap]
202
+ this.headersMap = new Map(init.headersMap)
203
+ this.sortedMap = init.sortedMap
134
204
  this.cookies = init.cookies === null ? null : [...init.cookies]
135
205
  } else {
136
- this[kHeadersMap] = new Map(init)
137
- this[kHeadersSortedMap] = null
206
+ this.headersMap = new Map(init)
207
+ this.sortedMap = null
138
208
  }
139
209
  }
140
210
 
@@ -148,12 +218,12 @@ class HeadersList {
148
218
  // contains a header whose name is a byte-case-insensitive
149
219
  // match for name.
150
220
 
151
- return this[kHeadersMap].has(isLowerCase ? name : name.toLowerCase())
221
+ return this.headersMap.has(isLowerCase ? name : name.toLowerCase())
152
222
  }
153
223
 
154
224
  clear () {
155
- this[kHeadersMap].clear()
156
- this[kHeadersSortedMap] = null
225
+ this.headersMap.clear()
226
+ this.sortedMap = null
157
227
  this.cookies = null
158
228
  }
159
229
 
@@ -164,22 +234,22 @@ class HeadersList {
164
234
  * @param {boolean} isLowerCase
165
235
  */
166
236
  append (name, value, isLowerCase) {
167
- this[kHeadersSortedMap] = null
237
+ this.sortedMap = null
168
238
 
169
239
  // 1. If list contains name, then set name to the first such
170
240
  // header’s name.
171
241
  const lowercaseName = isLowerCase ? name : name.toLowerCase()
172
- const exists = this[kHeadersMap].get(lowercaseName)
242
+ const exists = this.headersMap.get(lowercaseName)
173
243
 
174
244
  // 2. Append (name, value) to list.
175
245
  if (exists) {
176
246
  const delimiter = lowercaseName === 'cookie' ? '; ' : ', '
177
- this[kHeadersMap].set(lowercaseName, {
247
+ this.headersMap.set(lowercaseName, {
178
248
  name: exists.name,
179
249
  value: `${exists.value}${delimiter}${value}`
180
250
  })
181
251
  } else {
182
- this[kHeadersMap].set(lowercaseName, { name, value })
252
+ this.headersMap.set(lowercaseName, { name, value })
183
253
  }
184
254
 
185
255
  if (lowercaseName === 'set-cookie') {
@@ -194,7 +264,7 @@ class HeadersList {
194
264
  * @param {boolean} isLowerCase
195
265
  */
196
266
  set (name, value, isLowerCase) {
197
- this[kHeadersSortedMap] = null
267
+ this.sortedMap = null
198
268
  const lowercaseName = isLowerCase ? name : name.toLowerCase()
199
269
 
200
270
  if (lowercaseName === 'set-cookie') {
@@ -205,7 +275,7 @@ class HeadersList {
205
275
  // the first such header to value and remove the
206
276
  // others.
207
277
  // 2. Otherwise, append header (name, value) to list.
208
- this[kHeadersMap].set(lowercaseName, { name, value })
278
+ this.headersMap.set(lowercaseName, { name, value })
209
279
  }
210
280
 
211
281
  /**
@@ -214,14 +284,14 @@ class HeadersList {
214
284
  * @param {boolean} isLowerCase
215
285
  */
216
286
  delete (name, isLowerCase) {
217
- this[kHeadersSortedMap] = null
287
+ this.sortedMap = null
218
288
  if (!isLowerCase) name = name.toLowerCase()
219
289
 
220
290
  if (name === 'set-cookie') {
221
291
  this.cookies = null
222
292
  }
223
293
 
224
- this[kHeadersMap].delete(name)
294
+ this.headersMap.delete(name)
225
295
  }
226
296
 
227
297
  /**
@@ -235,12 +305,12 @@ class HeadersList {
235
305
  // 2. Return the values of all headers in list whose name
236
306
  // is a byte-case-insensitive match for name,
237
307
  // separated from each other by 0x2C 0x20, in order.
238
- return this[kHeadersMap].get(isLowerCase ? name : name.toLowerCase())?.value ?? null
308
+ return this.headersMap.get(isLowerCase ? name : name.toLowerCase())?.value ?? null
239
309
  }
240
310
 
241
311
  * [Symbol.iterator] () {
242
312
  // use the lowercased name
243
- for (const { 0: name, 1: { value } } of this[kHeadersMap]) {
313
+ for (const { 0: name, 1: { value } } of this.headersMap) {
244
314
  yield [name, value]
245
315
  }
246
316
  }
@@ -248,8 +318,8 @@ class HeadersList {
248
318
  get entries () {
249
319
  const headers = {}
250
320
 
251
- if (this[kHeadersMap].size !== 0) {
252
- for (const { name, value } of this[kHeadersMap].values()) {
321
+ if (this.headersMap.size !== 0) {
322
+ for (const { name, value } of this.headersMap.values()) {
253
323
  headers[name] = value
254
324
  }
255
325
  }
@@ -258,14 +328,14 @@ class HeadersList {
258
328
  }
259
329
 
260
330
  rawValues () {
261
- return this[kHeadersMap].values()
331
+ return this.headersMap.values()
262
332
  }
263
333
 
264
334
  get entriesList () {
265
335
  const headers = []
266
336
 
267
- if (this[kHeadersMap].size !== 0) {
268
- for (const { 0: lowerName, 1: { name, value } } of this[kHeadersMap]) {
337
+ if (this.headersMap.size !== 0) {
338
+ for (const { 0: lowerName, 1: { name, value } } of this.headersMap) {
269
339
  if (lowerName === 'set-cookie') {
270
340
  for (const cookie of this.cookies) {
271
341
  headers.push([name, cookie])
@@ -281,7 +351,7 @@ class HeadersList {
281
351
 
282
352
  // https://fetch.spec.whatwg.org/#convert-header-names-to-a-sorted-lowercase-set
283
353
  toSortedArray () {
284
- const size = this[kHeadersMap].size
354
+ const size = this.headersMap.size
285
355
  const array = new Array(size)
286
356
  // In most cases, you will use the fast-path.
287
357
  // fast-path: Use binary insertion sort for small arrays.
@@ -292,7 +362,7 @@ class HeadersList {
292
362
  }
293
363
  // Improve performance by unrolling loop and avoiding double-loop.
294
364
  // Double-loop-less version of the binary insertion sort.
295
- const iterator = this[kHeadersMap][Symbol.iterator]()
365
+ const iterator = this.headersMap[Symbol.iterator]()
296
366
  const firstValue = iterator.next().value
297
367
  // set [name, value] to first index.
298
368
  array[0] = [firstValue[0], firstValue[1].value]
@@ -342,7 +412,7 @@ class HeadersList {
342
412
  // This case would be a rare occurrence.
343
413
  // slow-path: fallback
344
414
  let i = 0
345
- for (const { 0: name, 1: { value } } of this[kHeadersMap]) {
415
+ for (const { 0: name, 1: { value } } of this.headersMap) {
346
416
  array[i++] = [name, value]
347
417
  // https://fetch.spec.whatwg.org/#concept-header-list-sort-and-combine
348
418
  // 3.2.2. Assert: value is non-null.
@@ -356,8 +426,15 @@ class HeadersList {
356
426
  // https://fetch.spec.whatwg.org/#headers-class
357
427
  class Headers {
358
428
  #guard
429
+ /**
430
+ * @type {HeadersList}
431
+ */
359
432
  #headersList
360
433
 
434
+ /**
435
+ * @param {HeadersInit|Symbol} [init]
436
+ * @returns
437
+ */
361
438
  constructor (init = undefined) {
362
439
  if (init === kConstruct) {
363
440
  return
@@ -545,58 +622,6 @@ class Headers {
545
622
  return []
546
623
  }
547
624
 
548
- // https://fetch.spec.whatwg.org/#concept-header-list-sort-and-combine
549
- get [kHeadersSortedMap] () {
550
- if (this.#headersList[kHeadersSortedMap]) {
551
- return this.#headersList[kHeadersSortedMap]
552
- }
553
-
554
- // 1. Let headers be an empty list of headers with the key being the name
555
- // and value the value.
556
- const headers = []
557
-
558
- // 2. Let names be the result of convert header names to a sorted-lowercase
559
- // set with all the names of the headers in list.
560
- const names = this.#headersList.toSortedArray()
561
-
562
- const cookies = this.#headersList.cookies
563
-
564
- // fast-path
565
- if (cookies === null || cookies.length === 1) {
566
- // Note: The non-null assertion of value has already been done by `HeadersList#toSortedArray`
567
- return (this.#headersList[kHeadersSortedMap] = names)
568
- }
569
-
570
- // 3. For each name of names:
571
- for (let i = 0; i < names.length; ++i) {
572
- const { 0: name, 1: value } = names[i]
573
- // 1. If name is `set-cookie`, then:
574
- if (name === 'set-cookie') {
575
- // 1. Let values be a list of all values of headers in list whose name
576
- // is a byte-case-insensitive match for name, in order.
577
-
578
- // 2. For each value of values:
579
- // 1. Append (name, value) to headers.
580
- for (let j = 0; j < cookies.length; ++j) {
581
- headers.push([name, cookies[j]])
582
- }
583
- } else {
584
- // 2. Otherwise:
585
-
586
- // 1. Let value be the result of getting name from list.
587
-
588
- // 2. Assert: value is non-null.
589
- // Note: This operation was done by `HeadersList#toSortedArray`.
590
-
591
- // 3. Append (name, value) to headers.
592
- headers.push([name, value])
593
- }
594
- }
595
-
596
- // 4. Return headers.
597
- return (this.#headersList[kHeadersSortedMap] = headers)
598
- }
599
-
600
625
  [util.inspect.custom] (depth, options) {
601
626
  options.depth ??= depth
602
627
 
@@ -611,12 +636,19 @@ class Headers {
611
636
  o.#guard = guard
612
637
  }
613
638
 
639
+ /**
640
+ * @param {Headers} o
641
+ */
614
642
  static getHeadersList (o) {
615
643
  return o.#headersList
616
644
  }
617
645
 
618
- static setHeadersList (o, list) {
619
- o.#headersList = list
646
+ /**
647
+ * @param {Headers} target
648
+ * @param {HeadersList} list
649
+ */
650
+ static setHeadersList (target, list) {
651
+ target.#headersList = list
620
652
  }
621
653
  }
622
654
 
@@ -626,7 +658,7 @@ Reflect.deleteProperty(Headers, 'setHeadersGuard')
626
658
  Reflect.deleteProperty(Headers, 'getHeadersList')
627
659
  Reflect.deleteProperty(Headers, 'setHeadersList')
628
660
 
629
- iteratorMixin('Headers', Headers, kHeadersSortedMap, 0, 1)
661
+ iteratorMixin('Headers', Headers, headersListSortAndCombine, 0, 1)
630
662
 
631
663
  Object.defineProperties(Headers.prototype, {
632
664
  append: kEnumerableProperty,
@@ -645,7 +677,7 @@ Object.defineProperties(Headers.prototype, {
645
677
  })
646
678
 
647
679
  webidl.converters.HeadersInit = function (V, prefix, argument) {
648
- if (webidl.util.Type(V) === 'Object') {
680
+ if (webidl.util.Type(V) === webidl.util.Types.OBJECT) {
649
681
  const iterator = Reflect.get(V, Symbol.iterator)
650
682
 
651
683
  // A work-around to ensure we send the properly-cased Headers when V is a Headers object.
@@ -7,10 +7,11 @@ const {
7
7
  makeAppropriateNetworkError,
8
8
  filterResponse,
9
9
  makeResponse,
10
- fromInnerResponse
10
+ fromInnerResponse,
11
+ getResponseState
11
12
  } = require('./response')
12
13
  const { HeadersList } = require('./headers')
13
- const { Request, cloneRequest } = require('./request')
14
+ const { Request, cloneRequest, getRequestDispatcher, getRequestState } = require('./request')
14
15
  const zlib = require('node:zlib')
15
16
  const {
16
17
  bytesMatch,
@@ -30,7 +31,6 @@ const {
30
31
  determineRequestsReferrer,
31
32
  coarsenedSharedCurrentTime,
32
33
  createDeferredPromise,
33
- isBlobLike,
34
34
  sameOrigin,
35
35
  isCancelled,
36
36
  isAborted,
@@ -47,7 +47,6 @@ const {
47
47
  createInflate,
48
48
  extractMimeType
49
49
  } = require('./util')
50
- const { kState, kDispatcher } = require('./symbols')
51
50
  const assert = require('node:assert')
52
51
  const { safelyExtractBody, extractBody } = require('./body')
53
52
  const {
@@ -58,8 +57,8 @@ const {
58
57
  subresourceSet
59
58
  } = require('./constants')
60
59
  const EE = require('node:events')
61
- const { Readable, pipeline, finished } = require('node:stream')
62
- const { addAbortListener, isErrored, isReadable, bufferToLowerCasedHeaderName } = require('../../core/util')
60
+ const { Readable, pipeline, finished, isErrored, isReadable } = require('node:stream')
61
+ const { addAbortListener, bufferToLowerCasedHeaderName } = require('../../core/util')
63
62
  const { dataURLProcessor, serializeAMimeType, minimizeSupportedMimeType } = require('./data-url')
64
63
  const { getGlobalDispatcher } = require('../../global')
65
64
  const { webidl } = require('./webidl')
@@ -144,7 +143,7 @@ function fetch (input, init = undefined) {
144
143
  }
145
144
 
146
145
  // 3. Let request be requestObject’s request.
147
- const request = requestObject[kState]
146
+ const request = getRequestState(requestObject)
148
147
 
149
148
  // 4. If requestObject’s signal’s aborted flag is set, then:
150
149
  if (requestObject.signal.aborted) {
@@ -244,7 +243,7 @@ function fetch (input, init = undefined) {
244
243
  request,
245
244
  processResponseEndOfBody: handleFetchDone,
246
245
  processResponse,
247
- dispatcher: requestObject[kDispatcher] // undici
246
+ dispatcher: getRequestDispatcher(requestObject) // undici
248
247
  })
249
248
 
250
249
  // 14. Return p.
@@ -327,7 +326,7 @@ function abortFetch (p, request, responseObject, error) {
327
326
 
328
327
  // 2. If request’s body is not null and is readable, then cancel request’s
329
328
  // body with error.
330
- if (request.body != null && isReadable(request.body?.stream)) {
329
+ if (request.body?.stream != null && isReadable(request.body.stream)) {
331
330
  request.body.stream.cancel(error).catch((err) => {
332
331
  if (err.code === 'ERR_INVALID_STATE') {
333
332
  // Node bug?
@@ -343,11 +342,11 @@ function abortFetch (p, request, responseObject, error) {
343
342
  }
344
343
 
345
344
  // 4. Let response be responseObject’s response.
346
- const response = responseObject[kState]
345
+ const response = getResponseState(responseObject)
347
346
 
348
347
  // 5. If response’s body is not null and is readable, then error response’s
349
348
  // body with error.
350
- if (response.body != null && isReadable(response.body?.stream)) {
349
+ if (response.body?.stream != null && isReadable(response.body.stream)) {
351
350
  response.body.stream.cancel(error).catch((err) => {
352
351
  if (err.code === 'ERR_INVALID_STATE') {
353
352
  // Node bug?
@@ -572,53 +571,46 @@ async function mainFetch (fetchParams, recursive = false) {
572
571
  // 11. If response is null, then set response to the result of running
573
572
  // the steps corresponding to the first matching statement:
574
573
  if (response === null) {
575
- response = await (async () => {
576
- const currentURL = requestCurrentURL(request)
577
-
578
- if (
579
- // - request’s current URL’s origin is same origin with request’s origin,
580
- // and request’s response tainting is "basic"
581
- (sameOrigin(currentURL, request.url) && request.responseTainting === 'basic') ||
582
- // request’s current URL’s scheme is "data"
583
- (currentURL.protocol === 'data:') ||
584
- // - request’s mode is "navigate" or "websocket"
585
- (request.mode === 'navigate' || request.mode === 'websocket')
586
- ) {
587
- // 1. Set request’s response tainting to "basic".
588
- request.responseTainting = 'basic'
589
-
590
- // 2. Return the result of running scheme fetch given fetchParams.
591
- return await schemeFetch(fetchParams)
592
- }
593
-
594
- // request’s mode is "same-origin"
595
- if (request.mode === 'same-origin') {
596
- // 1. Return a network error.
597
- return makeNetworkError('request mode cannot be "same-origin"')
598
- }
599
-
600
- // request’s mode is "no-cors"
601
- if (request.mode === 'no-cors') {
602
- // 1. If request’s redirect mode is not "follow", then return a network
603
- // error.
604
- if (request.redirect !== 'follow') {
605
- return makeNetworkError(
606
- 'redirect mode cannot be "follow" for "no-cors" request'
607
- )
608
- }
609
-
574
+ const currentURL = requestCurrentURL(request)
575
+ if (
576
+ // - request’s current URL’s origin is same origin with request’s origin,
577
+ // and request’s response tainting is "basic"
578
+ (sameOrigin(currentURL, request.url) && request.responseTainting === 'basic') ||
579
+ // request’s current URL’s scheme is "data"
580
+ (currentURL.protocol === 'data:') ||
581
+ // - request’s mode is "navigate" or "websocket"
582
+ (request.mode === 'navigate' || request.mode === 'websocket')
583
+ ) {
584
+ // 1. Set request’s response tainting to "basic".
585
+ request.responseTainting = 'basic'
586
+
587
+ // 2. Return the result of running scheme fetch given fetchParams.
588
+ response = await schemeFetch(fetchParams)
589
+
590
+ // request’s mode is "same-origin"
591
+ } else if (request.mode === 'same-origin') {
592
+ // 1. Return a network error.
593
+ response = makeNetworkError('request mode cannot be "same-origin"')
594
+
595
+ // request’s mode is "no-cors"
596
+ } else if (request.mode === 'no-cors') {
597
+ // 1. If request’s redirect mode is not "follow", then return a network
598
+ // error.
599
+ if (request.redirect !== 'follow') {
600
+ response = makeNetworkError(
601
+ 'redirect mode cannot be "follow" for "no-cors" request'
602
+ )
603
+ } else {
610
604
  // 2. Set request’s response tainting to "opaque".
611
605
  request.responseTainting = 'opaque'
612
606
 
613
607
  // 3. Return the result of running scheme fetch given fetchParams.
614
- return await schemeFetch(fetchParams)
615
- }
616
-
617
- // request’s current URL’s scheme is not an HTTP(S) scheme
618
- if (!urlIsHttpHttpsScheme(requestCurrentURL(request))) {
619
- // Return a network error.
620
- return makeNetworkError('URL scheme must be a HTTP(S) scheme')
608
+ response = await schemeFetch(fetchParams)
621
609
  }
610
+ // request’s current URL’s scheme is not an HTTP(S) scheme
611
+ } else if (!urlIsHttpHttpsScheme(requestCurrentURL(request))) {
612
+ // Return a network error.
613
+ response = makeNetworkError('URL scheme must be a HTTP(S) scheme')
622
614
 
623
615
  // - request’s use-CORS-preflight flag is set
624
616
  // - request’s unsafe-request flag is set and either request’s method is
@@ -632,13 +624,14 @@ async function mainFetch (fetchParams, recursive = false) {
632
624
  // 4. Return corsWithPreflightResponse.
633
625
  // TODO
634
626
 
635
- // Otherwise
627
+ // Otherwise
628
+ } else {
636
629
  // 1. Set request’s response tainting to "cors".
637
630
  request.responseTainting = 'cors'
638
631
 
639
632
  // 2. Return the result of running HTTP fetch given fetchParams.
640
- return await httpFetch(fetchParams)
641
- })()
633
+ response = await httpFetch(fetchParams)
634
+ }
642
635
  }
643
636
 
644
637
  // 12. If recursive is true, then return response.
@@ -810,7 +803,7 @@ function schemeFetch (fetchParams) {
810
803
 
811
804
  // 2. If request’s method is not `GET`, blobURLEntry is null, or blobURLEntry’s
812
805
  // object is not a Blob object, then return a network error.
813
- if (request.method !== 'GET' || !isBlobLike(blob)) {
806
+ if (request.method !== 'GET' || !webidl.is.Blob(blob)) {
814
807
  return Promise.resolve(makeNetworkError('invalid method'))
815
808
  }
816
809
 
@@ -1446,7 +1439,7 @@ async function httpNetworkOrCacheFetch (
1446
1439
  // 11. If httpRequest’s referrer is a URL, then append
1447
1440
  // `Referer`/httpRequest’s referrer, serialized and isomorphic encoded,
1448
1441
  // to httpRequest’s header list.
1449
- if (httpRequest.referrer instanceof URL) {
1442
+ if (webidl.is.URL(httpRequest.referrer)) {
1450
1443
  httpRequest.headersList.append('referer', isomorphicEncode(httpRequest.referrer.href), true)
1451
1444
  }
1452
1445
 
@@ -1460,7 +1453,7 @@ async function httpNetworkOrCacheFetch (
1460
1453
  // user agents should append `User-Agent`/default `User-Agent` value to
1461
1454
  // httpRequest’s header list.
1462
1455
  if (!httpRequest.headersList.contains('user-agent', true)) {
1463
- httpRequest.headersList.append('user-agent', defaultUserAgent)
1456
+ httpRequest.headersList.append('user-agent', defaultUserAgent, true)
1464
1457
  }
1465
1458
 
1466
1459
  // 15. If httpRequest’s cache mode is "default" and httpRequest’s header
@@ -1888,8 +1881,8 @@ async function httpNetworkFetch (
1888
1881
 
1889
1882
  // 11. Let pullAlgorithm be an action that resumes the ongoing fetch
1890
1883
  // if it is suspended.
1891
- const pullAlgorithm = async () => {
1892
- await fetchParams.controller.resume()
1884
+ const pullAlgorithm = () => {
1885
+ return fetchParams.controller.resume()
1893
1886
  }
1894
1887
 
1895
1888
  // 12. Let cancelAlgorithm be an algorithm that aborts fetchParams’s
@@ -2137,7 +2130,7 @@ async function httpNetworkFetch (
2137
2130
 
2138
2131
  // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding
2139
2132
  if (codings.length !== 0 && request.method !== 'HEAD' && request.method !== 'CONNECT' && !nullBodyStatus.includes(status) && !willFollow) {
2140
- for (let i = 0; i < codings.length; ++i) {
2133
+ for (let i = codings.length - 1; i >= 0; --i) {
2141
2134
  const coding = codings[i]
2142
2135
  // https://www.rfc-editor.org/rfc/rfc9112.html#section-7.2
2143
2136
  if (coding === 'x-gzip' || coding === 'gzip') {