undici 6.6.2 → 6.7.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 → docs/README.md} +19 -15
- package/docs/{api → docs/api}/Dispatcher.md +39 -3
- package/docs/docs/api/Fetch.md +57 -0
- package/docs/{api → docs/api}/ProxyAgent.md +3 -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/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 +1339 -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 +23 -52
- package/lib/{fetch/dataURL.js → web/fetch/data-url.js} +2 -0
- package/lib/{compat → web/fetch}/dispatcher-weakref.js +1 -1
- package/lib/{fetch → web/fetch}/file.js +2 -2
- package/lib/{fetch → web/fetch}/formdata.js +6 -67
- 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 +14 -6
- 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 +142 -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 -51
- 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.js +0 -3
- 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}/DiagnosticsChannel.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
|
@@ -60,10 +60,14 @@ console.log('trailers', trailers)
|
|
|
60
60
|
|
|
61
61
|
The `body` mixins are the most common way to format the request/response body. Mixins include:
|
|
62
62
|
|
|
63
|
-
- [`.
|
|
63
|
+
- [`.arrayBuffer()`](https://fetch.spec.whatwg.org/#dom-body-arraybuffer)
|
|
64
|
+
- [`.blob()`](https://fetch.spec.whatwg.org/#dom-body-blob)
|
|
64
65
|
- [`.json()`](https://fetch.spec.whatwg.org/#dom-body-json)
|
|
65
66
|
- [`.text()`](https://fetch.spec.whatwg.org/#dom-body-text)
|
|
66
67
|
|
|
68
|
+
> [!NOTE]
|
|
69
|
+
> The body returned from `undici.request` does not implement `.formData()`.
|
|
70
|
+
|
|
67
71
|
Example usage:
|
|
68
72
|
|
|
69
73
|
```js
|
|
@@ -90,14 +94,14 @@ For more information about their behavior, please reference the body mixin from
|
|
|
90
94
|
|
|
91
95
|
## Common API Methods
|
|
92
96
|
|
|
93
|
-
This section documents our most commonly used API methods. Additional APIs are documented in their own files within the [
|
|
97
|
+
This section documents our most commonly used API methods. Additional APIs are documented in their own files within the [documentation](./documentation/) folder and are accessible via the navigation list on the left side of the docs site.
|
|
94
98
|
|
|
95
99
|
### `undici.request([url, options]): Promise`
|
|
96
100
|
|
|
97
101
|
Arguments:
|
|
98
102
|
|
|
99
103
|
* **url** `string | URL | UrlObject`
|
|
100
|
-
* **options** [`RequestOptions`](./docs/api/Dispatcher.md#parameter-requestoptions)
|
|
104
|
+
* **options** [`RequestOptions`](./documentation/docs/api/Dispatcher.md#parameter-requestoptions)
|
|
101
105
|
* **dispatcher** `Dispatcher` - Default: [getGlobalDispatcher](#undicigetglobaldispatcher)
|
|
102
106
|
* **method** `String` - Default: `PUT` if `options.body`, otherwise `GET`
|
|
103
107
|
* **maxRedirections** `Integer` - Default: `0`
|
|
@@ -106,14 +110,14 @@ Returns a promise with the result of the `Dispatcher.request` method.
|
|
|
106
110
|
|
|
107
111
|
Calls `options.dispatcher.request(options)`.
|
|
108
112
|
|
|
109
|
-
See [Dispatcher.request](./docs/api/Dispatcher.md#dispatcherrequestoptions-callback) for more details, and [request examples](./examples/README.md) for examples.
|
|
113
|
+
See [Dispatcher.request](./documentation/docs/api/Dispatcher.md#dispatcherrequestoptions-callback) for more details, and [request examples](./documentation/examples/README.md) for examples.
|
|
110
114
|
|
|
111
115
|
### `undici.stream([url, options, ]factory): Promise`
|
|
112
116
|
|
|
113
117
|
Arguments:
|
|
114
118
|
|
|
115
119
|
* **url** `string | URL | UrlObject`
|
|
116
|
-
* **options** [`StreamOptions`](./docs/api/Dispatcher.md#parameter-streamoptions)
|
|
120
|
+
* **options** [`StreamOptions`](./documentation/docs/api/Dispatcher.md#parameter-streamoptions)
|
|
117
121
|
* **dispatcher** `Dispatcher` - Default: [getGlobalDispatcher](#undicigetglobaldispatcher)
|
|
118
122
|
* **method** `String` - Default: `PUT` if `options.body`, otherwise `GET`
|
|
119
123
|
* **maxRedirections** `Integer` - Default: `0`
|
|
@@ -123,14 +127,14 @@ Returns a promise with the result of the `Dispatcher.stream` method.
|
|
|
123
127
|
|
|
124
128
|
Calls `options.dispatcher.stream(options, factory)`.
|
|
125
129
|
|
|
126
|
-
See [Dispatcher.stream](docs/api/Dispatcher.md#dispatcherstreamoptions-factory-callback) for more details.
|
|
130
|
+
See [Dispatcher.stream](documentation/docs/api/Dispatcher.md#dispatcherstreamoptions-factory-callback) for more details.
|
|
127
131
|
|
|
128
132
|
### `undici.pipeline([url, options, ]handler): Duplex`
|
|
129
133
|
|
|
130
134
|
Arguments:
|
|
131
135
|
|
|
132
136
|
* **url** `string | URL | UrlObject`
|
|
133
|
-
* **options** [`PipelineOptions`](docs/api/Dispatcher.md#parameter-pipelineoptions)
|
|
137
|
+
* **options** [`PipelineOptions`](documentation/docs/api/Dispatcher.md#parameter-pipelineoptions)
|
|
134
138
|
* **dispatcher** `Dispatcher` - Default: [getGlobalDispatcher](#undicigetglobaldispatcher)
|
|
135
139
|
* **method** `String` - Default: `PUT` if `options.body`, otherwise `GET`
|
|
136
140
|
* **maxRedirections** `Integer` - Default: `0`
|
|
@@ -140,7 +144,7 @@ Returns: `stream.Duplex`
|
|
|
140
144
|
|
|
141
145
|
Calls `options.dispatch.pipeline(options, handler)`.
|
|
142
146
|
|
|
143
|
-
See [Dispatcher.pipeline](docs/api/Dispatcher.md#dispatcherpipelineoptions-handler) for more details.
|
|
147
|
+
See [Dispatcher.pipeline](documentation/docs/api/Dispatcher.md#dispatcherpipelineoptions-handler) for more details.
|
|
144
148
|
|
|
145
149
|
### `undici.connect([url, options]): Promise`
|
|
146
150
|
|
|
@@ -149,7 +153,7 @@ Starts two-way communications with the requested resource using [HTTP CONNECT](h
|
|
|
149
153
|
Arguments:
|
|
150
154
|
|
|
151
155
|
* **url** `string | URL | UrlObject`
|
|
152
|
-
* **options** [`ConnectOptions`](docs/api/Dispatcher.md#parameter-connectoptions)
|
|
156
|
+
* **options** [`ConnectOptions`](documentation/docs/api/Dispatcher.md#parameter-connectoptions)
|
|
153
157
|
* **dispatcher** `Dispatcher` - Default: [getGlobalDispatcher](#undicigetglobaldispatcher)
|
|
154
158
|
* **maxRedirections** `Integer` - Default: `0`
|
|
155
159
|
* **callback** `(err: Error | null, data: ConnectData | null) => void` (optional)
|
|
@@ -158,7 +162,7 @@ Returns a promise with the result of the `Dispatcher.connect` method.
|
|
|
158
162
|
|
|
159
163
|
Calls `options.dispatch.connect(options)`.
|
|
160
164
|
|
|
161
|
-
See [Dispatcher.connect](docs/api/Dispatcher.md#dispatcherconnectoptions-callback) for more details.
|
|
165
|
+
See [Dispatcher.connect](documentation/docs/api/Dispatcher.md#dispatcherconnectoptions-callback) for more details.
|
|
162
166
|
|
|
163
167
|
### `undici.fetch(input[, init]): Promise`
|
|
164
168
|
|
|
@@ -226,7 +230,7 @@ await fetch('https://example.com', { body: data, method: 'POST', duplex: 'half'
|
|
|
226
230
|
|
|
227
231
|
- half
|
|
228
232
|
|
|
229
|
-
In this implementation of fetch, `request.duplex` must be set if `request.body` is `ReadableStream` or `Async Iterables
|
|
233
|
+
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
234
|
|
|
231
235
|
#### `response.body`
|
|
232
236
|
|
|
@@ -297,7 +301,7 @@ Upgrade to a different protocol. See [MDN - HTTP - Protocol upgrade mechanism](h
|
|
|
297
301
|
Arguments:
|
|
298
302
|
|
|
299
303
|
* **url** `string | URL | UrlObject`
|
|
300
|
-
* **options** [`UpgradeOptions`](docs/api/Dispatcher.md#parameter-upgradeoptions)
|
|
304
|
+
* **options** [`UpgradeOptions`](documentation/docs/api/Dispatcher.md#parameter-upgradeoptions)
|
|
301
305
|
* **dispatcher** `Dispatcher` - Default: [getGlobalDispatcher](#undicigetglobaldispatcher)
|
|
302
306
|
* **maxRedirections** `Integer` - Default: `0`
|
|
303
307
|
* **callback** `(error: Error | null, data: UpgradeData) => void` (optional)
|
|
@@ -306,7 +310,7 @@ Returns a promise with the result of the `Dispatcher.upgrade` method.
|
|
|
306
310
|
|
|
307
311
|
Calls `options.dispatcher.upgrade(options)`.
|
|
308
312
|
|
|
309
|
-
See [Dispatcher.upgrade](docs/api/Dispatcher.md#dispatcherupgradeoptions-callback) for more details.
|
|
313
|
+
See [Dispatcher.upgrade](documentation/docs/api/Dispatcher.md#dispatcherupgradeoptions-callback) for more details.
|
|
310
314
|
|
|
311
315
|
### `undici.setGlobalDispatcher(dispatcher)`
|
|
312
316
|
|
|
@@ -400,9 +404,9 @@ Refs: https://fetch.spec.whatwg.org/#atomic-http-redirect-handling
|
|
|
400
404
|
|
|
401
405
|
If you experience problem when connecting to a remote server that is resolved by your DNS servers to a IPv6 (AAAA record)
|
|
402
406
|
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`.
|
|
407
|
+
undici will throw an error with code `UND_ERR_CONNECT_TIMEOUT`.
|
|
404
408
|
|
|
405
|
-
If the target server resolves to both a IPv6 and IPv4 (A records) address and you are using a compatible Node version
|
|
409
|
+
If the target server resolves to both a IPv6 and IPv4 (A records) address and you are using a compatible Node version
|
|
406
410
|
(18.3.0 and above), you can fix the problem by providing the `autoSelectFamily` option (support by both `undici.request`
|
|
407
411
|
and `undici.Agent`) which will enable the family autoselection algorithm when establishing the connection.
|
|
408
412
|
|
|
@@ -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,7 @@ 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
20
|
* **token** `string` (optional) - It can be passed by a string of token for authentication.
|
|
21
21
|
* **auth** `string` (**deprecated**) - Use token.
|
|
22
22
|
* **clientFactory** `(origin: URL, opts: Object) => Dispatcher` (optional) - Default: `(origin, opts) => new Pool(origin, opts)`
|
|
@@ -30,6 +30,8 @@ import { ProxyAgent } from 'undici'
|
|
|
30
30
|
|
|
31
31
|
const proxyAgent = new ProxyAgent('my.proxy.server')
|
|
32
32
|
// or
|
|
33
|
+
const proxyAgent = new ProxyAgent(new URL('my.proxy.server'))
|
|
34
|
+
// or
|
|
33
35
|
const proxyAgent = new ProxyAgent({ uri: 'my.proxy.server' })
|
|
34
36
|
```
|
|
35
37
|
|
|
@@ -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
|