undici 8.0.1 → 8.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/docs/api/Dispatcher.md +2 -2
- package/docs/docs/best-practices/migrating-from-v7-to-v8.md +231 -0
- package/index.js +2 -2
- package/lib/core/util.js +1 -5
- package/lib/dispatcher/agent.js +23 -7
- package/lib/dispatcher/balanced-pool.js +0 -3
- package/lib/dispatcher/client.js +6 -2
- package/lib/dispatcher/dispatcher-base.js +4 -0
- package/lib/dispatcher/dispatcher1-wrapper.js +6 -0
- package/lib/dispatcher/env-http-proxy-agent.js +2 -24
- package/lib/dispatcher/h2c-client.js +1 -1
- package/lib/dispatcher/pool.js +0 -3
- package/lib/dispatcher/proxy-agent.js +10 -4
- package/lib/dispatcher/round-robin-pool.js +0 -3
- package/lib/dispatcher/socks5-proxy-agent.js +68 -62
- package/lib/handler/redirect-handler.js +1 -51
- package/lib/interceptor/decompress.js +1 -2
- package/lib/interceptor/dns.js +1 -1
- package/lib/util/cache.js +7 -6
- package/lib/util/runtime-features.js +3 -34
- package/lib/web/cache/cache.js +6 -8
- package/lib/web/fetch/body.js +1 -2
- package/lib/web/fetch/index.js +214 -189
- package/lib/web/fetch/util.js +4 -2
- package/lib/web/webidl/index.js +2 -4
- package/lib/web/websocket/stream/websocketstream.js +5 -6
- package/package.json +3 -3
- package/types/agent.d.ts +0 -2
- package/types/client.d.ts +7 -12
- package/types/dispatcher.d.ts +0 -2
- package/types/h2c-client.d.ts +6 -6
- package/types/pool.d.ts +0 -2
- package/types/round-robin-pool.d.ts +0 -2
- package/types/webidl.d.ts +0 -1
- package/lib/util/promise.js +0 -28
|
@@ -533,7 +533,7 @@ The `RequestOptions.method` property should not be value `'CONNECT'`.
|
|
|
533
533
|
|
|
534
534
|
`body` contains the following additional extensions:
|
|
535
535
|
|
|
536
|
-
- `dump({ limit: Integer })`, dump the response by reading up to `limit` bytes without killing the socket (optional) - Default:
|
|
536
|
+
- `dump({ limit: Integer })`, dump the response by reading up to `limit` bytes without killing the socket (optional) - Default: 131072.
|
|
537
537
|
|
|
538
538
|
Note that body will still be a `Readable` even if it is empty, but attempting to deserialize it with `json()` will result in an exception. Recommended way to ensure there is a body to deserialize is to check if status code is not 204, and `content-type` header starts with `application/json`.
|
|
539
539
|
|
|
@@ -1031,7 +1031,7 @@ const client = new Client("http://service.example").compose(
|
|
|
1031
1031
|
The `dump` interceptor enables you to dump the response body from a request upon a given limit.
|
|
1032
1032
|
|
|
1033
1033
|
**Options**
|
|
1034
|
-
- `maxSize` - The maximum size (in bytes) of the response body to dump. If the size of the
|
|
1034
|
+
- `maxSize` - The maximum size (in bytes) of the response body to dump. If the size of the response's body exceeds this value then the connection will be closed. Default: `1048576`.
|
|
1035
1035
|
|
|
1036
1036
|
> The `Dispatcher#options` also gets extended with the options `dumpMaxSize`, `abortOnDumped`, and `waitForTrailers` which can be used to configure the interceptor at a request-per-request basis.
|
|
1037
1037
|
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
# Migrating from Undici 7 to 8
|
|
2
|
+
|
|
3
|
+
This guide covers the changes you are most likely to hit when upgrading an
|
|
4
|
+
application or library from Undici v7 to v8.
|
|
5
|
+
|
|
6
|
+
## Before you upgrade
|
|
7
|
+
|
|
8
|
+
- Make sure your runtime is Node.js `>= 22.19.0`.
|
|
9
|
+
- If you have custom dispatchers, interceptors, or handlers, review the
|
|
10
|
+
handler API changes before updating.
|
|
11
|
+
- If you rely on HTTP/1.1-only behavior, plan to set `allowH2: false`
|
|
12
|
+
explicitly.
|
|
13
|
+
|
|
14
|
+
## 1. Update your Node.js version
|
|
15
|
+
|
|
16
|
+
Undici v8 requires Node.js `>= 22.19.0`.
|
|
17
|
+
|
|
18
|
+
If you are still on Node.js 20 or an older Node.js 22 release, upgrade Node.js
|
|
19
|
+
first:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
node -v
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
If that command prints a version lower than `v22.19.0`, upgrade Node.js before
|
|
26
|
+
installing Undici v8.
|
|
27
|
+
|
|
28
|
+
## 2. Migrate custom dispatcher handlers to the v2 API
|
|
29
|
+
|
|
30
|
+
Undici v8 uses the newer dispatcher handler API consistently.
|
|
31
|
+
|
|
32
|
+
If you implemented custom dispatchers, interceptors, or wrappers around
|
|
33
|
+
`dispatch()`, update legacy callbacks such as `onConnect`, `onHeaders`, and
|
|
34
|
+
`onComplete` to the newer callback names.
|
|
35
|
+
|
|
36
|
+
### Old handler callbacks vs. v8 callbacks
|
|
37
|
+
|
|
38
|
+
| Undici 7 style | Undici 8 style |
|
|
39
|
+
|---|---|
|
|
40
|
+
| `onConnect(abort, context)` | `onRequestStart(controller, context)` |
|
|
41
|
+
| `onHeaders(statusCode, rawHeaders, resume, statusText)` | `onResponseStart(controller, statusCode, headers, statusText)` |
|
|
42
|
+
| `onData(chunk)` | `onResponseData(controller, chunk)` |
|
|
43
|
+
| `onComplete(trailers)` | `onResponseEnd(controller, trailers)` |
|
|
44
|
+
| `onError(err)` | `onResponseError(controller, err)` |
|
|
45
|
+
| `onUpgrade(statusCode, rawHeaders, socket)` | `onRequestUpgrade(controller, statusCode, headers, socket)` |
|
|
46
|
+
|
|
47
|
+
### Example
|
|
48
|
+
|
|
49
|
+
Before:
|
|
50
|
+
|
|
51
|
+
```js
|
|
52
|
+
client.dispatch(options, {
|
|
53
|
+
onConnect (abort) {
|
|
54
|
+
this.abort = abort
|
|
55
|
+
},
|
|
56
|
+
onHeaders (statusCode, headers, resume) {
|
|
57
|
+
this.resume = resume
|
|
58
|
+
return true
|
|
59
|
+
},
|
|
60
|
+
onData (chunk) {
|
|
61
|
+
chunks.push(chunk)
|
|
62
|
+
return true
|
|
63
|
+
},
|
|
64
|
+
onComplete (trailers) {
|
|
65
|
+
console.log(trailers)
|
|
66
|
+
},
|
|
67
|
+
onError (err) {
|
|
68
|
+
console.error(err)
|
|
69
|
+
}
|
|
70
|
+
})
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
After:
|
|
74
|
+
|
|
75
|
+
```js
|
|
76
|
+
client.dispatch(options, {
|
|
77
|
+
onRequestStart (controller) {
|
|
78
|
+
this.controller = controller
|
|
79
|
+
},
|
|
80
|
+
onResponseStart (controller, statusCode, headers, statusText) {
|
|
81
|
+
console.log(statusCode, statusText, headers)
|
|
82
|
+
},
|
|
83
|
+
onResponseData (controller, chunk) {
|
|
84
|
+
chunks.push(chunk)
|
|
85
|
+
},
|
|
86
|
+
onResponseEnd (controller, trailers) {
|
|
87
|
+
console.log(trailers)
|
|
88
|
+
},
|
|
89
|
+
onResponseError (controller, err) {
|
|
90
|
+
console.error(err)
|
|
91
|
+
}
|
|
92
|
+
})
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Pause, resume, and abort now go through the controller
|
|
96
|
+
|
|
97
|
+
In Undici v7, legacy handlers could return `false` or keep references to
|
|
98
|
+
`abort()` and `resume()` callbacks. In Undici v8, use the controller instead:
|
|
99
|
+
|
|
100
|
+
```js
|
|
101
|
+
onRequestStart (controller) {
|
|
102
|
+
this.controller = controller
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
onResponseData (controller, chunk) {
|
|
106
|
+
controller.pause()
|
|
107
|
+
setImmediate(() => controller.resume())
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
onResponseError (controller, err) {
|
|
111
|
+
controller.abort(err)
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Raw headers and trailers moved to the controller
|
|
116
|
+
|
|
117
|
+
If you need the raw header arrays, read them from the controller:
|
|
118
|
+
|
|
119
|
+
- `controller.rawHeaders`
|
|
120
|
+
- `controller.rawTrailers`
|
|
121
|
+
|
|
122
|
+
## 3. Update `onBodySent()` handlers
|
|
123
|
+
|
|
124
|
+
If you implemented `onBodySent()`, note that its signature changed.
|
|
125
|
+
|
|
126
|
+
Before, handlers received counters:
|
|
127
|
+
|
|
128
|
+
```js
|
|
129
|
+
onBodySent (chunkSize, totalBytesSent) {}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
In Undici v8, handlers receive the actual chunk:
|
|
133
|
+
|
|
134
|
+
```js
|
|
135
|
+
onBodySent (chunk) {}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
If you need a notification that the whole body has been sent, use
|
|
139
|
+
`onRequestSent()`:
|
|
140
|
+
|
|
141
|
+
```js
|
|
142
|
+
onRequestSent () {
|
|
143
|
+
console.log('request body fully sent')
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## 4. If you need HTTP/1.1 only, disable HTTP/2 explicitly
|
|
148
|
+
|
|
149
|
+
Undici v8 enables HTTP/2 by default when a TLS server negotiates it via ALPN.
|
|
150
|
+
|
|
151
|
+
If your application depends on HTTP/1.1-specific behavior, set `allowH2: false`
|
|
152
|
+
explicitly.
|
|
153
|
+
|
|
154
|
+
Before:
|
|
155
|
+
|
|
156
|
+
```js
|
|
157
|
+
const client = new Client('https://example.com')
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
After, to keep HTTP/1.1 only:
|
|
161
|
+
|
|
162
|
+
```js
|
|
163
|
+
const client = new Client('https://example.com', {
|
|
164
|
+
allowH2: false
|
|
165
|
+
})
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
The same applies when you configure an `Agent`:
|
|
169
|
+
|
|
170
|
+
```js
|
|
171
|
+
const agent = new Agent({
|
|
172
|
+
allowH2: false
|
|
173
|
+
})
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## 5. Use real `Blob` and `File` instances
|
|
177
|
+
|
|
178
|
+
Undici v8 no longer accepts fake Blob-like values that only imitate `Blob` or
|
|
179
|
+
`File` via properties such as `Symbol.toStringTag`.
|
|
180
|
+
|
|
181
|
+
If you were passing custom objects that looked like `Blob`s, replace them with
|
|
182
|
+
actual `Blob` or `File` instances:
|
|
183
|
+
|
|
184
|
+
```js
|
|
185
|
+
const body = new Blob(['hello'])
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## 6. Avoid depending on the internal global dispatcher symbol
|
|
189
|
+
|
|
190
|
+
`setGlobalDispatcher()` and `getGlobalDispatcher()` remain the public APIs and
|
|
191
|
+
should continue to be used.
|
|
192
|
+
|
|
193
|
+
Internally, Undici v8 stores its dispatcher under
|
|
194
|
+
`Symbol.for('undici.globalDispatcher.2')` and mirrors a v1-compatible wrapper
|
|
195
|
+
for legacy consumers such as Node.js built-in `fetch`.
|
|
196
|
+
|
|
197
|
+
If your code was reading or writing `Symbol.for('undici.globalDispatcher.1')`
|
|
198
|
+
directly, migrate to the public APIs instead:
|
|
199
|
+
|
|
200
|
+
```js
|
|
201
|
+
import { setGlobalDispatcher, getGlobalDispatcher, Agent } from 'undici'
|
|
202
|
+
|
|
203
|
+
setGlobalDispatcher(new Agent())
|
|
204
|
+
const dispatcher = getGlobalDispatcher()
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
If you must expose a dispatcher to legacy v1 handler consumers, wrap it with
|
|
208
|
+
`Dispatcher1Wrapper`:
|
|
209
|
+
|
|
210
|
+
```js
|
|
211
|
+
import { Agent, Dispatcher1Wrapper } from 'undici'
|
|
212
|
+
|
|
213
|
+
const legacyCompatibleDispatcher = new Dispatcher1Wrapper(new Agent())
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## 7. Verify the upgrade
|
|
217
|
+
|
|
218
|
+
After moving to Undici v8, it is worth checking these paths in your test suite:
|
|
219
|
+
|
|
220
|
+
- requests that use a custom `dispatcher`
|
|
221
|
+
- `setGlobalDispatcher()` behavior
|
|
222
|
+
- any custom interceptor or retry handler
|
|
223
|
+
- uploads that use `Blob`, `File`, or `FormData`
|
|
224
|
+
- integrations that depend on HTTP/1.1-only behavior
|
|
225
|
+
|
|
226
|
+
## Related documentation
|
|
227
|
+
|
|
228
|
+
- [Dispatcher](/docs/api/Dispatcher.md)
|
|
229
|
+
- [Client](/docs/api/Client.md)
|
|
230
|
+
- [Global Installation](/docs/api/GlobalInstallation.md)
|
|
231
|
+
- [Undici Module vs. Node.js Built-in Fetch](/docs/best-practices/undici-vs-builtin-fetch.md)
|
package/index.js
CHANGED
|
@@ -105,14 +105,14 @@ function makeDispatcher (fn) {
|
|
|
105
105
|
url = util.parseURL(url)
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
const { agent, dispatcher = getGlobalDispatcher() } = opts
|
|
108
|
+
const { agent, dispatcher = getGlobalDispatcher(), ...restOpts } = opts
|
|
109
109
|
|
|
110
110
|
if (agent) {
|
|
111
111
|
throw new InvalidArgumentError('unsupported opts.agent. Did you mean opts.client?')
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
return fn.call(dispatcher, {
|
|
115
|
-
...
|
|
115
|
+
...restOpts,
|
|
116
116
|
origin: url.origin,
|
|
117
117
|
path: url.search ? `${url.pathname}${url.search}` : url.pathname,
|
|
118
118
|
method: opts.method || (opts.body ? 'PUT' : 'GET')
|
package/lib/core/util.js
CHANGED
|
@@ -12,8 +12,6 @@ const { InvalidArgumentError, ConnectTimeoutError } = require('./errors')
|
|
|
12
12
|
const { headerNameLowerCasedRecord } = require('./constants')
|
|
13
13
|
const { tree } = require('./tree')
|
|
14
14
|
|
|
15
|
-
const [nodeMajor, nodeMinor] = process.versions.node.split('.', 2).map(v => Number(v))
|
|
16
|
-
|
|
17
15
|
class BodyAsyncIterable {
|
|
18
16
|
constructor (body) {
|
|
19
17
|
this[kBody] = body
|
|
@@ -323,7 +321,7 @@ function isIterable (obj) {
|
|
|
323
321
|
*/
|
|
324
322
|
function hasSafeIterator (obj) {
|
|
325
323
|
const prototype = Object.getPrototypeOf(obj)
|
|
326
|
-
const ownIterator = Object.
|
|
324
|
+
const ownIterator = Object.hasOwn(obj, Symbol.iterator)
|
|
327
325
|
return ownIterator || (prototype != null && prototype !== Object.prototype && typeof obj[Symbol.iterator] === 'function')
|
|
328
326
|
}
|
|
329
327
|
|
|
@@ -989,8 +987,6 @@ module.exports = {
|
|
|
989
987
|
normalizedMethodRecords,
|
|
990
988
|
isValidPort,
|
|
991
989
|
isHttpOrHttpsPrefixed,
|
|
992
|
-
nodeMajor,
|
|
993
|
-
nodeMinor,
|
|
994
990
|
safeHTTPMethods: Object.freeze(['GET', 'HEAD', 'OPTIONS', 'TRACE']),
|
|
995
991
|
wrapRequestBody,
|
|
996
992
|
setupConnectTimeout,
|
package/lib/dispatcher/agent.js
CHANGED
|
@@ -72,14 +72,17 @@ class Agent extends DispatcherBase {
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
[kDispatch] (opts, handler) {
|
|
75
|
-
let
|
|
75
|
+
let origin
|
|
76
76
|
if (opts.origin && (typeof opts.origin === 'string' || opts.origin instanceof URL)) {
|
|
77
|
-
|
|
77
|
+
origin = String(opts.origin)
|
|
78
78
|
} else {
|
|
79
79
|
throw new InvalidArgumentError('opts.origin must be a non-empty string or URL.')
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
const allowH2 = opts.allowH2 ?? this[kOptions].allowH2
|
|
83
|
+
const key = allowH2 === false ? `${origin}#http1-only` : origin
|
|
84
|
+
|
|
85
|
+
if (this[kOrigins].size >= this[kOptions].maxOrigins && !this[kOrigins].has(origin)) {
|
|
83
86
|
throw new MaxOriginsReachedError()
|
|
84
87
|
}
|
|
85
88
|
|
|
@@ -96,10 +99,23 @@ class Agent extends DispatcherBase {
|
|
|
96
99
|
result.dispatcher.close()
|
|
97
100
|
}
|
|
98
101
|
}
|
|
99
|
-
|
|
102
|
+
|
|
103
|
+
let hasOrigin = false
|
|
104
|
+
for (const entry of this[kClients].values()) {
|
|
105
|
+
if (entry.origin === origin) {
|
|
106
|
+
hasOrigin = true
|
|
107
|
+
break
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (!hasOrigin) {
|
|
112
|
+
this[kOrigins].delete(origin)
|
|
113
|
+
}
|
|
100
114
|
}
|
|
101
115
|
}
|
|
102
|
-
dispatcher = this[kFactory](opts.origin,
|
|
116
|
+
dispatcher = this[kFactory](opts.origin, allowH2 === false
|
|
117
|
+
? { ...this[kOptions], allowH2: false }
|
|
118
|
+
: this[kOptions])
|
|
103
119
|
.on('drain', this[kOnDrain])
|
|
104
120
|
.on('connect', (origin, targets) => {
|
|
105
121
|
const result = this[kClients].get(key)
|
|
@@ -117,8 +133,8 @@ class Agent extends DispatcherBase {
|
|
|
117
133
|
this[kOnConnectionError](origin, targets, err)
|
|
118
134
|
})
|
|
119
135
|
|
|
120
|
-
this[kClients].set(key, { count: 0, dispatcher })
|
|
121
|
-
this[kOrigins].add(
|
|
136
|
+
this[kClients].set(key, { count: 0, dispatcher, origin })
|
|
137
|
+
this[kOrigins].add(origin)
|
|
122
138
|
}
|
|
123
139
|
|
|
124
140
|
return dispatcher.dispatch(opts, handler)
|
package/lib/dispatcher/client.js
CHANGED
|
@@ -235,9 +235,13 @@ class Client extends DispatcherBase {
|
|
|
235
235
|
...(typeof autoSelectFamily === 'boolean' ? { autoSelectFamily, autoSelectFamilyAttemptTimeout } : undefined),
|
|
236
236
|
...connect
|
|
237
237
|
})
|
|
238
|
-
} else
|
|
238
|
+
} else {
|
|
239
239
|
const customConnect = connect
|
|
240
|
-
connect = (opts, callback) => customConnect({
|
|
240
|
+
connect = (opts, callback) => customConnect({
|
|
241
|
+
...opts,
|
|
242
|
+
...(socketPath != null ? { socketPath } : null),
|
|
243
|
+
...(allowH2 != null ? { allowH2 } : null)
|
|
244
|
+
}, callback)
|
|
241
245
|
}
|
|
242
246
|
|
|
243
247
|
this[kUrl] = util.parseOrigin(url)
|
|
@@ -138,6 +138,10 @@ class DispatcherBase extends Dispatcher {
|
|
|
138
138
|
throw new InvalidArgumentError('opts must be an object.')
|
|
139
139
|
}
|
|
140
140
|
|
|
141
|
+
if (opts.dispatcher) {
|
|
142
|
+
throw new InvalidArgumentError('opts.dispatcher is not supported by instance methods. Pass opts.dispatcher to the top-level undici functions or call the dispatcher instance method directly.')
|
|
143
|
+
}
|
|
144
|
+
|
|
141
145
|
if (this[kDestroyed] || this[kOnDestroyed]) {
|
|
142
146
|
throw new ClientDestroyedError()
|
|
143
147
|
}
|
|
@@ -86,6 +86,12 @@ class Dispatcher1Wrapper extends Dispatcher {
|
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
dispatch (opts, handler) {
|
|
89
|
+
// Legacy (v1) consumers do not support HTTP/2, so force HTTP/1.1.
|
|
90
|
+
// See https://github.com/nodejs/undici/issues/4989
|
|
91
|
+
if (opts.allowH2 !== false) {
|
|
92
|
+
opts = { ...opts, allowH2: false }
|
|
93
|
+
}
|
|
94
|
+
|
|
89
95
|
return this.#dispatcher.dispatch(opts, Dispatcher1Wrapper.wrapHandler(handler))
|
|
90
96
|
}
|
|
91
97
|
|
|
@@ -10,22 +10,6 @@ const DEFAULT_PORTS = {
|
|
|
10
10
|
'https:': 443
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
/**
|
|
14
|
-
* Normalizes a proxy URL by prepending a scheme if one is missing.
|
|
15
|
-
* This matches the behavior of curl and Go's httpproxy package, which
|
|
16
|
-
* assume http:// for scheme-less proxy values.
|
|
17
|
-
*
|
|
18
|
-
* @param {string} proxyUrl - The proxy URL to normalize
|
|
19
|
-
* @param {string} defaultScheme - The scheme to prepend if missing ('http' or 'https')
|
|
20
|
-
* @returns {string} The normalized proxy URL
|
|
21
|
-
*/
|
|
22
|
-
function normalizeProxyUrl (proxyUrl, defaultScheme) {
|
|
23
|
-
if (!proxyUrl) return proxyUrl
|
|
24
|
-
// If the value already contains a scheme (e.g. http://, https://, socks5://), return as-is
|
|
25
|
-
if (/^[a-z][a-z0-9+\-.]*:\/\//i.test(proxyUrl)) return proxyUrl
|
|
26
|
-
return `${defaultScheme}://${proxyUrl}`
|
|
27
|
-
}
|
|
28
|
-
|
|
29
13
|
class EnvHttpProxyAgent extends DispatcherBase {
|
|
30
14
|
#noProxyValue = null
|
|
31
15
|
#noProxyEntries = null
|
|
@@ -39,20 +23,14 @@ class EnvHttpProxyAgent extends DispatcherBase {
|
|
|
39
23
|
|
|
40
24
|
this[kNoProxyAgent] = new Agent(agentOpts)
|
|
41
25
|
|
|
42
|
-
const HTTP_PROXY =
|
|
43
|
-
httpProxy ?? process.env.http_proxy ?? process.env.HTTP_PROXY,
|
|
44
|
-
'http'
|
|
45
|
-
)
|
|
26
|
+
const HTTP_PROXY = httpProxy ?? process.env.http_proxy ?? process.env.HTTP_PROXY
|
|
46
27
|
if (HTTP_PROXY) {
|
|
47
28
|
this[kHttpProxyAgent] = new ProxyAgent({ ...agentOpts, uri: HTTP_PROXY })
|
|
48
29
|
} else {
|
|
49
30
|
this[kHttpProxyAgent] = this[kNoProxyAgent]
|
|
50
31
|
}
|
|
51
32
|
|
|
52
|
-
const HTTPS_PROXY =
|
|
53
|
-
httpsProxy ?? process.env.https_proxy ?? process.env.HTTPS_PROXY,
|
|
54
|
-
'https'
|
|
55
|
-
)
|
|
33
|
+
const HTTPS_PROXY = httpsProxy ?? process.env.https_proxy ?? process.env.HTTPS_PROXY
|
|
56
34
|
if (HTTPS_PROXY) {
|
|
57
35
|
this[kHttpsProxyAgent] = new ProxyAgent({ ...agentOpts, uri: HTTPS_PROXY })
|
|
58
36
|
} else {
|
|
@@ -15,7 +15,7 @@ class H2CClient extends Client {
|
|
|
15
15
|
)
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
const {
|
|
18
|
+
const { maxConcurrentStreams, pipelining, ...opts } =
|
|
19
19
|
clientOpts ?? {}
|
|
20
20
|
let defaultMaxConcurrentStreams = 100
|
|
21
21
|
let defaultPipelining = 100
|
package/lib/dispatcher/pool.js
CHANGED
|
@@ -68,9 +68,6 @@ class Pool extends PoolBase {
|
|
|
68
68
|
this[kConnections] = connections || null
|
|
69
69
|
this[kUrl] = util.parseOrigin(origin)
|
|
70
70
|
this[kOptions] = { ...util.deepClone(options), connect, allowH2, clientTtl, socketPath }
|
|
71
|
-
this[kOptions].interceptors = options.interceptors
|
|
72
|
-
? { ...options.interceptors }
|
|
73
|
-
: undefined
|
|
74
71
|
this[kFactory] = factory
|
|
75
72
|
|
|
76
73
|
this.on('connect', (origin, targets) => {
|
|
@@ -16,6 +16,7 @@ const kProxyHeaders = Symbol('proxy headers')
|
|
|
16
16
|
const kRequestTls = Symbol('request tls settings')
|
|
17
17
|
const kProxyTls = Symbol('proxy tls settings')
|
|
18
18
|
const kConnectEndpoint = Symbol('connect endpoint function')
|
|
19
|
+
const kConnectEndpointHTTP1 = Symbol('connect endpoint function (http/1.1 only)')
|
|
19
20
|
const kTunnelProxy = Symbol('tunnel proxy')
|
|
20
21
|
|
|
21
22
|
function defaultProtocolPort (protocol) {
|
|
@@ -103,7 +104,7 @@ class ProxyAgent extends DispatcherBase {
|
|
|
103
104
|
throw new InvalidArgumentError('Proxy opts.clientFactory must be a function.')
|
|
104
105
|
}
|
|
105
106
|
|
|
106
|
-
const { proxyTunnel = true } = opts
|
|
107
|
+
const { proxyTunnel = true, connectTimeout } = opts
|
|
107
108
|
|
|
108
109
|
super()
|
|
109
110
|
|
|
@@ -127,8 +128,9 @@ class ProxyAgent extends DispatcherBase {
|
|
|
127
128
|
this[kProxyHeaders]['proxy-authorization'] = `Basic ${Buffer.from(`${decodeURIComponent(username)}:${decodeURIComponent(password)}`).toString('base64')}`
|
|
128
129
|
}
|
|
129
130
|
|
|
130
|
-
const connect = buildConnector({ ...opts.proxyTls })
|
|
131
|
-
this[kConnectEndpoint] = buildConnector({ ...opts.requestTls })
|
|
131
|
+
const connect = buildConnector({ timeout: connectTimeout, ...opts.proxyTls })
|
|
132
|
+
this[kConnectEndpoint] = buildConnector({ timeout: connectTimeout, ...opts.requestTls })
|
|
133
|
+
this[kConnectEndpointHTTP1] = buildConnector({ timeout: connectTimeout, ...opts.requestTls, allowH2: false })
|
|
132
134
|
|
|
133
135
|
const agentFactory = opts.factory || defaultAgentFactory
|
|
134
136
|
const factory = (origin, options) => {
|
|
@@ -216,7 +218,11 @@ class ProxyAgent extends DispatcherBase {
|
|
|
216
218
|
} else {
|
|
217
219
|
servername = opts.servername
|
|
218
220
|
}
|
|
219
|
-
|
|
221
|
+
const connectEndpoint = opts.allowH2 === false
|
|
222
|
+
? this[kConnectEndpointHTTP1]
|
|
223
|
+
: this[kConnectEndpoint]
|
|
224
|
+
|
|
225
|
+
connectEndpoint({ ...opts, servername, httpSocket: socket }, callback)
|
|
220
226
|
} catch (err) {
|
|
221
227
|
if (err.code === 'ERR_TLS_CERT_ALTNAME_INVALID') {
|
|
222
228
|
// Throw a custom error to avoid loop in client.js#connect
|
|
@@ -69,9 +69,6 @@ class RoundRobinPool extends PoolBase {
|
|
|
69
69
|
this[kConnections] = connections || null
|
|
70
70
|
this[kUrl] = util.parseOrigin(origin)
|
|
71
71
|
this[kOptions] = { ...util.deepClone(options), connect, allowH2, clientTtl, socketPath }
|
|
72
|
-
this[kOptions].interceptors = options.interceptors
|
|
73
|
-
? { ...options.interceptors }
|
|
74
|
-
: undefined
|
|
75
72
|
this[kFactory] = factory
|
|
76
73
|
this[kIndex] = -1
|
|
77
74
|
|