undici 8.4.0 → 8.5.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/docs/docs/GettingStarted.md +278 -0
- package/docs/docs/api/BalancedPool.md +1 -1
- package/docs/docs/api/Client.md +1 -0
- package/docs/docs/api/Cookies.md +28 -1
- package/docs/docs/api/Dispatcher.md +15 -5
- package/docs/docs/api/EnvHttpProxyAgent.md +6 -9
- package/docs/docs/api/Errors.md +1 -1
- package/docs/docs/api/Fetch.md +2 -2
- package/docs/docs/api/H2CClient.md +1 -1
- package/docs/docs/api/MockAgent.md +1 -1
- package/docs/docs/api/MockCallHistory.md +1 -1
- package/docs/docs/api/Pool.md +1 -1
- package/docs/docs/api/RetryAgent.md +3 -3
- package/docs/docs/api/RetryHandler.md +6 -6
- package/docs/docs/api/RoundRobinPool.md +1 -1
- package/docs/docs/api/SnapshotAgent.md +3 -3
- package/docs/docs/api/Socks5ProxyAgent.md +1 -0
- package/docs/docs/api/api-lifecycle.md +4 -4
- package/lib/dispatcher/client-h1.js +69 -2
- package/lib/dispatcher/client-h2.js +107 -25
- package/lib/dispatcher/client.js +14 -4
- package/lib/dispatcher/dispatcher-base.js +1 -0
- package/lib/dispatcher/proxy-agent.js +2 -1
- package/lib/dispatcher/socks5-proxy-agent.js +4 -2
- package/lib/interceptor/dns.js +4 -0
- package/lib/util/cache.js +8 -2
- package/lib/web/cookies/parse.js +17 -25
- package/lib/web/eventsource/eventsource.js +7 -18
- package/lib/web/eventsource/util.js +32 -1
- package/lib/web/fetch/body.js +43 -0
- package/lib/web/fetch/request.js +1 -0
- package/lib/web/websocket/receiver.js +20 -3
- package/lib/web/websocket/stream/websocketstream.js +8 -1
- package/lib/web/websocket/websocket.js +3 -1
- package/package.json +1 -1
- package/types/client.d.ts +5 -0
- package/types/fetch.d.ts +1 -0
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
# Getting Started
|
|
2
|
+
|
|
3
|
+
## Installation
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm install undici
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Fetch
|
|
10
|
+
|
|
11
|
+
The quickest way to get started is with `fetch`, which follows the
|
|
12
|
+
[Fetch Standard](https://fetch.spec.whatwg.org/) and works the same way as
|
|
13
|
+
the browser API:
|
|
14
|
+
|
|
15
|
+
```js
|
|
16
|
+
import { fetch } from 'undici'
|
|
17
|
+
|
|
18
|
+
const res = await fetch('https://example.com')
|
|
19
|
+
const data = await res.json()
|
|
20
|
+
console.log(data)
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Using the Request object
|
|
24
|
+
|
|
25
|
+
undici also exports a `Request` class that follows the Fetch Standard:
|
|
26
|
+
|
|
27
|
+
```js
|
|
28
|
+
import { fetch, Request } from 'undici'
|
|
29
|
+
|
|
30
|
+
const req = new Request('https://example.com', {
|
|
31
|
+
method: 'POST',
|
|
32
|
+
headers: { 'content-type': 'application/json' },
|
|
33
|
+
body: JSON.stringify({ hello: 'world' })
|
|
34
|
+
})
|
|
35
|
+
const res = await fetch(req)
|
|
36
|
+
console.log(res.status)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Streaming the response
|
|
40
|
+
|
|
41
|
+
`res.body` is a web `ReadableStream`. Use `pipeline` from
|
|
42
|
+
`node:stream/promises` to stream it to a file:
|
|
43
|
+
|
|
44
|
+
```js
|
|
45
|
+
import { fetch } from 'undici'
|
|
46
|
+
import { pipeline } from 'node:stream/promises'
|
|
47
|
+
import { createWriteStream } from 'node:fs'
|
|
48
|
+
|
|
49
|
+
const res = await fetch('https://example.com/large-file.zip')
|
|
50
|
+
await pipeline(res.body, createWriteStream('./file.zip'))
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
> Always consume or cancel the response body. In Node.js, garbage collection
|
|
54
|
+
> is not aggressive enough to release connections promptly, so leaving a body
|
|
55
|
+
> unread can cause connection leaks and stalled requests. See
|
|
56
|
+
> [Specification Compliance - Garbage Collection](/docs/#garbage-collection)
|
|
57
|
+
> for details.
|
|
58
|
+
|
|
59
|
+
For more on `fetch`, see [API Reference: Fetch](/docs/docs/api/Fetch.md).
|
|
60
|
+
|
|
61
|
+
## Dispatchers: Connection reuse and pooling
|
|
62
|
+
|
|
63
|
+
By default, `fetch`, `request`, `stream`, and `pipeline` create a new connection
|
|
64
|
+
for each call. For applications that make many requests to the same origin,
|
|
65
|
+
this is wasteful. undici provides **dispatchers** that manage connections
|
|
66
|
+
internally.
|
|
67
|
+
|
|
68
|
+
### `Agent` — for requests to multiple origins
|
|
69
|
+
|
|
70
|
+
`Agent` is the most general-purpose dispatcher. It pools connections per-origin
|
|
71
|
+
and is the recommended default for most applications. Use it with
|
|
72
|
+
`setGlobalDispatcher` to affect all undici calls globally:
|
|
73
|
+
|
|
74
|
+
```js
|
|
75
|
+
import { Agent, setGlobalDispatcher, fetch } from 'undici'
|
|
76
|
+
|
|
77
|
+
const agent = new Agent({
|
|
78
|
+
keepAliveTimeout: 30_000,
|
|
79
|
+
keepAliveMaxTimeout: 600_000
|
|
80
|
+
})
|
|
81
|
+
setGlobalDispatcher(agent)
|
|
82
|
+
|
|
83
|
+
// All subsequent fetch/request/stream/pipeline calls reuse connections
|
|
84
|
+
const res = await fetch('https://api.example.com/data')
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
You can also pass a dispatcher per-request:
|
|
88
|
+
|
|
89
|
+
```js
|
|
90
|
+
await fetch('https://api.example.com/data', { dispatcher: agent })
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### `Pool` — for requests to a single origin
|
|
94
|
+
|
|
95
|
+
`Pool` manages a fixed set of connections to one origin. It gives you explicit
|
|
96
|
+
control over concurrency:
|
|
97
|
+
|
|
98
|
+
```js
|
|
99
|
+
import { Pool, request } from 'undici'
|
|
100
|
+
|
|
101
|
+
const pool = new Pool('https://api.example.com', { connections: 10 })
|
|
102
|
+
|
|
103
|
+
const { body } = await request('https://api.example.com/data', {
|
|
104
|
+
dispatcher: pool
|
|
105
|
+
})
|
|
106
|
+
const data = await body.json()
|
|
107
|
+
|
|
108
|
+
pool.close()
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### `Client` — for a single connection
|
|
112
|
+
|
|
113
|
+
`Client` maps to a single TCP connection. It supports pipelining (sending
|
|
114
|
+
multiple requests before responses arrive):
|
|
115
|
+
|
|
116
|
+
```js
|
|
117
|
+
import { Client } from 'undici'
|
|
118
|
+
|
|
119
|
+
const client = new Client('https://api.example.com', {
|
|
120
|
+
pipelining: 5
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
const { body } = await client.request({ path: '/', method: 'GET' })
|
|
124
|
+
await body.dump()
|
|
125
|
+
|
|
126
|
+
client.close()
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
For more on dispatcher options and lifecycle, see:
|
|
130
|
+
- [API Reference: Agent](/docs/docs/api/Agent.md)
|
|
131
|
+
- [API Reference: Pool](/docs/docs/api/Pool.md)
|
|
132
|
+
- [API Reference: Client](/docs/docs/api/Client.md)
|
|
133
|
+
|
|
134
|
+
## Timeouts
|
|
135
|
+
|
|
136
|
+
undici applies timeouts at two levels:
|
|
137
|
+
|
|
138
|
+
- **`headersTimeout`** — time to wait for response headers (default: 300s).
|
|
139
|
+
- **`bodyTimeout`** — time between consecutive body chunks (default: 300s).
|
|
140
|
+
|
|
141
|
+
Set these on the dispatcher or per-request:
|
|
142
|
+
|
|
143
|
+
```js
|
|
144
|
+
import { Agent, setGlobalDispatcher } from 'undici'
|
|
145
|
+
|
|
146
|
+
const agent = new Agent({
|
|
147
|
+
headersTimeout: 5_000,
|
|
148
|
+
bodyTimeout: 30_000
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
setGlobalDispatcher(agent)
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Timeout errors are thrown as `HeadersTimeoutError` and `BodyTimeoutError`.
|
|
155
|
+
See [API Reference: Errors](/docs/docs/api/Errors.md) for the full list.
|
|
156
|
+
|
|
157
|
+
## Error handling
|
|
158
|
+
|
|
159
|
+
undici exposes structured errors via `error.code`:
|
|
160
|
+
|
|
161
|
+
```js
|
|
162
|
+
import { request, errors } from 'undici'
|
|
163
|
+
|
|
164
|
+
try {
|
|
165
|
+
const { body } = await request('https://example.com')
|
|
166
|
+
await body.json()
|
|
167
|
+
} catch (err) {
|
|
168
|
+
switch (err.code) {
|
|
169
|
+
case 'UND_ERR_CONNECT_TIMEOUT':
|
|
170
|
+
console.error('Connection timed out')
|
|
171
|
+
break
|
|
172
|
+
case 'UND_ERR_HEADERS_TIMEOUT':
|
|
173
|
+
console.error('Headers timed out')
|
|
174
|
+
break
|
|
175
|
+
case 'UND_ERR_BODY_TIMEOUT':
|
|
176
|
+
console.error('Body timed out')
|
|
177
|
+
break
|
|
178
|
+
case 'UND_ERR_ABORTED':
|
|
179
|
+
console.error('Request was aborted')
|
|
180
|
+
break
|
|
181
|
+
default:
|
|
182
|
+
console.error(err)
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### Aborting requests
|
|
188
|
+
|
|
189
|
+
```js
|
|
190
|
+
import { request } from 'undici'
|
|
191
|
+
|
|
192
|
+
const ac = new AbortController()
|
|
193
|
+
|
|
194
|
+
setTimeout(() => ac.abort(), 1000)
|
|
195
|
+
|
|
196
|
+
try {
|
|
197
|
+
const { body } = await request('https://example.com', {
|
|
198
|
+
signal: ac.signal
|
|
199
|
+
})
|
|
200
|
+
await body.dump()
|
|
201
|
+
} catch (err) {
|
|
202
|
+
console.error(err.code) // UND_ERR_ABORTED
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## Common patterns
|
|
207
|
+
|
|
208
|
+
### Proxies
|
|
209
|
+
|
|
210
|
+
Use `ProxyAgent` for HTTP(S) proxies, or `EnvHttpProxyAgent` to pick up
|
|
211
|
+
proxy settings from environment variables:
|
|
212
|
+
|
|
213
|
+
```js
|
|
214
|
+
import { ProxyAgent, setGlobalDispatcher } from 'undici'
|
|
215
|
+
|
|
216
|
+
const proxy = new ProxyAgent('http://proxy.internal:8080')
|
|
217
|
+
setGlobalDispatcher(proxy)
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
See [Best Practices: Proxy](/docs/docs/best-practices/proxy.md) and
|
|
221
|
+
[API Reference: ProxyAgent](/docs/docs/api/ProxyAgent.md).
|
|
222
|
+
|
|
223
|
+
### Mocking in tests
|
|
224
|
+
|
|
225
|
+
```js
|
|
226
|
+
import { MockAgent, setGlobalDispatcher, request } from 'undici'
|
|
227
|
+
|
|
228
|
+
const mockAgent = new MockAgent()
|
|
229
|
+
setGlobalDispatcher(mockAgent)
|
|
230
|
+
|
|
231
|
+
const mockPool = mockAgent.get('https://api.example.com')
|
|
232
|
+
mockPool.intercept({ path: '/users' }).reply(200, [{ id: 1 }])
|
|
233
|
+
|
|
234
|
+
const { body } = await request('https://api.example.com/users')
|
|
235
|
+
console.log(await body.json())
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
See [Best Practices: Mocking Request](/docs/docs/best-practices/mocking-request.md)
|
|
239
|
+
and [API Reference: MockAgent](/docs/docs/api/MockAgent.md).
|
|
240
|
+
|
|
241
|
+
### Testing with undici
|
|
242
|
+
|
|
243
|
+
For test suites, set short keep-alive timeouts to avoid slow teardowns:
|
|
244
|
+
|
|
245
|
+
```js
|
|
246
|
+
import { Agent, setGlobalDispatcher } from 'undici'
|
|
247
|
+
|
|
248
|
+
const agent = new Agent({
|
|
249
|
+
keepAliveTimeout: 10,
|
|
250
|
+
keepAliveMaxTimeout: 10
|
|
251
|
+
})
|
|
252
|
+
setGlobalDispatcher(agent)
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
See [Best Practices: Writing Tests](/docs/docs/best-practices/writing-tests.md).
|
|
256
|
+
|
|
257
|
+
### Customizing the global fetch
|
|
258
|
+
|
|
259
|
+
You can override Node.js's built-in globals with `install()`:
|
|
260
|
+
|
|
261
|
+
```js
|
|
262
|
+
import { install } from 'undici'
|
|
263
|
+
|
|
264
|
+
install()
|
|
265
|
+
|
|
266
|
+
// Global fetch, Headers, Response, Request, and FormData
|
|
267
|
+
// now come from undici, not the Node.js bundle
|
|
268
|
+
const res = await fetch('https://example.com')
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
See [API Reference: Global Installation](/docs/docs/api/GlobalInstallation.md).
|
|
272
|
+
|
|
273
|
+
## Further reading
|
|
274
|
+
|
|
275
|
+
- [Undici vs. Built-in Fetch](/docs/docs/best-practices/undici-vs-builtin-fetch.md) —
|
|
276
|
+
when to install undici vs using Node.js built-in fetch
|
|
277
|
+
- [API Reference](/docs/docs/api/Dispatcher.md) — full dispatcher API documentation
|
|
278
|
+
- [Examples](/docs/examples/) — runnable code examples
|
|
@@ -34,7 +34,7 @@ Implements [Client.closed](/docs/docs/api/Client.md#clientclosed)
|
|
|
34
34
|
|
|
35
35
|
Implements [Client.destroyed](/docs/docs/api/Client.md#clientdestroyed)
|
|
36
36
|
|
|
37
|
-
### `
|
|
37
|
+
### `BalancedPool.stats`
|
|
38
38
|
|
|
39
39
|
Returns [`PoolStats`](/docs/docs/api/PoolStats.md) instance for this pool.
|
|
40
40
|
|
package/docs/docs/api/Client.md
CHANGED
|
@@ -25,6 +25,7 @@ Returns: `Client`
|
|
|
25
25
|
* **maxHeaderSize** `number | null` (optional) - Default: `--max-http-header-size` or `16384` - The maximum length of request headers in bytes. Defaults to Node.js' --max-http-header-size or 16KiB.
|
|
26
26
|
* **maxResponseSize** `number | null` (optional) - Default: `-1` - The maximum length of response body in bytes. Set to `-1` to disable.
|
|
27
27
|
* **webSocket** `WebSocketOptions` (optional) - WebSocket-specific configuration options.
|
|
28
|
+
* **maxFragments** `number` (optional) - Default: `131072` - Maximum number of fragments in a message. Set to 0 to disable the limit.
|
|
28
29
|
* **maxPayloadSize** `number` (optional) - Default: `134217728` (128 MB) - Maximum allowed payload size in bytes for WebSocket messages. Applied to uncompressed messages, compressed frame payloads, and decompressed (permessage-deflate) messages. Set to 0 to disable the limit.
|
|
29
30
|
* **pipelining** `number | null` (optional) - Default: `1` - The amount of concurrent requests to be sent over the single TCP/TLS connection according to [RFC7230](https://tools.ietf.org/html/rfc7230#section-6.3.2). Carefully consider your workload and environment before enabling concurrent requests as pipelining may reduce performance if used incorrectly. Pipelining is sensitive to network stack settings as well as head of line blocking caused by e.g. long running requests. Set to `0` to disable keep-alive connections. This option has no effect once HTTP/2 is negotiated — see `maxConcurrentStreams` for the h2 dispatch ceiling.
|
|
30
31
|
* **connect** `ConnectOptions | Function | null` (optional) - Default: `null` - Configures how undici establishes TCP/TLS connections. Accepts two forms:
|
package/docs/docs/api/Cookies.md
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* **path** `string` (optional)
|
|
11
11
|
* **secure** `boolean` (optional)
|
|
12
12
|
* **httpOnly** `boolean` (optional)
|
|
13
|
-
* **sameSite** `'
|
|
13
|
+
* **sameSite** `'Strict'|'Lax'|'None'` (optional)
|
|
14
14
|
* **unparsed** `string[]` (optional) Left over attributes that weren't parsed.
|
|
15
15
|
|
|
16
16
|
## `deleteCookie(headers, name[, attributes])`
|
|
@@ -80,6 +80,33 @@ Arguments:
|
|
|
80
80
|
|
|
81
81
|
Returns: `Cookie[]`
|
|
82
82
|
|
|
83
|
+
## `parseCookie(cookie)`
|
|
84
|
+
|
|
85
|
+
Parses a single `Set-Cookie` header value into a `Cookie` object.
|
|
86
|
+
|
|
87
|
+
```js
|
|
88
|
+
import { parseCookie } from 'undici'
|
|
89
|
+
|
|
90
|
+
console.log(parseCookie('undici=getSetCookies; Secure; SameSite=Lax'))
|
|
91
|
+
// {
|
|
92
|
+
// name: 'undici',
|
|
93
|
+
// value: 'getSetCookies',
|
|
94
|
+
// secure: true,
|
|
95
|
+
// sameSite: 'Lax'
|
|
96
|
+
// }
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Notes:
|
|
100
|
+
|
|
101
|
+
* The cookie value is returned as it appears in the header. Percent-encoded sequences such as `%20` or `%0D%0A` are **not** decoded.
|
|
102
|
+
* `sameSite` is only set for exact case-insensitive matches of `Strict`, `Lax`, or `None`.
|
|
103
|
+
|
|
104
|
+
Arguments:
|
|
105
|
+
|
|
106
|
+
* **cookie** `string`
|
|
107
|
+
|
|
108
|
+
Returns: `Cookie | null`
|
|
109
|
+
|
|
83
110
|
## `setCookie(headers, cookie)`
|
|
84
111
|
|
|
85
112
|
Appends a cookie to the `Set-Cookie` header.
|
|
@@ -211,6 +211,7 @@ Returns: `Boolean` - `false` if dispatcher is busy and further dispatch calls wo
|
|
|
211
211
|
* **onResponseData** `(controller: DispatchController, chunk: Buffer) => void` - Invoked when response payload data is received. Not required for `upgrade` requests.
|
|
212
212
|
* **onResponseEnd** `(controller: DispatchController, trailers: Record<string, string | string[]>) => void` - Invoked when response payload and trailers have been received and the request has completed. Not required for `upgrade` requests.
|
|
213
213
|
* **onResponseError** `(controller: DispatchController, error: Error) => void` - Invoked when an error has occurred. May not throw.
|
|
214
|
+
* **onBodySent** `(chunk: Buffer) => void` (optional) - Invoked when a chunk of the request body is sent.
|
|
214
215
|
|
|
215
216
|
#### Migration from legacy handler API
|
|
216
217
|
|
|
@@ -688,7 +689,7 @@ return null
|
|
|
688
689
|
|
|
689
690
|
A faster version of `Dispatcher.request`. This method expects the second argument `factory` to return a [`stream.Writable`](https://nodejs.org/api/stream.html#stream_class_stream_writable) stream which the response will be written to. This improves performance by avoiding creating an intermediate [`stream.Readable`](https://nodejs.org/api/stream.html#stream_readable_streams) stream when the user expects to directly pipe the response body to a [`stream.Writable`](https://nodejs.org/api/stream.html#stream_class_stream_writable) stream.
|
|
690
691
|
|
|
691
|
-
As demonstrated in [Example 1 - Basic GET stream request](/docs/docs/api/Dispatcher.md#example-1-basic-get-stream-request), it is recommended to use the `option.opaque` property to avoid creating a closure for the `factory` method. This pattern works well with Node.js Web Frameworks such as [Fastify](https://fastify.io). See [Example 2 - Stream to Fastify Response](/docs/docs/api/
|
|
692
|
+
As demonstrated in [Example 1 - Basic GET stream request](/docs/docs/api/Dispatcher.md#example-1-basic-get-stream-request), it is recommended to use the `option.opaque` property to avoid creating a closure for the `factory` method. This pattern works well with Node.js Web Frameworks such as [Fastify](https://fastify.io). See [Example 2 - Stream to Fastify Response](/docs/docs/api/Dispatcher.md#example-2-stream-to-fastify-response) for more details.
|
|
692
693
|
|
|
693
694
|
Arguments:
|
|
694
695
|
|
|
@@ -1016,7 +1017,7 @@ The `retry` interceptor allows you to customize the way your dispatcher handles
|
|
|
1016
1017
|
|
|
1017
1018
|
It accepts the same arguments as the [`RetryHandler` constructor](/docs/docs/api/RetryHandler.md).
|
|
1018
1019
|
|
|
1019
|
-
**Example - Basic
|
|
1020
|
+
**Example - Basic Retry Interceptor**
|
|
1020
1021
|
|
|
1021
1022
|
```js
|
|
1022
1023
|
const { Client, interceptors } = require("undici");
|
|
@@ -1112,7 +1113,7 @@ It represents a storage object for resolved DNS records.
|
|
|
1112
1113
|
**Example - Basic DNS Interceptor**
|
|
1113
1114
|
|
|
1114
1115
|
```js
|
|
1115
|
-
const {
|
|
1116
|
+
const { Agent, interceptors } = require("undici");
|
|
1116
1117
|
const { dns } = interceptors;
|
|
1117
1118
|
|
|
1118
1119
|
const client = new Agent().compose([
|
|
@@ -1128,7 +1129,7 @@ const response = await client.request({
|
|
|
1128
1129
|
**Example - DNS Interceptor and LRU cache as a storage**
|
|
1129
1130
|
|
|
1130
1131
|
```js
|
|
1131
|
-
const {
|
|
1132
|
+
const { Agent, interceptors } = require("undici");
|
|
1132
1133
|
const QuickLRU = require("quick-lru");
|
|
1133
1134
|
const { dns } = interceptors;
|
|
1134
1135
|
|
|
@@ -1375,14 +1376,23 @@ When using the array header format (`string[]`), Undici processes only indexed e
|
|
|
1375
1376
|
|
|
1376
1377
|
Response headers will derive a `host` from the `url` of the [Client](/docs/docs/api/Client.md#class-client) instance if no `host` header was previously specified.
|
|
1377
1378
|
|
|
1379
|
+
### Request header validation
|
|
1380
|
+
|
|
1381
|
+
Request headers that are managed by the HTTP connection are handled differently from ordinary headers:
|
|
1382
|
+
|
|
1383
|
+
* `transfer-encoding`, `keep-alive`, and `upgrade` cannot be set through `options.headers`; Undici throws an `InvalidArgumentError`.
|
|
1384
|
+
* `expect` is not supported; Undici throws a `NotSupportedError`.
|
|
1385
|
+
* `connection` must be a string containing comma-separated valid HTTP tokens. Undici rejects malformed tokens with `InvalidArgumentError: invalid connection header` and uses the `close` token to request connection reset behavior.
|
|
1386
|
+
* `host` and `content-length` are tracked separately from the raw header list. Duplicate `host` or `content-length` values are rejected, and `content-length` must contain only decimal digits.
|
|
1387
|
+
|
|
1378
1388
|
### Example 1 - Object
|
|
1379
1389
|
|
|
1380
1390
|
```js
|
|
1381
1391
|
{
|
|
1382
1392
|
'content-length': '123',
|
|
1383
1393
|
'content-type': 'text/plain',
|
|
1384
|
-
connection: 'keep-alive',
|
|
1385
1394
|
host: 'mysite.com',
|
|
1395
|
+
'accept-language': 'en',
|
|
1386
1396
|
accept: '*/*'
|
|
1387
1397
|
}
|
|
1388
1398
|
```
|
|
@@ -52,11 +52,11 @@ import { setGlobalDispatcher, fetch, EnvHttpProxyAgent } from 'undici'
|
|
|
52
52
|
const envHttpProxyAgent = new EnvHttpProxyAgent()
|
|
53
53
|
setGlobalDispatcher(envHttpProxyAgent)
|
|
54
54
|
|
|
55
|
-
const
|
|
55
|
+
const response = await fetch('http://localhost:3000/foo')
|
|
56
56
|
|
|
57
|
-
console.log('response received', status) // response received 200
|
|
57
|
+
console.log('response received', response.status) // response received 200
|
|
58
58
|
|
|
59
|
-
const data = await json() // data { foo: "bar" }
|
|
59
|
+
const data = await response.json() // data { foo: "bar" }
|
|
60
60
|
```
|
|
61
61
|
|
|
62
62
|
#### Example - Basic Proxy Request with global agent dispatcher
|
|
@@ -102,14 +102,11 @@ import { EnvHttpProxyAgent, fetch } from 'undici'
|
|
|
102
102
|
|
|
103
103
|
const envHttpProxyAgent = new EnvHttpProxyAgent()
|
|
104
104
|
|
|
105
|
-
const {
|
|
106
|
-
status,
|
|
107
|
-
json
|
|
108
|
-
} = await fetch('http://localhost:3000/foo', { dispatcher: envHttpProxyAgent })
|
|
105
|
+
const response = await fetch('http://localhost:3000/foo', { dispatcher: envHttpProxyAgent })
|
|
109
106
|
|
|
110
|
-
console.log('response received', status) // response received 200
|
|
107
|
+
console.log('response received', response.status) // response received 200
|
|
111
108
|
|
|
112
|
-
const data = await json() // data { foo: "bar" }
|
|
109
|
+
const data = await response.json() // data { foo: "bar" }
|
|
113
110
|
```
|
|
114
111
|
|
|
115
112
|
## Instance Methods
|
package/docs/docs/api/Errors.md
CHANGED
|
@@ -32,7 +32,7 @@ import { errors } from 'undici'
|
|
|
32
32
|
| `ResponseError` | `UND_ERR_RESPONSE` | response returned an error status code; carries `statusCode`, `headers` and `body`. |
|
|
33
33
|
| `MaxOriginsReachedError` | `UND_ERR_MAX_ORIGINS_REACHED` | the maximum number of allowed origins has been reached. |
|
|
34
34
|
| `BalancedPoolMissingUpstreamError` | `UND_ERR_BPL_MISSING_UPSTREAM` | no upstream has been added to the `BalancedPool`. |
|
|
35
|
-
| `Socks5ProxyError` | `UND_ERR_SOCKS5
|
|
35
|
+
| `Socks5ProxyError` | `UND_ERR_SOCKS5` | an error occurred during SOCKS5 proxy negotiation. |
|
|
36
36
|
| `HTTPParserError` | `HPE_*` | an error occurred while parsing the HTTP response (extends `Error`, not `UndiciError`). |
|
|
37
37
|
|
|
38
38
|
Be aware of the possible difference between the global dispatcher version and the actual undici version you might be using. We recommend to avoid the check `instanceof errors.UndiciError` and seek for the `error.code === '<error_code>'` instead to avoid inconsistencies.
|
package/docs/docs/api/Fetch.md
CHANGED
|
@@ -15,7 +15,7 @@ same implementation. Use the built-in global `FormData` with the built-in
|
|
|
15
15
|
global `fetch()`, and use `undici`'s `FormData` with `undici.fetch()`.
|
|
16
16
|
|
|
17
17
|
If you want the installed `undici` package to provide the globals, call
|
|
18
|
-
[`install()`](/docs/api/GlobalInstallation.md) so `fetch`, `Headers`,
|
|
18
|
+
[`install()`](/docs/docs/api/GlobalInstallation.md) so `fetch`, `Headers`,
|
|
19
19
|
`Response`, `Request`, and `FormData` are installed together as a matching set.
|
|
20
20
|
|
|
21
21
|
## Response
|
|
@@ -26,7 +26,7 @@ This API is implemented as per the standard, you can find documentation on [MDN]
|
|
|
26
26
|
|
|
27
27
|
This API is implemented as per the standard, you can find documentation on [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Request)
|
|
28
28
|
|
|
29
|
-
##
|
|
29
|
+
## Headers
|
|
30
30
|
|
|
31
31
|
This API is implemented as per the standard, you can find documentation on [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Headers)
|
|
32
32
|
|
|
@@ -17,7 +17,7 @@ const server = createServer((req, res) => {
|
|
|
17
17
|
})
|
|
18
18
|
|
|
19
19
|
server.listen()
|
|
20
|
-
once(server, 'listening').then(() => {
|
|
20
|
+
once(server, 'listening').then(async () => {
|
|
21
21
|
const client = new H2CClient(`http://localhost:${server.address().port}/`)
|
|
22
22
|
|
|
23
23
|
const response = await client.request({ path: '/', method: 'GET' })
|
|
@@ -165,7 +165,7 @@ Parameters :
|
|
|
165
165
|
|
|
166
166
|
- criteria : the first parameter. a function, regexp or object.
|
|
167
167
|
- function : filter MockCallHistoryLog when the function returns false
|
|
168
|
-
- regexp : filter MockCallHistoryLog when the regexp does not match on MockCallHistoryLog.toString() ([see](
|
|
168
|
+
- regexp : filter MockCallHistoryLog when the regexp does not match on MockCallHistoryLog.toString() ([see](/docs/docs/api/MockCallHistoryLog.md#to-string))
|
|
169
169
|
- object : an object with MockCallHistoryLog properties as keys to apply multiple filters. each values are a [filter parameter](/docs/docs/api/MockCallHistory.md#filter-parameter)
|
|
170
170
|
- options : the second parameter. an object.
|
|
171
171
|
- options.operator : `'AND'` or `'OR'` (default `'OR'`). Used only if criteria is an object. see below
|
package/docs/docs/api/Pool.md
CHANGED
|
@@ -36,7 +36,7 @@ Implements [Client.destroyed](/docs/docs/api/Client.md#clientdestroyed)
|
|
|
36
36
|
|
|
37
37
|
### `Pool.stats`
|
|
38
38
|
|
|
39
|
-
Returns [`PoolStats`](PoolStats.md) instance for this pool.
|
|
39
|
+
Returns [`PoolStats`](/docs/docs/api/PoolStats.md) instance for this pool.
|
|
40
40
|
|
|
41
41
|
## Instance Methods
|
|
42
42
|
|
|
@@ -12,7 +12,7 @@ Arguments:
|
|
|
12
12
|
* **dispatcher** `undici.Dispatcher` (required) - the dispatcher to wrap
|
|
13
13
|
* **options** `RetryHandlerOptions` (optional) - the options
|
|
14
14
|
|
|
15
|
-
Returns: `
|
|
15
|
+
Returns: `RetryAgent`
|
|
16
16
|
|
|
17
17
|
### Parameter: `RetryHandlerOptions`
|
|
18
18
|
|
|
@@ -23,9 +23,9 @@ Returns: `ProxyAgent`
|
|
|
23
23
|
- **minTimeout** `number` (optional) - Minimum number of milliseconds to wait before retrying. Default: `500` (half a second)
|
|
24
24
|
- **timeoutFactor** `number` (optional) - Factor to multiply the timeout by for each retry attempt. Default: `2`
|
|
25
25
|
- **retryAfter** `boolean` (optional) - It enables automatic retry after the `Retry-After` header is received. Default: `true`
|
|
26
|
-
- **methods** `string[]` (optional) - Array of HTTP methods to retry. Default: `['GET', '
|
|
26
|
+
- **methods** `string[]` (optional) - Array of HTTP methods to retry. Default: `['GET', 'HEAD', 'OPTIONS', 'PUT', 'DELETE', 'TRACE']`
|
|
27
27
|
- **statusCodes** `number[]` (optional) - Array of HTTP status codes to retry. Default: `[429, 500, 502, 503, 504]`
|
|
28
|
-
- **errorCodes** `string[]` (optional) - Array of Error codes to retry. Default: `['ECONNRESET', 'ECONNREFUSED', 'ENOTFOUND', 'ENETDOWN','ENETUNREACH', 'EHOSTDOWN', 'UND_ERR_SOCKET']`
|
|
28
|
+
- **errorCodes** `string[]` (optional) - Array of Error codes to retry. Default: `['ECONNRESET', 'ECONNREFUSED', 'ENOTFOUND', 'ENETDOWN', 'ENETUNREACH', 'EHOSTDOWN', 'EHOSTUNREACH', 'EPIPE', 'UND_ERR_SOCKET']`
|
|
29
29
|
|
|
30
30
|
**`RetryContext`**
|
|
31
31
|
|
|
@@ -4,12 +4,12 @@ Extends: `undici.DispatcherHandlers`
|
|
|
4
4
|
|
|
5
5
|
A handler class that implements the retry logic for a request.
|
|
6
6
|
|
|
7
|
-
## `new RetryHandler(
|
|
7
|
+
## `new RetryHandler(opts, { dispatch, handler })`
|
|
8
8
|
|
|
9
9
|
Arguments:
|
|
10
10
|
|
|
11
|
-
- **
|
|
12
|
-
- **
|
|
11
|
+
- **opts** `Dispatch.DispatchOptions & { retryOptions?: RetryOptions }` (required) - An intersection of `Dispatcher.DispatchOptions` and an optional `RetryOptions` object.
|
|
12
|
+
- **{ dispatch, handler }** `RetryHandlers` (required) - Object containing the `dispatch` to be used on every retry, and `handler` for handling the `dispatch` lifecycle.
|
|
13
13
|
|
|
14
14
|
Returns: `retryHandler`
|
|
15
15
|
|
|
@@ -20,15 +20,15 @@ Extends: [`Dispatch.DispatchOptions`](/docs/docs/api/Dispatcher.md#parameter-dis
|
|
|
20
20
|
#### `RetryOptions`
|
|
21
21
|
|
|
22
22
|
- **throwOnError** `boolean` (optional) - Disable to prevent throwing error on last retry attept, useful if you need the body on errors from server or if you have custom error handler.
|
|
23
|
-
- **retry** `(err: Error, context: RetryContext, callback: (err?: Error | null) => void) =>
|
|
23
|
+
- **retry** `(err: Error, context: RetryContext, callback: (err?: Error | null) => void) => void` (optional) - Function to be called after every retry. It should pass error if no more retries should be performed.
|
|
24
24
|
- **maxRetries** `number` (optional) - Maximum number of retries. Default: `5`
|
|
25
25
|
- **maxTimeout** `number` (optional) - Maximum number of milliseconds to wait before retrying. Default: `30000` (30 seconds)
|
|
26
26
|
- **minTimeout** `number` (optional) - Minimum number of milliseconds to wait before retrying. Default: `500` (half a second)
|
|
27
27
|
- **timeoutFactor** `number` (optional) - Factor to multiply the timeout by for each retry attempt. Default: `2`
|
|
28
28
|
- **retryAfter** `boolean` (optional) - It enables automatic retry after the `Retry-After` header is received. Default: `true`
|
|
29
|
-
- **methods** `string[]` (optional) - Array of HTTP methods to retry. Default: `['GET', '
|
|
29
|
+
- **methods** `string[]` (optional) - Array of HTTP methods to retry. Default: `['GET', 'HEAD', 'OPTIONS', 'PUT', 'DELETE', 'TRACE']`
|
|
30
30
|
- **statusCodes** `number[]` (optional) - Array of HTTP status codes to retry. Default: `[429, 500, 502, 503, 504]`
|
|
31
|
-
- **errorCodes** `string[]` (optional) - Array of Error codes to retry. Default: `['ECONNRESET', 'ECONNREFUSED', 'ENOTFOUND', 'ENETDOWN','ENETUNREACH', 'EHOSTDOWN', 'UND_ERR_SOCKET']`
|
|
31
|
+
- **errorCodes** `string[]` (optional) - Array of Error codes to retry. Default: `['ECONNRESET', 'ECONNREFUSED', 'ENOTFOUND', 'ENETDOWN', 'ENETUNREACH', 'EHOSTDOWN', 'EHOSTUNREACH', 'EPIPE', 'UND_ERR_SOCKET']`
|
|
32
32
|
|
|
33
33
|
**`RetryContext`**
|
|
34
34
|
|
|
@@ -66,7 +66,7 @@ Implements [Client.destroyed](/docs/docs/api/Client.md#clientdestroyed)
|
|
|
66
66
|
|
|
67
67
|
### `RoundRobinPool.stats`
|
|
68
68
|
|
|
69
|
-
Returns [`PoolStats`](PoolStats.md) instance for this pool.
|
|
69
|
+
Returns [`PoolStats`](/docs/docs/api/PoolStats.md) instance for this pool.
|
|
70
70
|
|
|
71
71
|
## Instance Methods
|
|
72
72
|
|
|
@@ -634,6 +634,6 @@ SnapshotAgent provides similar functionality to nock but is specifically designe
|
|
|
634
634
|
|
|
635
635
|
## See Also
|
|
636
636
|
|
|
637
|
-
- [MockAgent](
|
|
638
|
-
- [MockCallHistory](
|
|
639
|
-
- [Testing Best Practices](
|
|
637
|
+
- [MockAgent](/docs/docs/api/MockAgent.md) - Manual mocking for more control
|
|
638
|
+
- [MockCallHistory](/docs/docs/api/MockCallHistory.md) - Inspecting request history
|
|
639
|
+
- [Testing Best Practices](/docs/docs/best-practices/writing-tests.md) - General testing guidance
|
|
@@ -22,6 +22,7 @@ Extends: [`PoolOptions`](/docs/docs/api/Pool.md#parameter-pooloptions)
|
|
|
22
22
|
* **password** `string` (optional) - SOCKS5 proxy password for authentication. Can also be provided in the proxy URL.
|
|
23
23
|
* **connect** `Function` (optional) - Custom connector function for the proxy connection.
|
|
24
24
|
* **proxyTls** `BuildOptions` (optional) - TLS options for the proxy connection (when using SOCKS5 over TLS).
|
|
25
|
+
* **requestTls** `BuildOptions` (optional) - TLS options applied to the HTTPS connection to the target server through the SOCKS5 tunnel. Use this to configure `ca`, `cert`, `key`, `rejectUnauthorized`, `servername`, etc. for the target HTTPS endpoint.
|
|
25
26
|
|
|
26
27
|
Examples:
|
|
27
28
|
|
|
@@ -58,9 +58,9 @@ stateDiagram-v2
|
|
|
58
58
|
|
|
59
59
|
### idle
|
|
60
60
|
|
|
61
|
-
The **idle** state is the initial state of a `Client` instance. While an `origin` is required for instantiating a `Client` instance, the underlying socket connection will not be established until a request is queued using [`Client.dispatch()`](/docs/docs/api/Client.md#clientdispatchoptions-handlers). By calling `Client.dispatch()` directly or using one of the multiple implementations ([`Client.connect()`](Client.md#clientconnectoptions-callback), [`Client.pipeline()`](Client.md#clientpipelineoptions-handler), [`Client.request()`](Client.md#clientrequestoptions-callback), [`Client.stream()`](Client.md#clientstreamoptions-factory-callback), and [`Client.upgrade()`](/docs/docs/api/Client.md#clientupgradeoptions-callback)), the `Client` instance will transition from **idle** to [**pending**](/docs/docs/api/Client.md#pending) and then most likely directly to [**processing**](/docs/docs/api/Client.md#processing).
|
|
61
|
+
The **idle** state is the initial state of a `Client` instance. While an `origin` is required for instantiating a `Client` instance, the underlying socket connection will not be established until a request is queued using [`Client.dispatch()`](/docs/docs/api/Client.md#clientdispatchoptions-handlers). By calling `Client.dispatch()` directly or using one of the multiple implementations ([`Client.connect()`](/docs/docs/api/Client.md#clientconnectoptions-callback), [`Client.pipeline()`](/docs/docs/api/Client.md#clientpipelineoptions-handler), [`Client.request()`](/docs/docs/api/Client.md#clientrequestoptions-callback), [`Client.stream()`](/docs/docs/api/Client.md#clientstreamoptions-factory-callback), and [`Client.upgrade()`](/docs/docs/api/Client.md#clientupgradeoptions-callback)), the `Client` instance will transition from **idle** to [**pending**](/docs/docs/api/Client.md#pending) and then most likely directly to [**processing**](/docs/docs/api/Client.md#processing).
|
|
62
62
|
|
|
63
|
-
Calling [`Client.close()`](/docs/docs/api/Client.md#clientclosecallback) or [`Client.destroy()`](Client.md#clientdestroyerror-callback) transitions directly to the [**destroyed**](/docs/docs/api/Client.md#destroyed) state since the `Client` instance will have no queued requests in this state.
|
|
63
|
+
Calling [`Client.close()`](/docs/docs/api/Client.md#clientclosecallback) or [`Client.destroy()`](/docs/docs/api/Client.md#clientdestroyerror-callback) transitions directly to the [**destroyed**](/docs/docs/api/Client.md#destroyed) state since the `Client` instance will have no queued requests in this state.
|
|
64
64
|
|
|
65
65
|
### pending
|
|
66
66
|
|
|
@@ -72,11 +72,11 @@ Calling [`Client.destroy()`](/docs/docs/api/Client.md#clientdestroyerror-callbac
|
|
|
72
72
|
|
|
73
73
|
### processing
|
|
74
74
|
|
|
75
|
-
The **processing** state is a state machine within itself. It initializes to the [**processing.running**](/docs/docs/api/Client.md#running) state. The [`Client.dispatch()`](/docs/docs/api/Client.md#clientdispatchoptions-handlers), [`Client.close()`](Client.md#clientclosecallback), and [`Client.destroy()`](Client.md#clientdestroyerror-callback) can be called at any time while the `Client` is in this state. `Client.dispatch()` will add more requests to the queue while existing requests continue to be processed. `Client.close()` will transition to the [**processing.closing**](/docs/docs/api/Client.md#closing) state. And `Client.destroy()` will transition to [**destroyed**](/docs/docs/api/Client.md#destroyed).
|
|
75
|
+
The **processing** state is a state machine within itself. It initializes to the [**processing.running**](/docs/docs/api/Client.md#running) state. The [`Client.dispatch()`](/docs/docs/api/Client.md#clientdispatchoptions-handlers), [`Client.close()`](/docs/docs/api/Client.md#clientclosecallback), and [`Client.destroy()`](/docs/docs/api/Client.md#clientdestroyerror-callback) can be called at any time while the `Client` is in this state. `Client.dispatch()` will add more requests to the queue while existing requests continue to be processed. `Client.close()` will transition to the [**processing.closing**](/docs/docs/api/Client.md#closing) state. And `Client.destroy()` will transition to [**destroyed**](/docs/docs/api/Client.md#destroyed).
|
|
76
76
|
|
|
77
77
|
#### running
|
|
78
78
|
|
|
79
|
-
In the **processing.running** sub-state, queued requests are being processed in a FIFO order. If a request body requires draining, the *needDrain* event transitions to the [**processing.busy**](/docs/docs/api/Client.md#busy) sub-state. The *close* event transitions the Client to the [**process.closing**](/docs/docs/api/Client.md#closing) sub-state. If all queued requests are processed and neither [`Client.close()`](/docs/docs/api/Client.md#clientclosecallback) nor [`Client.destroy()`](Client.md#clientdestroyerror-callback) are called, then the [**processing**](/docs/docs/api/Client.md#processing) machine will trigger a *keepalive* event transitioning the `Client` back to the [**pending**](/docs/docs/api/Client.md#pending) state. During this time, the `Client` is waiting for the socket connection to timeout, and once it does, it triggers the *timeout* event and transitions to the [**idle**](/docs/docs/api/Client.md#idle) state.
|
|
79
|
+
In the **processing.running** sub-state, queued requests are being processed in a FIFO order. If a request body requires draining, the *needDrain* event transitions to the [**processing.busy**](/docs/docs/api/Client.md#busy) sub-state. The *close* event transitions the Client to the [**process.closing**](/docs/docs/api/Client.md#closing) sub-state. If all queued requests are processed and neither [`Client.close()`](/docs/docs/api/Client.md#clientclosecallback) nor [`Client.destroy()`](/docs/docs/api/Client.md#clientdestroyerror-callback) are called, then the [**processing**](/docs/docs/api/Client.md#processing) machine will trigger a *keepalive* event transitioning the `Client` back to the [**pending**](/docs/docs/api/Client.md#pending) state. During this time, the `Client` is waiting for the socket connection to timeout, and once it does, it triggers the *timeout* event and transitions to the [**idle**](/docs/docs/api/Client.md#idle) state.
|
|
80
80
|
|
|
81
81
|
#### busy
|
|
82
82
|
|