undici 6.6.2 → 6.7.1
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 +49 -27
- package/docs/{api → docs/api}/DiagnosticsChannel.md +2 -2
- package/docs/{api → docs/api}/Dispatcher.md +39 -3
- package/docs/docs/api/Fetch.md +57 -0
- package/docs/{api → docs/api}/ProxyAgent.md +5 -1
- package/docs/docs/api/RetryAgent.md +45 -0
- package/docs/{api → docs/api}/RetryHandler.md +1 -1
- package/docs/{api → docs/api}/api-lifecycle.md +33 -4
- package/docs/{best-practices → docs/best-practices}/proxy.md +6 -6
- package/index-fetch.js +9 -8
- package/index.js +31 -25
- package/lib/api/api-request.js +1 -1
- package/lib/api/readable.js +12 -9
- package/lib/api/util.js +8 -6
- package/lib/core/request.js +72 -135
- package/lib/core/symbols.js +6 -5
- package/lib/core/tree.js +46 -26
- package/lib/core/util.js +41 -20
- package/lib/{agent.js → dispatcher/agent.js} +4 -4
- package/lib/{balanced-pool.js → dispatcher/balanced-pool.js} +3 -3
- package/lib/dispatcher/client-h1.js +1352 -0
- package/lib/dispatcher/client-h2.js +639 -0
- package/lib/dispatcher/client.js +611 -0
- package/lib/{dispatcher-base.js → dispatcher/dispatcher-base.js} +2 -2
- package/lib/{pool-base.js → dispatcher/pool-base.js} +3 -3
- package/lib/{pool-stats.js → dispatcher/pool-stats.js} +1 -1
- package/lib/{pool.js → dispatcher/pool.js} +4 -4
- package/lib/{proxy-agent.js → dispatcher/proxy-agent.js} +29 -35
- package/lib/dispatcher/retry-agent.js +35 -0
- package/lib/global.js +1 -1
- package/lib/handler/{RetryHandler.js → retry-handler.js} +2 -2
- package/lib/interceptor/{redirectInterceptor.js → redirect-interceptor.js} +1 -1
- package/lib/mock/mock-agent.js +2 -2
- package/lib/mock/mock-client.js +1 -1
- package/lib/mock/mock-interceptor.js +2 -2
- package/lib/mock/mock-pool.js +1 -1
- package/lib/mock/mock-utils.js +6 -4
- package/lib/{cache → web/cache}/cache.js +2 -4
- package/lib/{cache → web/cache}/cachestorage.js +1 -1
- package/lib/web/cache/symbols.js +5 -0
- package/lib/{cache → web/cache}/util.js +5 -9
- package/lib/{cookies → web/cookies}/parse.js +1 -1
- package/lib/{cookies → web/cookies}/util.js +76 -60
- package/lib/{eventsource → web/eventsource}/eventsource.js +2 -6
- package/lib/{fetch → web/fetch}/body.js +56 -175
- package/lib/{fetch/dataURL.js → web/fetch/data-url.js} +5 -2
- package/lib/{compat → web/fetch}/dispatcher-weakref.js +1 -1
- package/lib/{fetch → web/fetch}/file.js +7 -8
- package/lib/web/fetch/formdata-parser.js +488 -0
- package/lib/{fetch → web/fetch}/formdata.js +7 -68
- package/lib/{fetch → web/fetch}/headers.js +99 -71
- package/lib/{fetch → web/fetch}/index.js +33 -25
- package/lib/{fetch → web/fetch}/request.js +15 -7
- package/lib/{fetch → web/fetch}/response.js +3 -3
- package/lib/{fetch → web/fetch}/symbols.js +2 -1
- package/lib/{fetch → web/fetch}/util.js +171 -48
- package/lib/{fetch → web/fetch}/webidl.js +46 -16
- package/lib/{fileapi → web/fileapi}/filereader.js +1 -1
- package/lib/{fileapi → web/fileapi}/util.js +1 -1
- package/lib/{websocket → web/websocket}/connection.js +20 -10
- package/lib/{websocket → web/websocket}/constants.js +7 -0
- package/lib/{websocket → web/websocket}/events.js +1 -1
- package/lib/{websocket → web/websocket}/frame.js +1 -0
- package/lib/{websocket → web/websocket}/receiver.js +9 -16
- package/lib/{websocket → web/websocket}/util.js +37 -23
- package/lib/{websocket → web/websocket}/websocket.js +21 -9
- package/package.json +26 -54
- package/types/dispatcher.d.ts +1 -1
- package/types/fetch.d.ts +20 -21
- package/types/index.d.ts +2 -1
- package/types/retry-agent.d.ts +11 -0
- package/types/webidl.d.ts +6 -1
- package/docs/api/Fetch.md +0 -27
- package/docs/assets/lifecycle-diagram.png +0 -0
- package/lib/cache/symbols.js +0 -5
- package/lib/client.js +0 -2295
- package/lib/llhttp/llhttp.wasm +0 -0
- package/lib/llhttp/llhttp_simd.wasm +0 -0
- /package/docs/{api → docs/api}/Agent.md +0 -0
- /package/docs/{api → docs/api}/BalancedPool.md +0 -0
- /package/docs/{api → docs/api}/CacheStorage.md +0 -0
- /package/docs/{api → docs/api}/Client.md +0 -0
- /package/docs/{api → docs/api}/Connector.md +0 -0
- /package/docs/{api → docs/api}/ContentType.md +0 -0
- /package/docs/{api → docs/api}/Cookies.md +0 -0
- /package/docs/{api → docs/api}/Debug.md +0 -0
- /package/docs/{api → docs/api}/DispatchInterceptor.md +0 -0
- /package/docs/{api → docs/api}/Errors.md +0 -0
- /package/docs/{api → docs/api}/EventSource.md +0 -0
- /package/docs/{api → docs/api}/MockAgent.md +0 -0
- /package/docs/{api → docs/api}/MockClient.md +0 -0
- /package/docs/{api → docs/api}/MockErrors.md +0 -0
- /package/docs/{api → docs/api}/MockPool.md +0 -0
- /package/docs/{api → docs/api}/Pool.md +0 -0
- /package/docs/{api → docs/api}/PoolStats.md +0 -0
- /package/docs/{api → docs/api}/RedirectHandler.md +0 -0
- /package/docs/{api → docs/api}/Util.md +0 -0
- /package/docs/{api → docs/api}/WebSocket.md +0 -0
- /package/docs/{best-practices → docs/best-practices}/client-certificate.md +0 -0
- /package/docs/{best-practices → docs/best-practices}/mocking-request.md +0 -0
- /package/docs/{best-practices → docs/best-practices}/writing-tests.md +0 -0
- /package/lib/{dispatcher.js → dispatcher/dispatcher.js} +0 -0
- /package/lib/{node → dispatcher}/fixed-queue.js +0 -0
- /package/lib/handler/{DecoratorHandler.js → decorator-handler.js} +0 -0
- /package/lib/handler/{RedirectHandler.js → redirect-handler.js} +0 -0
- /package/lib/{timers.js → util/timers.js} +0 -0
- /package/lib/{cookies → web/cookies}/constants.js +0 -0
- /package/lib/{cookies → web/cookies}/index.js +0 -0
- /package/lib/{eventsource → web/eventsource}/eventsource-stream.js +0 -0
- /package/lib/{eventsource → web/eventsource}/util.js +0 -0
- /package/lib/{fetch → web/fetch}/LICENSE +0 -0
- /package/lib/{fetch → web/fetch}/constants.js +0 -0
- /package/lib/{fetch → web/fetch}/global.js +0 -0
- /package/lib/{fileapi → web/fileapi}/encoding.js +0 -0
- /package/lib/{fileapi → web/fileapi}/progressevent.js +0 -0
- /package/lib/{fileapi → web/fileapi}/symbols.js +0 -0
- /package/lib/{websocket → web/websocket}/symbols.js +0 -0
package/README.md
CHANGED
|
@@ -17,24 +17,42 @@ npm i undici
|
|
|
17
17
|
|
|
18
18
|
## Benchmarks
|
|
19
19
|
|
|
20
|
-
The benchmark is a simple
|
|
20
|
+
The benchmark is a simple getting data [example](benchmarks/benchmark.js) using a
|
|
21
21
|
50 TCP connections with a pipelining depth of 10 running on Node 20.10.0.
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
23
|
+
| _Tests_ | _Samples_ | _Result_ | _Tolerance_ | _Difference with slowest_ |
|
|
24
|
+
| :-----------------: | :-------: | :--------------: | :---------: | :-----------------------: |
|
|
25
|
+
| undici - fetch | 30 | 3704.43 req/sec | ± 2.95 % | - |
|
|
26
|
+
| http - no keepalive | 20 | 4275.30 req/sec | ± 2.60 % | + 15.41 % |
|
|
27
|
+
| node-fetch | 10 | 4759.42 req/sec | ± 0.87 % | + 28.48 % |
|
|
28
|
+
| request | 40 | 4803.37 req/sec | ± 2.77 % | + 29.67 % |
|
|
29
|
+
| axios | 45 | 4951.97 req/sec | ± 2.88 % | + 33.68 % |
|
|
30
|
+
| got | 10 | 5969.67 req/sec | ± 2.64 % | + 61.15 % |
|
|
31
|
+
| superagent | 10 | 9471.48 req/sec | ± 1.50 % | + 155.68 % |
|
|
32
|
+
| http - keepalive | 25 | 10327.49 req/sec | ± 2.95 % | + 178.79 % |
|
|
33
|
+
| undici - pipeline | 10 | 15053.41 req/sec | ± 1.63 % | + 306.36 % |
|
|
34
|
+
| undici - request | 10 | 19264.24 req/sec | ± 1.74 % | + 420.03 % |
|
|
35
|
+
| undici - stream | 15 | 20317.29 req/sec | ± 2.13 % | + 448.46 % |
|
|
36
|
+
| undici - dispatch | 10 | 24883.28 req/sec | ± 1.54 % | + 571.72 % |
|
|
37
|
+
|
|
38
|
+
The benchmark is a simple sending data [example](benchmarks/post-benchmark.js) using a
|
|
39
|
+
50 TCP connections with a pipelining depth of 10 running on Node 20.10.0.
|
|
40
|
+
|
|
41
|
+
| _Tests_ | _Samples_ | _Result_ | _Tolerance_ | _Difference with slowest_ |
|
|
42
|
+
| :-----------------: | :-------: | :-------------: | :---------: | :-----------------------: |
|
|
43
|
+
| undici - fetch | 20 | 1968.42 req/sec | ± 2.63 % | - |
|
|
44
|
+
| http - no keepalive | 25 | 2330.30 req/sec | ± 2.99 % | + 18.38 % |
|
|
45
|
+
| node-fetch | 20 | 2485.36 req/sec | ± 2.70 % | + 26.26 % |
|
|
46
|
+
| got | 15 | 2787.68 req/sec | ± 2.56 % | + 41.62 % |
|
|
47
|
+
| request | 30 | 2805.10 req/sec | ± 2.59 % | + 42.50 % |
|
|
48
|
+
| axios | 10 | 3040.45 req/sec | ± 1.72 % | + 54.46 % |
|
|
49
|
+
| superagent | 20 | 3358.29 req/sec | ± 2.51 % | + 70.61 % |
|
|
50
|
+
| http - keepalive | 20 | 3477.94 req/sec | ± 2.51 % | + 76.69 % |
|
|
51
|
+
| undici - pipeline | 25 | 3812.61 req/sec | ± 2.80 % | + 93.69 % |
|
|
52
|
+
| undici - request | 10 | 6067.00 req/sec | ± 0.94 % | + 208.22 % |
|
|
53
|
+
| undici - stream | 10 | 6391.61 req/sec | ± 1.98 % | + 224.71 % |
|
|
54
|
+
| undici - dispatch | 10 | 6397.00 req/sec | ± 1.48 % | + 224.98 % |
|
|
55
|
+
|
|
38
56
|
|
|
39
57
|
## Quick Start
|
|
40
58
|
|
|
@@ -60,10 +78,14 @@ console.log('trailers', trailers)
|
|
|
60
78
|
|
|
61
79
|
The `body` mixins are the most common way to format the request/response body. Mixins include:
|
|
62
80
|
|
|
63
|
-
- [`.
|
|
81
|
+
- [`.arrayBuffer()`](https://fetch.spec.whatwg.org/#dom-body-arraybuffer)
|
|
82
|
+
- [`.blob()`](https://fetch.spec.whatwg.org/#dom-body-blob)
|
|
64
83
|
- [`.json()`](https://fetch.spec.whatwg.org/#dom-body-json)
|
|
65
84
|
- [`.text()`](https://fetch.spec.whatwg.org/#dom-body-text)
|
|
66
85
|
|
|
86
|
+
> [!NOTE]
|
|
87
|
+
> The body returned from `undici.request` does not implement `.formData()`.
|
|
88
|
+
|
|
67
89
|
Example usage:
|
|
68
90
|
|
|
69
91
|
```js
|
|
@@ -123,14 +145,14 @@ Returns a promise with the result of the `Dispatcher.stream` method.
|
|
|
123
145
|
|
|
124
146
|
Calls `options.dispatcher.stream(options, factory)`.
|
|
125
147
|
|
|
126
|
-
See [Dispatcher.stream](docs/api/Dispatcher.md#dispatcherstreamoptions-factory-callback) for more details.
|
|
148
|
+
See [Dispatcher.stream](./docs/api/Dispatcher.md#dispatcherstreamoptions-factory-callback) for more details.
|
|
127
149
|
|
|
128
150
|
### `undici.pipeline([url, options, ]handler): Duplex`
|
|
129
151
|
|
|
130
152
|
Arguments:
|
|
131
153
|
|
|
132
154
|
* **url** `string | URL | UrlObject`
|
|
133
|
-
* **options** [`PipelineOptions`](docs/api/Dispatcher.md#parameter-pipelineoptions)
|
|
155
|
+
* **options** [`PipelineOptions`](./docs/api/Dispatcher.md#parameter-pipelineoptions)
|
|
134
156
|
* **dispatcher** `Dispatcher` - Default: [getGlobalDispatcher](#undicigetglobaldispatcher)
|
|
135
157
|
* **method** `String` - Default: `PUT` if `options.body`, otherwise `GET`
|
|
136
158
|
* **maxRedirections** `Integer` - Default: `0`
|
|
@@ -140,7 +162,7 @@ Returns: `stream.Duplex`
|
|
|
140
162
|
|
|
141
163
|
Calls `options.dispatch.pipeline(options, handler)`.
|
|
142
164
|
|
|
143
|
-
See [Dispatcher.pipeline](docs/api/Dispatcher.md#dispatcherpipelineoptions-handler) for more details.
|
|
165
|
+
See [Dispatcher.pipeline](./docs/api/Dispatcher.md#dispatcherpipelineoptions-handler) for more details.
|
|
144
166
|
|
|
145
167
|
### `undici.connect([url, options]): Promise`
|
|
146
168
|
|
|
@@ -149,7 +171,7 @@ Starts two-way communications with the requested resource using [HTTP CONNECT](h
|
|
|
149
171
|
Arguments:
|
|
150
172
|
|
|
151
173
|
* **url** `string | URL | UrlObject`
|
|
152
|
-
* **options** [`ConnectOptions`](docs/api/Dispatcher.md#parameter-connectoptions)
|
|
174
|
+
* **options** [`ConnectOptions`](./docs/api/Dispatcher.md#parameter-connectoptions)
|
|
153
175
|
* **dispatcher** `Dispatcher` - Default: [getGlobalDispatcher](#undicigetglobaldispatcher)
|
|
154
176
|
* **maxRedirections** `Integer` - Default: `0`
|
|
155
177
|
* **callback** `(err: Error | null, data: ConnectData | null) => void` (optional)
|
|
@@ -158,7 +180,7 @@ Returns a promise with the result of the `Dispatcher.connect` method.
|
|
|
158
180
|
|
|
159
181
|
Calls `options.dispatch.connect(options)`.
|
|
160
182
|
|
|
161
|
-
See [Dispatcher.connect](docs/api/Dispatcher.md#dispatcherconnectoptions-callback) for more details.
|
|
183
|
+
See [Dispatcher.connect](./docs/api/Dispatcher.md#dispatcherconnectoptions-callback) for more details.
|
|
162
184
|
|
|
163
185
|
### `undici.fetch(input[, init]): Promise`
|
|
164
186
|
|
|
@@ -226,7 +248,7 @@ await fetch('https://example.com', { body: data, method: 'POST', duplex: 'half'
|
|
|
226
248
|
|
|
227
249
|
- half
|
|
228
250
|
|
|
229
|
-
In this implementation of fetch, `request.duplex` must be set if `request.body` is `ReadableStream` or `Async Iterables
|
|
251
|
+
In this implementation of fetch, `request.duplex` must be set if `request.body` is `ReadableStream` or `Async Iterables`, however, fetch requests are currently always full duplex. For more detail refer to the [Fetch Standard.](https://fetch.spec.whatwg.org/#dom-requestinit-duplex).
|
|
230
252
|
|
|
231
253
|
#### `response.body`
|
|
232
254
|
|
|
@@ -297,7 +319,7 @@ Upgrade to a different protocol. See [MDN - HTTP - Protocol upgrade mechanism](h
|
|
|
297
319
|
Arguments:
|
|
298
320
|
|
|
299
321
|
* **url** `string | URL | UrlObject`
|
|
300
|
-
* **options** [`UpgradeOptions`](docs/api/Dispatcher.md#parameter-upgradeoptions)
|
|
322
|
+
* **options** [`UpgradeOptions`](./docs/api/Dispatcher.md#parameter-upgradeoptions)
|
|
301
323
|
* **dispatcher** `Dispatcher` - Default: [getGlobalDispatcher](#undicigetglobaldispatcher)
|
|
302
324
|
* **maxRedirections** `Integer` - Default: `0`
|
|
303
325
|
* **callback** `(error: Error | null, data: UpgradeData) => void` (optional)
|
|
@@ -306,7 +328,7 @@ Returns a promise with the result of the `Dispatcher.upgrade` method.
|
|
|
306
328
|
|
|
307
329
|
Calls `options.dispatcher.upgrade(options)`.
|
|
308
330
|
|
|
309
|
-
See [Dispatcher.upgrade](docs/api/Dispatcher.md#dispatcherupgradeoptions-callback) for more details.
|
|
331
|
+
See [Dispatcher.upgrade](./docs/api/Dispatcher.md#dispatcherupgradeoptions-callback) for more details.
|
|
310
332
|
|
|
311
333
|
### `undici.setGlobalDispatcher(dispatcher)`
|
|
312
334
|
|
|
@@ -400,9 +422,9 @@ Refs: https://fetch.spec.whatwg.org/#atomic-http-redirect-handling
|
|
|
400
422
|
|
|
401
423
|
If you experience problem when connecting to a remote server that is resolved by your DNS servers to a IPv6 (AAAA record)
|
|
402
424
|
first, there are chances that your local router or ISP might have problem connecting to IPv6 networks. In that case
|
|
403
|
-
undici will throw an error with code `UND_ERR_CONNECT_TIMEOUT`.
|
|
425
|
+
undici will throw an error with code `UND_ERR_CONNECT_TIMEOUT`.
|
|
404
426
|
|
|
405
|
-
If the target server resolves to both a IPv6 and IPv4 (A records) address and you are using a compatible Node version
|
|
427
|
+
If the target server resolves to both a IPv6 and IPv4 (A records) address and you are using a compatible Node version
|
|
406
428
|
(18.3.0 and above), you can fix the problem by providing the `autoSelectFamily` option (support by both `undici.request`
|
|
407
429
|
and `undici.Agent`) which will enable the family autoselection algorithm when establishing the connection.
|
|
408
430
|
|
|
@@ -19,9 +19,9 @@ diagnosticsChannel.channel('undici:request:create').subscribe(({ request }) => {
|
|
|
19
19
|
console.log('completed', request.completed)
|
|
20
20
|
console.log('method', request.method)
|
|
21
21
|
console.log('path', request.path)
|
|
22
|
-
console.log('headers') //
|
|
22
|
+
console.log('headers') // array of strings, e.g: ['foo', 'bar']
|
|
23
23
|
request.addHeader('hello', 'world')
|
|
24
|
-
console.log('headers', request.headers) // e.g. '
|
|
24
|
+
console.log('headers', request.headers) // e.g. ['foo', 'bar', 'hello', 'world']
|
|
25
25
|
})
|
|
26
26
|
```
|
|
27
27
|
|
|
@@ -855,10 +855,12 @@ Emitted when dispatcher is no longer busy.
|
|
|
855
855
|
|
|
856
856
|
## Parameter: `UndiciHeaders`
|
|
857
857
|
|
|
858
|
-
* `Record<string, string | string[] | undefined> | string[] | null`
|
|
859
|
-
|
|
860
|
-
Header arguments such as `options.headers` in [`Client.dispatch`](Client.md#clientdispatchoptions-handlers) can be specified in two forms; either as an object specified by the `Record<string, string | string[] | undefined>` (`IncomingHttpHeaders`) type, or an array of strings. An array representation of a header list must have an even length or an `InvalidArgumentError` will be thrown.
|
|
858
|
+
* `Record<string, string | string[] | undefined> | string[] | Iterable<[string, string | string[] | undefined]> | null`
|
|
861
859
|
|
|
860
|
+
Header arguments such as `options.headers` in [`Client.dispatch`](Client.md#clientdispatchoptions-handlers) can be specified in three forms:
|
|
861
|
+
* As an object specified by the `Record<string, string | string[] | undefined>` (`IncomingHttpHeaders`) type.
|
|
862
|
+
* As an array of strings. An array representation of a header list must have an even length, or an `InvalidArgumentError` will be thrown.
|
|
863
|
+
* As an iterable that can encompass `Headers`, `Map`, or a custom iterator returning key-value pairs.
|
|
862
864
|
Keys are lowercase and values are not modified.
|
|
863
865
|
|
|
864
866
|
Response headers will derive a `host` from the `url` of the [Client](Client.md#class-client) instance if no `host` header was previously specified.
|
|
@@ -886,3 +888,37 @@ Response headers will derive a `host` from the `url` of the [Client](Client.md#c
|
|
|
886
888
|
'accept', '*/*'
|
|
887
889
|
]
|
|
888
890
|
```
|
|
891
|
+
|
|
892
|
+
### Example 3 - Iterable
|
|
893
|
+
|
|
894
|
+
```js
|
|
895
|
+
new Headers({
|
|
896
|
+
'content-length': '123',
|
|
897
|
+
'content-type': 'text/plain',
|
|
898
|
+
connection: 'keep-alive',
|
|
899
|
+
host: 'mysite.com',
|
|
900
|
+
accept: '*/*'
|
|
901
|
+
})
|
|
902
|
+
```
|
|
903
|
+
or
|
|
904
|
+
```js
|
|
905
|
+
new Map([
|
|
906
|
+
['content-length', '123'],
|
|
907
|
+
['content-type', 'text/plain'],
|
|
908
|
+
['connection', 'keep-alive'],
|
|
909
|
+
['host', 'mysite.com'],
|
|
910
|
+
['accept', '*/*']
|
|
911
|
+
])
|
|
912
|
+
```
|
|
913
|
+
or
|
|
914
|
+
```js
|
|
915
|
+
{
|
|
916
|
+
*[Symbol.iterator] () {
|
|
917
|
+
yield ['content-length', '123']
|
|
918
|
+
yield ['content-type', 'text/plain']
|
|
919
|
+
yield ['connection', 'keep-alive']
|
|
920
|
+
yield ['host', 'mysite.com']
|
|
921
|
+
yield ['accept', '*/*']
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
```
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Fetch
|
|
2
|
+
|
|
3
|
+
Undici exposes a fetch() method starts the process of fetching a resource from the network.
|
|
4
|
+
|
|
5
|
+
Documentation and examples can be found on [MDN](https://developer.mozilla.org/en-US/docs/Web/API/fetch).
|
|
6
|
+
|
|
7
|
+
## File
|
|
8
|
+
|
|
9
|
+
This API is implemented as per the standard, you can find documentation on [MDN](https://developer.mozilla.org/en-US/docs/Web/API/File)
|
|
10
|
+
|
|
11
|
+
In Node versions v18.13.0 and above and v19.2.0 and above, undici will default to using Node's [File](https://nodejs.org/api/buffer.html#class-file) class. In versions where it's not available, it will default to the undici one.
|
|
12
|
+
|
|
13
|
+
## FormData
|
|
14
|
+
|
|
15
|
+
This API is implemented as per the standard, you can find documentation on [MDN](https://developer.mozilla.org/en-US/docs/Web/API/FormData).
|
|
16
|
+
|
|
17
|
+
If any parameters are passed to the FormData constructor other than `undefined`, an error will be thrown. Other parameters are ignored.
|
|
18
|
+
|
|
19
|
+
## Response
|
|
20
|
+
|
|
21
|
+
This API is implemented as per the standard, you can find documentation on [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Response)
|
|
22
|
+
|
|
23
|
+
## Request
|
|
24
|
+
|
|
25
|
+
This API is implemented as per the standard, you can find documentation on [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Request)
|
|
26
|
+
|
|
27
|
+
## Header
|
|
28
|
+
|
|
29
|
+
This API is implemented as per the standard, you can find documentation on [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Headers)
|
|
30
|
+
|
|
31
|
+
# Body Mixins
|
|
32
|
+
|
|
33
|
+
`Response` and `Request` body inherit body mixin methods. These methods include:
|
|
34
|
+
|
|
35
|
+
- [`.arrayBuffer()`](https://fetch.spec.whatwg.org/#dom-body-arraybuffer)
|
|
36
|
+
- [`.blob()`](https://fetch.spec.whatwg.org/#dom-body-blob)
|
|
37
|
+
- [`.formData()`](https://fetch.spec.whatwg.org/#dom-body-formdata)
|
|
38
|
+
- [`.json()`](https://fetch.spec.whatwg.org/#dom-body-json)
|
|
39
|
+
- [`.text()`](https://fetch.spec.whatwg.org/#dom-body-text)
|
|
40
|
+
|
|
41
|
+
There is an ongoing discussion regarding `.formData()` and its usefulness and performance in server environments. It is recommended to use a dedicated library for parsing `multipart/form-data` bodies, such as [Busboy](https://www.npmjs.com/package/busboy) or [@fastify/busboy](https://www.npmjs.com/package/@fastify/busboy).
|
|
42
|
+
|
|
43
|
+
These libraries can be interfaced with fetch with the following example code:
|
|
44
|
+
|
|
45
|
+
```mjs
|
|
46
|
+
import { Busboy } from '@fastify/busboy'
|
|
47
|
+
import { Readable } from 'node:stream'
|
|
48
|
+
|
|
49
|
+
const response = await fetch('...')
|
|
50
|
+
const busboy = new Busboy({
|
|
51
|
+
headers: {
|
|
52
|
+
'content-type': response.headers.get('content-type')
|
|
53
|
+
}
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
Readable.fromWeb(response.body).pipe(busboy)
|
|
57
|
+
```
|
|
@@ -16,7 +16,9 @@ Returns: `ProxyAgent`
|
|
|
16
16
|
|
|
17
17
|
Extends: [`AgentOptions`](Agent.md#parameter-agentoptions)
|
|
18
18
|
|
|
19
|
-
* **uri** `string` (required) -
|
|
19
|
+
* **uri** `string | URL` (required) - The URI of the proxy server. This can be provided as a string, as an instance of the URL class, or as an object with a `uri` property of type string.
|
|
20
|
+
If the `uri` is provided as a string or `uri` is an object with an `uri` property of type string, then it will be parsed into a `URL` object according to the [WHATWG URL Specification](https://url.spec.whatwg.org).
|
|
21
|
+
For detailed information on the parsing process and potential validation errors, please refer to the ["Writing" section](https://url.spec.whatwg.org/#writing) of the WHATWG URL Specification.
|
|
20
22
|
* **token** `string` (optional) - It can be passed by a string of token for authentication.
|
|
21
23
|
* **auth** `string` (**deprecated**) - Use token.
|
|
22
24
|
* **clientFactory** `(origin: URL, opts: Object) => Dispatcher` (optional) - Default: `(origin, opts) => new Pool(origin, opts)`
|
|
@@ -30,6 +32,8 @@ import { ProxyAgent } from 'undici'
|
|
|
30
32
|
|
|
31
33
|
const proxyAgent = new ProxyAgent('my.proxy.server')
|
|
32
34
|
// or
|
|
35
|
+
const proxyAgent = new ProxyAgent(new URL('my.proxy.server'))
|
|
36
|
+
// or
|
|
33
37
|
const proxyAgent = new ProxyAgent({ uri: 'my.proxy.server' })
|
|
34
38
|
```
|
|
35
39
|
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Class: RetryAgent
|
|
2
|
+
|
|
3
|
+
Extends: `undici.Dispatcher`
|
|
4
|
+
|
|
5
|
+
A `undici.Dispatcher` that allows to automatically retry a request.
|
|
6
|
+
Wraps a `undici.RetryHandler`.
|
|
7
|
+
|
|
8
|
+
## `new RetryAgent(dispatcher, [options])`
|
|
9
|
+
|
|
10
|
+
Arguments:
|
|
11
|
+
|
|
12
|
+
* **dispatcher** `undici.Dispatcher` (required) - the dispatcher to wrap
|
|
13
|
+
* **options** `RetryHandlerOptions` (optional) - the options
|
|
14
|
+
|
|
15
|
+
Returns: `ProxyAgent`
|
|
16
|
+
|
|
17
|
+
### Parameter: `RetryHandlerOptions`
|
|
18
|
+
|
|
19
|
+
- **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.
|
|
20
|
+
- **maxRetries** `number` (optional) - Maximum number of retries. Default: `5`
|
|
21
|
+
- **maxTimeout** `number` (optional) - Maximum number of milliseconds to wait before retrying. Default: `30000` (30 seconds)
|
|
22
|
+
- **minTimeout** `number` (optional) - Minimum number of milliseconds to wait before retrying. Default: `500` (half a second)
|
|
23
|
+
- **timeoutFactor** `number` (optional) - Factor to multiply the timeout by for each retry attempt. Default: `2`
|
|
24
|
+
- **retryAfter** `boolean` (optional) - It enables automatic retry after the `Retry-After` header is received. Default: `true`
|
|
25
|
+
-
|
|
26
|
+
- **methods** `string[]` (optional) - Array of HTTP methods to retry. Default: `['GET', 'PUT', 'HEAD', 'OPTIONS', 'DELETE']`
|
|
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']`
|
|
29
|
+
|
|
30
|
+
**`RetryContext`**
|
|
31
|
+
|
|
32
|
+
- `state`: `RetryState` - Current retry state. It can be mutated.
|
|
33
|
+
- `opts`: `Dispatch.DispatchOptions & RetryOptions` - Options passed to the retry handler.
|
|
34
|
+
|
|
35
|
+
Example:
|
|
36
|
+
|
|
37
|
+
```js
|
|
38
|
+
import { Agent, RetryAgent } from 'undici'
|
|
39
|
+
|
|
40
|
+
const agent = new RetryAgent(new Agent())
|
|
41
|
+
|
|
42
|
+
const res = await agent.request('http://example.com')
|
|
43
|
+
console.log(res.statuCode)
|
|
44
|
+
console.log(await res.body.text())
|
|
45
|
+
```
|
|
@@ -28,7 +28,7 @@ Extends: [`Dispatch.DispatchOptions`](Dispatcher.md#parameter-dispatchoptions).
|
|
|
28
28
|
-
|
|
29
29
|
- **methods** `string[]` (optional) - Array of HTTP methods to retry. Default: `['GET', 'PUT', 'HEAD', 'OPTIONS', 'DELETE']`
|
|
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',
|
|
31
|
+
- **errorCodes** `string[]` (optional) - Array of Error codes to retry. Default: `['ECONNRESET', 'ECONNREFUSED', 'ENOTFOUND', 'ENETDOWN','ENETUNREACH', 'EHOSTDOWN', 'UND_ERR_SOCKET']`
|
|
32
32
|
|
|
33
33
|
**`RetryContext`**
|
|
34
34
|
|
|
@@ -21,10 +21,39 @@ An Undici [Client](Client.md) can be best described as a state machine. The foll
|
|
|
21
21
|
* At any point in time, the *destroy* event will transition the `Client` from the **processing** state to the **destroyed** state, destroying any queued requests.
|
|
22
22
|
* The **destroyed** state is a final state and the `Client` is no longer functional.
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
A state diagram representing an Undici Client instance:
|
|
25
|
+
|
|
26
|
+
```mermaid
|
|
27
|
+
stateDiagram-v2
|
|
28
|
+
[*] --> idle
|
|
29
|
+
idle --> pending : connect
|
|
30
|
+
idle --> destroyed : destroy/close
|
|
31
|
+
|
|
32
|
+
pending --> idle : timeout
|
|
33
|
+
pending --> destroyed : destroy
|
|
34
|
+
|
|
35
|
+
state close_fork <<fork>>
|
|
36
|
+
pending --> close_fork : close
|
|
37
|
+
close_fork --> processing
|
|
38
|
+
close_fork --> destroyed
|
|
39
|
+
|
|
40
|
+
pending --> processing : process
|
|
41
|
+
|
|
42
|
+
processing --> pending : keepalive
|
|
43
|
+
processing --> destroyed : done
|
|
44
|
+
processing --> destroyed : destroy
|
|
45
|
+
|
|
46
|
+
destroyed --> [*]
|
|
47
|
+
|
|
48
|
+
state processing {
|
|
49
|
+
[*] --> running
|
|
50
|
+
running --> closing : close
|
|
51
|
+
running --> busy : needDrain
|
|
52
|
+
busy --> running : drainComplete
|
|
53
|
+
running --> [*] : keepalive
|
|
54
|
+
closing --> [*] : done
|
|
55
|
+
}
|
|
56
|
+
```
|
|
28
57
|
## State details
|
|
29
58
|
|
|
30
59
|
### idle
|
|
@@ -17,7 +17,7 @@ If you proxy requires basic authentication, you can send it via the `proxy-autho
|
|
|
17
17
|
```js
|
|
18
18
|
import { Client } from 'undici'
|
|
19
19
|
import { createServer } from 'http'
|
|
20
|
-
import
|
|
20
|
+
import { createProxy } from 'proxy'
|
|
21
21
|
|
|
22
22
|
const server = await buildServer()
|
|
23
23
|
const proxyServer = await buildProxy()
|
|
@@ -59,7 +59,7 @@ function buildServer () {
|
|
|
59
59
|
|
|
60
60
|
function buildProxy () {
|
|
61
61
|
return new Promise((resolve, reject) => {
|
|
62
|
-
const server =
|
|
62
|
+
const server = createProxy(createServer())
|
|
63
63
|
server.listen(0, () => resolve(server))
|
|
64
64
|
})
|
|
65
65
|
}
|
|
@@ -70,7 +70,7 @@ function buildProxy () {
|
|
|
70
70
|
```js
|
|
71
71
|
import { Client } from 'undici'
|
|
72
72
|
import { createServer } from 'http'
|
|
73
|
-
import
|
|
73
|
+
import { createProxy } from 'proxy'
|
|
74
74
|
|
|
75
75
|
const server = await buildServer()
|
|
76
76
|
const proxyServer = await buildProxy()
|
|
@@ -78,8 +78,8 @@ const proxyServer = await buildProxy()
|
|
|
78
78
|
const serverUrl = `http://localhost:${server.address().port}`
|
|
79
79
|
const proxyUrl = `http://localhost:${proxyServer.address().port}`
|
|
80
80
|
|
|
81
|
-
proxyServer.authenticate = function (req
|
|
82
|
-
|
|
81
|
+
proxyServer.authenticate = function (req) {
|
|
82
|
+
return req.headers['proxy-authorization'] === `Basic ${Buffer.from('user:pass').toString('base64')}`
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
server.on('request', (req, res) => {
|
|
@@ -119,7 +119,7 @@ function buildServer () {
|
|
|
119
119
|
|
|
120
120
|
function buildProxy () {
|
|
121
121
|
return new Promise((resolve, reject) => {
|
|
122
|
-
const server =
|
|
122
|
+
const server = createProxy(createServer())
|
|
123
123
|
server.listen(0, () => resolve(server))
|
|
124
124
|
})
|
|
125
125
|
}
|
package/index-fetch.js
CHANGED
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const fetchImpl = require('./lib/fetch').fetch
|
|
3
|
+
const fetchImpl = require('./lib/web/fetch').fetch
|
|
4
4
|
|
|
5
5
|
module.exports.fetch = function fetch (resource, init = undefined) {
|
|
6
6
|
return fetchImpl(resource, init).catch((err) => {
|
|
7
|
-
if (typeof err === 'object') {
|
|
7
|
+
if (err && typeof err === 'object') {
|
|
8
8
|
Error.captureStackTrace(err, this)
|
|
9
9
|
}
|
|
10
10
|
throw err
|
|
11
11
|
})
|
|
12
12
|
}
|
|
13
|
-
module.exports.FormData = require('./lib/fetch/formdata').FormData
|
|
14
|
-
module.exports.Headers = require('./lib/fetch/headers').Headers
|
|
15
|
-
module.exports.Response = require('./lib/fetch/response').Response
|
|
16
|
-
module.exports.Request = require('./lib/fetch/request').Request
|
|
13
|
+
module.exports.FormData = require('./lib/web/fetch/formdata').FormData
|
|
14
|
+
module.exports.Headers = require('./lib/web/fetch/headers').Headers
|
|
15
|
+
module.exports.Response = require('./lib/web/fetch/response').Response
|
|
16
|
+
module.exports.Request = require('./lib/web/fetch/request').Request
|
|
17
17
|
|
|
18
|
-
module.exports.WebSocket = require('./lib/websocket/websocket').WebSocket
|
|
18
|
+
module.exports.WebSocket = require('./lib/web/websocket/websocket').WebSocket
|
|
19
|
+
module.exports.MessageEvent = require('./lib/web/websocket/events').MessageEvent
|
|
19
20
|
|
|
20
|
-
module.exports.EventSource = require('./lib/eventsource/eventsource').EventSource
|
|
21
|
+
module.exports.EventSource = require('./lib/web/eventsource/eventsource').EventSource
|
package/index.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const Client = require('./lib/client')
|
|
4
|
-
const Dispatcher = require('./lib/dispatcher')
|
|
3
|
+
const Client = require('./lib/dispatcher/client')
|
|
4
|
+
const Dispatcher = require('./lib/dispatcher/dispatcher')
|
|
5
|
+
const Pool = require('./lib/dispatcher/pool')
|
|
6
|
+
const BalancedPool = require('./lib/dispatcher/balanced-pool')
|
|
7
|
+
const Agent = require('./lib/dispatcher/agent')
|
|
8
|
+
const ProxyAgent = require('./lib/dispatcher/proxy-agent')
|
|
9
|
+
const RetryAgent = require('./lib/dispatcher/retry-agent')
|
|
5
10
|
const errors = require('./lib/core/errors')
|
|
6
|
-
const Pool = require('./lib/pool')
|
|
7
|
-
const BalancedPool = require('./lib/balanced-pool')
|
|
8
|
-
const Agent = require('./lib/agent')
|
|
9
11
|
const util = require('./lib/core/util')
|
|
10
12
|
const { InvalidArgumentError } = errors
|
|
11
13
|
const api = require('./lib/api')
|
|
@@ -14,12 +16,11 @@ const MockClient = require('./lib/mock/mock-client')
|
|
|
14
16
|
const MockAgent = require('./lib/mock/mock-agent')
|
|
15
17
|
const MockPool = require('./lib/mock/mock-pool')
|
|
16
18
|
const mockErrors = require('./lib/mock/mock-errors')
|
|
17
|
-
const
|
|
18
|
-
const RetryHandler = require('./lib/handler/RetryHandler')
|
|
19
|
+
const RetryHandler = require('./lib/handler/retry-handler')
|
|
19
20
|
const { getGlobalDispatcher, setGlobalDispatcher } = require('./lib/global')
|
|
20
|
-
const DecoratorHandler = require('./lib/handler/
|
|
21
|
-
const RedirectHandler = require('./lib/handler/
|
|
22
|
-
const createRedirectInterceptor = require('./lib/interceptor/
|
|
21
|
+
const DecoratorHandler = require('./lib/handler/decorator-handler')
|
|
22
|
+
const RedirectHandler = require('./lib/handler/redirect-handler')
|
|
23
|
+
const createRedirectInterceptor = require('./lib/interceptor/redirect-interceptor')
|
|
23
24
|
|
|
24
25
|
Object.assign(Dispatcher.prototype, api)
|
|
25
26
|
|
|
@@ -29,6 +30,7 @@ module.exports.Pool = Pool
|
|
|
29
30
|
module.exports.BalancedPool = BalancedPool
|
|
30
31
|
module.exports.Agent = Agent
|
|
31
32
|
module.exports.ProxyAgent = ProxyAgent
|
|
33
|
+
module.exports.RetryAgent = RetryAgent
|
|
32
34
|
module.exports.RetryHandler = RetryHandler
|
|
33
35
|
|
|
34
36
|
module.exports.DecoratorHandler = DecoratorHandler
|
|
@@ -94,50 +96,54 @@ function makeDispatcher (fn) {
|
|
|
94
96
|
module.exports.setGlobalDispatcher = setGlobalDispatcher
|
|
95
97
|
module.exports.getGlobalDispatcher = getGlobalDispatcher
|
|
96
98
|
|
|
97
|
-
const fetchImpl = require('./lib/fetch').fetch
|
|
99
|
+
const fetchImpl = require('./lib/web/fetch').fetch
|
|
98
100
|
module.exports.fetch = async function fetch (init, options = undefined) {
|
|
99
101
|
try {
|
|
100
102
|
return await fetchImpl(init, options)
|
|
101
103
|
} catch (err) {
|
|
102
|
-
if (typeof err === 'object') {
|
|
104
|
+
if (err && typeof err === 'object') {
|
|
103
105
|
Error.captureStackTrace(err, this)
|
|
104
106
|
}
|
|
105
107
|
|
|
106
108
|
throw err
|
|
107
109
|
}
|
|
108
110
|
}
|
|
109
|
-
module.exports.Headers = require('./lib/fetch/headers').Headers
|
|
110
|
-
module.exports.Response = require('./lib/fetch/response').Response
|
|
111
|
-
module.exports.Request = require('./lib/fetch/request').Request
|
|
112
|
-
module.exports.FormData = require('./lib/fetch/formdata').FormData
|
|
113
|
-
module.exports.File = require('./lib/fetch/file').File
|
|
114
|
-
module.exports.FileReader = require('./lib/fileapi/filereader').FileReader
|
|
111
|
+
module.exports.Headers = require('./lib/web/fetch/headers').Headers
|
|
112
|
+
module.exports.Response = require('./lib/web/fetch/response').Response
|
|
113
|
+
module.exports.Request = require('./lib/web/fetch/request').Request
|
|
114
|
+
module.exports.FormData = require('./lib/web/fetch/formdata').FormData
|
|
115
|
+
module.exports.File = require('./lib/web/fetch/file').File
|
|
116
|
+
module.exports.FileReader = require('./lib/web/fileapi/filereader').FileReader
|
|
115
117
|
|
|
116
|
-
const { setGlobalOrigin, getGlobalOrigin } = require('./lib/fetch/global')
|
|
118
|
+
const { setGlobalOrigin, getGlobalOrigin } = require('./lib/web/fetch/global')
|
|
117
119
|
|
|
118
120
|
module.exports.setGlobalOrigin = setGlobalOrigin
|
|
119
121
|
module.exports.getGlobalOrigin = getGlobalOrigin
|
|
120
122
|
|
|
121
|
-
const { CacheStorage } = require('./lib/cache/cachestorage')
|
|
122
|
-
const { kConstruct } = require('./lib/cache/symbols')
|
|
123
|
+
const { CacheStorage } = require('./lib/web/cache/cachestorage')
|
|
124
|
+
const { kConstruct } = require('./lib/web/cache/symbols')
|
|
123
125
|
|
|
124
126
|
// Cache & CacheStorage are tightly coupled with fetch. Even if it may run
|
|
125
127
|
// in an older version of Node, it doesn't have any use without fetch.
|
|
126
128
|
module.exports.caches = new CacheStorage(kConstruct)
|
|
127
129
|
|
|
128
|
-
const { deleteCookie, getCookies, getSetCookies, setCookie } = require('./lib/cookies')
|
|
130
|
+
const { deleteCookie, getCookies, getSetCookies, setCookie } = require('./lib/web/cookies')
|
|
129
131
|
|
|
130
132
|
module.exports.deleteCookie = deleteCookie
|
|
131
133
|
module.exports.getCookies = getCookies
|
|
132
134
|
module.exports.getSetCookies = getSetCookies
|
|
133
135
|
module.exports.setCookie = setCookie
|
|
134
136
|
|
|
135
|
-
const { parseMIMEType, serializeAMimeType } = require('./lib/fetch/
|
|
137
|
+
const { parseMIMEType, serializeAMimeType } = require('./lib/web/fetch/data-url')
|
|
136
138
|
|
|
137
139
|
module.exports.parseMIMEType = parseMIMEType
|
|
138
140
|
module.exports.serializeAMimeType = serializeAMimeType
|
|
139
141
|
|
|
140
|
-
|
|
142
|
+
const { CloseEvent, ErrorEvent, MessageEvent } = require('./lib/web/websocket/events')
|
|
143
|
+
module.exports.WebSocket = require('./lib/web/websocket/websocket').WebSocket
|
|
144
|
+
module.exports.CloseEvent = CloseEvent
|
|
145
|
+
module.exports.ErrorEvent = ErrorEvent
|
|
146
|
+
module.exports.MessageEvent = MessageEvent
|
|
141
147
|
|
|
142
148
|
module.exports.request = makeDispatcher(api.request)
|
|
143
149
|
module.exports.stream = makeDispatcher(api.stream)
|
|
@@ -150,6 +156,6 @@ module.exports.MockPool = MockPool
|
|
|
150
156
|
module.exports.MockAgent = MockAgent
|
|
151
157
|
module.exports.mockErrors = mockErrors
|
|
152
158
|
|
|
153
|
-
const { EventSource } = require('./lib/eventsource/eventsource')
|
|
159
|
+
const { EventSource } = require('./lib/web/eventsource/eventsource')
|
|
154
160
|
|
|
155
161
|
module.exports.EventSource = EventSource
|