undici 6.14.1 → 6.16.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.
@@ -33,9 +33,10 @@ class CompatFinalizer {
33
33
  }
34
34
 
35
35
  module.exports = function () {
36
- // FIXME: remove workaround when the Node bug is fixed
36
+ // FIXME: remove workaround when the Node bug is backported to v18
37
37
  // https://github.com/nodejs/node/issues/49344#issuecomment-1741776308
38
- if (process.env.NODE_V8_COVERAGE) {
38
+ if (process.env.NODE_V8_COVERAGE && process.version.startsWith('v18')) {
39
+ process._rawDebug('Using compatibility WeakRef and FinalizationRegistry')
39
40
  return {
40
41
  WeakRef: CompatWeakRef,
41
42
  FinalizationRegistry: CompatFinalizer
@@ -28,7 +28,8 @@ class FormData {
28
28
  append (name, value, filename = undefined) {
29
29
  webidl.brandCheck(this, FormData)
30
30
 
31
- webidl.argumentLengthCheck(arguments, 2, { header: 'FormData.append' })
31
+ const prefix = 'FormData.append'
32
+ webidl.argumentLengthCheck(arguments, 2, prefix)
32
33
 
33
34
  if (arguments.length === 3 && !isBlobLike(value)) {
34
35
  throw new TypeError(
@@ -38,12 +39,12 @@ class FormData {
38
39
 
39
40
  // 1. Let value be value if given; otherwise blobValue.
40
41
 
41
- name = webidl.converters.USVString(name)
42
+ name = webidl.converters.USVString(name, prefix, 'name')
42
43
  value = isBlobLike(value)
43
- ? webidl.converters.Blob(value, { strict: false })
44
- : webidl.converters.USVString(value)
44
+ ? webidl.converters.Blob(value, prefix, 'value', { strict: false })
45
+ : webidl.converters.USVString(value, prefix, 'value')
45
46
  filename = arguments.length === 3
46
- ? webidl.converters.USVString(filename)
47
+ ? webidl.converters.USVString(filename, prefix, 'filename')
47
48
  : undefined
48
49
 
49
50
  // 2. Let entry be the result of creating an entry with
@@ -57,9 +58,10 @@ class FormData {
57
58
  delete (name) {
58
59
  webidl.brandCheck(this, FormData)
59
60
 
60
- webidl.argumentLengthCheck(arguments, 1, { header: 'FormData.delete' })
61
+ const prefix = 'FormData.delete'
62
+ webidl.argumentLengthCheck(arguments, 1, prefix)
61
63
 
62
- name = webidl.converters.USVString(name)
64
+ name = webidl.converters.USVString(name, prefix, 'name')
63
65
 
64
66
  // The delete(name) method steps are to remove all entries whose name
65
67
  // is name from this’s entry list.
@@ -69,9 +71,10 @@ class FormData {
69
71
  get (name) {
70
72
  webidl.brandCheck(this, FormData)
71
73
 
72
- webidl.argumentLengthCheck(arguments, 1, { header: 'FormData.get' })
74
+ const prefix = 'FormData.get'
75
+ webidl.argumentLengthCheck(arguments, 1, prefix)
73
76
 
74
- name = webidl.converters.USVString(name)
77
+ name = webidl.converters.USVString(name, prefix, 'name')
75
78
 
76
79
  // 1. If there is no entry whose name is name in this’s entry list,
77
80
  // then return null.
@@ -88,9 +91,10 @@ class FormData {
88
91
  getAll (name) {
89
92
  webidl.brandCheck(this, FormData)
90
93
 
91
- webidl.argumentLengthCheck(arguments, 1, { header: 'FormData.getAll' })
94
+ const prefix = 'FormData.getAll'
95
+ webidl.argumentLengthCheck(arguments, 1, prefix)
92
96
 
93
- name = webidl.converters.USVString(name)
97
+ name = webidl.converters.USVString(name, prefix, 'name')
94
98
 
95
99
  // 1. If there is no entry whose name is name in this’s entry list,
96
100
  // then return the empty list.
@@ -104,9 +108,10 @@ class FormData {
104
108
  has (name) {
105
109
  webidl.brandCheck(this, FormData)
106
110
 
107
- webidl.argumentLengthCheck(arguments, 1, { header: 'FormData.has' })
111
+ const prefix = 'FormData.has'
112
+ webidl.argumentLengthCheck(arguments, 1, prefix)
108
113
 
109
- name = webidl.converters.USVString(name)
114
+ name = webidl.converters.USVString(name, prefix, 'name')
110
115
 
111
116
  // The has(name) method steps are to return true if there is an entry
112
117
  // whose name is name in this’s entry list; otherwise false.
@@ -116,7 +121,8 @@ class FormData {
116
121
  set (name, value, filename = undefined) {
117
122
  webidl.brandCheck(this, FormData)
118
123
 
119
- webidl.argumentLengthCheck(arguments, 2, { header: 'FormData.set' })
124
+ const prefix = 'FormData.set'
125
+ webidl.argumentLengthCheck(arguments, 2, prefix)
120
126
 
121
127
  if (arguments.length === 3 && !isBlobLike(value)) {
122
128
  throw new TypeError(
@@ -129,12 +135,12 @@ class FormData {
129
135
 
130
136
  // 1. Let value be value if given; otherwise blobValue.
131
137
 
132
- name = webidl.converters.USVString(name)
138
+ name = webidl.converters.USVString(name, prefix, 'name')
133
139
  value = isBlobLike(value)
134
- ? webidl.converters.Blob(value, { strict: false })
135
- : webidl.converters.USVString(value)
140
+ ? webidl.converters.Blob(value, prefix, 'name', { strict: false })
141
+ : webidl.converters.USVString(value, prefix, 'name')
136
142
  filename = arguments.length === 3
137
- ? webidl.converters.USVString(filename)
143
+ ? webidl.converters.USVString(filename, prefix, 'name')
138
144
  : undefined
139
145
 
140
146
  // 2. Let entry be the result of creating an entry with name, value, and
@@ -250,7 +250,7 @@ class HeadersList {
250
250
  get entries () {
251
251
  const headers = {}
252
252
 
253
- if (this[kHeadersMap].size) {
253
+ if (this[kHeadersMap].size !== 0) {
254
254
  for (const { name, value } of this[kHeadersMap].values()) {
255
255
  headers[name] = value
256
256
  }
@@ -259,6 +259,28 @@ class HeadersList {
259
259
  return headers
260
260
  }
261
261
 
262
+ rawValues () {
263
+ return this[kHeadersMap].values()
264
+ }
265
+
266
+ get entriesList () {
267
+ const headers = []
268
+
269
+ if (this[kHeadersMap].size !== 0) {
270
+ for (const { 0: lowerName, 1: { name, value } } of this[kHeadersMap]) {
271
+ if (lowerName === 'set-cookie') {
272
+ for (const cookie of this.cookies) {
273
+ headers.push([name, cookie])
274
+ }
275
+ } else {
276
+ headers.push([name, value])
277
+ }
278
+ }
279
+ }
280
+
281
+ return headers
282
+ }
283
+
262
284
  // https://fetch.spec.whatwg.org/#convert-header-names-to-a-sorted-lowercase-set
263
285
  toSortedArray () {
264
286
  const size = this[kHeadersMap].size
@@ -348,7 +370,7 @@ class Headers {
348
370
 
349
371
  // 2. If init is given, then fill this with init.
350
372
  if (init !== undefined) {
351
- init = webidl.converters.HeadersInit(init)
373
+ init = webidl.converters.HeadersInit(init, 'Headers contructor', 'init')
352
374
  fill(this, init)
353
375
  }
354
376
  }
@@ -357,10 +379,11 @@ class Headers {
357
379
  append (name, value) {
358
380
  webidl.brandCheck(this, Headers)
359
381
 
360
- webidl.argumentLengthCheck(arguments, 2, { header: 'Headers.append' })
382
+ webidl.argumentLengthCheck(arguments, 2, 'Headers.append')
361
383
 
362
- name = webidl.converters.ByteString(name)
363
- value = webidl.converters.ByteString(value)
384
+ const prefix = 'Headers.append'
385
+ name = webidl.converters.ByteString(name, prefix, 'name')
386
+ value = webidl.converters.ByteString(value, prefix, 'value')
364
387
 
365
388
  return appendHeader(this, name, value)
366
389
  }
@@ -369,9 +392,10 @@ class Headers {
369
392
  delete (name) {
370
393
  webidl.brandCheck(this, Headers)
371
394
 
372
- webidl.argumentLengthCheck(arguments, 1, { header: 'Headers.delete' })
395
+ webidl.argumentLengthCheck(arguments, 1, 'Headers.delete')
373
396
 
374
- name = webidl.converters.ByteString(name)
397
+ const prefix = 'Headers.delete'
398
+ name = webidl.converters.ByteString(name, prefix, 'name')
375
399
 
376
400
  // 1. If name is not a header name, then throw a TypeError.
377
401
  if (!isValidHeaderName(name)) {
@@ -414,14 +438,15 @@ class Headers {
414
438
  get (name) {
415
439
  webidl.brandCheck(this, Headers)
416
440
 
417
- webidl.argumentLengthCheck(arguments, 1, { header: 'Headers.get' })
441
+ webidl.argumentLengthCheck(arguments, 1, 'Headers.get')
418
442
 
419
- name = webidl.converters.ByteString(name)
443
+ const prefix = 'Headers.get'
444
+ name = webidl.converters.ByteString(name, prefix, 'name')
420
445
 
421
446
  // 1. If name is not a header name, then throw a TypeError.
422
447
  if (!isValidHeaderName(name)) {
423
448
  throw webidl.errors.invalidArgument({
424
- prefix: 'Headers.get',
449
+ prefix,
425
450
  value: name,
426
451
  type: 'header name'
427
452
  })
@@ -436,14 +461,15 @@ class Headers {
436
461
  has (name) {
437
462
  webidl.brandCheck(this, Headers)
438
463
 
439
- webidl.argumentLengthCheck(arguments, 1, { header: 'Headers.has' })
464
+ webidl.argumentLengthCheck(arguments, 1, 'Headers.has')
440
465
 
441
- name = webidl.converters.ByteString(name)
466
+ const prefix = 'Headers.has'
467
+ name = webidl.converters.ByteString(name, prefix, 'name')
442
468
 
443
469
  // 1. If name is not a header name, then throw a TypeError.
444
470
  if (!isValidHeaderName(name)) {
445
471
  throw webidl.errors.invalidArgument({
446
- prefix: 'Headers.has',
472
+ prefix,
447
473
  value: name,
448
474
  type: 'header name'
449
475
  })
@@ -458,10 +484,11 @@ class Headers {
458
484
  set (name, value) {
459
485
  webidl.brandCheck(this, Headers)
460
486
 
461
- webidl.argumentLengthCheck(arguments, 2, { header: 'Headers.set' })
487
+ webidl.argumentLengthCheck(arguments, 2, 'Headers.set')
462
488
 
463
- name = webidl.converters.ByteString(name)
464
- value = webidl.converters.ByteString(value)
489
+ const prefix = 'Headers.set'
490
+ name = webidl.converters.ByteString(name, prefix, 'name')
491
+ value = webidl.converters.ByteString(value, prefix, 'value')
465
492
 
466
493
  // 1. Normalize value.
467
494
  value = headerValueNormalize(value)
@@ -470,13 +497,13 @@ class Headers {
470
497
  // header value, then throw a TypeError.
471
498
  if (!isValidHeaderName(name)) {
472
499
  throw webidl.errors.invalidArgument({
473
- prefix: 'Headers.set',
500
+ prefix,
474
501
  value: name,
475
502
  type: 'header name'
476
503
  })
477
504
  } else if (!isValidHeaderValue(value)) {
478
505
  throw webidl.errors.invalidArgument({
479
- prefix: 'Headers.set',
506
+ prefix,
480
507
  value,
481
508
  type: 'header value'
482
509
  })
@@ -598,15 +625,21 @@ Object.defineProperties(Headers.prototype, {
598
625
  }
599
626
  })
600
627
 
601
- webidl.converters.HeadersInit = function (V) {
628
+ webidl.converters.HeadersInit = function (V, prefix, argument) {
602
629
  if (webidl.util.Type(V) === 'Object') {
603
630
  const iterator = Reflect.get(V, Symbol.iterator)
604
631
 
632
+ // A work-around to ensure we send the properly-cased Headers when V is a Headers object.
633
+ // Read https://github.com/nodejs/undici/pull/3159#issuecomment-2075537226 before touching, please.
634
+ if (!util.types.isProxy(V) && kHeadersList in V && iterator === Headers.prototype.entries) { // Headers object
635
+ return V[kHeadersList].entriesList
636
+ }
637
+
605
638
  if (typeof iterator === 'function') {
606
- return webidl.converters['sequence<sequence<ByteString>>'](V, iterator.bind(V))
639
+ return webidl.converters['sequence<sequence<ByteString>>'](V, prefix, argument, iterator.bind(V))
607
640
  }
608
641
 
609
- return webidl.converters['record<ByteString, ByteString>'](V)
642
+ return webidl.converters['record<ByteString, ByteString>'](V, prefix, argument)
610
643
  }
611
644
 
612
645
  throw webidl.errors.conversionFailed({
@@ -120,12 +120,16 @@ class Fetch extends EE {
120
120
  }
121
121
  }
122
122
 
123
+ function handleFetchDone (response) {
124
+ finalizeAndReportTiming(response, 'fetch')
125
+ }
126
+
123
127
  // https://fetch.spec.whatwg.org/#fetch-method
124
128
  function fetch (input, init = undefined) {
125
- webidl.argumentLengthCheck(arguments, 1, { header: 'globalThis.fetch' })
129
+ webidl.argumentLengthCheck(arguments, 1, 'globalThis.fetch')
126
130
 
127
131
  // 1. Let p be a new promise.
128
- const p = createDeferredPromise()
132
+ let p = createDeferredPromise()
129
133
 
130
134
  // 2. Let requestObject be the result of invoking the initial value of
131
135
  // Request as constructor with input and init as arguments. If this throws
@@ -185,16 +189,17 @@ function fetch (input, init = undefined) {
185
189
  // 3. Abort controller with requestObject’s signal’s abort reason.
186
190
  controller.abort(requestObject.signal.reason)
187
191
 
192
+ const realResponse = responseObject?.deref()
193
+
188
194
  // 4. Abort the fetch() call with p, request, responseObject,
189
195
  // and requestObject’s signal’s abort reason.
190
- abortFetch(p, request, responseObject, requestObject.signal.reason)
196
+ abortFetch(p, request, realResponse, requestObject.signal.reason)
191
197
  }
192
198
  )
193
199
 
194
200
  // 12. Let handleFetchDone given response response be to finalize and
195
201
  // report timing with response, globalObject, and "fetch".
196
- const handleFetchDone = (response) =>
197
- finalizeAndReportTiming(response, 'fetch')
202
+ // see function handleFetchDone
198
203
 
199
204
  // 13. Set controller to the result of calling fetch given request,
200
205
  // with processResponseEndOfBody set to handleFetchDone, and processResponse
@@ -228,10 +233,11 @@ function fetch (input, init = undefined) {
228
233
 
229
234
  // 4. Set responseObject to the result of creating a Response object,
230
235
  // given response, "immutable", and relevantRealm.
231
- responseObject = fromInnerResponse(response, 'immutable')
236
+ responseObject = new WeakRef(fromInnerResponse(response, 'immutable'))
232
237
 
233
238
  // 5. Resolve p with responseObject.
234
- p.resolve(responseObject)
239
+ p.resolve(responseObject.deref())
240
+ p = null
235
241
  }
236
242
 
237
243
  controller = fetching({
@@ -314,7 +320,10 @@ const markResourceTiming = performance.markResourceTiming
314
320
  // https://fetch.spec.whatwg.org/#abort-fetch
315
321
  function abortFetch (p, request, responseObject, error) {
316
322
  // 1. Reject promise with error.
317
- p.reject(error)
323
+ if (p) {
324
+ // We might have already resolved the promise at this stage
325
+ p.reject(error)
326
+ }
318
327
 
319
328
  // 2. If request’s body is not null and is readable, then cancel request’s
320
329
  // body with error.
@@ -1066,7 +1075,10 @@ function fetchFinale (fetchParams, response) {
1066
1075
  // 4. If fetchParams’s process response is non-null, then queue a fetch task to run fetchParams’s
1067
1076
  // process response given response, with fetchParams’s task destination.
1068
1077
  if (fetchParams.processResponse != null) {
1069
- queueMicrotask(() => fetchParams.processResponse(response))
1078
+ queueMicrotask(() => {
1079
+ fetchParams.processResponse(response)
1080
+ fetchParams.processResponse = null
1081
+ })
1070
1082
  }
1071
1083
 
1072
1084
  // 5. Let internalResponse be response, if response is a network error; otherwise response’s internal response.
@@ -1537,7 +1549,7 @@ async function httpNetworkOrCacheFetch (
1537
1549
 
1538
1550
  // 24. If httpRequest’s cache mode is neither "no-store" nor "reload",
1539
1551
  // then:
1540
- if (httpRequest.mode !== 'no-store' && httpRequest.mode !== 'reload') {
1552
+ if (httpRequest.cache !== 'no-store' && httpRequest.cache !== 'reload') {
1541
1553
  // TODO: cache
1542
1554
  }
1543
1555
 
@@ -1548,7 +1560,7 @@ async function httpNetworkOrCacheFetch (
1548
1560
  if (response == null) {
1549
1561
  // 1. If httpRequest’s cache mode is "only-if-cached", then return a
1550
1562
  // network error.
1551
- if (httpRequest.mode === 'only-if-cached') {
1563
+ if (httpRequest.cache === 'only-if-cached') {
1552
1564
  return makeNetworkError('only if cached')
1553
1565
  }
1554
1566
 
@@ -1884,7 +1896,11 @@ async function httpNetworkFetch (
1884
1896
  // 12. Let cancelAlgorithm be an algorithm that aborts fetchParams’s
1885
1897
  // controller with reason, given reason.
1886
1898
  const cancelAlgorithm = (reason) => {
1887
- fetchParams.controller.abort(reason)
1899
+ // If the aborted fetch was already terminated, then we do not
1900
+ // need to do anything.
1901
+ if (!isCancelled(fetchParams)) {
1902
+ fetchParams.controller.abort(reason)
1903
+ }
1888
1904
  }
1889
1905
 
1890
1906
  // 13. Let highWaterMark be a non-negative, non-NaN number, chosen by
@@ -2102,20 +2118,16 @@ async function httpNetworkFetch (
2102
2118
 
2103
2119
  const headersList = new HeadersList()
2104
2120
 
2105
- // For H2, the rawHeaders are a plain JS object
2106
- // We distinguish between them and iterate accordingly
2107
- if (Array.isArray(rawHeaders)) {
2108
- for (let i = 0; i < rawHeaders.length; i += 2) {
2109
- headersList.append(bufferToLowerCasedHeaderName(rawHeaders[i]), rawHeaders[i + 1].toString('latin1'), true)
2110
- }
2111
- const contentEncoding = headersList.get('content-encoding', true)
2112
- if (contentEncoding) {
2113
- // https://www.rfc-editor.org/rfc/rfc7231#section-3.1.2.1
2114
- // "All content-coding values are case-insensitive..."
2115
- codings = contentEncoding.toLowerCase().split(',').map((x) => x.trim())
2116
- }
2117
- location = headersList.get('location', true)
2121
+ for (let i = 0; i < rawHeaders.length; i += 2) {
2122
+ headersList.append(bufferToLowerCasedHeaderName(rawHeaders[i]), rawHeaders[i + 1].toString('latin1'), true)
2123
+ }
2124
+ const contentEncoding = headersList.get('content-encoding', true)
2125
+ if (contentEncoding) {
2126
+ // https://www.rfc-editor.org/rfc/rfc7231#section-3.1.2.1
2127
+ // "All content-coding values are case-insensitive..."
2128
+ codings = contentEncoding.toLowerCase().split(',').map((x) => x.trim())
2118
2129
  }
2130
+ location = headersList.get('location', true)
2119
2131
 
2120
2132
  this.body = new Readable({ read: resume })
2121
2133
 
@@ -2125,7 +2137,7 @@ async function httpNetworkFetch (
2125
2137
  redirectStatusSet.has(status)
2126
2138
 
2127
2139
  // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding
2128
- if (request.method !== 'HEAD' && request.method !== 'CONNECT' && !nullBodyStatus.includes(status) && !willFollow) {
2140
+ if (codings.length !== 0 && request.method !== 'HEAD' && request.method !== 'CONNECT' && !nullBodyStatus.includes(status) && !willFollow) {
2129
2141
  for (let i = 0; i < codings.length; ++i) {
2130
2142
  const coding = codings[i]
2131
2143
  // https://www.rfc-editor.org/rfc/rfc9112.html#section-7.2