undici 5.22.1 → 5.24.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.
package/README.md CHANGED
@@ -18,30 +18,34 @@ npm i undici
18
18
  ## Benchmarks
19
19
 
20
20
  The benchmark is a simple `hello world` [example](benchmarks/benchmark.js) using a
21
- number of unix sockets (connections) with a pipelining depth of 10 running on Node 16.
22
- The benchmarks below have the [simd](https://github.com/WebAssembly/simd) feature enabled.
21
+ number of unix sockets (connections) with a pipelining depth of 10 running on Node 20.6.0.
23
22
 
24
23
  ### Connections 1
25
24
 
25
+
26
26
  | Tests | Samples | Result | Tolerance | Difference with slowest |
27
27
  |---------------------|---------|---------------|-----------|-------------------------|
28
- | http - no keepalive | 15 | 4.63 req/sec | ± 2.77 % | - |
29
- | http - keepalive | 10 | 4.81 req/sec | ± 2.16 % | + 3.94 % |
30
- | undici - stream | 25 | 62.22 req/sec | ± 2.67 % | + 1244.58 % |
31
- | undici - dispatch | 15 | 64.33 req/sec | ± 2.47 % | + 1290.24 % |
32
- | undici - request | 15 | 66.08 req/sec | ± 2.48 % | + 1327.88 % |
33
- | undici - pipeline | 10 | 66.13 req/sec | ± 1.39 % | + 1329.08 % |
28
+ | http - no keepalive | 15 | 5.32 req/sec | ± 2.61 % | - |
29
+ | http - keepalive | 10 | 5.35 req/sec | ± 2.47 % | + 0.44 % |
30
+ | undici - fetch | 15 | 41.85 req/sec | ± 2.49 % | + 686.04 % |
31
+ | undici - pipeline | 40 | 50.36 req/sec | ± 2.77 % | + 845.92 % |
32
+ | undici - stream | 15 | 60.58 req/sec | ± 2.75 % | + 1037.72 % |
33
+ | undici - request | 10 | 61.19 req/sec | ± 2.60 % | + 1049.24 % |
34
+ | undici - dispatch | 20 | 64.84 req/sec | ± 2.81 % | + 1117.81 % |
35
+
34
36
 
35
37
  ### Connections 50
36
38
 
37
39
  | Tests | Samples | Result | Tolerance | Difference with slowest |
38
40
  |---------------------|---------|------------------|-----------|-------------------------|
39
- | http - no keepalive | 50 | 3546.49 req/sec | ± 2.90 % | - |
40
- | http - keepalive | 15 | 5692.67 req/sec | ± 2.48 % | + 60.52 % |
41
- | undici - pipeline | 25 | 8478.71 req/sec | ± 2.62 % | + 139.07 % |
42
- | undici - request | 20 | 9766.66 req/sec | ± 2.79 % | + 175.39 % |
43
- | undici - stream | 15 | 10109.74 req/sec | ± 2.94 % | + 185.06 % |
44
- | undici - dispatch | 25 | 10949.73 req/sec | ± 2.54 % | + 208.75 % |
41
+ | undici - fetch | 30 | 2107.19 req/sec | ± 2.69 % | - |
42
+ | http - no keepalive | 10 | 2698.90 req/sec | ± 2.68 % | + 28.08 % |
43
+ | http - keepalive | 10 | 4639.49 req/sec | ± 2.55 % | + 120.17 % |
44
+ | undici - pipeline | 40 | 6123.33 req/sec | ± 2.97 % | + 190.59 % |
45
+ | undici - stream | 50 | 9426.51 req/sec | ± 2.92 % | + 347.35 % |
46
+ | undici - request | 10 | 10162.88 req/sec | ± 2.13 % | + 382.29 % |
47
+ | undici - dispatch | 50 | 11191.11 req/sec | ± 2.98 % | + 431.09 % |
48
+
45
49
 
46
50
  ## Quick Start
47
51
 
@@ -17,6 +17,8 @@ Returns: `Client`
17
17
 
18
18
  ### Parameter: `ClientOptions`
19
19
 
20
+ > ⚠️ Warning: The `H2` support is experimental.
21
+
20
22
  * **bodyTimeout** `number | null` (optional) - Default: `300e3` - 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 300 seconds.
21
23
  * **headersTimeout** `number | null` (optional) - Default: `300e3` - The amount of time the parser will wait to receive the complete HTTP headers while not sending the request. Defaults to 300 seconds.
22
24
  * **keepAliveMaxTimeout** `number | null` (optional) - Default: `600e3` - The maximum allowed `keepAliveTimeout` when overridden by *keep-alive* hints from the server. Defaults to 10 minutes.
@@ -30,6 +32,8 @@ Returns: `Client`
30
32
  * **interceptors** `{ Client: DispatchInterceptor[] }` - Default: `[RedirectInterceptor]` - A list of interceptors that are applied to the dispatch method. Additional logic can be applied (such as, but not limited to: 302 status code handling, authentication, cookies, compression and caching). Note that the behavior of interceptors is Experimental and might change at any given time.
31
33
  * **autoSelectFamily**: `boolean` (optional) - Default: depends on local Node version, on Node 18.13.0 and above is `false`. Enables a family autodetection algorithm that loosely implements section 5 of [RFC 8305](https://tools.ietf.org/html/rfc8305#section-5). See [here](https://nodejs.org/api/net.html#socketconnectoptions-connectlistener) for more details. This option is ignored if not supported by the current Node version.
32
34
  * **autoSelectFamilyAttemptTimeout**: `number` - Default: depends on local Node version, on Node 18.13.0 and above is `250`. The amount of time in milliseconds to wait for a connection attempt to finish before trying the next address when using the `autoSelectFamily` option. See [here](https://nodejs.org/api/net.html#socketconnectoptions-connectlistener) for more details.
35
+ * **allowH2**: `boolean` - Default: `false`. Enables support for H2 if the server has assigned bigger priority to it through ALPN negotiation.
36
+ * **maxConcurrentStreams**: `number` - Default: `100`. Dictates the maximum number of concurrent streams for a single H2 session. It can be overriden by a SETTINGS remote frame.
33
37
 
34
38
  #### Parameter: `ConnectOptions`
35
39
 
@@ -202,6 +202,7 @@ Returns: `Boolean` - `false` if dispatcher is busy and further dispatch calls wo
202
202
  * **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 300 seconds.
203
203
  * **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 300 seconds.
204
204
  * **throwOnError** `boolean` (optional) - Default: `false` - Whether Undici should throw an error upon receiving a 4xx or 5xx response from the server.
205
+ * **expectContinue** `boolean` (optional) - Default: `false` - For H2, it appends the expect: 100-continue header, and halts the request body until a 100-continue is received from the remote server
205
206
 
206
207
  #### Parameter: `DispatchHandler`
207
208
 
@@ -35,7 +35,8 @@ const mockPool = mockAgent.get('http://localhost:3000')
35
35
 
36
36
  ### `MockPool.intercept(options)`
37
37
 
38
- This method defines the interception rules for matching against requests for a MockPool or MockPool. We can intercept multiple times on a single instance.
38
+ This method defines the interception rules for matching against requests for a MockPool or MockPool. We can intercept multiple times on a single instance, but each intercept is only used once.
39
+ For example if you expect to make 2 requests inside a test, you need to call `intercept()` twice. Assuming you use `disableNetConnect()` you will get `MockNotMatchedError` on the second request when you only call `intercept()` once.
39
40
 
40
41
  When defining interception rules, all the rules must pass for a request to be intercepted. If a request is not intercepted, a real request will be attempted.
41
42
 
@@ -19,7 +19,9 @@ Extends: [`AgentOptions`](Agent.md#parameter-agentoptions)
19
19
  * **uri** `string` (required) - It can be passed either by a string or a object containing `uri` as string.
20
20
  * **token** `string` (optional) - It can be passed by a string of token for authentication.
21
21
  * **auth** `string` (**deprecated**) - Use token.
22
- * **clientFactory** `(origin: URL, opts: Object) => Dispatcher` - Default: `(origin, opts) => new Pool(origin, opts)`
22
+ * **clientFactory** `(origin: URL, opts: Object) => Dispatcher` (optional) - Default: `(origin, opts) => new Pool(origin, opts)`
23
+ * **requestTls** `BuildOptions` (optional) - Options object passed when creating the underlying socket via the connector builder for the request. See [TLS](https://nodejs.org/api/tls.html#tlsconnectoptions-callback).
24
+ * **proxyTls** `BuildOptions` (optional) - Options object passed when creating the underlying socket via the connector builder for the proxy server. See [TLS](https://nodejs.org/api/tls.html#tlsconnectoptions-callback).
23
25
 
24
26
  Examples:
25
27
 
package/index.js CHANGED
@@ -106,7 +106,10 @@ if (util.nodeMajor > 16 || (util.nodeMajor === 16 && util.nodeMinor >= 8)) {
106
106
  try {
107
107
  return await fetchImpl(...arguments)
108
108
  } catch (err) {
109
- Error.captureStackTrace(err, this)
109
+ if (typeof err === 'object') {
110
+ Error.captureStackTrace(err, this)
111
+ }
112
+
110
113
  throw err
111
114
  }
112
115
  }
@@ -1,3 +1,4 @@
1
+ const { addAbortListener } = require('../core/util')
1
2
  const { RequestAbortedError } = require('../core/errors')
2
3
 
3
4
  const kListener = Symbol('kListener')
@@ -29,11 +30,7 @@ function addSignal (self, signal) {
29
30
  abort(self)
30
31
  }
31
32
 
32
- if ('addEventListener' in self[kSignal]) {
33
- self[kSignal].addEventListener('abort', self[kListener])
34
- } else {
35
- self[kSignal].addListener('abort', self[kListener])
36
- }
33
+ addAbortListener(self[kSignal], self[kListener])
37
34
  }
38
35
 
39
36
  function removeSignal (self) {
@@ -1,7 +1,7 @@
1
1
  'use strict'
2
2
 
3
- const { InvalidArgumentError, RequestAbortedError, SocketError } = require('../core/errors')
4
3
  const { AsyncResource } = require('async_hooks')
4
+ const { InvalidArgumentError, RequestAbortedError, SocketError } = require('../core/errors')
5
5
  const util = require('../core/util')
6
6
  const { addSignal, removeSignal } = require('./abort-signal')
7
7
 
@@ -50,7 +50,13 @@ class ConnectHandler extends AsyncResource {
50
50
  removeSignal(this)
51
51
 
52
52
  this.callback = null
53
- const headers = this.responseHeaders === 'raw' ? util.parseRawHeaders(rawHeaders) : util.parseHeaders(rawHeaders)
53
+
54
+ let headers = rawHeaders
55
+ // Indicates is an HTTP2Session
56
+ if (headers != null) {
57
+ headers = this.responseHeaders === 'raw' ? util.parseRawHeaders(rawHeaders) : util.parseHeaders(rawHeaders)
58
+ }
59
+
54
60
  this.runInAsyncScope(callback, null, null, {
55
61
  statusCode,
56
62
  headers,
@@ -95,7 +95,6 @@ class RequestHandler extends AsyncResource {
95
95
 
96
96
  this.callback = null
97
97
  this.res = body
98
-
99
98
  if (callback !== null) {
100
99
  if (this.throwOnError && statusCode >= 400) {
101
100
  this.runInAsyncScope(getResolveErrorBodyCallback, null,
@@ -155,12 +155,13 @@ module.exports = class BodyReadable extends Readable {
155
155
  const abortFn = () => {
156
156
  this.destroy()
157
157
  }
158
+ let signalListenerCleanup
158
159
  if (signal) {
159
160
  if (typeof signal !== 'object' || !('aborted' in signal)) {
160
161
  throw new InvalidArgumentError('signal must be an AbortSignal')
161
162
  }
162
163
  util.throwIfAborted(signal)
163
- signal.addEventListener('abort', abortFn, { once: true })
164
+ signalListenerCleanup = util.addAbortListener(signal, abortFn)
164
165
  }
165
166
  try {
166
167
  for await (const chunk of this) {
@@ -173,8 +174,10 @@ module.exports = class BodyReadable extends Readable {
173
174
  } catch {
174
175
  util.throwIfAborted(signal)
175
176
  } finally {
176
- if (signal) {
177
- signal.removeEventListener('abort', abortFn)
177
+ if (typeof signalListenerCleanup === 'function') {
178
+ signalListenerCleanup()
179
+ } else if (signalListenerCleanup) {
180
+ signalListenerCleanup[Symbol.dispose]()
178
181
  }
179
182
  }
180
183
  }
@@ -379,11 +379,7 @@ class Cache {
379
379
  const reader = stream.getReader()
380
380
 
381
381
  // 11.3
382
- readAllBytes(
383
- reader,
384
- (bytes) => bodyReadPromise.resolve(bytes),
385
- (error) => bodyReadPromise.reject(error)
386
- )
382
+ readAllBytes(reader).then(bodyReadPromise.resolve, bodyReadPromise.reject)
387
383
  } else {
388
384
  bodyReadPromise.resolve(undefined)
389
385
  }