undici 6.18.1 → 6.18.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.
@@ -46,6 +46,9 @@ It represents the retry state for a given request.
46
46
  - **dispatch** `(options: Dispatch.DispatchOptions, handlers: Dispatch.DispatchHandlers) => Promise<Dispatch.DispatchResponse>` (required) - Dispatch function to be called after every retry.
47
47
  - **handler** Extends [`Dispatch.DispatchHandlers`](Dispatcher.md#dispatcherdispatchoptions-handler) (required) - Handler function to be called after the request is successful or the retries are exhausted.
48
48
 
49
+ >__Note__: The `RetryHandler` does not retry over stateful bodies (e.g. streams, AsyncIterable) as those, once consumed, are left in an state that cannot be reutilized. For these situations the `RetryHandler` will identify
50
+ >the body as stateful and will not retry the request rejecting with the error `UND_ERR_REQ_RETRY`.
51
+
49
52
  Examples:
50
53
 
51
54
  ```js
@@ -20,6 +20,7 @@ module.exports = {
20
20
  kHost: Symbol('host'),
21
21
  kNoRef: Symbol('no ref'),
22
22
  kBodyUsed: Symbol('used'),
23
+ kBody: Symbol('abstracted request body'),
23
24
  kRunning: Symbol('running'),
24
25
  kBlocking: Symbol('blocking'),
25
26
  kPending: Symbol('pending'),
package/lib/core/util.js CHANGED
@@ -1,19 +1,72 @@
1
1
  'use strict'
2
2
 
3
3
  const assert = require('node:assert')
4
- const { kDestroyed, kBodyUsed, kListeners } = require('./symbols')
4
+ const { kDestroyed, kBodyUsed, kListeners, kBody } = require('./symbols')
5
5
  const { IncomingMessage } = require('node:http')
6
6
  const stream = require('node:stream')
7
7
  const net = require('node:net')
8
- const { InvalidArgumentError } = require('./errors')
9
8
  const { Blob } = require('node:buffer')
10
9
  const nodeUtil = require('node:util')
11
10
  const { stringify } = require('node:querystring')
11
+ const { EventEmitter: EE } = require('node:events')
12
+ const { InvalidArgumentError } = require('./errors')
12
13
  const { headerNameLowerCasedRecord } = require('./constants')
13
14
  const { tree } = require('./tree')
14
15
 
15
16
  const [nodeMajor, nodeMinor] = process.versions.node.split('.').map(v => Number(v))
16
17
 
18
+ class BodyAsyncIterable {
19
+ constructor (body) {
20
+ this[kBody] = body
21
+ this[kBodyUsed] = false
22
+ }
23
+
24
+ async * [Symbol.asyncIterator] () {
25
+ assert(!this[kBodyUsed], 'disturbed')
26
+ this[kBodyUsed] = true
27
+ yield * this[kBody]
28
+ }
29
+ }
30
+
31
+ function wrapRequestBody (body) {
32
+ if (isStream(body)) {
33
+ // TODO (fix): Provide some way for the user to cache the file to e.g. /tmp
34
+ // so that it can be dispatched again?
35
+ // TODO (fix): Do we need 100-expect support to provide a way to do this properly?
36
+ if (bodyLength(body) === 0) {
37
+ body
38
+ .on('data', function () {
39
+ assert(false)
40
+ })
41
+ }
42
+
43
+ if (typeof body.readableDidRead !== 'boolean') {
44
+ body[kBodyUsed] = false
45
+ EE.prototype.on.call(body, 'data', function () {
46
+ this[kBodyUsed] = true
47
+ })
48
+ }
49
+
50
+ return body
51
+ } else if (body && typeof body.pipeTo === 'function') {
52
+ // TODO (fix): We can't access ReadableStream internal state
53
+ // to determine whether or not it has been disturbed. This is just
54
+ // a workaround.
55
+ return new BodyAsyncIterable(body)
56
+ } else if (
57
+ body &&
58
+ typeof body !== 'string' &&
59
+ !ArrayBuffer.isView(body) &&
60
+ isIterable(body)
61
+ ) {
62
+ // TODO: Should we allow re-using iterable if !this.opts.idempotent
63
+ // or through some other flag?
64
+ return new BodyAsyncIterable(body)
65
+ } else {
66
+ return body
67
+ }
68
+ }
69
+
17
70
  function nop () {}
18
71
 
19
72
  function isStream (obj) {
@@ -634,5 +687,6 @@ module.exports = {
634
687
  isHttpOrHttpsPrefixed,
635
688
  nodeMajor,
636
689
  nodeMinor,
637
- safeHTTPMethods: ['GET', 'HEAD', 'OPTIONS', 'TRACE']
690
+ safeHTTPMethods: ['GET', 'HEAD', 'OPTIONS', 'TRACE'],
691
+ wrapRequestBody
638
692
  }
@@ -3,7 +3,12 @@ const assert = require('node:assert')
3
3
 
4
4
  const { kRetryHandlerDefaultRetry } = require('../core/symbols')
5
5
  const { RequestRetryError } = require('../core/errors')
6
- const { isDisturbed, parseHeaders, parseRangeHeader } = require('../core/util')
6
+ const {
7
+ isDisturbed,
8
+ parseHeaders,
9
+ parseRangeHeader,
10
+ wrapRequestBody
11
+ } = require('../core/util')
7
12
 
8
13
  function calculateRetryAfterHeader (retryAfter) {
9
14
  const current = Date.now()
@@ -29,7 +34,7 @@ class RetryHandler {
29
34
 
30
35
  this.dispatch = handlers.dispatch
31
36
  this.handler = handlers.handler
32
- this.opts = dispatchOpts
37
+ this.opts = { ...dispatchOpts, body: wrapRequestBody(opts.body) }
33
38
  this.abort = null
34
39
  this.aborted = false
35
40
  this.retryOpts = {
@@ -174,7 +179,9 @@ class RetryHandler {
174
179
  this.abort(
175
180
  new RequestRetryError('Request failed', statusCode, {
176
181
  headers,
177
- count: this.retryCount
182
+ data: {
183
+ count: this.retryCount
184
+ }
178
185
  })
179
186
  )
180
187
  return false
@@ -278,7 +285,7 @@ class RetryHandler {
278
285
 
279
286
  const err = new RequestRetryError('Request failed', statusCode, {
280
287
  headers,
281
- count: this.retryCount
288
+ data: { count: this.retryCount }
282
289
  })
283
290
 
284
291
  this.abort(err)
@@ -1,7 +1,7 @@
1
1
  'use strict'
2
2
 
3
3
  const { parseSetCookie } = require('./parse')
4
- const { stringify, getHeadersList } = require('./util')
4
+ const { stringify } = require('./util')
5
5
  const { webidl } = require('../fetch/webidl')
6
6
  const { Headers } = require('../fetch/headers')
7
7
 
@@ -78,14 +78,13 @@ function getSetCookies (headers) {
78
78
 
79
79
  webidl.brandCheck(headers, Headers, { strict: false })
80
80
 
81
- const cookies = getHeadersList(headers).cookies
81
+ const cookies = headers.getSetCookie()
82
82
 
83
83
  if (!cookies) {
84
84
  return []
85
85
  }
86
86
 
87
- // In older versions of undici, cookies is a list of name:value.
88
- return cookies.map((pair) => parseSetCookie(Array.isArray(pair) ? pair[1] : pair))
87
+ return cookies.map((pair) => parseSetCookie(pair))
89
88
  }
90
89
 
91
90
  /**
@@ -1,8 +1,5 @@
1
1
  'use strict'
2
2
 
3
- const assert = require('node:assert')
4
- const { getHeadersList: internalGetHeadersList } = require('../fetch/headers')
5
-
6
3
  /**
7
4
  * @param {string} value
8
5
  * @returns {boolean}
@@ -275,35 +272,11 @@ function stringify (cookie) {
275
272
  return out.join('; ')
276
273
  }
277
274
 
278
- let kHeadersListNode
279
-
280
- function getHeadersList (headers) {
281
- try {
282
- return internalGetHeadersList(headers)
283
- } catch {
284
- // fall-through
285
- }
286
-
287
- if (!kHeadersListNode) {
288
- kHeadersListNode = Object.getOwnPropertySymbols(headers).find(
289
- (symbol) => symbol.description === 'headers list'
290
- )
291
-
292
- assert(kHeadersListNode, 'Headers cannot be parsed')
293
- }
294
-
295
- const headersList = headers[kHeadersListNode]
296
- assert(headersList)
297
-
298
- return headersList
299
- }
300
-
301
275
  module.exports = {
302
276
  isCTLExcludingHtab,
303
277
  validateCookieName,
304
278
  validateCookiePath,
305
279
  validateCookieValue,
306
280
  toIMFDate,
307
- stringify,
308
- getHeadersList
281
+ stringify
309
282
  }
@@ -641,14 +641,6 @@ Object.defineProperties(Headers.prototype, {
641
641
  },
642
642
  [util.inspect.custom]: {
643
643
  enumerable: false
644
- },
645
- // Compatibility for global headers
646
- [Symbol('headers list')]: {
647
- configurable: false,
648
- enumerable: false,
649
- get: function () {
650
- return getHeadersList(this)
651
- }
652
644
  }
653
645
  })
654
646
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "undici",
3
- "version": "6.18.1",
3
+ "version": "6.18.2",
4
4
  "description": "An HTTP/1.1 client, written from scratch for Node.js",
5
5
  "homepage": "https://undici.nodejs.org",
6
6
  "bugs": {
@@ -107,7 +107,7 @@
107
107
  "@sinonjs/fake-timers": "^11.1.0",
108
108
  "@types/node": "^18.0.3",
109
109
  "abort-controller": "^3.0.0",
110
- "borp": "^0.13.0",
110
+ "borp": "^0.14.0",
111
111
  "c8": "^9.1.0",
112
112
  "cross-env": "^7.0.3",
113
113
  "dns-packet": "^5.4.0",