undici 6.21.0 → 7.0.0-alpha.10
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 +27 -46
- package/docs/docs/api/Agent.md +14 -17
- package/docs/docs/api/BalancedPool.md +16 -16
- package/docs/docs/api/CacheStore.md +131 -0
- package/docs/docs/api/Client.md +12 -14
- package/docs/docs/api/Debug.md +1 -1
- package/docs/docs/api/Dispatcher.md +98 -194
- package/docs/docs/api/EnvHttpProxyAgent.md +12 -13
- package/docs/docs/api/MockAgent.md +5 -3
- package/docs/docs/api/MockClient.md +5 -5
- package/docs/docs/api/MockPool.md +4 -3
- package/docs/docs/api/Pool.md +15 -16
- package/docs/docs/api/PoolStats.md +1 -1
- package/docs/docs/api/ProxyAgent.md +3 -3
- package/docs/docs/api/RedirectHandler.md +1 -1
- package/docs/docs/api/RetryAgent.md +1 -1
- package/docs/docs/api/RetryHandler.md +4 -4
- package/docs/docs/api/WebSocket.md +46 -4
- package/docs/docs/api/api-lifecycle.md +11 -11
- package/docs/docs/best-practices/mocking-request.md +2 -2
- package/docs/docs/best-practices/proxy.md +1 -1
- package/index.d.ts +1 -1
- package/index.js +23 -7
- package/lib/api/abort-signal.js +2 -0
- package/lib/api/api-connect.js +3 -1
- package/lib/api/api-pipeline.js +7 -6
- package/lib/api/api-request.js +33 -48
- package/lib/api/api-stream.js +39 -50
- package/lib/api/api-upgrade.js +5 -3
- package/lib/api/readable.js +235 -62
- package/lib/api/util.js +2 -0
- package/lib/cache/memory-cache-store.js +177 -0
- package/lib/cache/sqlite-cache-store.js +446 -0
- package/lib/core/constants.js +35 -10
- package/lib/core/diagnostics.js +122 -128
- package/lib/core/errors.js +6 -6
- package/lib/core/request.js +13 -11
- package/lib/core/symbols.js +2 -1
- package/lib/core/tree.js +9 -1
- package/lib/core/util.js +237 -49
- package/lib/dispatcher/agent.js +3 -17
- package/lib/dispatcher/balanced-pool.js +5 -8
- package/lib/dispatcher/client-h1.js +379 -134
- package/lib/dispatcher/client-h2.js +173 -107
- package/lib/dispatcher/client.js +19 -32
- package/lib/dispatcher/dispatcher-base.js +6 -35
- package/lib/dispatcher/dispatcher.js +7 -24
- package/lib/dispatcher/fixed-queue.js +91 -49
- package/lib/dispatcher/pool-stats.js +2 -0
- package/lib/dispatcher/pool.js +3 -6
- package/lib/dispatcher/proxy-agent.js +3 -6
- package/lib/handler/cache-handler.js +393 -0
- package/lib/handler/cache-revalidation-handler.js +124 -0
- package/lib/handler/decorator-handler.js +27 -0
- package/lib/handler/redirect-handler.js +54 -59
- package/lib/handler/retry-handler.js +77 -109
- package/lib/handler/unwrap-handler.js +96 -0
- package/lib/handler/wrap-handler.js +98 -0
- package/lib/interceptor/cache.js +350 -0
- package/lib/interceptor/dns.js +375 -0
- package/lib/interceptor/dump.js +2 -2
- package/lib/interceptor/redirect.js +11 -14
- package/lib/interceptor/response-error.js +18 -7
- package/lib/llhttp/constants.d.ts +97 -0
- package/lib/llhttp/constants.js +412 -192
- package/lib/llhttp/constants.js.map +1 -0
- package/lib/llhttp/llhttp-wasm.js +11 -1
- package/lib/llhttp/llhttp_simd-wasm.js +11 -1
- package/lib/llhttp/utils.d.ts +2 -0
- package/lib/llhttp/utils.js +9 -9
- package/lib/llhttp/utils.js.map +1 -0
- package/lib/mock/mock-agent.js +5 -8
- package/lib/mock/mock-client.js +9 -4
- package/lib/mock/mock-errors.js +3 -1
- package/lib/mock/mock-interceptor.js +8 -6
- package/lib/mock/mock-pool.js +9 -4
- package/lib/mock/mock-symbols.js +3 -1
- package/lib/mock/mock-utils.js +29 -5
- package/lib/util/cache.js +360 -0
- package/lib/web/cache/cache.js +24 -21
- package/lib/web/cache/cachestorage.js +1 -1
- package/lib/web/cookies/index.js +29 -14
- package/lib/web/cookies/parse.js +8 -3
- package/lib/web/eventsource/eventsource-stream.js +9 -8
- package/lib/web/eventsource/eventsource.js +10 -6
- package/lib/web/fetch/body.js +43 -41
- package/lib/web/fetch/constants.js +12 -5
- package/lib/web/fetch/data-url.js +3 -3
- package/lib/web/fetch/formdata-parser.js +72 -45
- package/lib/web/fetch/formdata.js +65 -54
- package/lib/web/fetch/headers.js +118 -86
- package/lib/web/fetch/index.js +58 -67
- package/lib/web/fetch/request.js +136 -77
- package/lib/web/fetch/response.js +87 -56
- package/lib/web/fetch/util.js +259 -109
- package/lib/web/fetch/webidl.js +113 -68
- package/lib/web/websocket/connection.js +76 -147
- package/lib/web/websocket/constants.js +70 -10
- package/lib/web/websocket/events.js +4 -2
- package/lib/web/websocket/frame.js +45 -3
- package/lib/web/websocket/receiver.js +29 -33
- package/lib/web/websocket/sender.js +18 -13
- package/lib/web/websocket/stream/websocketerror.js +83 -0
- package/lib/web/websocket/stream/websocketstream.js +485 -0
- package/lib/web/websocket/util.js +128 -77
- package/lib/web/websocket/websocket.js +234 -135
- package/package.json +24 -36
- package/scripts/strip-comments.js +3 -1
- package/types/agent.d.ts +7 -7
- package/types/api.d.ts +24 -24
- package/types/balanced-pool.d.ts +11 -11
- package/types/cache-interceptor.d.ts +172 -0
- package/types/client.d.ts +11 -12
- package/types/cookies.d.ts +2 -0
- package/types/diagnostics-channel.d.ts +10 -10
- package/types/dispatcher.d.ts +113 -90
- package/types/env-http-proxy-agent.d.ts +2 -2
- package/types/errors.d.ts +53 -47
- package/types/fetch.d.ts +17 -16
- package/types/formdata.d.ts +7 -7
- package/types/global-dispatcher.d.ts +4 -4
- package/types/global-origin.d.ts +5 -5
- package/types/handlers.d.ts +7 -7
- package/types/header.d.ts +157 -1
- package/types/index.d.ts +44 -46
- package/types/interceptors.d.ts +25 -8
- package/types/mock-agent.d.ts +21 -18
- package/types/mock-client.d.ts +4 -4
- package/types/mock-errors.d.ts +3 -3
- package/types/mock-interceptor.d.ts +19 -19
- package/types/mock-pool.d.ts +4 -4
- package/types/patch.d.ts +0 -4
- package/types/pool-stats.d.ts +8 -8
- package/types/pool.d.ts +12 -12
- package/types/proxy-agent.d.ts +4 -4
- package/types/readable.d.ts +18 -15
- package/types/retry-agent.d.ts +1 -1
- package/types/retry-handler.d.ts +10 -10
- package/types/util.d.ts +3 -3
- package/types/utility.d.ts +7 -0
- package/types/webidl.d.ts +44 -6
- package/types/websocket.d.ts +34 -1
- package/docs/docs/api/DispatchInterceptor.md +0 -60
- package/lib/interceptor/redirect-interceptor.js +0 -21
- package/lib/mock/pluralizer.js +0 -29
- package/lib/web/cache/symbols.js +0 -5
- package/lib/web/fetch/file.js +0 -126
- package/lib/web/fetch/symbols.js +0 -9
- package/lib/web/fileapi/encoding.js +0 -290
- package/lib/web/fileapi/filereader.js +0 -344
- package/lib/web/fileapi/progressevent.js +0 -78
- package/lib/web/fileapi/symbols.js +0 -10
- package/lib/web/fileapi/util.js +0 -391
- package/lib/web/websocket/symbols.js +0 -12
- package/types/file.d.ts +0 -39
- package/types/filereader.d.ts +0 -54
|
@@ -197,23 +197,20 @@ Returns: `Boolean` - `false` if dispatcher is busy and further dispatch calls wo
|
|
|
197
197
|
* **headers** `UndiciHeaders | string[]` (optional) - Default: `null`.
|
|
198
198
|
* **query** `Record<string, any> | null` (optional) - Default: `null` - Query string params to be embedded in the request URL. Note that both keys and values of query are encoded using `encodeURIComponent`. If for some reason you need to send them unencoded, embed query params into path directly instead.
|
|
199
199
|
* **idempotent** `boolean` (optional) - Default: `true` if `method` is `'HEAD'` or `'GET'` - Whether the requests can be safely retried or not. If `false` the request won't be sent until all preceding requests in the pipeline has completed.
|
|
200
|
-
* **blocking** `boolean` (optional) - Default: `
|
|
200
|
+
* **blocking** `boolean` (optional) - Default: `method !== 'HEAD'` - Whether the response is expected to take a long time and would end up blocking the pipeline. When this is set to `true` further pipelining will be avoided on the same connection until headers have been received.
|
|
201
201
|
* **upgrade** `string | null` (optional) - Default: `null` - Upgrade the request. Should be used to specify the kind of upgrade i.e. `'Websocket'`.
|
|
202
202
|
* **bodyTimeout** `number | null` (optional) - The timeout after which a request will time out, in milliseconds. Monitors time between receiving body data. Use `0` to disable it entirely. Defaults to 300 seconds.
|
|
203
203
|
* **headersTimeout** `number | null` (optional) - The amount of time, in milliseconds, the parser will wait to receive the complete HTTP headers while not sending the request. Defaults to 300 seconds.
|
|
204
|
-
* **throwOnError** `boolean` (optional) - Default: `false` - Whether Undici should throw an error upon receiving a 4xx or 5xx response from the server.
|
|
205
204
|
* **expectContinue** `boolean` (optional) - Default: `false` - For H2, it appends the expect: 100-continue header, and halts the request body until a 100-continue is received from the remote server
|
|
206
205
|
|
|
207
206
|
#### Parameter: `DispatchHandler`
|
|
208
207
|
|
|
209
|
-
* **
|
|
210
|
-
* **
|
|
211
|
-
* **
|
|
212
|
-
* **
|
|
213
|
-
* **
|
|
214
|
-
* **
|
|
215
|
-
* **onComplete** `(trailers: Buffer[]) => void` - Invoked when response payload and trailers have been received and the request has completed. Not required for `upgrade` requests.
|
|
216
|
-
* **onBodySent** `(chunk: string | Buffer | Uint8Array) => void` - Invoked when a body chunk is sent to the server. Not required. For a stream or iterable body this will be invoked for every chunk. For other body types, it will be invoked once after the body is sent.
|
|
208
|
+
* **onRequestStart** `(controller: DispatchController, context: object) => void` - Invoked before request is dispatched on socket. May be invoked multiple times when a request is retried when the request at the head of the pipeline fails.
|
|
209
|
+
* **onRequestUpgrade** `(controller: DispatchController, statusCode: number, headers: Record<string, string | string[]>, socket: Duplex) => void` (optional) - Invoked when request is upgraded. Required if `DispatchOptions.upgrade` is defined or `DispatchOptions.method === 'CONNECT'`.
|
|
210
|
+
* **onResponseStart** `(controller: DispatchController, statusCode: number, headers: Record<string, string | string []>, statusMessage?: string) => void` - Invoked when statusCode and headers have been received. May be invoked multiple times due to 1xx informational headers. Not required for `upgrade` requests.
|
|
211
|
+
* **onResponseData** `(controller: DispatchController, chunk: Buffer) => void` - Invoked when response payload data is received. Not required for `upgrade` requests.
|
|
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
|
+
* **onResponseError** `(error: Error) => void` - Invoked when an error has occurred. May not throw.
|
|
217
214
|
|
|
218
215
|
#### Example 1 - Dispatch GET request
|
|
219
216
|
|
|
@@ -378,7 +375,7 @@ Returns: `stream.Duplex`
|
|
|
378
375
|
|
|
379
376
|
#### Parameter: PipelineOptions
|
|
380
377
|
|
|
381
|
-
Extends: [`RequestOptions`](#parameter-requestoptions)
|
|
378
|
+
Extends: [`RequestOptions`](/docs/docs/api/Dispatcher.md#parameter-requestoptions)
|
|
382
379
|
|
|
383
380
|
* **objectMode** `boolean` (optional) - Default: `false` - Set to `true` if the `handler` will return an object stream.
|
|
384
381
|
|
|
@@ -468,7 +465,7 @@ Returns: `void | Promise<ResponseData>` - Only returns a `Promise` if no `callba
|
|
|
468
465
|
|
|
469
466
|
#### Parameter: `RequestOptions`
|
|
470
467
|
|
|
471
|
-
Extends: [`DispatchOptions`](#parameter-dispatchoptions)
|
|
468
|
+
Extends: [`DispatchOptions`](/docs/docs/api/Dispatcher.md#parameter-dispatchoptions)
|
|
472
469
|
|
|
473
470
|
* **opaque** `unknown` (optional) - Default: `null` - Used for passing through context to `ResponseData`.
|
|
474
471
|
* **signal** `AbortSignal | events.EventEmitter | null` (optional) - Default: `null`.
|
|
@@ -479,7 +476,7 @@ The `RequestOptions.method` property should not be value `'CONNECT'`.
|
|
|
479
476
|
#### Parameter: `ResponseData`
|
|
480
477
|
|
|
481
478
|
* **statusCode** `number`
|
|
482
|
-
* **headers** `Record<string, string | string[]>` - Note that all header keys are lower-cased, e.
|
|
479
|
+
* **headers** `Record<string, string | string[]>` - Note that all header keys are lower-cased, e.g. `content-type`.
|
|
483
480
|
* **body** `stream.Readable` which also implements [the body mixin from the Fetch Standard](https://fetch.spec.whatwg.org/#body-mixin).
|
|
484
481
|
* **trailers** `Record<string, string>` - This object starts out
|
|
485
482
|
as empty and will be mutated to contain trailers after `body` has emitted `'end'`.
|
|
@@ -528,6 +525,7 @@ try {
|
|
|
528
525
|
console.log('headers', headers)
|
|
529
526
|
body.setEncoding('utf8')
|
|
530
527
|
body.on('data', console.log)
|
|
528
|
+
body.on('error', console.error)
|
|
531
529
|
body.on('end', () => {
|
|
532
530
|
console.log('trailers', trailers)
|
|
533
531
|
})
|
|
@@ -631,11 +629,30 @@ try {
|
|
|
631
629
|
}
|
|
632
630
|
```
|
|
633
631
|
|
|
632
|
+
#### Example 3 - Conditionally reading the body
|
|
633
|
+
|
|
634
|
+
Remember to fully consume the body even in the case when it is not read.
|
|
635
|
+
|
|
636
|
+
```js
|
|
637
|
+
const { body, statusCode } = await client.request({
|
|
638
|
+
path: '/',
|
|
639
|
+
method: 'GET'
|
|
640
|
+
})
|
|
641
|
+
|
|
642
|
+
if (statusCode === 200) {
|
|
643
|
+
return await body.arrayBuffer()
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
await body.dump()
|
|
647
|
+
|
|
648
|
+
return null
|
|
649
|
+
```
|
|
650
|
+
|
|
634
651
|
### `Dispatcher.stream(options, factory[, callback])`
|
|
635
652
|
|
|
636
653
|
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.
|
|
637
654
|
|
|
638
|
-
As demonstrated in [Example 1 - Basic GET stream request](#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](#example-2---stream-to-fastify-response) for more details.
|
|
655
|
+
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/Dispatch.md#example-2---stream-to-fastify-response) for more details.
|
|
639
656
|
|
|
640
657
|
Arguments:
|
|
641
658
|
|
|
@@ -917,7 +934,7 @@ await client.request({ path: '/', method: 'GET' })
|
|
|
917
934
|
|
|
918
935
|
The `redirect` interceptor allows you to customize the way your dispatcher handles redirects.
|
|
919
936
|
|
|
920
|
-
It accepts the same arguments as the [`RedirectHandler` constructor](
|
|
937
|
+
It accepts the same arguments as the [`RedirectHandler` constructor](/docs/docs/api/RedirectHandler.md).
|
|
921
938
|
|
|
922
939
|
**Example - Basic Redirect Interceptor**
|
|
923
940
|
|
|
@@ -935,7 +952,7 @@ client.request({ path: "/" })
|
|
|
935
952
|
|
|
936
953
|
The `retry` interceptor allows you to customize the way your dispatcher handles retries.
|
|
937
954
|
|
|
938
|
-
It accepts the same arguments as the [`RetryHandler` constructor](
|
|
955
|
+
It accepts the same arguments as the [`RetryHandler` constructor](/docs/docs/api/RetryHandler.md).
|
|
939
956
|
|
|
940
957
|
**Example - Basic Redirect Interceptor**
|
|
941
958
|
|
|
@@ -975,7 +992,7 @@ const client = new Client("http://example.com").compose(
|
|
|
975
992
|
})
|
|
976
993
|
);
|
|
977
994
|
|
|
978
|
-
// or
|
|
995
|
+
// or
|
|
979
996
|
client.dispatch(
|
|
980
997
|
{
|
|
981
998
|
path: "/",
|
|
@@ -986,202 +1003,89 @@ client.dispatch(
|
|
|
986
1003
|
);
|
|
987
1004
|
```
|
|
988
1005
|
|
|
989
|
-
##### `
|
|
990
|
-
|
|
991
|
-
**Introduction**
|
|
992
|
-
|
|
993
|
-
The Response Error Interceptor is designed to handle HTTP response errors efficiently. It intercepts responses and throws detailed errors for responses with status codes indicating failure (4xx, 5xx). This interceptor enhances error handling by providing structured error information, including response headers, data, and status codes.
|
|
1006
|
+
##### `dns`
|
|
994
1007
|
|
|
995
|
-
|
|
1008
|
+
The `dns` interceptor enables you to cache DNS lookups for a given duration, per origin.
|
|
996
1009
|
|
|
997
|
-
|
|
1010
|
+
>It is well suited for scenarios where you want to cache DNS lookups to avoid the overhead of resolving the same domain multiple times
|
|
998
1011
|
|
|
999
|
-
**
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1012
|
+
**Options**
|
|
1013
|
+
- `maxTTL` - The maximum time-to-live (in milliseconds) of the DNS cache. It should be a positive integer. Default: `10000`.
|
|
1014
|
+
- Set `0` to disable TTL.
|
|
1015
|
+
- `maxItems` - The maximum number of items to cache. It should be a positive integer. Default: `Infinity`.
|
|
1016
|
+
- `dualStack` - Whether to resolve both IPv4 and IPv6 addresses. Default: `true`.
|
|
1017
|
+
- It will also attempt a happy-eyeballs-like approach to connect to the available addresses in case of a connection failure.
|
|
1018
|
+
- `affinity` - Whether to use IPv4 or IPv6 addresses. Default: `4`.
|
|
1019
|
+
- It can be either `'4` or `6`.
|
|
1020
|
+
- It will only take effect if `dualStack` is `false`.
|
|
1021
|
+
- `lookup: (hostname: string, options: LookupOptions, callback: (err: NodeJS.ErrnoException | null, addresses: DNSInterceptorRecord[]) => void) => void` - Custom lookup function. Default: `dns.lookup`.
|
|
1022
|
+
- For more info see [dns.lookup](https://nodejs.org/api/dns.html#dns_dns_lookup_hostname_options_callback).
|
|
1023
|
+
- `pick: (origin: URL, records: DNSInterceptorRecords, affinity: 4 | 6) => DNSInterceptorRecord` - Custom pick function. Default: `RoundRobin`.
|
|
1024
|
+
- The function should return a single record from the records array.
|
|
1025
|
+
- By default a simplified version of Round Robin is used.
|
|
1026
|
+
- The `records` property can be mutated to store the state of the balancing algorithm.
|
|
1027
|
+
|
|
1028
|
+
> The `Dispatcher#options` also gets extended with the options `dns.affinity`, `dns.dualStack`, `dns.lookup` and `dns.pick` which can be used to configure the interceptor at a request-per-request basis.
|
|
1029
|
+
|
|
1030
|
+
|
|
1031
|
+
**DNSInterceptorRecord**
|
|
1032
|
+
It represents a DNS record.
|
|
1033
|
+
- `family` - (`number`) The IP family of the address. It can be either `4` or `6`.
|
|
1034
|
+
- `address` - (`string`) The IP address.
|
|
1035
|
+
|
|
1036
|
+
**DNSInterceptorOriginRecords**
|
|
1037
|
+
It represents a map of DNS IP addresses records for a single origin.
|
|
1038
|
+
- `4.ips` - (`DNSInterceptorRecord[] | null`) The IPv4 addresses.
|
|
1039
|
+
- `6.ips` - (`DNSInterceptorRecord[] | null`) The IPv6 addresses.
|
|
1040
|
+
|
|
1041
|
+
**Example - Basic DNS Interceptor**
|
|
1028
1042
|
|
|
1029
1043
|
```js
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
#handler;
|
|
1033
|
-
#statusCode;
|
|
1034
|
-
#contentType;
|
|
1035
|
-
#decoder;
|
|
1036
|
-
#headers;
|
|
1037
|
-
#body;
|
|
1038
|
-
|
|
1039
|
-
constructor (opts, { handler }) {
|
|
1040
|
-
super(handler);
|
|
1041
|
-
this.#handler = handler;
|
|
1042
|
-
}
|
|
1043
|
-
|
|
1044
|
-
onConnect (abort) {
|
|
1045
|
-
this.#statusCode = 0;
|
|
1046
|
-
this.#contentType = null;
|
|
1047
|
-
this.#decoder = null;
|
|
1048
|
-
this.#headers = null;
|
|
1049
|
-
this.#body = '';
|
|
1050
|
-
return this.#handler.onConnect(abort);
|
|
1051
|
-
}
|
|
1052
|
-
|
|
1053
|
-
onHeaders (statusCode, rawHeaders, resume, statusMessage, headers = parseHeaders(rawHeaders)) {
|
|
1054
|
-
this.#statusCode = statusCode;
|
|
1055
|
-
this.#headers = headers;
|
|
1056
|
-
this.#contentType = headers['content-type'];
|
|
1057
|
-
|
|
1058
|
-
if (this.#statusCode < 400) {
|
|
1059
|
-
return this.#handler.onHeaders(statusCode, rawHeaders, resume, statusMessage, headers);
|
|
1060
|
-
}
|
|
1061
|
-
|
|
1062
|
-
if (this.#contentType === 'application/json' || this.#contentType === 'text/plain') {
|
|
1063
|
-
this.#decoder = new TextDecoder('utf-8');
|
|
1064
|
-
}
|
|
1065
|
-
}
|
|
1066
|
-
|
|
1067
|
-
onData (chunk) {
|
|
1068
|
-
if (this.#statusCode < 400) {
|
|
1069
|
-
return this.#handler.onData(chunk);
|
|
1070
|
-
}
|
|
1071
|
-
this.#body += this.#decoder?.decode(chunk, { stream: true }) ?? '';
|
|
1072
|
-
}
|
|
1073
|
-
|
|
1074
|
-
onComplete (rawTrailers) {
|
|
1075
|
-
if (this.#statusCode >= 400) {
|
|
1076
|
-
this.#body += this.#decoder?.decode(undefined, { stream: false }) ?? '';
|
|
1077
|
-
if (this.#contentType === 'application/json') {
|
|
1078
|
-
try {
|
|
1079
|
-
this.#body = JSON.parse(this.#body);
|
|
1080
|
-
} catch {
|
|
1081
|
-
// Do nothing...
|
|
1082
|
-
}
|
|
1083
|
-
}
|
|
1084
|
-
|
|
1085
|
-
let err;
|
|
1086
|
-
const stackTraceLimit = Error.stackTraceLimit;
|
|
1087
|
-
Error.stackTraceLimit = 0;
|
|
1088
|
-
try {
|
|
1089
|
-
err = new ResponseError('Response Error', this.#statusCode, this.#headers, this.#body);
|
|
1090
|
-
} finally {
|
|
1091
|
-
Error.stackTraceLimit = stackTraceLimit;
|
|
1092
|
-
}
|
|
1093
|
-
|
|
1094
|
-
this.#handler.onError(err);
|
|
1095
|
-
} else {
|
|
1096
|
-
this.#handler.onComplete(rawTrailers);
|
|
1097
|
-
}
|
|
1098
|
-
}
|
|
1044
|
+
const { Client, interceptors } = require("undici");
|
|
1045
|
+
const { dns } = interceptors;
|
|
1099
1046
|
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
}
|
|
1047
|
+
const client = new Agent().compose([
|
|
1048
|
+
dns({ ...opts })
|
|
1049
|
+
])
|
|
1104
1050
|
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1051
|
+
const response = await client.request({
|
|
1052
|
+
origin: `http://localhost:3030`,
|
|
1053
|
+
...requestOpts
|
|
1054
|
+
})
|
|
1108
1055
|
```
|
|
1109
1056
|
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
Unit tests ensure the interceptor functions correctly, handling both error and non-error responses appropriately.
|
|
1057
|
+
##### `responseError`
|
|
1113
1058
|
|
|
1114
|
-
|
|
1059
|
+
The `responseError` interceptor throws an error for responses with status code errors (>= 400).
|
|
1115
1060
|
|
|
1116
|
-
|
|
1061
|
+
**Example**
|
|
1117
1062
|
|
|
1118
1063
|
```js
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
const handler = { onError: () => {}, onData: () => {}, onComplete: () => {} };
|
|
1122
|
-
const interceptor = createResponseErrorInterceptor((opts, handler) => handler.onComplete());
|
|
1123
|
-
assert.doesNotThrow(() => interceptor(opts, handler));
|
|
1124
|
-
});
|
|
1125
|
-
```
|
|
1126
|
-
|
|
1127
|
-
- **Error if Status Code is in Specified Error Codes**:
|
|
1128
|
-
|
|
1129
|
-
```js
|
|
1130
|
-
test('should error if request status code is in the specified error codes', async (t) => {
|
|
1131
|
-
const opts = { throwOnError: true, statusCodes: [500] };
|
|
1132
|
-
const response = { statusCode: 500 };
|
|
1133
|
-
let capturedError;
|
|
1134
|
-
const handler = {
|
|
1135
|
-
onError: (err) => { capturedError = err; },
|
|
1136
|
-
onData: () => {},
|
|
1137
|
-
onComplete: () => {}
|
|
1138
|
-
};
|
|
1139
|
-
|
|
1140
|
-
const interceptor = createResponseErrorInterceptor((opts, handler) => {
|
|
1141
|
-
if (opts.throwOnError && opts.statusCodes.includes(response.statusCode)) {
|
|
1142
|
-
handler.onError(new Error('Response Error'));
|
|
1143
|
-
} else {
|
|
1144
|
-
handler.onComplete();
|
|
1145
|
-
}
|
|
1146
|
-
});
|
|
1147
|
-
|
|
1148
|
-
interceptor({ ...opts, response }, handler);
|
|
1064
|
+
const { Client, interceptors } = require("undici");
|
|
1065
|
+
const { responseError } = interceptors;
|
|
1149
1066
|
|
|
1150
|
-
|
|
1067
|
+
const client = new Client("http://example.com").compose(
|
|
1068
|
+
responseError()
|
|
1069
|
+
);
|
|
1151
1070
|
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1071
|
+
// Will throw a ResponseError for status codes >= 400
|
|
1072
|
+
await client.request({
|
|
1073
|
+
method: "GET",
|
|
1074
|
+
path: "/"
|
|
1155
1075
|
});
|
|
1156
1076
|
```
|
|
1157
1077
|
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
```js
|
|
1161
|
-
test('should not error if request status code is not in the specified error codes', async (t) => {
|
|
1162
|
-
const opts = { throwOnError: true, statusCodes: [500] };
|
|
1163
|
-
const response = { statusCode: 404 };
|
|
1164
|
-
const handler = {
|
|
1165
|
-
onError: () => {},
|
|
1166
|
-
onData: () => {},
|
|
1167
|
-
onComplete: () => {}
|
|
1168
|
-
};
|
|
1169
|
-
|
|
1170
|
-
const interceptor = createResponseErrorInterceptor((opts, handler) => {
|
|
1171
|
-
if (opts.throwOnError && opts.statusCodes.includes(response.statusCode)) {
|
|
1172
|
-
handler.onError(new Error('Response Error'));
|
|
1173
|
-
} else {
|
|
1174
|
-
handler.onComplete();
|
|
1175
|
-
}
|
|
1176
|
-
});
|
|
1078
|
+
##### `Cache Interceptor`
|
|
1177
1079
|
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
```
|
|
1080
|
+
The `cache` interceptor implements client-side response caching as described in
|
|
1081
|
+
[RFC9111](https://www.rfc-editor.org/rfc/rfc9111.html).
|
|
1181
1082
|
|
|
1182
|
-
**
|
|
1083
|
+
**Options**
|
|
1183
1084
|
|
|
1184
|
-
|
|
1085
|
+
- `store` - The [`CacheStore`](/docs/docs/api/CacheStore.md) to store and retrieve responses from. Default is [`MemoryCacheStore`](/docs/docs/api/CacheStore.md#memorycachestore).
|
|
1086
|
+
- `methods` - The [**safe** HTTP methods](https://www.rfc-editor.org/rfc/rfc9110#section-9.2.1) to cache the response of.
|
|
1087
|
+
- `cacheByDefault` - The default expiration time to cache responses by if they don't have an explicit expiration. If this isn't present, responses without explicit expiration will not be cached. Default `undefined`.
|
|
1088
|
+
- `type` - The type of cache for Undici to act as. Can be `shared` or `private`. Default `shared`.
|
|
1185
1089
|
|
|
1186
1090
|
## Instance Events
|
|
1187
1091
|
|
|
@@ -1229,13 +1133,13 @@ Emitted when dispatcher is no longer busy.
|
|
|
1229
1133
|
|
|
1230
1134
|
* `Record<string, string | string[] | undefined> | string[] | Iterable<[string, string | string[] | undefined]> | null`
|
|
1231
1135
|
|
|
1232
|
-
Header arguments such as `options.headers` in [`Client.dispatch`](Client.md#clientdispatchoptions-handlers) can be specified in three forms:
|
|
1136
|
+
Header arguments such as `options.headers` in [`Client.dispatch`](/docs/docs/api/Client.md#clientdispatchoptions-handlers) can be specified in three forms:
|
|
1233
1137
|
* As an object specified by the `Record<string, string | string[] | undefined>` (`IncomingHttpHeaders`) type.
|
|
1234
1138
|
* As an array of strings. An array representation of a header list must have an even length, or an `InvalidArgumentError` will be thrown.
|
|
1235
1139
|
* As an iterable that can encompass `Headers`, `Map`, or a custom iterator returning key-value pairs.
|
|
1236
1140
|
Keys are lowercase and values are not modified.
|
|
1237
1141
|
|
|
1238
|
-
Response headers will derive a `host` from the `url` of the [Client](Client.md#class-client) instance if no `host` header was previously specified.
|
|
1142
|
+
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.
|
|
1239
1143
|
|
|
1240
1144
|
### Example 1 - Object
|
|
1241
1145
|
|
|
@@ -20,7 +20,7 @@ Returns: `EnvHttpProxyAgent`
|
|
|
20
20
|
|
|
21
21
|
### Parameter: `EnvHttpProxyAgentOptions`
|
|
22
22
|
|
|
23
|
-
Extends: [`AgentOptions`](Agent.md#parameter-agentoptions)
|
|
23
|
+
Extends: [`AgentOptions`](/docs/docs/api/Agent.md#parameter-agentoptions)
|
|
24
24
|
|
|
25
25
|
* **httpProxy** `string` (optional) - When set, it will override the `HTTP_PROXY` environment variable.
|
|
26
26
|
* **httpsProxy** `string` (optional) - When set, it will override the `HTTPS_PROXY` environment variable.
|
|
@@ -118,45 +118,44 @@ const data = await json() // data { foo: "bar" }
|
|
|
118
118
|
|
|
119
119
|
### `EnvHttpProxyAgent.close([callback])`
|
|
120
120
|
|
|
121
|
-
Implements [`Dispatcher.close([callback])`](Dispatcher.md#dispatcherclosecallback-promise).
|
|
121
|
+
Implements [`Dispatcher.close([callback])`](/docs/docs/api/Dispatcher.md#dispatcherclosecallback-promise).
|
|
122
122
|
|
|
123
123
|
### `EnvHttpProxyAgent.destroy([error, callback])`
|
|
124
124
|
|
|
125
|
-
Implements [`Dispatcher.destroy([error, callback])`](Dispatcher.md#dispatcherdestroyerror-callback-promise).
|
|
125
|
+
Implements [`Dispatcher.destroy([error, callback])`](/docs/docs/api/Dispatcher.md#dispatcherdestroyerror-callback-promise).
|
|
126
126
|
|
|
127
127
|
### `EnvHttpProxyAgent.dispatch(options, handler: AgentDispatchOptions)`
|
|
128
128
|
|
|
129
|
-
Implements [`Dispatcher.dispatch(options, handler)`](Dispatcher.md#dispatcherdispatchoptions-handler).
|
|
129
|
+
Implements [`Dispatcher.dispatch(options, handler)`](/docs/docs/api/Dispatcher.md#dispatcherdispatchoptions-handler).
|
|
130
130
|
|
|
131
131
|
#### Parameter: `AgentDispatchOptions`
|
|
132
132
|
|
|
133
|
-
Extends: [`DispatchOptions`](Dispatcher.md#parameter-dispatchoptions)
|
|
133
|
+
Extends: [`DispatchOptions`](/docs/docs/api/Dispatcher.md#parameter-dispatchoptions)
|
|
134
134
|
|
|
135
135
|
* **origin** `string | URL`
|
|
136
|
-
* **maxRedirections** `Integer`.
|
|
137
136
|
|
|
138
|
-
Implements [`Dispatcher.destroy([error, callback])`](Dispatcher.md#dispatcherdestroyerror-callback-promise).
|
|
137
|
+
Implements [`Dispatcher.destroy([error, callback])`](/docs/docs/api/Dispatcher.md#dispatcherdestroyerror-callback-promise).
|
|
139
138
|
|
|
140
139
|
### `EnvHttpProxyAgent.connect(options[, callback])`
|
|
141
140
|
|
|
142
|
-
See [`Dispatcher.connect(options[, callback])`](Dispatcher.md#dispatcherconnectoptions-callback).
|
|
141
|
+
See [`Dispatcher.connect(options[, callback])`](/docs/docs/api/Dispatcher.md#dispatcherconnectoptions-callback).
|
|
143
142
|
|
|
144
143
|
### `EnvHttpProxyAgent.dispatch(options, handler)`
|
|
145
144
|
|
|
146
|
-
Implements [`Dispatcher.dispatch(options, handler)`](Dispatcher.md#dispatcherdispatchoptions-handler).
|
|
145
|
+
Implements [`Dispatcher.dispatch(options, handler)`](/docs/docs/api/Dispatcher.md#dispatcherdispatchoptions-handler).
|
|
147
146
|
|
|
148
147
|
### `EnvHttpProxyAgent.pipeline(options, handler)`
|
|
149
148
|
|
|
150
|
-
See [`Dispatcher.pipeline(options, handler)`](Dispatcher.md#dispatcherpipelineoptions-handler).
|
|
149
|
+
See [`Dispatcher.pipeline(options, handler)`](/docs/docs/api/Dispatcher.md#dispatcherpipelineoptions-handler).
|
|
151
150
|
|
|
152
151
|
### `EnvHttpProxyAgent.request(options[, callback])`
|
|
153
152
|
|
|
154
|
-
See [`Dispatcher.request(options [, callback])`](Dispatcher.md#dispatcherrequestoptions-callback).
|
|
153
|
+
See [`Dispatcher.request(options [, callback])`](/docs/docs/api/Dispatcher.md#dispatcherrequestoptions-callback).
|
|
155
154
|
|
|
156
155
|
### `EnvHttpProxyAgent.stream(options, factory[, callback])`
|
|
157
156
|
|
|
158
|
-
See [`Dispatcher.stream(options, factory[, callback])`](Dispatcher.md#dispatcherstreamoptions-factory-callback).
|
|
157
|
+
See [`Dispatcher.stream(options, factory[, callback])`](/docs/docs/api/Dispatcher.md#dispatcherstreamoptions-factory-callback).
|
|
159
158
|
|
|
160
159
|
### `EnvHttpProxyAgent.upgrade(options[, callback])`
|
|
161
160
|
|
|
162
|
-
See [`Dispatcher.upgrade(options[, callback])`](Dispatcher.md#dispatcherupgradeoptions-callback).
|
|
161
|
+
See [`Dispatcher.upgrade(options[, callback])`](/docs/docs/api/Dispatcher.md#dispatcherupgradeoptions-callback).
|
|
@@ -14,10 +14,12 @@ Returns: `MockAgent`
|
|
|
14
14
|
|
|
15
15
|
### Parameter: `MockAgentOptions`
|
|
16
16
|
|
|
17
|
-
Extends: [`AgentOptions`](Agent.md#parameter-agentoptions)
|
|
17
|
+
Extends: [`AgentOptions`](/docs/docs/api/Agent.md#parameter-agentoptions)
|
|
18
18
|
|
|
19
19
|
* **agent** `Agent` (optional) - Default: `new Agent([options])` - a custom agent encapsulated by the MockAgent.
|
|
20
20
|
|
|
21
|
+
* **ignoreTrailingSlash** `boolean` (optional) - Default: `false` - set the default value for `ignoreTrailingSlash` for interceptors.
|
|
22
|
+
|
|
21
23
|
### Example - Basic MockAgent instantiation
|
|
22
24
|
|
|
23
25
|
This will instantiate the MockAgent. It will not do anything until registered as the agent to use with requests and mock interceptions are added.
|
|
@@ -299,11 +301,11 @@ await mockAgent.close()
|
|
|
299
301
|
|
|
300
302
|
### `MockAgent.dispatch(options, handlers)`
|
|
301
303
|
|
|
302
|
-
Implements [`Agent.dispatch(options, handlers)`](Agent.md#parameter-agentdispatchoptions).
|
|
304
|
+
Implements [`Agent.dispatch(options, handlers)`](/docs/docs/api/Agent.md#parameter-agentdispatchoptions).
|
|
303
305
|
|
|
304
306
|
### `MockAgent.request(options[, callback])`
|
|
305
307
|
|
|
306
|
-
See [`Dispatcher.request(options [, callback])`](Dispatcher.md#dispatcherrequestoptions-callback).
|
|
308
|
+
See [`Dispatcher.request(options [, callback])`](/docs/docs/api/Dispatcher.md#dispatcherrequestoptions-callback).
|
|
307
309
|
|
|
308
310
|
#### Example - MockAgent request
|
|
309
311
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Extends: `undici.Client`
|
|
4
4
|
|
|
5
|
-
A mock client class that implements the same api as [MockPool](MockPool.md).
|
|
5
|
+
A mock client class that implements the same api as [MockPool](/docs/docs/api/MockPool.md).
|
|
6
6
|
|
|
7
7
|
## `new MockClient(origin, [options])`
|
|
8
8
|
|
|
@@ -36,19 +36,19 @@ const mockClient = mockAgent.get('http://localhost:3000')
|
|
|
36
36
|
|
|
37
37
|
### `MockClient.intercept(options)`
|
|
38
38
|
|
|
39
|
-
Implements: [`MockPool.intercept(options)`](MockPool.md#mockpoolinterceptoptions)
|
|
39
|
+
Implements: [`MockPool.intercept(options)`](/docs/docs/api/MockPool.md#mockpoolinterceptoptions)
|
|
40
40
|
|
|
41
41
|
### `MockClient.close()`
|
|
42
42
|
|
|
43
|
-
Implements: [`MockPool.close()`](MockPool.md#mockpoolclose)
|
|
43
|
+
Implements: [`MockPool.close()`](/docs/docs/api/MockPool.md#mockpoolclose)
|
|
44
44
|
|
|
45
45
|
### `MockClient.dispatch(options, handlers)`
|
|
46
46
|
|
|
47
|
-
Implements [`Dispatcher.dispatch(options, handlers)`](Dispatcher.md#dispatcherdispatchoptions-handler).
|
|
47
|
+
Implements [`Dispatcher.dispatch(options, handlers)`](/docs/docs/api/Dispatcher.md#dispatcherdispatchoptions-handler).
|
|
48
48
|
|
|
49
49
|
### `MockClient.request(options[, callback])`
|
|
50
50
|
|
|
51
|
-
See [`Dispatcher.request(options [, callback])`](Dispatcher.md#dispatcherrequestoptions-callback).
|
|
51
|
+
See [`Dispatcher.request(options [, callback])`](/docs/docs/api/Dispatcher.md#dispatcherrequestoptions-callback).
|
|
52
52
|
|
|
53
53
|
#### Example - MockClient request
|
|
54
54
|
|
|
@@ -58,6 +58,7 @@ Returns: `MockInterceptor` corresponding to the input options.
|
|
|
58
58
|
* **body** `string | RegExp | (body: string) => boolean` - (optional) - a matcher for the HTTP request body.
|
|
59
59
|
* **headers** `Record<string, string | RegExp | (body: string) => boolean`> - (optional) - a matcher for the HTTP request headers. To be intercepted, a request must match all defined headers. Extra headers not defined here may (or may not) be included in the request and do not affect the interception in any way.
|
|
60
60
|
* **query** `Record<string, any> | null` - (optional) - a matcher for the HTTP request query string params. Only applies when a `string` was provided for `MockPoolInterceptOptions.path`.
|
|
61
|
+
* **ignoreTrailingSlash** `boolean` - (optional) - set to `true` if the matcher should also match by ignoring potential trailing slashes in `MockPoolInterceptOptions.path`.
|
|
61
62
|
|
|
62
63
|
### Return: `MockInterceptor`
|
|
63
64
|
|
|
@@ -81,7 +82,7 @@ By default, `reply` and `replyWithError` define the behaviour for the first matc
|
|
|
81
82
|
|
|
82
83
|
### Return: `MockScope`
|
|
83
84
|
|
|
84
|
-
A `MockScope` is associated with a single `MockInterceptor`. With this, we can configure the default behaviour of
|
|
85
|
+
A `MockScope` is associated with a single `MockInterceptor`. With this, we can configure the default behaviour of an intercepted reply.
|
|
85
86
|
|
|
86
87
|
* **delay** `(waitInMs: number) => MockScope` - delay the associated reply by a set amount in ms.
|
|
87
88
|
* **persist** `() => MockScope` - any matching request will always reply with the defined response indefinitely.
|
|
@@ -511,11 +512,11 @@ await mockPool.close()
|
|
|
511
512
|
|
|
512
513
|
### `MockPool.dispatch(options, handlers)`
|
|
513
514
|
|
|
514
|
-
Implements [`Dispatcher.dispatch(options, handlers)`](Dispatcher.md#dispatcherdispatchoptions-handler).
|
|
515
|
+
Implements [`Dispatcher.dispatch(options, handlers)`](/docs/docs/api/Dispatcher.md#dispatcherdispatchoptions-handler).
|
|
515
516
|
|
|
516
517
|
### `MockPool.request(options[, callback])`
|
|
517
518
|
|
|
518
|
-
See [`Dispatcher.request(options [, callback])`](Dispatcher.md#dispatcherrequestoptions-callback).
|
|
519
|
+
See [`Dispatcher.request(options [, callback])`](/docs/docs/api/Dispatcher.md#dispatcherrequestoptions-callback).
|
|
519
520
|
|
|
520
521
|
#### Example - MockPool request
|
|
521
522
|
|