undici 5.8.2 → 5.9.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.
@@ -18,7 +18,7 @@ Returns: `Client`
18
18
  ### Parameter: `ClientOptions`
19
19
 
20
20
  * **bodyTimeout** `number | null` (optional) - Default: `30e3` - The timeout after which a request will time out, in milliseconds. Monitors time between receiving body data. Use `0` to disable it entirely. Defaults to 30 seconds.
21
- * **headersTimeout** `number | null` (optional) - Default: `30e3` - The amount of time the parser will wait to receive the complete HTTP headers. Defaults to 30 seconds.
21
+ * **headersTimeout** `number | null` (optional) - Default: `30e3` - The amount of time the parser will wait to receive the complete HTTP headers while not sending the request. Defaults to 30 seconds.
22
22
  * **keepAliveMaxTimeout** `number | null` (optional) - Default: `600e3` - The maximum allowed `keepAliveTimeout` when overridden by *keep-alive* hints from the server. Defaults to 10 minutes.
23
23
  * **keepAliveTimeout** `number | null` (optional) - Default: `4e3` - The timeout after which a socket without active requests will time out. Monitors time between activity on a connected socket. This value may be overridden by *keep-alive* hints from the server. See [MDN: HTTP - Headers - Keep-Alive directives](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Keep-Alive#directives) for more details. Defaults to 4 seconds.
24
24
  * **keepAliveTimeoutThreshold** `number | null` (optional) - Default: `1e3` - A number subtracted from server *keep-alive* hints when overriding `keepAliveTimeout` to account for timing inaccuracies caused by e.g. transport latency. Defaults to 1 second.
@@ -199,7 +199,7 @@ Returns: `Boolean` - `false` if dispatcher is busy and further dispatch calls wo
199
199
  * **blocking** `boolean` (optional) - Default: `false` - Whether the response is expected to take a long time and would end up blocking the pipeline. When this is set to `true` further pipelining will be avoided on the same connection until headers have been received.
200
200
  * **upgrade** `string | null` (optional) - Default: `null` - Upgrade the request. Should be used to specify the kind of upgrade i.e. `'Websocket'`.
201
201
  * **bodyTimeout** `number | null` (optional) - The timeout after which a request will time out, in milliseconds. Monitors time between receiving body data. Use `0` to disable it entirely. Defaults to 30 seconds.
202
- * **headersTimeout** `number | null` (optional) - The amount of time the parser will wait to receive the complete HTTP headers. Defaults to 30 seconds.
202
+ * **headersTimeout** `number | null` (optional) - The amount of time the parser will wait to receive the complete HTTP headers while not sending the request. Defaults to 30 seconds.
203
203
  * **throwOnError** `boolean` (optional) - Default: `false` - Whether Undici should throw an error upon receiving a 4xx or 5xx response from the server.
204
204
 
205
205
  #### Parameter: `DispatchHandler`
package/lib/client.js CHANGED
@@ -889,8 +889,10 @@ function onParserTimeout (parser) {
889
889
 
890
890
  /* istanbul ignore else */
891
891
  if (timeoutType === TIMEOUT_HEADERS) {
892
- assert(!parser.paused, 'cannot be paused while waiting for headers')
893
- util.destroy(socket, new HeadersTimeoutError())
892
+ if (!socket[kWriting] || socket.writableNeedDrain || client[kRunning] > 1) {
893
+ assert(!parser.paused, 'cannot be paused while waiting for headers')
894
+ util.destroy(socket, new HeadersTimeoutError())
895
+ }
894
896
  } else if (timeoutType === TIMEOUT_BODY) {
895
897
  if (!parser.paused) {
896
898
  util.destroy(socket, new BodyTimeoutError())
@@ -1641,7 +1643,18 @@ class AsyncWriter {
1641
1643
  this.bytesWritten += len
1642
1644
 
1643
1645
  const ret = socket.write(chunk)
1646
+
1644
1647
  request.onBodySent(chunk)
1648
+
1649
+ if (!ret) {
1650
+ if (socket[kParser].timeout && socket[kParser].timeoutType === TIMEOUT_HEADERS) {
1651
+ // istanbul ignore else: only for jest
1652
+ if (socket[kParser].timeout.refresh) {
1653
+ socket[kParser].timeout.refresh()
1654
+ }
1655
+ }
1656
+ }
1657
+
1645
1658
  return ret
1646
1659
  }
1647
1660
 
package/lib/core/util.js CHANGED
@@ -244,7 +244,11 @@ function parseHeaders (headers, obj = {}) {
244
244
  const key = headers[i].toString().toLowerCase()
245
245
  let val = obj[key]
246
246
  if (!val) {
247
- obj[key] = headers[i + 1].toString()
247
+ if (Array.isArray(headers[i + 1])) {
248
+ obj[key] = headers[i + 1]
249
+ } else {
250
+ obj[key] = headers[i + 1].toString()
251
+ }
248
252
  } else {
249
253
  if (!Array.isArray(val)) {
250
254
  val = [val]
package/lib/fetch/file.js CHANGED
@@ -278,7 +278,9 @@ function processBlobParts (parts, options) {
278
278
  if (!element.buffer) { // ArrayBuffer
279
279
  bytes.push(new Uint8Array(element))
280
280
  } else {
281
- bytes.push(element.buffer)
281
+ bytes.push(
282
+ new Uint8Array(element.buffer, element.byteOffset, element.byteLength)
283
+ )
282
284
  }
283
285
  } else if (isBlobLike(element)) {
284
286
  // 3. If element is a Blob, append the bytes it represents
@@ -34,7 +34,8 @@ const {
34
34
  sameOrigin,
35
35
  isCancelled,
36
36
  isAborted,
37
- isErrorLike
37
+ isErrorLike,
38
+ fullyReadBody
38
39
  } = require('./util')
39
40
  const { kState, kHeaders, kGuard, kRealm } = require('./symbols')
40
41
  const assert = require('assert')
@@ -738,11 +739,7 @@ async function mainFetch (fetchParams, recursive = false) {
738
739
  }
739
740
 
740
741
  // 4. Fully read response’s body given processBody and processBodyError.
741
- try {
742
- processBody(await response.arrayBuffer())
743
- } catch (err) {
744
- processBodyError(err)
745
- }
742
+ await fullyReadBody(response.body, processBody, processBodyError)
746
743
  } else {
747
744
  // 21. Otherwise, run fetch finale given fetchParams and response.
748
745
  fetchFinale(fetchParams, response)
@@ -974,11 +971,7 @@ async function fetchFinale (fetchParams, response) {
974
971
  } else {
975
972
  // 4. Otherwise, fully read response’s body given processBody, processBodyError,
976
973
  // and fetchParams’s task destination.
977
- try {
978
- processBody(await response.body.stream.arrayBuffer())
979
- } catch (err) {
980
- processBodyError(err)
981
- }
974
+ await fullyReadBody(response.body, processBody, processBodyError)
982
975
  }
983
976
  }
984
977
  }
package/lib/fetch/util.js CHANGED
@@ -4,6 +4,8 @@ const { redirectStatus } = require('./constants')
4
4
  const { performance } = require('perf_hooks')
5
5
  const { isBlobLike, toUSVString, ReadableStreamFrom } = require('../core/util')
6
6
  const assert = require('assert')
7
+ const { isUint8Array } = require('util/types')
8
+ const { createHash } = require('crypto')
7
9
 
8
10
  let File
9
11
 
@@ -340,7 +342,8 @@ function determineRequestsReferrer (request) {
340
342
  }
341
343
 
342
344
  function matchRequestIntegrity (request, bytes) {
343
- return false
345
+ const [algo, expectedHashValue] = request.integrity.split('-', 2)
346
+ return createHash(algo).update(bytes).digest('hex') === expectedHashValue
344
347
  }
345
348
 
346
349
  // https://w3c.github.io/webappsec-upgrade-insecure-requests/#upgrade-request
@@ -438,6 +441,53 @@ function makeIterator (iterator, name) {
438
441
  return Object.setPrototypeOf({}, i)
439
442
  }
440
443
 
444
+ /**
445
+ * @see https://fetch.spec.whatwg.org/#body-fully-read
446
+ */
447
+ async function fullyReadBody (body, processBody, processBodyError) {
448
+ // 1. If taskDestination is null, then set taskDestination to
449
+ // the result of starting a new parallel queue.
450
+
451
+ // 2. Let promise be the result of fully reading body as promise
452
+ // given body.
453
+ try {
454
+ /** @type {Uint8Array[]} */
455
+ const chunks = []
456
+ let length = 0
457
+
458
+ const reader = body.stream.getReader()
459
+
460
+ while (true) {
461
+ const { done, value } = await reader.read()
462
+
463
+ if (done === true) {
464
+ break
465
+ }
466
+
467
+ // read-loop chunk steps
468
+ assert(isUint8Array(value))
469
+
470
+ chunks.push(value)
471
+ length += value.byteLength
472
+ }
473
+
474
+ // 3. Let fulfilledSteps given a byte sequence bytes be to queue
475
+ // a fetch task to run processBody given bytes, with
476
+ // taskDestination.
477
+ const fulfilledSteps = (bytes) => queueMicrotask(() => {
478
+ processBody(bytes)
479
+ })
480
+
481
+ fulfilledSteps(Buffer.concat(chunks, length))
482
+ } catch (err) {
483
+ // 4. Let rejectedSteps be to queue a fetch task to run
484
+ // processBodyError, with taskDestination.
485
+ queueMicrotask(() => processBodyError(err))
486
+ }
487
+
488
+ // 5. React to promise with fulfilledSteps and rejectedSteps.
489
+ }
490
+
441
491
  /**
442
492
  * Fetch supports node >= 16.8.0, but Object.hasOwn was added in v16.9.0.
443
493
  */
@@ -477,5 +527,6 @@ module.exports = {
477
527
  isValidHeaderName,
478
528
  isValidHeaderValue,
479
529
  hasOwn,
480
- isErrorLike
530
+ isErrorLike,
531
+ fullyReadBody
481
532
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "undici",
3
- "version": "5.8.2",
3
+ "version": "5.9.0",
4
4
  "description": "An HTTP/1.1 client, written from scratch for Node.js",
5
5
  "homepage": "https://undici.nodejs.org",
6
6
  "bugs": {