undici 7.0.0-alpha.1 → 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 (71) hide show
  1. package/README.md +2 -2
  2. package/docs/docs/api/Client.md +1 -1
  3. package/docs/docs/api/Debug.md +1 -1
  4. package/docs/docs/api/Dispatcher.md +53 -2
  5. package/docs/docs/api/MockAgent.md +2 -0
  6. package/docs/docs/api/MockPool.md +2 -1
  7. package/docs/docs/api/RetryAgent.md +1 -1
  8. package/docs/docs/api/RetryHandler.md +1 -1
  9. package/docs/docs/api/WebSocket.md +45 -3
  10. package/index.js +6 -2
  11. package/lib/api/abort-signal.js +2 -0
  12. package/lib/api/api-pipeline.js +4 -2
  13. package/lib/api/api-request.js +4 -2
  14. package/lib/api/api-stream.js +3 -1
  15. package/lib/api/api-upgrade.js +2 -2
  16. package/lib/api/readable.js +194 -41
  17. package/lib/api/util.js +2 -0
  18. package/lib/core/connect.js +49 -22
  19. package/lib/core/constants.js +11 -9
  20. package/lib/core/diagnostics.js +122 -128
  21. package/lib/core/request.js +4 -4
  22. package/lib/core/symbols.js +2 -0
  23. package/lib/core/tree.js +4 -2
  24. package/lib/core/util.js +220 -39
  25. package/lib/dispatcher/client-h1.js +299 -60
  26. package/lib/dispatcher/client-h2.js +1 -1
  27. package/lib/dispatcher/client.js +24 -7
  28. package/lib/dispatcher/fixed-queue.js +91 -49
  29. package/lib/dispatcher/pool-stats.js +2 -0
  30. package/lib/dispatcher/proxy-agent.js +3 -1
  31. package/lib/handler/redirect-handler.js +2 -2
  32. package/lib/handler/retry-handler.js +2 -2
  33. package/lib/interceptor/dns.js +346 -0
  34. package/lib/mock/mock-agent.js +5 -8
  35. package/lib/mock/mock-client.js +7 -2
  36. package/lib/mock/mock-errors.js +3 -1
  37. package/lib/mock/mock-interceptor.js +8 -6
  38. package/lib/mock/mock-pool.js +7 -2
  39. package/lib/mock/mock-symbols.js +2 -1
  40. package/lib/mock/mock-utils.js +33 -5
  41. package/lib/util/timers.js +50 -6
  42. package/lib/web/cache/cache.js +24 -21
  43. package/lib/web/cache/cachestorage.js +1 -1
  44. package/lib/web/cookies/index.js +6 -4
  45. package/lib/web/fetch/body.js +42 -34
  46. package/lib/web/fetch/constants.js +35 -26
  47. package/lib/web/fetch/formdata-parser.js +14 -3
  48. package/lib/web/fetch/formdata.js +40 -20
  49. package/lib/web/fetch/headers.js +116 -84
  50. package/lib/web/fetch/index.js +65 -59
  51. package/lib/web/fetch/request.js +130 -55
  52. package/lib/web/fetch/response.js +79 -36
  53. package/lib/web/fetch/util.js +104 -57
  54. package/lib/web/fetch/webidl.js +38 -14
  55. package/lib/web/websocket/connection.js +92 -15
  56. package/lib/web/websocket/constants.js +2 -3
  57. package/lib/web/websocket/events.js +4 -2
  58. package/lib/web/websocket/receiver.js +20 -26
  59. package/lib/web/websocket/stream/websocketerror.js +83 -0
  60. package/lib/web/websocket/stream/websocketstream.js +485 -0
  61. package/lib/web/websocket/util.js +115 -10
  62. package/lib/web/websocket/websocket.js +45 -170
  63. package/package.json +6 -6
  64. package/types/interceptors.d.ts +14 -0
  65. package/types/mock-agent.d.ts +3 -0
  66. package/types/readable.d.ts +10 -7
  67. package/types/webidl.d.ts +24 -4
  68. package/types/websocket.d.ts +33 -0
  69. package/lib/mock/pluralizer.js +0 -29
  70. package/lib/web/cache/symbols.js +0 -5
  71. package/lib/web/fetch/symbols.js +0 -8
@@ -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,
@@ -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,
@@ -46,7 +47,6 @@ const {
46
47
  createInflate,
47
48
  extractMimeType
48
49
  } = require('./util')
49
- const { kState, kDispatcher } = require('./symbols')
50
50
  const assert = require('node:assert')
51
51
  const { safelyExtractBody, extractBody } = require('./body')
52
52
  const {
@@ -143,7 +143,7 @@ function fetch (input, init = undefined) {
143
143
  }
144
144
 
145
145
  // 3. Let request be requestObject’s request.
146
- const request = requestObject[kState]
146
+ const request = getRequestState(requestObject)
147
147
 
148
148
  // 4. If requestObject’s signal’s aborted flag is set, then:
149
149
  if (requestObject.signal.aborted) {
@@ -243,7 +243,7 @@ function fetch (input, init = undefined) {
243
243
  request,
244
244
  processResponseEndOfBody: handleFetchDone,
245
245
  processResponse,
246
- dispatcher: requestObject[kDispatcher] // undici
246
+ dispatcher: getRequestDispatcher(requestObject) // undici
247
247
  })
248
248
 
249
249
  // 14. Return p.
@@ -342,7 +342,7 @@ function abortFetch (p, request, responseObject, error) {
342
342
  }
343
343
 
344
344
  // 4. Let response be responseObject’s response.
345
- const response = responseObject[kState]
345
+ const response = getResponseState(responseObject)
346
346
 
347
347
  // 5. If response’s body is not null and is readable, then error response’s
348
348
  // body with error.
@@ -571,53 +571,46 @@ async function mainFetch (fetchParams, recursive = false) {
571
571
  // 11. If response is null, then set response to the result of running
572
572
  // the steps corresponding to the first matching statement:
573
573
  if (response === null) {
574
- response = await (async () => {
575
- const currentURL = requestCurrentURL(request)
576
-
577
- if (
578
- // - request’s current URL’s origin is same origin with request’s origin,
579
- // and request’s response tainting is "basic"
580
- (sameOrigin(currentURL, request.url) && request.responseTainting === 'basic') ||
581
- // request’s current URL’s scheme is "data"
582
- (currentURL.protocol === 'data:') ||
583
- // - request’s mode is "navigate" or "websocket"
584
- (request.mode === 'navigate' || request.mode === 'websocket')
585
- ) {
586
- // 1. Set request’s response tainting to "basic".
587
- request.responseTainting = 'basic'
588
-
589
- // 2. Return the result of running scheme fetch given fetchParams.
590
- return await schemeFetch(fetchParams)
591
- }
592
-
593
- // request’s mode is "same-origin"
594
- if (request.mode === 'same-origin') {
595
- // 1. Return a network error.
596
- return makeNetworkError('request mode cannot be "same-origin"')
597
- }
598
-
599
- // request’s mode is "no-cors"
600
- if (request.mode === 'no-cors') {
601
- // 1. If request’s redirect mode is not "follow", then return a network
602
- // error.
603
- if (request.redirect !== 'follow') {
604
- return makeNetworkError(
605
- 'redirect mode cannot be "follow" for "no-cors" request'
606
- )
607
- }
608
-
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 {
609
604
  // 2. Set request’s response tainting to "opaque".
610
605
  request.responseTainting = 'opaque'
611
606
 
612
607
  // 3. Return the result of running scheme fetch given fetchParams.
613
- return await schemeFetch(fetchParams)
614
- }
615
-
616
- // request’s current URL’s scheme is not an HTTP(S) scheme
617
- if (!urlIsHttpHttpsScheme(requestCurrentURL(request))) {
618
- // Return a network error.
619
- return makeNetworkError('URL scheme must be a HTTP(S) scheme')
608
+ response = await schemeFetch(fetchParams)
620
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')
621
614
 
622
615
  // - request’s use-CORS-preflight flag is set
623
616
  // - request’s unsafe-request flag is set and either request’s method is
@@ -631,13 +624,14 @@ async function mainFetch (fetchParams, recursive = false) {
631
624
  // 4. Return corsWithPreflightResponse.
632
625
  // TODO
633
626
 
634
- // Otherwise
627
+ // Otherwise
628
+ } else {
635
629
  // 1. Set request’s response tainting to "cors".
636
630
  request.responseTainting = 'cors'
637
631
 
638
632
  // 2. Return the result of running HTTP fetch given fetchParams.
639
- return await httpFetch(fetchParams)
640
- })()
633
+ response = await httpFetch(fetchParams)
634
+ }
641
635
  }
642
636
 
643
637
  // 12. If recursive is true, then return response.
@@ -809,7 +803,7 @@ function schemeFetch (fetchParams) {
809
803
 
810
804
  // 2. If request’s method is not `GET`, blobURLEntry is null, or blobURLEntry’s
811
805
  // object is not a Blob object, then return a network error.
812
- if (request.method !== 'GET' || !(blob instanceof Blob)) {
806
+ if (request.method !== 'GET' || !webidl.is.Blob(blob)) {
813
807
  return Promise.resolve(makeNetworkError('invalid method'))
814
808
  }
815
809
 
@@ -1445,7 +1439,7 @@ async function httpNetworkOrCacheFetch (
1445
1439
  // 11. If httpRequest’s referrer is a URL, then append
1446
1440
  // `Referer`/httpRequest’s referrer, serialized and isomorphic encoded,
1447
1441
  // to httpRequest’s header list.
1448
- if (httpRequest.referrer instanceof URL) {
1442
+ if (webidl.is.URL(httpRequest.referrer)) {
1449
1443
  httpRequest.headersList.append('referer', isomorphicEncode(httpRequest.referrer.href), true)
1450
1444
  }
1451
1445
 
@@ -1887,8 +1881,8 @@ async function httpNetworkFetch (
1887
1881
 
1888
1882
  // 11. Let pullAlgorithm be an action that resumes the ongoing fetch
1889
1883
  // if it is suspended.
1890
- const pullAlgorithm = async () => {
1891
- await fetchParams.controller.resume()
1884
+ const pullAlgorithm = () => {
1885
+ return fetchParams.controller.resume()
1892
1886
  }
1893
1887
 
1894
1888
  // 12. Let cancelAlgorithm be an algorithm that aborts fetchParams’s
@@ -2149,9 +2143,15 @@ async function httpNetworkFetch (
2149
2143
  finishFlush: zlib.constants.Z_SYNC_FLUSH
2150
2144
  }))
2151
2145
  } else if (coding === 'deflate') {
2152
- decoders.push(createInflate())
2146
+ decoders.push(createInflate({
2147
+ flush: zlib.constants.Z_SYNC_FLUSH,
2148
+ finishFlush: zlib.constants.Z_SYNC_FLUSH
2149
+ }))
2153
2150
  } else if (coding === 'br') {
2154
- decoders.push(zlib.createBrotliDecompress())
2151
+ decoders.push(zlib.createBrotliDecompress({
2152
+ flush: zlib.constants.BROTLI_OPERATION_FLUSH,
2153
+ finishFlush: zlib.constants.BROTLI_OPERATION_FLUSH
2154
+ }))
2155
2155
  } else {
2156
2156
  decoders.length = 0
2157
2157
  break
@@ -2159,13 +2159,19 @@ async function httpNetworkFetch (
2159
2159
  }
2160
2160
  }
2161
2161
 
2162
+ const onError = this.onError.bind(this)
2163
+
2162
2164
  resolve({
2163
2165
  status,
2164
2166
  statusText,
2165
2167
  headersList,
2166
2168
  body: decoders.length
2167
- ? pipeline(this.body, ...decoders, () => { })
2168
- : this.body.on('error', () => { })
2169
+ ? pipeline(this.body, ...decoders, (err) => {
2170
+ if (err) {
2171
+ this.onError(err)
2172
+ }
2173
+ }).on('error', onError)
2174
+ : this.body.on('error', onError)
2169
2175
  })
2170
2176
 
2171
2177
  return true