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
package/lib/core/util.js
CHANGED
|
@@ -28,6 +28,10 @@ class BodyAsyncIterable {
|
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
/**
|
|
32
|
+
* @param {*} body
|
|
33
|
+
* @returns {*}
|
|
34
|
+
*/
|
|
31
35
|
function wrapRequestBody (body) {
|
|
32
36
|
if (isStream(body)) {
|
|
33
37
|
// TODO (fix): Provide some way for the user to cache the file to e.g. /tmp
|
|
@@ -67,13 +71,19 @@ function wrapRequestBody (body) {
|
|
|
67
71
|
}
|
|
68
72
|
}
|
|
69
73
|
|
|
70
|
-
|
|
71
|
-
|
|
74
|
+
/**
|
|
75
|
+
* @param {*} obj
|
|
76
|
+
* @returns {obj is import('node:stream').Stream}
|
|
77
|
+
*/
|
|
72
78
|
function isStream (obj) {
|
|
73
79
|
return obj && typeof obj === 'object' && typeof obj.pipe === 'function' && typeof obj.on === 'function'
|
|
74
80
|
}
|
|
75
81
|
|
|
76
|
-
|
|
82
|
+
/**
|
|
83
|
+
* @param {*} object
|
|
84
|
+
* @returns {object is Blob}
|
|
85
|
+
* based on https://github.com/node-fetch/fetch-blob/blob/8ab587d34080de94140b54f07168451e7d0b655e/index.js#L229-L241 (MIT License)
|
|
86
|
+
*/
|
|
77
87
|
function isBlobLike (object) {
|
|
78
88
|
if (object === null) {
|
|
79
89
|
return false
|
|
@@ -91,7 +101,12 @@ function isBlobLike (object) {
|
|
|
91
101
|
}
|
|
92
102
|
}
|
|
93
103
|
|
|
94
|
-
|
|
104
|
+
/**
|
|
105
|
+
* @param {string} url The URL to add the query params to
|
|
106
|
+
* @param {import('node:querystring').ParsedUrlQueryInput} queryParams The object to serialize into a URL query string
|
|
107
|
+
* @returns {string} The URL with the query params added
|
|
108
|
+
*/
|
|
109
|
+
function serializePathWithQuery (url, queryParams) {
|
|
95
110
|
if (url.includes('?') || url.includes('#')) {
|
|
96
111
|
throw new Error('Query params cannot be passed when url already contains "?" or "#".')
|
|
97
112
|
}
|
|
@@ -105,6 +120,10 @@ function buildURL (url, queryParams) {
|
|
|
105
120
|
return url
|
|
106
121
|
}
|
|
107
122
|
|
|
123
|
+
/**
|
|
124
|
+
* @param {number|string|undefined} port
|
|
125
|
+
* @returns {boolean}
|
|
126
|
+
*/
|
|
108
127
|
function isValidPort (port) {
|
|
109
128
|
const value = parseInt(port, 10)
|
|
110
129
|
return (
|
|
@@ -114,6 +133,12 @@ function isValidPort (port) {
|
|
|
114
133
|
)
|
|
115
134
|
}
|
|
116
135
|
|
|
136
|
+
/**
|
|
137
|
+
* Check if the value is a valid http or https prefixed string.
|
|
138
|
+
*
|
|
139
|
+
* @param {string} value
|
|
140
|
+
* @returns {boolean}
|
|
141
|
+
*/
|
|
117
142
|
function isHttpOrHttpsPrefixed (value) {
|
|
118
143
|
return (
|
|
119
144
|
value != null &&
|
|
@@ -131,8 +156,15 @@ function isHttpOrHttpsPrefixed (value) {
|
|
|
131
156
|
)
|
|
132
157
|
}
|
|
133
158
|
|
|
159
|
+
/**
|
|
160
|
+
* @param {string|URL|Record<string,string>} url
|
|
161
|
+
* @returns {URL}
|
|
162
|
+
*/
|
|
134
163
|
function parseURL (url) {
|
|
135
164
|
if (typeof url === 'string') {
|
|
165
|
+
/**
|
|
166
|
+
* @type {URL}
|
|
167
|
+
*/
|
|
136
168
|
url = new URL(url)
|
|
137
169
|
|
|
138
170
|
if (!isHttpOrHttpsPrefixed(url.origin || url.protocol)) {
|
|
@@ -202,6 +234,10 @@ function parseURL (url) {
|
|
|
202
234
|
return url
|
|
203
235
|
}
|
|
204
236
|
|
|
237
|
+
/**
|
|
238
|
+
* @param {string|URL|Record<string, string>} url
|
|
239
|
+
* @returns {URL}
|
|
240
|
+
*/
|
|
205
241
|
function parseOrigin (url) {
|
|
206
242
|
url = parseURL(url)
|
|
207
243
|
|
|
@@ -212,6 +248,10 @@ function parseOrigin (url) {
|
|
|
212
248
|
return url
|
|
213
249
|
}
|
|
214
250
|
|
|
251
|
+
/**
|
|
252
|
+
* @param {string} host
|
|
253
|
+
* @returns {string}
|
|
254
|
+
*/
|
|
215
255
|
function getHostname (host) {
|
|
216
256
|
if (host[0] === '[') {
|
|
217
257
|
const idx = host.indexOf(']')
|
|
@@ -226,8 +266,12 @@ function getHostname (host) {
|
|
|
226
266
|
return host.substring(0, idx)
|
|
227
267
|
}
|
|
228
268
|
|
|
229
|
-
|
|
230
|
-
|
|
269
|
+
/**
|
|
270
|
+
* IP addresses are not valid server names per RFC6066
|
|
271
|
+
* Currently, the only server names supported are DNS hostnames
|
|
272
|
+
* @param {string|null} host
|
|
273
|
+
* @returns {string|null}
|
|
274
|
+
*/
|
|
231
275
|
function getServerName (host) {
|
|
232
276
|
if (!host) {
|
|
233
277
|
return null
|
|
@@ -243,18 +287,36 @@ function getServerName (host) {
|
|
|
243
287
|
return servername
|
|
244
288
|
}
|
|
245
289
|
|
|
290
|
+
/**
|
|
291
|
+
* @function
|
|
292
|
+
* @template T
|
|
293
|
+
* @param {T} obj
|
|
294
|
+
* @returns {T}
|
|
295
|
+
*/
|
|
246
296
|
function deepClone (obj) {
|
|
247
297
|
return JSON.parse(JSON.stringify(obj))
|
|
248
298
|
}
|
|
249
299
|
|
|
300
|
+
/**
|
|
301
|
+
* @param {*} obj
|
|
302
|
+
* @returns {obj is AsyncIterable}
|
|
303
|
+
*/
|
|
250
304
|
function isAsyncIterable (obj) {
|
|
251
305
|
return !!(obj != null && typeof obj[Symbol.asyncIterator] === 'function')
|
|
252
306
|
}
|
|
253
307
|
|
|
308
|
+
/**
|
|
309
|
+
* @param {*} obj
|
|
310
|
+
* @returns {obj is Iterable}
|
|
311
|
+
*/
|
|
254
312
|
function isIterable (obj) {
|
|
255
313
|
return !!(obj != null && (typeof obj[Symbol.iterator] === 'function' || typeof obj[Symbol.asyncIterator] === 'function'))
|
|
256
314
|
}
|
|
257
315
|
|
|
316
|
+
/**
|
|
317
|
+
* @param {Blob|Buffer|import ('stream').Stream} body
|
|
318
|
+
* @returns {number|null}
|
|
319
|
+
*/
|
|
258
320
|
function bodyLength (body) {
|
|
259
321
|
if (body == null) {
|
|
260
322
|
return 0
|
|
@@ -272,10 +334,19 @@ function bodyLength (body) {
|
|
|
272
334
|
return null
|
|
273
335
|
}
|
|
274
336
|
|
|
337
|
+
/**
|
|
338
|
+
* @param {import ('stream').Stream} body
|
|
339
|
+
* @returns {boolean}
|
|
340
|
+
*/
|
|
275
341
|
function isDestroyed (body) {
|
|
276
342
|
return body && !!(body.destroyed || body[kDestroyed] || (stream.isDestroyed?.(body)))
|
|
277
343
|
}
|
|
278
344
|
|
|
345
|
+
/**
|
|
346
|
+
* @param {import ('stream').Stream} stream
|
|
347
|
+
* @param {Error} [err]
|
|
348
|
+
* @returns {void}
|
|
349
|
+
*/
|
|
279
350
|
function destroy (stream, err) {
|
|
280
351
|
if (stream == null || !isStream(stream) || isDestroyed(stream)) {
|
|
281
352
|
return
|
|
@@ -300,8 +371,12 @@ function destroy (stream, err) {
|
|
|
300
371
|
}
|
|
301
372
|
|
|
302
373
|
const KEEPALIVE_TIMEOUT_EXPR = /timeout=(\d+)/
|
|
374
|
+
/**
|
|
375
|
+
* @param {string} val
|
|
376
|
+
* @returns {number | null}
|
|
377
|
+
*/
|
|
303
378
|
function parseKeepAliveTimeout (val) {
|
|
304
|
-
const m = val.
|
|
379
|
+
const m = val.match(KEEPALIVE_TIMEOUT_EXPR)
|
|
305
380
|
return m ? parseInt(m[1], 10) * 1000 : null
|
|
306
381
|
}
|
|
307
382
|
|
|
@@ -326,12 +401,13 @@ function bufferToLowerCasedHeaderName (value) {
|
|
|
326
401
|
}
|
|
327
402
|
|
|
328
403
|
/**
|
|
329
|
-
* @param {
|
|
404
|
+
* @param {(Buffer | string)[]} headers
|
|
330
405
|
* @param {Record<string, string | string[]>} [obj]
|
|
331
406
|
* @returns {Record<string, string | string[]>}
|
|
332
407
|
*/
|
|
333
408
|
function parseHeaders (headers, obj) {
|
|
334
409
|
if (obj === undefined) obj = {}
|
|
410
|
+
|
|
335
411
|
for (let i = 0; i < headers.length; i += 2) {
|
|
336
412
|
const key = headerNameToString(headers[i])
|
|
337
413
|
let val = obj[key]
|
|
@@ -360,9 +436,16 @@ function parseHeaders (headers, obj) {
|
|
|
360
436
|
return obj
|
|
361
437
|
}
|
|
362
438
|
|
|
439
|
+
/**
|
|
440
|
+
* @param {Buffer[]} headers
|
|
441
|
+
* @returns {string[]}
|
|
442
|
+
*/
|
|
363
443
|
function parseRawHeaders (headers) {
|
|
364
|
-
const
|
|
365
|
-
|
|
444
|
+
const headersLength = headers.length
|
|
445
|
+
/**
|
|
446
|
+
* @type {string[]}
|
|
447
|
+
*/
|
|
448
|
+
const ret = new Array(headersLength)
|
|
366
449
|
|
|
367
450
|
let hasContentLength = false
|
|
368
451
|
let contentDispositionIdx = -1
|
|
@@ -370,7 +453,7 @@ function parseRawHeaders (headers) {
|
|
|
370
453
|
let val
|
|
371
454
|
let kLen = 0
|
|
372
455
|
|
|
373
|
-
for (let n = 0; n <
|
|
456
|
+
for (let n = 0; n < headersLength; n += 2) {
|
|
374
457
|
key = headers[n]
|
|
375
458
|
val = headers[n + 1]
|
|
376
459
|
|
|
@@ -395,16 +478,44 @@ function parseRawHeaders (headers) {
|
|
|
395
478
|
return ret
|
|
396
479
|
}
|
|
397
480
|
|
|
481
|
+
/**
|
|
482
|
+
* @param {string[]} headers
|
|
483
|
+
* @param {Buffer[]} headers
|
|
484
|
+
*/
|
|
485
|
+
function encodeRawHeaders (headers) {
|
|
486
|
+
if (!Array.isArray(headers)) {
|
|
487
|
+
throw new TypeError('expected headers to be an array')
|
|
488
|
+
}
|
|
489
|
+
return headers.map(x => Buffer.from(x))
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
/**
|
|
493
|
+
* @param {*} buffer
|
|
494
|
+
* @returns {buffer is Buffer}
|
|
495
|
+
*/
|
|
398
496
|
function isBuffer (buffer) {
|
|
399
497
|
// See, https://github.com/mcollina/undici/pull/319
|
|
400
498
|
return buffer instanceof Uint8Array || Buffer.isBuffer(buffer)
|
|
401
499
|
}
|
|
402
500
|
|
|
403
|
-
|
|
501
|
+
/**
|
|
502
|
+
* Asserts that the handler object is a request handler.
|
|
503
|
+
*
|
|
504
|
+
* @param {object} handler
|
|
505
|
+
* @param {string} method
|
|
506
|
+
* @param {string} [upgrade]
|
|
507
|
+
* @returns {asserts handler is import('../api/api-request').RequestHandler}
|
|
508
|
+
*/
|
|
509
|
+
function assertRequestHandler (handler, method, upgrade) {
|
|
404
510
|
if (!handler || typeof handler !== 'object') {
|
|
405
511
|
throw new InvalidArgumentError('handler must be an object')
|
|
406
512
|
}
|
|
407
513
|
|
|
514
|
+
if (typeof handler.onRequestStart === 'function') {
|
|
515
|
+
// TODO (fix): More checks...
|
|
516
|
+
return
|
|
517
|
+
}
|
|
518
|
+
|
|
408
519
|
if (typeof handler.onConnect !== 'function') {
|
|
409
520
|
throw new InvalidArgumentError('invalid onConnect method')
|
|
410
521
|
}
|
|
@@ -436,21 +547,33 @@ function validateHandler (handler, method, upgrade) {
|
|
|
436
547
|
}
|
|
437
548
|
}
|
|
438
549
|
|
|
439
|
-
|
|
440
|
-
|
|
550
|
+
/**
|
|
551
|
+
* A body is disturbed if it has been read from and it cannot be re-used without
|
|
552
|
+
* losing state or data.
|
|
553
|
+
* @param {import('node:stream').Readable} body
|
|
554
|
+
* @returns {boolean}
|
|
555
|
+
*/
|
|
441
556
|
function isDisturbed (body) {
|
|
442
557
|
// TODO (fix): Why is body[kBodyUsed] needed?
|
|
443
558
|
return !!(body && (stream.isDisturbed(body) || body[kBodyUsed]))
|
|
444
559
|
}
|
|
445
560
|
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
}
|
|
561
|
+
/**
|
|
562
|
+
* @typedef {object} SocketInfo
|
|
563
|
+
* @property {string} [localAddress]
|
|
564
|
+
* @property {number} [localPort]
|
|
565
|
+
* @property {string} [remoteAddress]
|
|
566
|
+
* @property {number} [remotePort]
|
|
567
|
+
* @property {string} [remoteFamily]
|
|
568
|
+
* @property {number} [timeout]
|
|
569
|
+
* @property {number} bytesWritten
|
|
570
|
+
* @property {number} bytesRead
|
|
571
|
+
*/
|
|
453
572
|
|
|
573
|
+
/**
|
|
574
|
+
* @param {import('net').Socket} socket
|
|
575
|
+
* @returns {SocketInfo}
|
|
576
|
+
*/
|
|
454
577
|
function getSocketInfo (socket) {
|
|
455
578
|
return {
|
|
456
579
|
localAddress: socket.localAddress,
|
|
@@ -464,7 +587,10 @@ function getSocketInfo (socket) {
|
|
|
464
587
|
}
|
|
465
588
|
}
|
|
466
589
|
|
|
467
|
-
/**
|
|
590
|
+
/**
|
|
591
|
+
* @param {Iterable} iterable
|
|
592
|
+
* @returns {ReadableStream}
|
|
593
|
+
*/
|
|
468
594
|
function ReadableStreamFrom (iterable) {
|
|
469
595
|
// We cannot use ReadableStream.from here because it does not return a byte stream.
|
|
470
596
|
|
|
@@ -489,7 +615,7 @@ function ReadableStreamFrom (iterable) {
|
|
|
489
615
|
}
|
|
490
616
|
return controller.desiredSize > 0
|
|
491
617
|
},
|
|
492
|
-
async cancel (
|
|
618
|
+
async cancel () {
|
|
493
619
|
await iterator.return()
|
|
494
620
|
},
|
|
495
621
|
type: 'bytes'
|
|
@@ -497,8 +623,12 @@ function ReadableStreamFrom (iterable) {
|
|
|
497
623
|
)
|
|
498
624
|
}
|
|
499
625
|
|
|
500
|
-
|
|
501
|
-
|
|
626
|
+
/**
|
|
627
|
+
* The object should be a FormData instance and contains all the required
|
|
628
|
+
* methods.
|
|
629
|
+
* @param {*} object
|
|
630
|
+
* @returns {object is FormData}
|
|
631
|
+
*/
|
|
502
632
|
function isFormDataLike (object) {
|
|
503
633
|
return (
|
|
504
634
|
object &&
|
|
@@ -518,31 +648,56 @@ function addAbortListener (signal, listener) {
|
|
|
518
648
|
signal.addEventListener('abort', listener, { once: true })
|
|
519
649
|
return () => signal.removeEventListener('abort', listener)
|
|
520
650
|
}
|
|
521
|
-
signal.
|
|
651
|
+
signal.once('abort', listener)
|
|
522
652
|
return () => signal.removeListener('abort', listener)
|
|
523
653
|
}
|
|
524
654
|
|
|
525
|
-
const hasToWellFormed = typeof String.prototype.toWellFormed === 'function'
|
|
526
|
-
const hasIsWellFormed = typeof String.prototype.isWellFormed === 'function'
|
|
527
|
-
|
|
528
655
|
/**
|
|
529
|
-
* @
|
|
656
|
+
* @function
|
|
657
|
+
* @param {string} value
|
|
658
|
+
* @returns {string}
|
|
530
659
|
*/
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
660
|
+
const toUSVString = (() => {
|
|
661
|
+
if (typeof String.prototype.toWellFormed === 'function') {
|
|
662
|
+
/**
|
|
663
|
+
* @param {string} value
|
|
664
|
+
* @returns {string}
|
|
665
|
+
*/
|
|
666
|
+
return (value) => `${value}`.toWellFormed()
|
|
667
|
+
} else {
|
|
668
|
+
/**
|
|
669
|
+
* @param {string} value
|
|
670
|
+
* @returns {string}
|
|
671
|
+
*/
|
|
672
|
+
return nodeUtil.toUSVString
|
|
673
|
+
}
|
|
674
|
+
})()
|
|
534
675
|
|
|
535
676
|
/**
|
|
536
|
-
* @param {
|
|
677
|
+
* @param {*} value
|
|
678
|
+
* @returns {boolean}
|
|
537
679
|
*/
|
|
538
680
|
// TODO: move this to webidl
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
681
|
+
const isUSVString = (() => {
|
|
682
|
+
if (typeof String.prototype.isWellFormed === 'function') {
|
|
683
|
+
/**
|
|
684
|
+
* @param {*} value
|
|
685
|
+
* @returns {boolean}
|
|
686
|
+
*/
|
|
687
|
+
return (value) => `${value}`.isWellFormed()
|
|
688
|
+
} else {
|
|
689
|
+
/**
|
|
690
|
+
* @param {*} value
|
|
691
|
+
* @returns {boolean}
|
|
692
|
+
*/
|
|
693
|
+
return (value) => toUSVString(value) === `${value}`
|
|
694
|
+
}
|
|
695
|
+
})()
|
|
542
696
|
|
|
543
697
|
/**
|
|
544
698
|
* @see https://tools.ietf.org/html/rfc7230#section-3.2.6
|
|
545
699
|
* @param {number} c
|
|
700
|
+
* @returns {boolean}
|
|
546
701
|
*/
|
|
547
702
|
function isTokenCharCode (c) {
|
|
548
703
|
switch (c) {
|
|
@@ -573,6 +728,7 @@ function isTokenCharCode (c) {
|
|
|
573
728
|
|
|
574
729
|
/**
|
|
575
730
|
* @param {string} characters
|
|
731
|
+
* @returns {boolean}
|
|
576
732
|
*/
|
|
577
733
|
function isValidHTTPToken (characters) {
|
|
578
734
|
if (characters.length === 0) {
|
|
@@ -599,17 +755,31 @@ const headerCharRegex = /[^\t\x20-\x7e\x80-\xff]/
|
|
|
599
755
|
|
|
600
756
|
/**
|
|
601
757
|
* @param {string} characters
|
|
758
|
+
* @returns {boolean}
|
|
602
759
|
*/
|
|
603
760
|
function isValidHeaderValue (characters) {
|
|
604
761
|
return !headerCharRegex.test(characters)
|
|
605
762
|
}
|
|
606
763
|
|
|
607
|
-
|
|
608
|
-
|
|
764
|
+
const rangeHeaderRegex = /^bytes (\d+)-(\d+)\/(\d+)?$/
|
|
765
|
+
|
|
766
|
+
/**
|
|
767
|
+
* @typedef {object} RangeHeader
|
|
768
|
+
* @property {number} start
|
|
769
|
+
* @property {number | null} end
|
|
770
|
+
* @property {number | null} size
|
|
771
|
+
*/
|
|
772
|
+
|
|
773
|
+
/**
|
|
774
|
+
* Parse accordingly to RFC 9110
|
|
775
|
+
* @see https://www.rfc-editor.org/rfc/rfc9110#field.content-range
|
|
776
|
+
* @param {string} [range]
|
|
777
|
+
* @returns {RangeHeader|null}
|
|
778
|
+
*/
|
|
609
779
|
function parseRangeHeader (range) {
|
|
610
780
|
if (range == null || range === '') return { start: 0, end: null, size: null }
|
|
611
781
|
|
|
612
|
-
const m = range ? range.match(
|
|
782
|
+
const m = range ? range.match(rangeHeaderRegex) : null
|
|
613
783
|
return m
|
|
614
784
|
? {
|
|
615
785
|
start: parseInt(m[1]),
|
|
@@ -619,6 +789,13 @@ function parseRangeHeader (range) {
|
|
|
619
789
|
: null
|
|
620
790
|
}
|
|
621
791
|
|
|
792
|
+
/**
|
|
793
|
+
* @template {import("events").EventEmitter} T
|
|
794
|
+
* @param {T} obj
|
|
795
|
+
* @param {string} name
|
|
796
|
+
* @param {(...args: any[]) => void} listener
|
|
797
|
+
* @returns {T}
|
|
798
|
+
*/
|
|
622
799
|
function addListener (obj, name, listener) {
|
|
623
800
|
const listeners = (obj[kListeners] ??= [])
|
|
624
801
|
listeners.push([name, listener])
|
|
@@ -626,13 +803,26 @@ function addListener (obj, name, listener) {
|
|
|
626
803
|
return obj
|
|
627
804
|
}
|
|
628
805
|
|
|
806
|
+
/**
|
|
807
|
+
* @template {import("events").EventEmitter} T
|
|
808
|
+
* @param {T} obj
|
|
809
|
+
* @returns {T}
|
|
810
|
+
*/
|
|
629
811
|
function removeAllListeners (obj) {
|
|
630
|
-
|
|
631
|
-
|
|
812
|
+
if (obj[kListeners] != null) {
|
|
813
|
+
for (const [name, listener] of obj[kListeners]) {
|
|
814
|
+
obj.removeListener(name, listener)
|
|
815
|
+
}
|
|
816
|
+
obj[kListeners] = null
|
|
632
817
|
}
|
|
633
|
-
obj
|
|
818
|
+
return obj
|
|
634
819
|
}
|
|
635
820
|
|
|
821
|
+
/**
|
|
822
|
+
* @param {import ('../dispatcher/client')} client
|
|
823
|
+
* @param {import ('../core/request')} request
|
|
824
|
+
* @param {Error} err
|
|
825
|
+
*/
|
|
636
826
|
function errorRequest (client, request, err) {
|
|
637
827
|
try {
|
|
638
828
|
request.onError(err)
|
|
@@ -672,10 +862,7 @@ Object.setPrototypeOf(normalizedMethodRecords, null)
|
|
|
672
862
|
|
|
673
863
|
module.exports = {
|
|
674
864
|
kEnumerableProperty,
|
|
675
|
-
nop,
|
|
676
865
|
isDisturbed,
|
|
677
|
-
isErrored,
|
|
678
|
-
isReadable,
|
|
679
866
|
toUSVString,
|
|
680
867
|
isUSVString,
|
|
681
868
|
isBlobLike,
|
|
@@ -692,6 +879,7 @@ module.exports = {
|
|
|
692
879
|
removeAllListeners,
|
|
693
880
|
errorRequest,
|
|
694
881
|
parseRawHeaders,
|
|
882
|
+
encodeRawHeaders,
|
|
695
883
|
parseHeaders,
|
|
696
884
|
parseKeepAliveTimeout,
|
|
697
885
|
destroy,
|
|
@@ -699,10 +887,10 @@ module.exports = {
|
|
|
699
887
|
deepClone,
|
|
700
888
|
ReadableStreamFrom,
|
|
701
889
|
isBuffer,
|
|
702
|
-
|
|
890
|
+
assertRequestHandler,
|
|
703
891
|
getSocketInfo,
|
|
704
892
|
isFormDataLike,
|
|
705
|
-
|
|
893
|
+
serializePathWithQuery,
|
|
706
894
|
addAbortListener,
|
|
707
895
|
isValidHTTPToken,
|
|
708
896
|
isValidHeaderValue,
|
|
@@ -714,6 +902,6 @@ module.exports = {
|
|
|
714
902
|
isHttpOrHttpsPrefixed,
|
|
715
903
|
nodeMajor,
|
|
716
904
|
nodeMinor,
|
|
717
|
-
safeHTTPMethods: ['GET', 'HEAD', 'OPTIONS', 'TRACE'],
|
|
905
|
+
safeHTTPMethods: Object.freeze(['GET', 'HEAD', 'OPTIONS', 'TRACE']),
|
|
718
906
|
wrapRequestBody
|
|
719
907
|
}
|
package/lib/dispatcher/agent.js
CHANGED
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const { InvalidArgumentError } = require('../core/errors')
|
|
4
|
-
const { kClients, kRunning, kClose, kDestroy, kDispatch
|
|
4
|
+
const { kClients, kRunning, kClose, kDestroy, kDispatch } = require('../core/symbols')
|
|
5
5
|
const DispatcherBase = require('./dispatcher-base')
|
|
6
6
|
const Pool = require('./pool')
|
|
7
7
|
const Client = require('./client')
|
|
8
8
|
const util = require('../core/util')
|
|
9
|
-
const createRedirectInterceptor = require('../interceptor/redirect-interceptor')
|
|
10
9
|
|
|
11
10
|
const kOnConnect = Symbol('onConnect')
|
|
12
11
|
const kOnDisconnect = Symbol('onDisconnect')
|
|
13
12
|
const kOnConnectionError = Symbol('onConnectionError')
|
|
14
|
-
const kMaxRedirections = Symbol('maxRedirections')
|
|
15
13
|
const kOnDrain = Symbol('onDrain')
|
|
16
14
|
const kFactory = Symbol('factory')
|
|
17
15
|
const kOptions = Symbol('options')
|
|
@@ -23,9 +21,7 @@ function defaultFactory (origin, opts) {
|
|
|
23
21
|
}
|
|
24
22
|
|
|
25
23
|
class Agent extends DispatcherBase {
|
|
26
|
-
constructor ({ factory = defaultFactory,
|
|
27
|
-
super()
|
|
28
|
-
|
|
24
|
+
constructor ({ factory = defaultFactory, connect, ...options } = {}) {
|
|
29
25
|
if (typeof factory !== 'function') {
|
|
30
26
|
throw new InvalidArgumentError('factory must be a function.')
|
|
31
27
|
}
|
|
@@ -34,23 +30,13 @@ class Agent extends DispatcherBase {
|
|
|
34
30
|
throw new InvalidArgumentError('connect must be a function or an object')
|
|
35
31
|
}
|
|
36
32
|
|
|
37
|
-
|
|
38
|
-
throw new InvalidArgumentError('maxRedirections must be a positive number')
|
|
39
|
-
}
|
|
33
|
+
super()
|
|
40
34
|
|
|
41
35
|
if (connect && typeof connect !== 'function') {
|
|
42
36
|
connect = { ...connect }
|
|
43
37
|
}
|
|
44
38
|
|
|
45
|
-
this[kInterceptors] = options.interceptors?.Agent && Array.isArray(options.interceptors.Agent)
|
|
46
|
-
? options.interceptors.Agent
|
|
47
|
-
: [createRedirectInterceptor({ maxRedirections })]
|
|
48
|
-
|
|
49
39
|
this[kOptions] = { ...util.deepClone(options), connect }
|
|
50
|
-
this[kOptions].interceptors = options.interceptors
|
|
51
|
-
? { ...options.interceptors }
|
|
52
|
-
: undefined
|
|
53
|
-
this[kMaxRedirections] = maxRedirections
|
|
54
40
|
this[kFactory] = factory
|
|
55
41
|
this[kClients] = new Map()
|
|
56
42
|
|
|
@@ -13,7 +13,7 @@ const {
|
|
|
13
13
|
kGetDispatcher
|
|
14
14
|
} = require('./pool-base')
|
|
15
15
|
const Pool = require('./pool')
|
|
16
|
-
const { kUrl
|
|
16
|
+
const { kUrl } = require('../core/symbols')
|
|
17
17
|
const { parseOrigin } = require('../core/util')
|
|
18
18
|
const kFactory = Symbol('factory')
|
|
19
19
|
|
|
@@ -50,6 +50,10 @@ function defaultFactory (origin, opts) {
|
|
|
50
50
|
|
|
51
51
|
class BalancedPool extends PoolBase {
|
|
52
52
|
constructor (upstreams = [], { factory = defaultFactory, ...opts } = {}) {
|
|
53
|
+
if (typeof factory !== 'function') {
|
|
54
|
+
throw new InvalidArgumentError('factory must be a function.')
|
|
55
|
+
}
|
|
56
|
+
|
|
53
57
|
super()
|
|
54
58
|
|
|
55
59
|
this[kOptions] = opts
|
|
@@ -63,13 +67,6 @@ class BalancedPool extends PoolBase {
|
|
|
63
67
|
upstreams = [upstreams]
|
|
64
68
|
}
|
|
65
69
|
|
|
66
|
-
if (typeof factory !== 'function') {
|
|
67
|
-
throw new InvalidArgumentError('factory must be a function.')
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
this[kInterceptors] = opts.interceptors?.BalancedPool && Array.isArray(opts.interceptors.BalancedPool)
|
|
71
|
-
? opts.interceptors.BalancedPool
|
|
72
|
-
: []
|
|
73
70
|
this[kFactory] = factory
|
|
74
71
|
|
|
75
72
|
for (const upstream of upstreams) {
|