@openclaw/discord 2026.6.8 → 2026.6.9
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/dist/action-runtime-api.js +1 -1
- package/dist/{api-BmXlcjYW.js → api-4GFMoWu0.js} +4 -1
- package/dist/api.js +15 -15
- package/dist/{approval-handler.runtime-B3gyUd-L.js → approval-handler.runtime-20qhf0uH.js} +3 -3
- package/dist/{audit-BlfewK04.js → audit-CCffO6o7.js} +3 -3
- package/dist/{channel-actions-BnPHwCZ_.js → channel-actions-DDT9kJXw.js} +12 -6
- package/dist/{channel-actions.runtime-DX5iW6ut.js → channel-actions.runtime-CfMUtfAv.js} +5 -5
- package/dist/{channel-b0hY1EJw.js → channel-fEBKaO2Q.js} +28 -20
- package/dist/channel-plugin-api.js +1 -1
- package/dist/{channel.setup-By5cfELZ.js → channel.setup-DRJiHe7I.js} +2 -3
- package/dist/{components-D-CYw0-b.js → components-CXEorOhu.js} +1 -1
- package/dist/{conversation-identity-CKzQAqFF.js → conversation-identity-BvlP-djn.js} +2 -2
- package/dist/{directory-config-Bu7FYOsl.js → directory-config-Bg8bulWB.js} +1 -1
- package/dist/directory-contract-api.js +1 -1
- package/dist/{directory-live-LjENjK6L.js → directory-live-BAJw6kS2.js} +2 -2
- package/dist/{handle-action.guild-admin-BS2qnhXF.js → handle-action.guild-admin-RxnJFLgU.js} +14 -9
- package/dist/index.js +1 -1
- package/dist/{manager.runtime-BU1vkOeO.js → manager.runtime-Bh5X0o8P.js} +12 -4
- package/dist/{message-handler-CQVkXHMN.js → message-handler-Dg3DRD0e.js} +6 -6
- package/dist/{message-handler.preflight-DAnLOeDA.js → message-handler.preflight-CcexCYuy.js} +10 -10
- package/dist/{message-handler.process-Dp5NQT05.js → message-handler.process-CN8bVDxj.js} +37 -45
- package/dist/{message-utils-Bx993JLN.js → message-utils-ConlUKCU.js} +1 -1
- package/dist/{outbound-adapter-CmN7ao1t.js → outbound-adapter-XuMiR77A.js} +6 -6
- package/dist/{pluralkit-SYmlmerw.js → pluralkit-o6s-EP9g.js} +4 -2
- package/dist/{probe-2MdGjUAf.js → probe-CxnDikWm.js} +5 -2
- package/dist/{probe.runtime-J927koKG.js → probe.runtime-DLFkSDVB.js} +1 -1
- package/dist/{provider-DrScDA1p.js → provider-4VG5CvC-.js} +98 -69
- package/dist/{provider-session.runtime-DXTzSYOJ.js → provider-session.runtime-ChdHgGCo.js} +3 -3
- package/dist/provider.runtime-B4QuZl_o.js +2 -0
- package/dist/{resolve-allowlist-common-B3p7cT8F.js → resolve-allowlist-common-BDpBb8dF.js} +1 -1
- package/dist/{resolve-channels-Rautpk8n.js → resolve-channels-DpM2v0Am.js} +3 -3
- package/dist/{resolve-users-Bw7vvtsi.js → resolve-users-Dl6np4pH.js} +3 -3
- package/dist/{runtime-C80YEJ7Z.js → runtime-CkdyyjTb.js} +24 -10
- package/dist/runtime-api.actions.js +2 -2
- package/dist/runtime-api.js +19 -19
- package/dist/runtime-api.lookup.js +5 -5
- package/dist/runtime-api.monitor-9pShEBWs.js +6 -0
- package/dist/runtime-api.monitor.js +4 -4
- package/dist/runtime-api.send.js +5 -5
- package/dist/runtime-api.threads.js +3 -3
- package/dist/{send-zGsXF-up.js → send-CIhsDMqs.js} +8 -5
- package/dist/{send.components-ktzrUTkt.js → send.components-DxoGyTkn.js} +4 -4
- package/dist/{send.outbound-C8oC51um.js → send.outbound-DaUyYuZa.js} +53 -10
- package/dist/{send.receipt-DsQWEQ2O.js → send.receipt-De_2iEWU.js} +2 -24
- package/dist/{send.shared-Dvo2ZCVG.js → send.shared-gtgBGDuV.js} +13 -5
- package/dist/session-binding-contract-api.js +1 -1
- package/dist/setup-plugin-api.js +1 -1
- package/dist/{subagent-hooks-kjrWDeDg.js → subagent-hooks-DPCipg2M.js} +2 -2
- package/dist/subagent-hooks-api.js +1 -1
- package/dist/{system-events-CvU3Aduf.js → system-events-4vmuyaBF.js} +1 -1
- package/dist/{target-resolver-DMPTzuo7.js → target-resolver-CVrOhQyC.js} +2 -2
- package/dist/targets-CRGgRKs7.js +3 -0
- package/dist/{thread-bindings-DO32M2kW.js → thread-bindings-D2SBj6F8.js} +4 -4
- package/dist/{thread-bindings.discord-api-304M1PMr.js → thread-bindings.discord-api-C7STOVXm.js} +4 -4
- package/dist/{thread-bindings.manager-C9YT7wF2.js → thread-bindings.manager-CN4MIGoJ.js} +3 -3
- package/dist/{transcripts-source-Chy2OrO_.js → transcripts-source-39NBdNeE.js} +1 -1
- package/dist/transcripts-source-api.js +1 -1
- package/dist/{typing-BaivbXIG.js → typing-CxIYS3vQ.js} +1 -1
- package/node_modules/undici/README.md +59 -18
- package/node_modules/undici/docs/docs/GettingStarted.md +278 -0
- package/node_modules/undici/docs/docs/api/Agent.md +3 -0
- package/node_modules/undici/docs/docs/api/BalancedPool.md +1 -1
- package/node_modules/undici/docs/docs/api/Client.md +44 -5
- package/node_modules/undici/docs/docs/api/Connector.md +1 -0
- package/node_modules/undici/docs/docs/api/Cookies.md +28 -1
- package/node_modules/undici/docs/docs/api/Dispatcher.md +22 -5
- package/node_modules/undici/docs/docs/api/EnvHttpProxyAgent.md +6 -9
- package/node_modules/undici/docs/docs/api/Errors.md +12 -0
- package/node_modules/undici/docs/docs/api/EventSource.md +50 -3
- package/node_modules/undici/docs/docs/api/Fetch.md +5 -3
- package/node_modules/undici/docs/docs/api/H2CClient.md +3 -3
- package/node_modules/undici/docs/docs/api/MockAgent.md +1 -1
- package/node_modules/undici/docs/docs/api/MockCallHistory.md +1 -1
- package/node_modules/undici/docs/docs/api/Pool.md +4 -1
- package/node_modules/undici/docs/docs/api/RedirectHandler.md +4 -1
- package/node_modules/undici/docs/docs/api/RetryAgent.md +3 -3
- package/node_modules/undici/docs/docs/api/RetryHandler.md +6 -6
- package/node_modules/undici/docs/docs/api/RoundRobinPool.md +1 -1
- package/node_modules/undici/docs/docs/api/SnapshotAgent.md +3 -3
- package/node_modules/undici/docs/docs/api/Socks5ProxyAgent.md +1 -0
- package/node_modules/undici/docs/docs/api/api-lifecycle.md +4 -4
- package/node_modules/undici/lib/core/connect.js +29 -4
- package/node_modules/undici/lib/core/util.js +8 -6
- package/node_modules/undici/lib/dispatcher/client-h1.js +69 -2
- package/node_modules/undici/lib/dispatcher/client-h2.js +160 -37
- package/node_modules/undici/lib/dispatcher/client.js +36 -7
- package/node_modules/undici/lib/dispatcher/dispatcher-base.js +1 -0
- package/node_modules/undici/lib/dispatcher/proxy-agent.js +2 -1
- package/node_modules/undici/lib/dispatcher/socks5-proxy-agent.js +4 -2
- package/node_modules/undici/lib/handler/redirect-handler.js +36 -11
- package/node_modules/undici/lib/interceptor/dns.js +4 -0
- package/node_modules/undici/lib/interceptor/redirect.js +3 -3
- package/node_modules/undici/lib/mock/mock-call-history.js +1 -1
- package/node_modules/undici/lib/mock/snapshot-agent.js +9 -1
- package/node_modules/undici/lib/util/cache.js +8 -2
- package/node_modules/undici/lib/web/cookies/parse.js +17 -25
- package/node_modules/undici/lib/web/eventsource/eventsource.js +7 -18
- package/node_modules/undici/lib/web/eventsource/util.js +32 -1
- package/node_modules/undici/lib/web/fetch/body.js +43 -0
- package/node_modules/undici/lib/web/fetch/index.js +17 -3
- package/node_modules/undici/lib/web/fetch/request.js +33 -3
- package/node_modules/undici/lib/web/websocket/receiver.js +20 -3
- package/node_modules/undici/lib/web/websocket/stream/websocketstream.js +8 -1
- package/node_modules/undici/lib/web/websocket/websocket.js +3 -1
- package/node_modules/undici/package.json +1 -1
- package/node_modules/undici/types/client.d.ts +5 -0
- package/node_modules/undici/types/connector.d.ts +1 -0
- package/node_modules/undici/types/fetch.d.ts +5 -1
- package/node_modules/undici/types/interceptors.d.ts +1 -1
- package/npm-shrinkwrap.json +7 -7
- package/package.json +5 -5
- package/dist/provider.runtime-nb-6cRoy.js +0 -2
- package/dist/runtime-api.monitor-D8KNDAd5.js +0 -6
- package/dist/targets-CKaNidbk.js +0 -3
|
@@ -211,6 +211,7 @@ Returns: `Boolean` - `false` if dispatcher is busy and further dispatch calls wo
|
|
|
211
211
|
* **onResponseData** `(controller: DispatchController, chunk: Buffer) => void` - Invoked when response payload data is received. Not required for `upgrade` requests.
|
|
212
212
|
* **onResponseEnd** `(controller: DispatchController, trailers: Record<string, string | string[]>) => void` - Invoked when response payload and trailers have been received and the request has completed. Not required for `upgrade` requests.
|
|
213
213
|
* **onResponseError** `(controller: DispatchController, error: Error) => void` - Invoked when an error has occurred. May not throw.
|
|
214
|
+
* **onBodySent** `(chunk: Buffer) => void` (optional) - Invoked when a chunk of the request body is sent.
|
|
214
215
|
|
|
215
216
|
#### Migration from legacy handler API
|
|
216
217
|
|
|
@@ -688,7 +689,7 @@ return null
|
|
|
688
689
|
|
|
689
690
|
A faster version of `Dispatcher.request`. This method expects the second argument `factory` to return a [`stream.Writable`](https://nodejs.org/api/stream.html#stream_class_stream_writable) stream which the response will be written to. This improves performance by avoiding creating an intermediate [`stream.Readable`](https://nodejs.org/api/stream.html#stream_readable_streams) stream when the user expects to directly pipe the response body to a [`stream.Writable`](https://nodejs.org/api/stream.html#stream_class_stream_writable) stream.
|
|
690
691
|
|
|
691
|
-
As demonstrated in [Example 1 - Basic GET stream request](/docs/docs/api/Dispatcher.md#example-1-basic-get-stream-request), it is recommended to use the `option.opaque` property to avoid creating a closure for the `factory` method. This pattern works well with Node.js Web Frameworks such as [Fastify](https://fastify.io). See [Example 2 - Stream to Fastify Response](/docs/docs/api/
|
|
692
|
+
As demonstrated in [Example 1 - Basic GET stream request](/docs/docs/api/Dispatcher.md#example-1-basic-get-stream-request), it is recommended to use the `option.opaque` property to avoid creating a closure for the `factory` method. This pattern works well with Node.js Web Frameworks such as [Fastify](https://fastify.io). See [Example 2 - Stream to Fastify Response](/docs/docs/api/Dispatcher.md#example-2-stream-to-fastify-response) for more details.
|
|
692
693
|
|
|
693
694
|
Arguments:
|
|
694
695
|
|
|
@@ -991,6 +992,13 @@ The `redirect` interceptor allows you to customize the way your dispatcher handl
|
|
|
991
992
|
|
|
992
993
|
It accepts the same arguments as the [`RedirectHandler` constructor](/docs/docs/api/RedirectHandler.md).
|
|
993
994
|
|
|
995
|
+
Options:
|
|
996
|
+
|
|
997
|
+
- **maxRedirections** `number` - Maximum number of redirections allowed.
|
|
998
|
+
- **throwOnMaxRedirect** `boolean` - Throw when the maximum number of redirections is reached.
|
|
999
|
+
- **stripHeadersOnRedirect** `string[]` - Header names to remove from all redirected requests.
|
|
1000
|
+
- **stripHeadersOnCrossOriginRedirect** `string[]` - Header names to remove from cross-origin redirected requests.
|
|
1001
|
+
|
|
994
1002
|
**Example - Basic Redirect Interceptor**
|
|
995
1003
|
|
|
996
1004
|
```js
|
|
@@ -1009,7 +1017,7 @@ The `retry` interceptor allows you to customize the way your dispatcher handles
|
|
|
1009
1017
|
|
|
1010
1018
|
It accepts the same arguments as the [`RetryHandler` constructor](/docs/docs/api/RetryHandler.md).
|
|
1011
1019
|
|
|
1012
|
-
**Example - Basic
|
|
1020
|
+
**Example - Basic Retry Interceptor**
|
|
1013
1021
|
|
|
1014
1022
|
```js
|
|
1015
1023
|
const { Client, interceptors } = require("undici");
|
|
@@ -1105,7 +1113,7 @@ It represents a storage object for resolved DNS records.
|
|
|
1105
1113
|
**Example - Basic DNS Interceptor**
|
|
1106
1114
|
|
|
1107
1115
|
```js
|
|
1108
|
-
const {
|
|
1116
|
+
const { Agent, interceptors } = require("undici");
|
|
1109
1117
|
const { dns } = interceptors;
|
|
1110
1118
|
|
|
1111
1119
|
const client = new Agent().compose([
|
|
@@ -1121,7 +1129,7 @@ const response = await client.request({
|
|
|
1121
1129
|
**Example - DNS Interceptor and LRU cache as a storage**
|
|
1122
1130
|
|
|
1123
1131
|
```js
|
|
1124
|
-
const {
|
|
1132
|
+
const { Agent, interceptors } = require("undici");
|
|
1125
1133
|
const QuickLRU = require("quick-lru");
|
|
1126
1134
|
const { dns } = interceptors;
|
|
1127
1135
|
|
|
@@ -1368,14 +1376,23 @@ When using the array header format (`string[]`), Undici processes only indexed e
|
|
|
1368
1376
|
|
|
1369
1377
|
Response headers will derive a `host` from the `url` of the [Client](/docs/docs/api/Client.md#class-client) instance if no `host` header was previously specified.
|
|
1370
1378
|
|
|
1379
|
+
### Request header validation
|
|
1380
|
+
|
|
1381
|
+
Request headers that are managed by the HTTP connection are handled differently from ordinary headers:
|
|
1382
|
+
|
|
1383
|
+
* `transfer-encoding`, `keep-alive`, and `upgrade` cannot be set through `options.headers`; Undici throws an `InvalidArgumentError`.
|
|
1384
|
+
* `expect` is not supported; Undici throws a `NotSupportedError`.
|
|
1385
|
+
* `connection` must be a string containing comma-separated valid HTTP tokens. Undici rejects malformed tokens with `InvalidArgumentError: invalid connection header` and uses the `close` token to request connection reset behavior.
|
|
1386
|
+
* `host` and `content-length` are tracked separately from the raw header list. Duplicate `host` or `content-length` values are rejected, and `content-length` must contain only decimal digits.
|
|
1387
|
+
|
|
1371
1388
|
### Example 1 - Object
|
|
1372
1389
|
|
|
1373
1390
|
```js
|
|
1374
1391
|
{
|
|
1375
1392
|
'content-length': '123',
|
|
1376
1393
|
'content-type': 'text/plain',
|
|
1377
|
-
connection: 'keep-alive',
|
|
1378
1394
|
host: 'mysite.com',
|
|
1395
|
+
'accept-language': 'en',
|
|
1379
1396
|
accept: '*/*'
|
|
1380
1397
|
}
|
|
1381
1398
|
```
|
|
@@ -52,11 +52,11 @@ import { setGlobalDispatcher, fetch, EnvHttpProxyAgent } from 'undici'
|
|
|
52
52
|
const envHttpProxyAgent = new EnvHttpProxyAgent()
|
|
53
53
|
setGlobalDispatcher(envHttpProxyAgent)
|
|
54
54
|
|
|
55
|
-
const
|
|
55
|
+
const response = await fetch('http://localhost:3000/foo')
|
|
56
56
|
|
|
57
|
-
console.log('response received', status) // response received 200
|
|
57
|
+
console.log('response received', response.status) // response received 200
|
|
58
58
|
|
|
59
|
-
const data = await json() // data { foo: "bar" }
|
|
59
|
+
const data = await response.json() // data { foo: "bar" }
|
|
60
60
|
```
|
|
61
61
|
|
|
62
62
|
#### Example - Basic Proxy Request with global agent dispatcher
|
|
@@ -102,14 +102,11 @@ import { EnvHttpProxyAgent, fetch } from 'undici'
|
|
|
102
102
|
|
|
103
103
|
const envHttpProxyAgent = new EnvHttpProxyAgent()
|
|
104
104
|
|
|
105
|
-
const {
|
|
106
|
-
status,
|
|
107
|
-
json
|
|
108
|
-
} = await fetch('http://localhost:3000/foo', { dispatcher: envHttpProxyAgent })
|
|
105
|
+
const response = await fetch('http://localhost:3000/foo', { dispatcher: envHttpProxyAgent })
|
|
109
106
|
|
|
110
|
-
console.log('response received', status) // response received 200
|
|
107
|
+
console.log('response received', response.status) // response received 200
|
|
111
108
|
|
|
112
|
-
const data = await json() // data { foo: "bar" }
|
|
109
|
+
const data = await response.json() // data { foo: "bar" }
|
|
113
110
|
```
|
|
114
111
|
|
|
115
112
|
## Instance Methods
|
|
@@ -27,8 +27,20 @@ import { errors } from 'undici'
|
|
|
27
27
|
| `ResponseExceededMaxSizeError` | `UND_ERR_RES_EXCEEDED_MAX_SIZE` | response body exceed the max size allowed |
|
|
28
28
|
| `SecureProxyConnectionError` | `UND_ERR_PRX_TLS` | tls connection to a proxy failed |
|
|
29
29
|
| `MessageSizeExceededError` | `UND_ERR_WS_MESSAGE_SIZE_EXCEEDED` | WebSocket decompressed message exceeded the maximum allowed size |
|
|
30
|
+
| `AbortError` | `UND_ERR_ABORT` | the operation was aborted (base class of `RequestAbortedError`). |
|
|
31
|
+
| `RequestRetryError` | `UND_ERR_REQ_RETRY` | request failed and could not be retried; carries `statusCode`, `headers` and `data`. |
|
|
32
|
+
| `ResponseError` | `UND_ERR_RESPONSE` | response returned an error status code; carries `statusCode`, `headers` and `body`. |
|
|
33
|
+
| `MaxOriginsReachedError` | `UND_ERR_MAX_ORIGINS_REACHED` | the maximum number of allowed origins has been reached. |
|
|
34
|
+
| `BalancedPoolMissingUpstreamError` | `UND_ERR_BPL_MISSING_UPSTREAM` | no upstream has been added to the `BalancedPool`. |
|
|
35
|
+
| `Socks5ProxyError` | `UND_ERR_SOCKS5` | an error occurred during SOCKS5 proxy negotiation. |
|
|
36
|
+
| `HTTPParserError` | `HPE_*` | an error occurred while parsing the HTTP response (extends `Error`, not `UndiciError`). |
|
|
30
37
|
|
|
31
38
|
Be aware of the possible difference between the global dispatcher version and the actual undici version you might be using. We recommend to avoid the check `instanceof errors.UndiciError` and seek for the `error.code === '<error_code>'` instead to avoid inconsistencies.
|
|
39
|
+
|
|
40
|
+
### `ConnectTimeoutError`
|
|
41
|
+
|
|
42
|
+
When `autoSelectFamily` is enabled and every attempted address fails with a timeout, Node raises an `AggregateError`. Undici surfaces these multi-address timeouts as `ConnectTimeoutError` (so the error shape is the same regardless of whether Node's family-attempt timer or undici's `connectTimeout` wins the race); the original `AggregateError` is preserved on `error.cause`.
|
|
43
|
+
|
|
32
44
|
### `SocketError`
|
|
33
45
|
|
|
34
46
|
The `SocketError` has a `.socket` property which holds socket metadata:
|
|
@@ -7,7 +7,7 @@ for [Server-Sent Events](https://developer.mozilla.org/en-US/docs/Web/API/Server
|
|
|
7
7
|
|
|
8
8
|
## Instantiating EventSource
|
|
9
9
|
|
|
10
|
-
Undici exports
|
|
10
|
+
Undici exports an EventSource class. You can instantiate the EventSource as
|
|
11
11
|
follows:
|
|
12
12
|
|
|
13
13
|
```mjs
|
|
@@ -19,9 +19,57 @@ eventSource.onmessage = (event) => {
|
|
|
19
19
|
}
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
+
## Receiving events from a server
|
|
23
|
+
|
|
24
|
+
EventSource connects to an HTTP endpoint that responds with a `text/event-stream`
|
|
25
|
+
content type. The connection stays open and receives events as the server writes
|
|
26
|
+
them.
|
|
27
|
+
|
|
28
|
+
```mjs
|
|
29
|
+
import { createServer } from 'node:http'
|
|
30
|
+
import { EventSource } from 'undici'
|
|
31
|
+
|
|
32
|
+
const server = createServer((request, response) => {
|
|
33
|
+
response.writeHead(200, {
|
|
34
|
+
'content-type': 'text/event-stream',
|
|
35
|
+
'cache-control': 'no-cache',
|
|
36
|
+
connection: 'keep-alive'
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
response.write('event: ping\n')
|
|
40
|
+
response.write('data: connected\n\n')
|
|
41
|
+
|
|
42
|
+
const interval = setInterval(() => {
|
|
43
|
+
response.write(`data: ${Date.now()}\n\n`)
|
|
44
|
+
}, 1000)
|
|
45
|
+
|
|
46
|
+
request.on('close', () => clearInterval(interval))
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
server.listen(3000, () => {
|
|
50
|
+
const eventSource = new EventSource('http://localhost:3000')
|
|
51
|
+
|
|
52
|
+
eventSource.addEventListener('ping', (event) => {
|
|
53
|
+
console.log('ping:', event.data)
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
eventSource.onmessage = (event) => {
|
|
57
|
+
console.log('message:', event.data)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
eventSource.onerror = () => {
|
|
61
|
+
eventSource.close()
|
|
62
|
+
server.close()
|
|
63
|
+
}
|
|
64
|
+
})
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
The `message` event receives events without an explicit `event:` field. Use
|
|
68
|
+
`addEventListener()` to subscribe to named events.
|
|
69
|
+
|
|
22
70
|
## Using a custom Dispatcher
|
|
23
71
|
|
|
24
|
-
|
|
72
|
+
Undici allows you to set your own Dispatcher in the EventSource constructor.
|
|
25
73
|
|
|
26
74
|
An example which allows you to modify the request headers is:
|
|
27
75
|
|
|
@@ -38,7 +86,6 @@ class CustomHeaderAgent extends Agent {
|
|
|
38
86
|
const eventSource = new EventSource('http://localhost:3000', {
|
|
39
87
|
dispatcher: new CustomHeaderAgent()
|
|
40
88
|
})
|
|
41
|
-
|
|
42
89
|
```
|
|
43
90
|
|
|
44
91
|
More information about the EventSource API can be found on
|
|
@@ -15,7 +15,7 @@ same implementation. Use the built-in global `FormData` with the built-in
|
|
|
15
15
|
global `fetch()`, and use `undici`'s `FormData` with `undici.fetch()`.
|
|
16
16
|
|
|
17
17
|
If you want the installed `undici` package to provide the globals, call
|
|
18
|
-
[`install()`](/docs/api/GlobalInstallation.md) so `fetch`, `Headers`,
|
|
18
|
+
[`install()`](/docs/docs/api/GlobalInstallation.md) so `fetch`, `Headers`,
|
|
19
19
|
`Response`, `Request`, and `FormData` are installed together as a matching set.
|
|
20
20
|
|
|
21
21
|
## Response
|
|
@@ -26,7 +26,7 @@ This API is implemented as per the standard, you can find documentation on [MDN]
|
|
|
26
26
|
|
|
27
27
|
This API is implemented as per the standard, you can find documentation on [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Request)
|
|
28
28
|
|
|
29
|
-
##
|
|
29
|
+
## Headers
|
|
30
30
|
|
|
31
31
|
This API is implemented as per the standard, you can find documentation on [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Headers)
|
|
32
32
|
|
|
@@ -41,7 +41,9 @@ This API is implemented as per the standard, you can find documentation on [MDN]
|
|
|
41
41
|
- [`.json()`](https://fetch.spec.whatwg.org/#dom-body-json)
|
|
42
42
|
- [`.text()`](https://fetch.spec.whatwg.org/#dom-body-text)
|
|
43
43
|
|
|
44
|
-
There is an ongoing discussion regarding
|
|
44
|
+
There is an ongoing discussion regarding `body.formData()` and its usefulness, performance, and security in server environments. Calling `body.formData()` causes undici to buffer and parse the entire body. Because multipart parsing has inherent security risks, `body.formData()` must only be called on responses from trusted servers.
|
|
45
|
+
|
|
46
|
+
For responses from untrusted or user-controlled servers, use a dedicated streaming 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), and apply application-specific limits.
|
|
45
47
|
|
|
46
48
|
These libraries can be interfaced with fetch with the following example code:
|
|
47
49
|
|
|
@@ -17,7 +17,7 @@ const server = createServer((req, res) => {
|
|
|
17
17
|
})
|
|
18
18
|
|
|
19
19
|
server.listen()
|
|
20
|
-
once(server, 'listening').then(() => {
|
|
20
|
+
once(server, 'listening').then(async () => {
|
|
21
21
|
const client = new H2CClient(`http://localhost:${server.address().port}/`)
|
|
22
22
|
|
|
23
23
|
const response = await client.request({ path: '/', method: 'GET' })
|
|
@@ -46,8 +46,8 @@ Returns: `H2CClient`
|
|
|
46
46
|
- **keepAliveTimeoutThreshold** `number | null` (optional) - Default: `2e3` - A number of milliseconds subtracted from server _keep-alive_ hints when overriding `keepAliveTimeout` to account for timing inaccuracies caused by e.g. transport latency. Defaults to 2 seconds.
|
|
47
47
|
- **maxHeaderSize** `number | null` (optional) - Default: `--max-http-header-size` or `16384` - The maximum length of request headers in bytes. Defaults to Node.js' --max-http-header-size or 16KiB.
|
|
48
48
|
- **maxResponseSize** `number | null` (optional) - Default: `-1` - The maximum length of response body in bytes. Set to `-1` to disable.
|
|
49
|
-
- **maxConcurrentStreams**: `number` - Default: `100`.
|
|
50
|
-
- **pipelining** `number | null` (optional) - Default to `maxConcurrentStreams` - The amount of concurrent requests sent over a single HTTP/2 session in accordance with [RFC-7540](https://httpwg.org/specs/rfc7540.html#StreamsLayer) Stream specification. Streams can be closed up by remote server at any time.
|
|
49
|
+
- **maxConcurrentStreams**: `number` - Default: `100`. The maximum number of concurrent HTTP/2 streams per session — also advertised to the server as `peerMaxConcurrentStreams` (the cap on streams the server may push back). The initial value is replaced by the server's `SETTINGS_MAX_CONCURRENT_STREAMS` whenever the server sends one, so a user-supplied value acts as a pre-`SETTINGS` default rather than a hard cap.
|
|
50
|
+
- **pipelining** `number | null` (optional) - Default to `maxConcurrentStreams` - The amount of concurrent requests sent over a single HTTP/2 session in accordance with [RFC-7540](https://httpwg.org/specs/rfc7540.html#StreamsLayer) Stream specification. Streams can be closed up by remote server at any time. Unlike on a regular [`Client`](/docs/docs/api/Client.md), `H2CClient` aliases `pipelining` to `maxConcurrentStreams` at construction time, so the two move together.
|
|
51
51
|
- **pingInterval**: `number` - Default: `60e3`. The time interval in milliseconds between PING frames sent to the server. Set to `0` to disable PING frames. This is only applicable for HTTP/2 connections.
|
|
52
52
|
- **connect** `ConnectOptions | null` (optional) - Default: `null`.
|
|
53
53
|
- **strictContentLength** `Boolean` (optional) - Default: `true` - Whether to treat request content length mismatches as errors. If true, an error is thrown when the request content-length header doesn't match the length of the request body. **Security Warning:** Disabling this option can expose your application to HTTP Request Smuggling attacks, where mismatched content-length headers cause servers and proxies to interpret request boundaries differently. This can lead to cache poisoning, credential hijacking, and bypassing security controls. Only disable this in controlled environments where you fully trust the request source.
|
|
@@ -165,7 +165,7 @@ Parameters :
|
|
|
165
165
|
|
|
166
166
|
- criteria : the first parameter. a function, regexp or object.
|
|
167
167
|
- function : filter MockCallHistoryLog when the function returns false
|
|
168
|
-
- regexp : filter MockCallHistoryLog when the regexp does not match on MockCallHistoryLog.toString() ([see](
|
|
168
|
+
- regexp : filter MockCallHistoryLog when the regexp does not match on MockCallHistoryLog.toString() ([see](/docs/docs/api/MockCallHistoryLog.md#to-string))
|
|
169
169
|
- object : an object with MockCallHistoryLog properties as keys to apply multiple filters. each values are a [filter parameter](/docs/docs/api/MockCallHistory.md#filter-parameter)
|
|
170
170
|
- options : the second parameter. an object.
|
|
171
171
|
- options.operator : `'AND'` or `'OR'` (default `'OR'`). Used only if criteria is an object. see below
|
|
@@ -21,6 +21,9 @@ Extends: [`ClientOptions`](/docs/docs/api/Client.md#parameter-clientoptions)
|
|
|
21
21
|
* **connections** `number | null` (optional) - Default: `null` - The number of `Client` instances to create. When set to `null`, the `Pool` instance will create an unlimited amount of `Client` instances.
|
|
22
22
|
* **clientTtl** `number | null` (optional) - Default: `null` - The amount of time before a `Client` instance is removed from the `Pool` and closed. When set to `null`, `Client` instances will not be removed or closed based on age.
|
|
23
23
|
|
|
24
|
+
> [!NOTE]
|
|
25
|
+
> `Pool` inherits all [`ClientOptions`](/docs/docs/api/Client.md#parameter-clientoptions), including `allowH2` (default `true`) and `maxConcurrentStreams` (default `100`). With the unlimited default of `connections`, `Pool` will open a new `Client` — and therefore a new TCP/TLS socket — per concurrent dispatch, which defeats HTTP/2 multiplexing on a shared session. To benefit from h2 multiplexing on a single session, cap `connections` (e.g. `connections: 1`) so that concurrent requests share a session up to `maxConcurrentStreams`.
|
|
26
|
+
|
|
24
27
|
## Instance Properties
|
|
25
28
|
|
|
26
29
|
### `Pool.closed`
|
|
@@ -33,7 +36,7 @@ Implements [Client.destroyed](/docs/docs/api/Client.md#clientdestroyed)
|
|
|
33
36
|
|
|
34
37
|
### `Pool.stats`
|
|
35
38
|
|
|
36
|
-
Returns [`PoolStats`](PoolStats.md) instance for this pool.
|
|
39
|
+
Returns [`PoolStats`](/docs/docs/api/PoolStats.md) instance for this pool.
|
|
37
40
|
|
|
38
41
|
## Instance Methods
|
|
39
42
|
|
|
@@ -8,7 +8,7 @@ Arguments:
|
|
|
8
8
|
|
|
9
9
|
- **dispatch** `function` - The dispatch function to be called after every retry.
|
|
10
10
|
- **maxRedirections** `number` - Maximum number of redirections allowed.
|
|
11
|
-
- **opts** `object` - Options for handling redirection.
|
|
11
|
+
- **opts** `object` - Options for handling redirection. Supports `throwOnMaxRedirect`, `stripHeadersOnRedirect`, and `stripHeadersOnCrossOriginRedirect`.
|
|
12
12
|
- **handler** `object` - An object containing handlers for different stages of the request lifecycle.
|
|
13
13
|
|
|
14
14
|
Returns: `RedirectHandler`
|
|
@@ -18,6 +18,9 @@ Returns: `RedirectHandler`
|
|
|
18
18
|
- **dispatch** `(options: Dispatch.DispatchOptions, handlers: Dispatch.DispatchHandler) => Promise<Dispatch.DispatchResponse>` (required) - Dispatch function to be called after every redirection.
|
|
19
19
|
- **maxRedirections** `number` (required) - Maximum number of redirections allowed.
|
|
20
20
|
- **opts** `object` (required) - Options for handling redirection.
|
|
21
|
+
- **throwOnMaxRedirect** `boolean` - Throw when the maximum number of redirections is reached.
|
|
22
|
+
- **stripHeadersOnRedirect** `string[]` - Header names to remove from all redirected requests.
|
|
23
|
+
- **stripHeadersOnCrossOriginRedirect** `string[]` - Header names to remove from cross-origin redirected requests.
|
|
21
24
|
- **handler** `object` (required) - Handlers for different stages of the request lifecycle.
|
|
22
25
|
|
|
23
26
|
### Properties
|
|
@@ -12,7 +12,7 @@ Arguments:
|
|
|
12
12
|
* **dispatcher** `undici.Dispatcher` (required) - the dispatcher to wrap
|
|
13
13
|
* **options** `RetryHandlerOptions` (optional) - the options
|
|
14
14
|
|
|
15
|
-
Returns: `
|
|
15
|
+
Returns: `RetryAgent`
|
|
16
16
|
|
|
17
17
|
### Parameter: `RetryHandlerOptions`
|
|
18
18
|
|
|
@@ -23,9 +23,9 @@ Returns: `ProxyAgent`
|
|
|
23
23
|
- **minTimeout** `number` (optional) - Minimum number of milliseconds to wait before retrying. Default: `500` (half a second)
|
|
24
24
|
- **timeoutFactor** `number` (optional) - Factor to multiply the timeout by for each retry attempt. Default: `2`
|
|
25
25
|
- **retryAfter** `boolean` (optional) - It enables automatic retry after the `Retry-After` header is received. Default: `true`
|
|
26
|
-
- **methods** `string[]` (optional) - Array of HTTP methods to retry. Default: `['GET', '
|
|
26
|
+
- **methods** `string[]` (optional) - Array of HTTP methods to retry. Default: `['GET', 'HEAD', 'OPTIONS', 'PUT', 'DELETE', 'TRACE']`
|
|
27
27
|
- **statusCodes** `number[]` (optional) - Array of HTTP status codes to retry. Default: `[429, 500, 502, 503, 504]`
|
|
28
|
-
- **errorCodes** `string[]` (optional) - Array of Error codes to retry. Default: `['ECONNRESET', 'ECONNREFUSED', 'ENOTFOUND', 'ENETDOWN','ENETUNREACH', 'EHOSTDOWN', 'UND_ERR_SOCKET']`
|
|
28
|
+
- **errorCodes** `string[]` (optional) - Array of Error codes to retry. Default: `['ECONNRESET', 'ECONNREFUSED', 'ENOTFOUND', 'ENETDOWN', 'ENETUNREACH', 'EHOSTDOWN', 'EHOSTUNREACH', 'EPIPE', 'UND_ERR_SOCKET']`
|
|
29
29
|
|
|
30
30
|
**`RetryContext`**
|
|
31
31
|
|
|
@@ -4,12 +4,12 @@ Extends: `undici.DispatcherHandlers`
|
|
|
4
4
|
|
|
5
5
|
A handler class that implements the retry logic for a request.
|
|
6
6
|
|
|
7
|
-
## `new RetryHandler(
|
|
7
|
+
## `new RetryHandler(opts, { dispatch, handler })`
|
|
8
8
|
|
|
9
9
|
Arguments:
|
|
10
10
|
|
|
11
|
-
- **
|
|
12
|
-
- **
|
|
11
|
+
- **opts** `Dispatch.DispatchOptions & { retryOptions?: RetryOptions }` (required) - An intersection of `Dispatcher.DispatchOptions` and an optional `RetryOptions` object.
|
|
12
|
+
- **{ dispatch, handler }** `RetryHandlers` (required) - Object containing the `dispatch` to be used on every retry, and `handler` for handling the `dispatch` lifecycle.
|
|
13
13
|
|
|
14
14
|
Returns: `retryHandler`
|
|
15
15
|
|
|
@@ -20,15 +20,15 @@ Extends: [`Dispatch.DispatchOptions`](/docs/docs/api/Dispatcher.md#parameter-dis
|
|
|
20
20
|
#### `RetryOptions`
|
|
21
21
|
|
|
22
22
|
- **throwOnError** `boolean` (optional) - Disable to prevent throwing error on last retry attept, useful if you need the body on errors from server or if you have custom error handler.
|
|
23
|
-
- **retry** `(err: Error, context: RetryContext, callback: (err?: Error | null) => void) =>
|
|
23
|
+
- **retry** `(err: Error, context: RetryContext, callback: (err?: Error | null) => void) => void` (optional) - Function to be called after every retry. It should pass error if no more retries should be performed.
|
|
24
24
|
- **maxRetries** `number` (optional) - Maximum number of retries. Default: `5`
|
|
25
25
|
- **maxTimeout** `number` (optional) - Maximum number of milliseconds to wait before retrying. Default: `30000` (30 seconds)
|
|
26
26
|
- **minTimeout** `number` (optional) - Minimum number of milliseconds to wait before retrying. Default: `500` (half a second)
|
|
27
27
|
- **timeoutFactor** `number` (optional) - Factor to multiply the timeout by for each retry attempt. Default: `2`
|
|
28
28
|
- **retryAfter** `boolean` (optional) - It enables automatic retry after the `Retry-After` header is received. Default: `true`
|
|
29
|
-
- **methods** `string[]` (optional) - Array of HTTP methods to retry. Default: `['GET', '
|
|
29
|
+
- **methods** `string[]` (optional) - Array of HTTP methods to retry. Default: `['GET', 'HEAD', 'OPTIONS', 'PUT', 'DELETE', 'TRACE']`
|
|
30
30
|
- **statusCodes** `number[]` (optional) - Array of HTTP status codes to retry. Default: `[429, 500, 502, 503, 504]`
|
|
31
|
-
- **errorCodes** `string[]` (optional) - Array of Error codes to retry. Default: `['ECONNRESET', 'ECONNREFUSED', 'ENOTFOUND', 'ENETDOWN','ENETUNREACH', 'EHOSTDOWN', 'UND_ERR_SOCKET']`
|
|
31
|
+
- **errorCodes** `string[]` (optional) - Array of Error codes to retry. Default: `['ECONNRESET', 'ECONNREFUSED', 'ENOTFOUND', 'ENETDOWN', 'ENETUNREACH', 'EHOSTDOWN', 'EHOSTUNREACH', 'EPIPE', 'UND_ERR_SOCKET']`
|
|
32
32
|
|
|
33
33
|
**`RetryContext`**
|
|
34
34
|
|
|
@@ -66,7 +66,7 @@ Implements [Client.destroyed](/docs/docs/api/Client.md#clientdestroyed)
|
|
|
66
66
|
|
|
67
67
|
### `RoundRobinPool.stats`
|
|
68
68
|
|
|
69
|
-
Returns [`PoolStats`](PoolStats.md) instance for this pool.
|
|
69
|
+
Returns [`PoolStats`](/docs/docs/api/PoolStats.md) instance for this pool.
|
|
70
70
|
|
|
71
71
|
## Instance Methods
|
|
72
72
|
|
|
@@ -634,6 +634,6 @@ SnapshotAgent provides similar functionality to nock but is specifically designe
|
|
|
634
634
|
|
|
635
635
|
## See Also
|
|
636
636
|
|
|
637
|
-
- [MockAgent](
|
|
638
|
-
- [MockCallHistory](
|
|
639
|
-
- [Testing Best Practices](
|
|
637
|
+
- [MockAgent](/docs/docs/api/MockAgent.md) - Manual mocking for more control
|
|
638
|
+
- [MockCallHistory](/docs/docs/api/MockCallHistory.md) - Inspecting request history
|
|
639
|
+
- [Testing Best Practices](/docs/docs/best-practices/writing-tests.md) - General testing guidance
|
|
@@ -22,6 +22,7 @@ Extends: [`PoolOptions`](/docs/docs/api/Pool.md#parameter-pooloptions)
|
|
|
22
22
|
* **password** `string` (optional) - SOCKS5 proxy password for authentication. Can also be provided in the proxy URL.
|
|
23
23
|
* **connect** `Function` (optional) - Custom connector function for the proxy connection.
|
|
24
24
|
* **proxyTls** `BuildOptions` (optional) - TLS options for the proxy connection (when using SOCKS5 over TLS).
|
|
25
|
+
* **requestTls** `BuildOptions` (optional) - TLS options applied to the HTTPS connection to the target server through the SOCKS5 tunnel. Use this to configure `ca`, `cert`, `key`, `rejectUnauthorized`, `servername`, etc. for the target HTTPS endpoint.
|
|
25
26
|
|
|
26
27
|
Examples:
|
|
27
28
|
|
|
@@ -58,9 +58,9 @@ stateDiagram-v2
|
|
|
58
58
|
|
|
59
59
|
### idle
|
|
60
60
|
|
|
61
|
-
The **idle** state is the initial state of a `Client` instance. While an `origin` is required for instantiating a `Client` instance, the underlying socket connection will not be established until a request is queued using [`Client.dispatch()`](/docs/docs/api/Client.md#clientdispatchoptions-handlers). By calling `Client.dispatch()` directly or using one of the multiple implementations ([`Client.connect()`](Client.md#clientconnectoptions-callback), [`Client.pipeline()`](Client.md#clientpipelineoptions-handler), [`Client.request()`](Client.md#clientrequestoptions-callback), [`Client.stream()`](Client.md#clientstreamoptions-factory-callback), and [`Client.upgrade()`](/docs/docs/api/Client.md#clientupgradeoptions-callback)), the `Client` instance will transition from **idle** to [**pending**](/docs/docs/api/Client.md#pending) and then most likely directly to [**processing**](/docs/docs/api/Client.md#processing).
|
|
61
|
+
The **idle** state is the initial state of a `Client` instance. While an `origin` is required for instantiating a `Client` instance, the underlying socket connection will not be established until a request is queued using [`Client.dispatch()`](/docs/docs/api/Client.md#clientdispatchoptions-handlers). By calling `Client.dispatch()` directly or using one of the multiple implementations ([`Client.connect()`](/docs/docs/api/Client.md#clientconnectoptions-callback), [`Client.pipeline()`](/docs/docs/api/Client.md#clientpipelineoptions-handler), [`Client.request()`](/docs/docs/api/Client.md#clientrequestoptions-callback), [`Client.stream()`](/docs/docs/api/Client.md#clientstreamoptions-factory-callback), and [`Client.upgrade()`](/docs/docs/api/Client.md#clientupgradeoptions-callback)), the `Client` instance will transition from **idle** to [**pending**](/docs/docs/api/Client.md#pending) and then most likely directly to [**processing**](/docs/docs/api/Client.md#processing).
|
|
62
62
|
|
|
63
|
-
Calling [`Client.close()`](/docs/docs/api/Client.md#clientclosecallback) or [`Client.destroy()`](Client.md#clientdestroyerror-callback) transitions directly to the [**destroyed**](/docs/docs/api/Client.md#destroyed) state since the `Client` instance will have no queued requests in this state.
|
|
63
|
+
Calling [`Client.close()`](/docs/docs/api/Client.md#clientclosecallback) or [`Client.destroy()`](/docs/docs/api/Client.md#clientdestroyerror-callback) transitions directly to the [**destroyed**](/docs/docs/api/Client.md#destroyed) state since the `Client` instance will have no queued requests in this state.
|
|
64
64
|
|
|
65
65
|
### pending
|
|
66
66
|
|
|
@@ -72,11 +72,11 @@ Calling [`Client.destroy()`](/docs/docs/api/Client.md#clientdestroyerror-callbac
|
|
|
72
72
|
|
|
73
73
|
### processing
|
|
74
74
|
|
|
75
|
-
The **processing** state is a state machine within itself. It initializes to the [**processing.running**](/docs/docs/api/Client.md#running) state. The [`Client.dispatch()`](/docs/docs/api/Client.md#clientdispatchoptions-handlers), [`Client.close()`](Client.md#clientclosecallback), and [`Client.destroy()`](Client.md#clientdestroyerror-callback) can be called at any time while the `Client` is in this state. `Client.dispatch()` will add more requests to the queue while existing requests continue to be processed. `Client.close()` will transition to the [**processing.closing**](/docs/docs/api/Client.md#closing) state. And `Client.destroy()` will transition to [**destroyed**](/docs/docs/api/Client.md#destroyed).
|
|
75
|
+
The **processing** state is a state machine within itself. It initializes to the [**processing.running**](/docs/docs/api/Client.md#running) state. The [`Client.dispatch()`](/docs/docs/api/Client.md#clientdispatchoptions-handlers), [`Client.close()`](/docs/docs/api/Client.md#clientclosecallback), and [`Client.destroy()`](/docs/docs/api/Client.md#clientdestroyerror-callback) can be called at any time while the `Client` is in this state. `Client.dispatch()` will add more requests to the queue while existing requests continue to be processed. `Client.close()` will transition to the [**processing.closing**](/docs/docs/api/Client.md#closing) state. And `Client.destroy()` will transition to [**destroyed**](/docs/docs/api/Client.md#destroyed).
|
|
76
76
|
|
|
77
77
|
#### running
|
|
78
78
|
|
|
79
|
-
In the **processing.running** sub-state, queued requests are being processed in a FIFO order. If a request body requires draining, the *needDrain* event transitions to the [**processing.busy**](/docs/docs/api/Client.md#busy) sub-state. The *close* event transitions the Client to the [**process.closing**](/docs/docs/api/Client.md#closing) sub-state. If all queued requests are processed and neither [`Client.close()`](/docs/docs/api/Client.md#clientclosecallback) nor [`Client.destroy()`](Client.md#clientdestroyerror-callback) are called, then the [**processing**](/docs/docs/api/Client.md#processing) machine will trigger a *keepalive* event transitioning the `Client` back to the [**pending**](/docs/docs/api/Client.md#pending) state. During this time, the `Client` is waiting for the socket connection to timeout, and once it does, it triggers the *timeout* event and transitions to the [**idle**](/docs/docs/api/Client.md#idle) state.
|
|
79
|
+
In the **processing.running** sub-state, queued requests are being processed in a FIFO order. If a request body requires draining, the *needDrain* event transitions to the [**processing.busy**](/docs/docs/api/Client.md#busy) sub-state. The *close* event transitions the Client to the [**process.closing**](/docs/docs/api/Client.md#closing) sub-state. If all queued requests are processed and neither [`Client.close()`](/docs/docs/api/Client.md#clientclosecallback) nor [`Client.destroy()`](/docs/docs/api/Client.md#clientdestroyerror-callback) are called, then the [**processing**](/docs/docs/api/Client.md#processing) machine will trigger a *keepalive* event transitioning the `Client` back to the [**pending**](/docs/docs/api/Client.md#pending) state. During this time, the `Client` is waiting for the socket connection to timeout, and once it does, it triggers the *timeout* event and transitions to the [**idle**](/docs/docs/api/Client.md#idle) state.
|
|
80
80
|
|
|
81
81
|
#### busy
|
|
82
82
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const net = require('node:net')
|
|
4
4
|
const assert = require('node:assert')
|
|
5
5
|
const util = require('./util')
|
|
6
|
-
const { InvalidArgumentError } = require('./errors')
|
|
6
|
+
const { InvalidArgumentError, ConnectTimeoutError } = require('./errors')
|
|
7
7
|
|
|
8
8
|
let tls // include tls conditionally since it is not always available
|
|
9
9
|
|
|
@@ -59,7 +59,7 @@ const SessionCache = class WeakSessionCache {
|
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
function buildConnector ({ allowH2, useH2c, maxCachedSessions, socketPath, timeout, session: customSession, ...opts }) {
|
|
62
|
+
function buildConnector ({ allowH2, preferH2, useH2c, maxCachedSessions, socketPath, timeout, session: customSession, ...opts }) {
|
|
63
63
|
if (maxCachedSessions != null && (!Number.isInteger(maxCachedSessions) || maxCachedSessions < 0)) {
|
|
64
64
|
throw new InvalidArgumentError('maxCachedSessions must be a positive integer or zero')
|
|
65
65
|
}
|
|
@@ -89,7 +89,7 @@ function buildConnector ({ allowH2, useH2c, maxCachedSessions, socketPath, timeo
|
|
|
89
89
|
servername,
|
|
90
90
|
session,
|
|
91
91
|
localAddress,
|
|
92
|
-
ALPNProtocols: allowH2 ? ['http/1.1', 'h2'] : ['http/1.1'],
|
|
92
|
+
ALPNProtocols: allowH2 ? (preferH2 ? ['h2', 'http/1.1'] : ['http/1.1', 'h2']) : ['http/1.1'],
|
|
93
93
|
socket: httpSocket, // upgrade socket connection
|
|
94
94
|
port,
|
|
95
95
|
host: hostname
|
|
@@ -142,7 +142,7 @@ function buildConnector ({ allowH2, useH2c, maxCachedSessions, socketPath, timeo
|
|
|
142
142
|
if (callback) {
|
|
143
143
|
const cb = callback
|
|
144
144
|
callback = null
|
|
145
|
-
cb(err)
|
|
145
|
+
cb(maybeNormalizeConnectError(err, this, { timeout, hostname, port }))
|
|
146
146
|
}
|
|
147
147
|
})
|
|
148
148
|
|
|
@@ -150,4 +150,29 @@ function buildConnector ({ allowH2, useH2c, maxCachedSessions, socketPath, timeo
|
|
|
150
150
|
}
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
+
// `net.connect` with `autoSelectFamily` raises an `AggregateError` when every
|
|
154
|
+
// attempted address fails. If any of those failures is a timeout, surface the
|
|
155
|
+
// error as a `ConnectTimeoutError` so callers see the same error regardless of
|
|
156
|
+
// which timer (Node's internal one or undici's `connectTimeout`) wins the race.
|
|
157
|
+
// The original `AggregateError` is preserved on `.cause`.
|
|
158
|
+
function maybeNormalizeConnectError (err, socket, opts) {
|
|
159
|
+
if (
|
|
160
|
+
err instanceof AggregateError &&
|
|
161
|
+
(err.code === 'ETIMEDOUT' || err.errors.some((e) => e != null && e.code === 'ETIMEDOUT'))
|
|
162
|
+
) {
|
|
163
|
+
let message = 'Connect Timeout Error'
|
|
164
|
+
if (Array.isArray(socket.autoSelectFamilyAttemptedAddresses)) {
|
|
165
|
+
message += ` (attempted addresses: ${socket.autoSelectFamilyAttemptedAddresses.join(', ')},`
|
|
166
|
+
} else {
|
|
167
|
+
message += ` (attempted address: ${opts.hostname}:${opts.port},`
|
|
168
|
+
}
|
|
169
|
+
message += ` timeout: ${opts.timeout}ms)`
|
|
170
|
+
|
|
171
|
+
const wrapped = new ConnectTimeoutError(message)
|
|
172
|
+
wrapped.cause = err
|
|
173
|
+
return wrapped
|
|
174
|
+
}
|
|
175
|
+
return err
|
|
176
|
+
}
|
|
177
|
+
|
|
153
178
|
module.exports = buildConnector
|
|
@@ -698,9 +698,8 @@ function isFormDataLike (object) {
|
|
|
698
698
|
}
|
|
699
699
|
|
|
700
700
|
function addAbortListener (signal, listener) {
|
|
701
|
-
if (signal
|
|
702
|
-
|
|
703
|
-
return () => disposable[Symbol.dispose]()
|
|
701
|
+
if (!signal || 'aborted' in signal) {
|
|
702
|
+
return addAbortListenerNative(signal, listener)[Symbol.dispose]
|
|
704
703
|
}
|
|
705
704
|
|
|
706
705
|
if (typeof signal.addEventListener === 'function') {
|
|
@@ -793,8 +792,9 @@ const rangeHeaderRegex = /^bytes (\d+)-(\d+)\/(\d+|\*)?$/
|
|
|
793
792
|
*/
|
|
794
793
|
function parseRangeHeader (range) {
|
|
795
794
|
if (range == null || range === '') return { start: 0, end: null, size: null }
|
|
795
|
+
if (!range) return null
|
|
796
796
|
|
|
797
|
-
const m =
|
|
797
|
+
const m = rangeHeaderRegex.exec(range)
|
|
798
798
|
return m
|
|
799
799
|
? {
|
|
800
800
|
start: parseInt(m[1]),
|
|
@@ -943,8 +943,10 @@ function getProtocolFromUrlString (urlString) {
|
|
|
943
943
|
return urlString.slice(0, urlString.indexOf(':') + 1)
|
|
944
944
|
}
|
|
945
945
|
|
|
946
|
-
const kEnumerableProperty =
|
|
947
|
-
|
|
946
|
+
const kEnumerableProperty = {
|
|
947
|
+
__proto__: null,
|
|
948
|
+
enumerable: true
|
|
949
|
+
}
|
|
948
950
|
|
|
949
951
|
const normalizedMethodRecordsBase = {
|
|
950
952
|
delete: 'DELETE',
|