undici 6.3.0 → 6.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -31
- package/docs/api/Debug.md +2 -2
- package/docs/api/RedirectHandler.md +96 -0
- package/index.js +4 -20
- package/lib/cache/cache.js +78 -68
- package/lib/cache/cachestorage.js +1 -1
- package/lib/client.js +1 -1
- package/lib/core/util.js +2 -8
- package/lib/fetch/index.js +2 -9
- package/lib/fetch/request.js +22 -11
- package/lib/handler/RedirectHandler.js +11 -0
- package/lib/mock/mock-utils.js +15 -5
- package/package.json +7 -3
- package/types/dispatcher.d.ts +7 -1
- package/types/handlers.d.ts +10 -4
- package/types/readable.d.ts +2 -3
package/README.md
CHANGED
|
@@ -18,34 +18,21 @@ npm i undici
|
|
|
18
18
|
## Benchmarks
|
|
19
19
|
|
|
20
20
|
The benchmark is a simple `hello world` [example](benchmarks/benchmark.js) using a
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
### Connections 50
|
|
38
|
-
|
|
39
|
-
| Tests | Samples | Result | Tolerance | Difference with slowest |
|
|
40
|
-
|---------------------|---------|------------------|-----------|-------------------------|
|
|
41
|
-
| undici - fetch | 30 | 2107.19 req/sec | ± 2.69 % | - |
|
|
42
|
-
| http - no keepalive | 10 | 2698.90 req/sec | ± 2.68 % | + 28.08 % |
|
|
43
|
-
| http - keepalive | 10 | 4639.49 req/sec | ± 2.55 % | + 120.17 % |
|
|
44
|
-
| undici - pipeline | 40 | 6123.33 req/sec | ± 2.97 % | + 190.59 % |
|
|
45
|
-
| undici - stream | 50 | 9426.51 req/sec | ± 2.92 % | + 347.35 % |
|
|
46
|
-
| undici - request | 10 | 10162.88 req/sec | ± 2.13 % | + 382.29 % |
|
|
47
|
-
| undici - dispatch | 50 | 11191.11 req/sec | ± 2.98 % | + 431.09 % |
|
|
48
|
-
|
|
21
|
+
50 TCP connections with a pipelining depth of 10 running on Node 20.10.0.
|
|
22
|
+
|
|
23
|
+
│ Tests │ Samples │ Result │ Tolerance │ Difference with slowest │
|
|
24
|
+
|─────────────────────|─────────|─────────────────|───────────|─────────────────────────|
|
|
25
|
+
│ got │ 45 │ 1661.71 req/sec │ ± 2.93 % │ - │
|
|
26
|
+
│ node-fetch │ 20 │ 2164.81 req/sec │ ± 2.63 % │ + 30.28 % │
|
|
27
|
+
│ undici - fetch │ 35 │ 2274.27 req/sec │ ± 2.70 % │ + 36.86 % │
|
|
28
|
+
│ http - no keepalive │ 15 │ 2376.04 req/sec │ ± 2.99 % │ + 42.99 % │
|
|
29
|
+
│ axios │ 25 │ 2612.93 req/sec │ ± 2.89 % │ + 57.24 % │
|
|
30
|
+
│ request │ 40 │ 2712.19 req/sec │ ± 2.92 % │ + 63.22 % │
|
|
31
|
+
│ http - keepalive │ 45 │ 4393.25 req/sec │ ± 2.86 % │ + 164.38 % │
|
|
32
|
+
│ undici - pipeline │ 45 │ 5484.69 req/sec │ ± 2.87 % │ + 230.06 % │
|
|
33
|
+
│ undici - request │ 55 │ 7773.98 req/sec │ ± 2.93 % │ + 367.83 % │
|
|
34
|
+
│ undici - stream │ 70 │ 8425.96 req/sec │ ± 2.91 % │ + 407.07 % │
|
|
35
|
+
│ undici - dispatch │ 50 │ 9488.99 req/sec │ ± 2.85 % │ + 471.04 % │
|
|
49
36
|
|
|
50
37
|
## Quick Start
|
|
51
38
|
|
|
@@ -62,9 +49,7 @@ const {
|
|
|
62
49
|
console.log('response received', statusCode)
|
|
63
50
|
console.log('headers', headers)
|
|
64
51
|
|
|
65
|
-
for await (const data of body) {
|
|
66
|
-
console.log('data', data)
|
|
67
|
-
}
|
|
52
|
+
for await (const data of body) { console.log('data', data) }
|
|
68
53
|
|
|
69
54
|
console.log('trailers', trailers)
|
|
70
55
|
```
|
package/docs/api/Debug.md
CHANGED
|
@@ -53,10 +53,10 @@ This flag enables debug statements for the `Websocket` API.
|
|
|
53
53
|
> **Note**: statements can overlap with `UNDICI` ones if `undici` or `fetch` flag has been enabled as well.
|
|
54
54
|
|
|
55
55
|
```sh
|
|
56
|
-
NODE_DEBUG=
|
|
56
|
+
NODE_DEBUG=websocket node script.js
|
|
57
57
|
|
|
58
58
|
WEBSOCKET 18309: connecting to echo.websocket.org using https:h1
|
|
59
59
|
WEBSOCKET 18309: connected to echo.websocket.org using https:h1
|
|
60
60
|
WEBSOCKET 18309: sending request to GET https://echo.websocket.org//
|
|
61
61
|
WEBSOCKET 18309: connection opened <ip_address>
|
|
62
|
-
```
|
|
62
|
+
```
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# Class: RedirectHandler
|
|
2
|
+
|
|
3
|
+
A class that handles redirection logic for HTTP requests.
|
|
4
|
+
|
|
5
|
+
## `new RedirectHandler(dispatch, maxRedirections, opts, handler, redirectionLimitReached)`
|
|
6
|
+
|
|
7
|
+
Arguments:
|
|
8
|
+
|
|
9
|
+
- **dispatch** `function` - The dispatch function to be called after every retry.
|
|
10
|
+
- **maxRedirections** `number` - Maximum number of redirections allowed.
|
|
11
|
+
- **opts** `object` - Options for handling redirection.
|
|
12
|
+
- **handler** `object` - An object containing handlers for different stages of the request lifecycle.
|
|
13
|
+
- **redirectionLimitReached** `boolean` (default: `false`) - A flag that the implementer can provide to enable or disable the feature. If set to `false`, it indicates that the caller doesn't want to use the feature and prefers the old behavior.
|
|
14
|
+
|
|
15
|
+
Returns: `RedirectHandler`
|
|
16
|
+
|
|
17
|
+
### Parameters
|
|
18
|
+
|
|
19
|
+
- **dispatch** `(options: Dispatch.DispatchOptions, handlers: Dispatch.DispatchHandlers) => Promise<Dispatch.DispatchResponse>` (required) - Dispatch function to be called after every redirection.
|
|
20
|
+
- **maxRedirections** `number` (required) - Maximum number of redirections allowed.
|
|
21
|
+
- **opts** `object` (required) - Options for handling redirection.
|
|
22
|
+
- **handler** `object` (required) - Handlers for different stages of the request lifecycle.
|
|
23
|
+
- **redirectionLimitReached** `boolean` (default: `false`) - A flag that the implementer can provide to enable or disable the feature. If set to `false`, it indicates that the caller doesn't want to use the feature and prefers the old behavior.
|
|
24
|
+
|
|
25
|
+
### Properties
|
|
26
|
+
|
|
27
|
+
- **location** `string` - The current redirection location.
|
|
28
|
+
- **abort** `function` - The abort function.
|
|
29
|
+
- **opts** `object` - The options for handling redirection.
|
|
30
|
+
- **maxRedirections** `number` - Maximum number of redirections allowed.
|
|
31
|
+
- **handler** `object` - Handlers for different stages of the request lifecycle.
|
|
32
|
+
- **history** `Array` - An array representing the history of URLs during redirection.
|
|
33
|
+
- **redirectionLimitReached** `boolean` - Indicates whether the redirection limit has been reached.
|
|
34
|
+
|
|
35
|
+
### Methods
|
|
36
|
+
|
|
37
|
+
#### `onConnect(abort)`
|
|
38
|
+
|
|
39
|
+
Called when the connection is established.
|
|
40
|
+
|
|
41
|
+
Parameters:
|
|
42
|
+
|
|
43
|
+
- **abort** `function` - The abort function.
|
|
44
|
+
|
|
45
|
+
#### `onUpgrade(statusCode, headers, socket)`
|
|
46
|
+
|
|
47
|
+
Called when an upgrade is requested.
|
|
48
|
+
|
|
49
|
+
Parameters:
|
|
50
|
+
|
|
51
|
+
- **statusCode** `number` - The HTTP status code.
|
|
52
|
+
- **headers** `object` - The headers received in the response.
|
|
53
|
+
- **socket** `object` - The socket object.
|
|
54
|
+
|
|
55
|
+
#### `onError(error)`
|
|
56
|
+
|
|
57
|
+
Called when an error occurs.
|
|
58
|
+
|
|
59
|
+
Parameters:
|
|
60
|
+
|
|
61
|
+
- **error** `Error` - The error that occurred.
|
|
62
|
+
|
|
63
|
+
#### `onHeaders(statusCode, headers, resume, statusText)`
|
|
64
|
+
|
|
65
|
+
Called when headers are received.
|
|
66
|
+
|
|
67
|
+
Parameters:
|
|
68
|
+
|
|
69
|
+
- **statusCode** `number` - The HTTP status code.
|
|
70
|
+
- **headers** `object` - The headers received in the response.
|
|
71
|
+
- **resume** `function` - The resume function.
|
|
72
|
+
- **statusText** `string` - The status text.
|
|
73
|
+
|
|
74
|
+
#### `onData(chunk)`
|
|
75
|
+
|
|
76
|
+
Called when data is received.
|
|
77
|
+
|
|
78
|
+
Parameters:
|
|
79
|
+
|
|
80
|
+
- **chunk** `Buffer` - The data chunk received.
|
|
81
|
+
|
|
82
|
+
#### `onComplete(trailers)`
|
|
83
|
+
|
|
84
|
+
Called when the request is complete.
|
|
85
|
+
|
|
86
|
+
Parameters:
|
|
87
|
+
|
|
88
|
+
- **trailers** `object` - The trailers received.
|
|
89
|
+
|
|
90
|
+
#### `onBodySent(chunk)`
|
|
91
|
+
|
|
92
|
+
Called when the request body is sent.
|
|
93
|
+
|
|
94
|
+
Parameters:
|
|
95
|
+
|
|
96
|
+
- **chunk** `Buffer` - The chunk of the request body sent.
|
package/index.js
CHANGED
|
@@ -21,14 +21,6 @@ const DecoratorHandler = require('./lib/handler/DecoratorHandler')
|
|
|
21
21
|
const RedirectHandler = require('./lib/handler/RedirectHandler')
|
|
22
22
|
const createRedirectInterceptor = require('./lib/interceptor/redirectInterceptor')
|
|
23
23
|
|
|
24
|
-
let hasCrypto
|
|
25
|
-
try {
|
|
26
|
-
require('crypto')
|
|
27
|
-
hasCrypto = true
|
|
28
|
-
} catch {
|
|
29
|
-
hasCrypto = false
|
|
30
|
-
}
|
|
31
|
-
|
|
32
24
|
Object.assign(Dispatcher.prototype, api)
|
|
33
25
|
|
|
34
26
|
module.exports.Dispatcher = Dispatcher
|
|
@@ -102,14 +94,10 @@ function makeDispatcher (fn) {
|
|
|
102
94
|
module.exports.setGlobalDispatcher = setGlobalDispatcher
|
|
103
95
|
module.exports.getGlobalDispatcher = getGlobalDispatcher
|
|
104
96
|
|
|
105
|
-
|
|
106
|
-
module.exports.fetch = async function fetch (
|
|
107
|
-
if (!fetchImpl) {
|
|
108
|
-
fetchImpl = require('./lib/fetch').fetch
|
|
109
|
-
}
|
|
110
|
-
|
|
97
|
+
const fetchImpl = require('./lib/fetch').fetch
|
|
98
|
+
module.exports.fetch = async function fetch (init, options = undefined) {
|
|
111
99
|
try {
|
|
112
|
-
return await fetchImpl(
|
|
100
|
+
return await fetchImpl(init, options)
|
|
113
101
|
} catch (err) {
|
|
114
102
|
if (typeof err === 'object') {
|
|
115
103
|
Error.captureStackTrace(err, this)
|
|
@@ -149,11 +137,7 @@ const { parseMIMEType, serializeAMimeType } = require('./lib/fetch/dataURL')
|
|
|
149
137
|
module.exports.parseMIMEType = parseMIMEType
|
|
150
138
|
module.exports.serializeAMimeType = serializeAMimeType
|
|
151
139
|
|
|
152
|
-
|
|
153
|
-
const { WebSocket } = require('./lib/websocket/websocket')
|
|
154
|
-
|
|
155
|
-
module.exports.WebSocket = WebSocket
|
|
156
|
-
}
|
|
140
|
+
module.exports.WebSocket = require('./lib/websocket/websocket').WebSocket
|
|
157
141
|
|
|
158
142
|
module.exports.request = makeDispatcher(api.request)
|
|
159
143
|
module.exports.stream = makeDispatcher(api.stream)
|
package/lib/cache/cache.js
CHANGED
|
@@ -6,8 +6,9 @@ const { kEnumerableProperty, isDisturbed } = require('../core/util')
|
|
|
6
6
|
const { kHeadersList } = require('../core/symbols')
|
|
7
7
|
const { webidl } = require('../fetch/webidl')
|
|
8
8
|
const { Response, cloneResponse } = require('../fetch/response')
|
|
9
|
-
const { Request } = require('../fetch/request')
|
|
10
|
-
const {
|
|
9
|
+
const { Request, fromInnerRequest } = require('../fetch/request')
|
|
10
|
+
const { Headers } = require('../fetch/headers')
|
|
11
|
+
const { kState, kHeaders, kGuard } = require('../fetch/symbols')
|
|
11
12
|
const { fetching } = require('../fetch/index')
|
|
12
13
|
const { urlIsHttpHttpsScheme, createDeferredPromise, readAllBytes } = require('../fetch/util')
|
|
13
14
|
const assert = require('assert')
|
|
@@ -49,7 +50,7 @@ class Cache {
|
|
|
49
50
|
request = webidl.converters.RequestInfo(request)
|
|
50
51
|
options = webidl.converters.CacheQueryOptions(options)
|
|
51
52
|
|
|
52
|
-
const p =
|
|
53
|
+
const p = this.#internalMatchAll(request, options, 1)
|
|
53
54
|
|
|
54
55
|
if (p.length === 0) {
|
|
55
56
|
return
|
|
@@ -64,64 +65,7 @@ class Cache {
|
|
|
64
65
|
if (request !== undefined) request = webidl.converters.RequestInfo(request)
|
|
65
66
|
options = webidl.converters.CacheQueryOptions(options)
|
|
66
67
|
|
|
67
|
-
|
|
68
|
-
let r = null
|
|
69
|
-
|
|
70
|
-
// 2.
|
|
71
|
-
if (request !== undefined) {
|
|
72
|
-
if (request instanceof Request) {
|
|
73
|
-
// 2.1.1
|
|
74
|
-
r = request[kState]
|
|
75
|
-
|
|
76
|
-
// 2.1.2
|
|
77
|
-
if (r.method !== 'GET' && !options.ignoreMethod) {
|
|
78
|
-
return []
|
|
79
|
-
}
|
|
80
|
-
} else if (typeof request === 'string') {
|
|
81
|
-
// 2.2.1
|
|
82
|
-
r = new Request(request)[kState]
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// 5.
|
|
87
|
-
// 5.1
|
|
88
|
-
const responses = []
|
|
89
|
-
|
|
90
|
-
// 5.2
|
|
91
|
-
if (request === undefined) {
|
|
92
|
-
// 5.2.1
|
|
93
|
-
for (const requestResponse of this.#relevantRequestResponseList) {
|
|
94
|
-
responses.push(requestResponse[1])
|
|
95
|
-
}
|
|
96
|
-
} else { // 5.3
|
|
97
|
-
// 5.3.1
|
|
98
|
-
const requestResponses = this.#queryCache(r, options)
|
|
99
|
-
|
|
100
|
-
// 5.3.2
|
|
101
|
-
for (const requestResponse of requestResponses) {
|
|
102
|
-
responses.push(requestResponse[1])
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// 5.4
|
|
107
|
-
// We don't implement CORs so we don't need to loop over the responses, yay!
|
|
108
|
-
|
|
109
|
-
// 5.5.1
|
|
110
|
-
const responseList = []
|
|
111
|
-
|
|
112
|
-
// 5.5.2
|
|
113
|
-
for (const response of responses) {
|
|
114
|
-
// 5.5.2.1
|
|
115
|
-
const responseObject = new Response(null)
|
|
116
|
-
responseObject[kState] = response
|
|
117
|
-
responseObject[kHeaders][kHeadersList] = response.headersList
|
|
118
|
-
responseObject[kHeaders][kGuard] = 'immutable'
|
|
119
|
-
|
|
120
|
-
responseList.push(responseObject.clone())
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// 6.
|
|
124
|
-
return Object.freeze(responseList)
|
|
68
|
+
return this.#internalMatchAll(request, options)
|
|
125
69
|
}
|
|
126
70
|
|
|
127
71
|
async add (request) {
|
|
@@ -500,7 +444,7 @@ class Cache {
|
|
|
500
444
|
* @see https://w3c.github.io/ServiceWorker/#dom-cache-keys
|
|
501
445
|
* @param {any} request
|
|
502
446
|
* @param {import('../../types/cache').CacheQueryOptions} options
|
|
503
|
-
* @returns {readonly Request[]}
|
|
447
|
+
* @returns {Promise<readonly Request[]>}
|
|
504
448
|
*/
|
|
505
449
|
async keys (request = undefined, options = {}) {
|
|
506
450
|
webidl.brandCheck(this, Cache)
|
|
@@ -559,12 +503,12 @@ class Cache {
|
|
|
559
503
|
|
|
560
504
|
// 5.4.2
|
|
561
505
|
for (const request of requests) {
|
|
562
|
-
const requestObject =
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
506
|
+
const requestObject = fromInnerRequest(
|
|
507
|
+
request,
|
|
508
|
+
new AbortController().signal,
|
|
509
|
+
'immutable',
|
|
510
|
+
{ settingsObject: request.client }
|
|
511
|
+
)
|
|
568
512
|
// 5.4.2.1
|
|
569
513
|
requestList.push(requestObject)
|
|
570
514
|
}
|
|
@@ -789,6 +733,72 @@ class Cache {
|
|
|
789
733
|
|
|
790
734
|
return true
|
|
791
735
|
}
|
|
736
|
+
|
|
737
|
+
#internalMatchAll (request, options, maxResponses = Infinity) {
|
|
738
|
+
// 1.
|
|
739
|
+
let r = null
|
|
740
|
+
|
|
741
|
+
// 2.
|
|
742
|
+
if (request !== undefined) {
|
|
743
|
+
if (request instanceof Request) {
|
|
744
|
+
// 2.1.1
|
|
745
|
+
r = request[kState]
|
|
746
|
+
|
|
747
|
+
// 2.1.2
|
|
748
|
+
if (r.method !== 'GET' && !options.ignoreMethod) {
|
|
749
|
+
return []
|
|
750
|
+
}
|
|
751
|
+
} else if (typeof request === 'string') {
|
|
752
|
+
// 2.2.1
|
|
753
|
+
r = new Request(request)[kState]
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
// 5.
|
|
758
|
+
// 5.1
|
|
759
|
+
const responses = []
|
|
760
|
+
|
|
761
|
+
// 5.2
|
|
762
|
+
if (request === undefined) {
|
|
763
|
+
// 5.2.1
|
|
764
|
+
for (const requestResponse of this.#relevantRequestResponseList) {
|
|
765
|
+
responses.push(requestResponse[1])
|
|
766
|
+
}
|
|
767
|
+
} else { // 5.3
|
|
768
|
+
// 5.3.1
|
|
769
|
+
const requestResponses = this.#queryCache(r, options)
|
|
770
|
+
|
|
771
|
+
// 5.3.2
|
|
772
|
+
for (const requestResponse of requestResponses) {
|
|
773
|
+
responses.push(requestResponse[1])
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
// 5.4
|
|
778
|
+
// We don't implement CORs so we don't need to loop over the responses, yay!
|
|
779
|
+
|
|
780
|
+
// 5.5.1
|
|
781
|
+
const responseList = []
|
|
782
|
+
|
|
783
|
+
// 5.5.2
|
|
784
|
+
for (const response of responses) {
|
|
785
|
+
// 5.5.2.1
|
|
786
|
+
const responseObject = new Response(kConstruct)
|
|
787
|
+
responseObject[kState] = response
|
|
788
|
+
responseObject[kHeaders] = new Headers(kConstruct)
|
|
789
|
+
responseObject[kHeaders][kHeadersList] = response.headersList
|
|
790
|
+
responseObject[kHeaders][kGuard] = 'immutable'
|
|
791
|
+
|
|
792
|
+
responseList.push(responseObject.clone())
|
|
793
|
+
|
|
794
|
+
if (responseList.length >= maxResponses) {
|
|
795
|
+
break
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
// 6.
|
|
800
|
+
return Object.freeze(responseList)
|
|
801
|
+
}
|
|
792
802
|
}
|
|
793
803
|
|
|
794
804
|
Object.defineProperties(Cache.prototype, {
|
package/lib/client.js
CHANGED
|
@@ -1769,7 +1769,7 @@ function writeH2 (client, session, request) {
|
|
|
1769
1769
|
|
|
1770
1770
|
session.ref()
|
|
1771
1771
|
|
|
1772
|
-
const shouldEndStream = method === 'GET' || method === 'HEAD'
|
|
1772
|
+
const shouldEndStream = method === 'GET' || method === 'HEAD' || body === null
|
|
1773
1773
|
if (expectContinue) {
|
|
1774
1774
|
headers[HTTP2_HEADER_EXPECT] = '100-continue'
|
|
1775
1775
|
stream = session.request(headers, { endStream: shouldEndStream, signal })
|
package/lib/core/util.js
CHANGED
|
@@ -349,14 +349,8 @@ function validateHandler (handler, method, upgrade) {
|
|
|
349
349
|
// A body is disturbed if it has been read from and it cannot
|
|
350
350
|
// be re-used without losing state or data.
|
|
351
351
|
function isDisturbed (body) {
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
? stream.isDisturbed(body) || body[kBodyUsed] // TODO (fix): Why is body[kBodyUsed] needed?
|
|
355
|
-
: body[kBodyUsed] ||
|
|
356
|
-
body.readableDidRead ||
|
|
357
|
-
(body._readableState && body._readableState.dataEmitted) ||
|
|
358
|
-
isReadableAborted(body)
|
|
359
|
-
))
|
|
352
|
+
// TODO (fix): Why is body[kBodyUsed] needed?
|
|
353
|
+
return !!(body && (stream.isDisturbed(body) || body[kBodyUsed]))
|
|
360
354
|
}
|
|
361
355
|
|
|
362
356
|
function isErrored (body) {
|
package/lib/fetch/index.js
CHANGED
|
@@ -122,7 +122,7 @@ class Fetch extends EE {
|
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
// https://fetch.spec.whatwg.org/#fetch-method
|
|
125
|
-
function fetch (input, init =
|
|
125
|
+
function fetch (input, init = undefined) {
|
|
126
126
|
webidl.argumentLengthCheck(arguments, 1, { header: 'globalThis.fetch' })
|
|
127
127
|
|
|
128
128
|
// 1. Let p be a new promise.
|
|
@@ -248,7 +248,7 @@ function fetch (input, init = {}) {
|
|
|
248
248
|
request,
|
|
249
249
|
processResponseEndOfBody: handleFetchDone,
|
|
250
250
|
processResponse,
|
|
251
|
-
dispatcher: init
|
|
251
|
+
dispatcher: init?.dispatcher ?? getGlobalDispatcher() // undici
|
|
252
252
|
})
|
|
253
253
|
|
|
254
254
|
// 14. Return p.
|
|
@@ -327,13 +327,6 @@ function markResourceTiming (timingInfo, originalURL, initiatorType, globalThis,
|
|
|
327
327
|
|
|
328
328
|
// https://fetch.spec.whatwg.org/#abort-fetch
|
|
329
329
|
function abortFetch (p, request, responseObject, error) {
|
|
330
|
-
// Note: AbortSignal.reason was added in node v17.2.0
|
|
331
|
-
// which would give us an undefined error to reject with.
|
|
332
|
-
// Remove this once node v16 is no longer supported.
|
|
333
|
-
if (!error) {
|
|
334
|
-
error = new DOMException('The operation was aborted.', 'AbortError')
|
|
335
|
-
}
|
|
336
|
-
|
|
337
330
|
// 1. Reject promise with error.
|
|
338
331
|
p.reject(error)
|
|
339
332
|
|
package/lib/fetch/request.js
CHANGED
|
@@ -751,14 +751,6 @@ class Request {
|
|
|
751
751
|
|
|
752
752
|
// 3. Let clonedRequestObject be the result of creating a Request object,
|
|
753
753
|
// given clonedRequest, this’s headers’s guard, and this’s relevant Realm.
|
|
754
|
-
const clonedRequestObject = new Request(kConstruct)
|
|
755
|
-
clonedRequestObject[kState] = clonedRequest
|
|
756
|
-
clonedRequestObject[kRealm] = this[kRealm]
|
|
757
|
-
clonedRequestObject[kHeaders] = new Headers(kConstruct)
|
|
758
|
-
clonedRequestObject[kHeaders][kHeadersList] = clonedRequest.headersList
|
|
759
|
-
clonedRequestObject[kHeaders][kGuard] = this[kHeaders][kGuard]
|
|
760
|
-
clonedRequestObject[kHeaders][kRealm] = this[kHeaders][kRealm]
|
|
761
|
-
|
|
762
754
|
// 4. Make clonedRequestObject’s signal follow this’s signal.
|
|
763
755
|
const ac = new AbortController()
|
|
764
756
|
if (this.signal.aborted) {
|
|
@@ -771,10 +763,9 @@ class Request {
|
|
|
771
763
|
}
|
|
772
764
|
)
|
|
773
765
|
}
|
|
774
|
-
clonedRequestObject[kSignal] = ac.signal
|
|
775
766
|
|
|
776
767
|
// 4. Return clonedRequestObject.
|
|
777
|
-
return
|
|
768
|
+
return fromInnerRequest(clonedRequest, ac.signal, this[kHeaders][kGuard], this[kRealm])
|
|
778
769
|
}
|
|
779
770
|
}
|
|
780
771
|
|
|
@@ -844,6 +835,26 @@ function cloneRequest (request) {
|
|
|
844
835
|
return newRequest
|
|
845
836
|
}
|
|
846
837
|
|
|
838
|
+
/**
|
|
839
|
+
* @param {any} innerRequest
|
|
840
|
+
* @param {AbortSignal} signal
|
|
841
|
+
* @param {'request' | 'immutable' | 'request-no-cors' | 'response' | 'none'} guard
|
|
842
|
+
* @param {any} [realm]
|
|
843
|
+
* @returns {Request}
|
|
844
|
+
*/
|
|
845
|
+
function fromInnerRequest (innerRequest, signal, guard, realm) {
|
|
846
|
+
const request = new Request(kConstruct)
|
|
847
|
+
request[kState] = innerRequest
|
|
848
|
+
request[kRealm] = realm
|
|
849
|
+
request[kSignal] = signal
|
|
850
|
+
request[kSignal][kRealm] = realm
|
|
851
|
+
request[kHeaders] = new Headers(kConstruct)
|
|
852
|
+
request[kHeaders][kHeadersList] = innerRequest.headersList
|
|
853
|
+
request[kHeaders][kGuard] = guard
|
|
854
|
+
request[kHeaders][kRealm] = realm
|
|
855
|
+
return request
|
|
856
|
+
}
|
|
857
|
+
|
|
847
858
|
Object.defineProperties(Request.prototype, {
|
|
848
859
|
method: kEnumerableProperty,
|
|
849
860
|
url: kEnumerableProperty,
|
|
@@ -970,4 +981,4 @@ webidl.converters.RequestInit = webidl.dictionaryConverter([
|
|
|
970
981
|
}
|
|
971
982
|
])
|
|
972
983
|
|
|
973
|
-
module.exports = { Request, makeRequest }
|
|
984
|
+
module.exports = { Request, makeRequest, fromInnerRequest }
|
|
@@ -38,6 +38,7 @@ class RedirectHandler {
|
|
|
38
38
|
this.maxRedirections = maxRedirections
|
|
39
39
|
this.handler = handler
|
|
40
40
|
this.history = []
|
|
41
|
+
this.redirectionLimitReached = false
|
|
41
42
|
|
|
42
43
|
if (util.isStream(this.opts.body)) {
|
|
43
44
|
// TODO (fix): Provide some way for the user to cache the file to e.g. /tmp
|
|
@@ -91,6 +92,16 @@ class RedirectHandler {
|
|
|
91
92
|
? null
|
|
92
93
|
: parseLocation(statusCode, headers)
|
|
93
94
|
|
|
95
|
+
if (this.opts.throwOnMaxRedirect && this.history.length >= this.maxRedirections) {
|
|
96
|
+
if (this.request) {
|
|
97
|
+
this.request.abort(new Error('max redirects'))
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
this.redirectionLimitReached = true
|
|
101
|
+
this.abort(new Error('max redirects'))
|
|
102
|
+
return
|
|
103
|
+
}
|
|
104
|
+
|
|
94
105
|
if (this.opts.origin) {
|
|
95
106
|
this.history.push(new URL(this.opts.path, this.opts.origin))
|
|
96
107
|
}
|
package/lib/mock/mock-utils.js
CHANGED
|
@@ -188,11 +188,21 @@ function buildKey (opts) {
|
|
|
188
188
|
}
|
|
189
189
|
|
|
190
190
|
function generateKeyValues (data) {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
191
|
+
const keys = Object.keys(data)
|
|
192
|
+
const result = []
|
|
193
|
+
for (let i = 0; i < keys.length; ++i) {
|
|
194
|
+
const key = keys[i]
|
|
195
|
+
const value = data[key]
|
|
196
|
+
const name = Buffer.from(`${key}`)
|
|
197
|
+
if (Array.isArray(value)) {
|
|
198
|
+
for (let j = 0; j < value.length; ++j) {
|
|
199
|
+
result.push(name, Buffer.from(`${value[j]}`))
|
|
200
|
+
}
|
|
201
|
+
} else {
|
|
202
|
+
result.push(name, Buffer.from(`${value}`))
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return result
|
|
196
206
|
}
|
|
197
207
|
|
|
198
208
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "undici",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.4.0",
|
|
4
4
|
"description": "An HTTP/1.1 client, written from scratch for Node.js",
|
|
5
5
|
"homepage": "https://undici.nodejs.org",
|
|
6
6
|
"bugs": {
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
"bench": "PORT=3042 concurrently -k -s first npm:bench:server npm:bench:run",
|
|
93
93
|
"bench:server": "node benchmarks/server.js",
|
|
94
94
|
"prebench:run": "node benchmarks/wait.js",
|
|
95
|
-
"bench:run": "
|
|
95
|
+
"bench:run": "SAMPLES=100 CONNECTIONS=50 node benchmarks/benchmark.js",
|
|
96
96
|
"serve:website": "docsify serve .",
|
|
97
97
|
"prepare": "husky install",
|
|
98
98
|
"fuzz": "jsfuzz test/fuzzing/fuzz.js corpus"
|
|
@@ -136,7 +136,11 @@
|
|
|
136
136
|
"tsd": "^0.30.1",
|
|
137
137
|
"typescript": "^5.0.2",
|
|
138
138
|
"wait-on": "^7.0.1",
|
|
139
|
-
"ws": "^8.11.0"
|
|
139
|
+
"ws": "^8.11.0",
|
|
140
|
+
"axios": "^1.6.5",
|
|
141
|
+
"got": "^14.0.0",
|
|
142
|
+
"node-fetch": "^3.3.2",
|
|
143
|
+
"request": "^2.88.2"
|
|
140
144
|
},
|
|
141
145
|
"engines": {
|
|
142
146
|
"node": ">=18.0"
|
package/types/dispatcher.d.ts
CHANGED
|
@@ -131,6 +131,8 @@ declare namespace Dispatcher {
|
|
|
131
131
|
opaque?: unknown;
|
|
132
132
|
/** Default: 0 */
|
|
133
133
|
maxRedirections?: number;
|
|
134
|
+
/** Default: false */
|
|
135
|
+
redirectionLimitReached?: boolean;
|
|
134
136
|
/** Default: `null` */
|
|
135
137
|
responseHeader?: 'raw' | null;
|
|
136
138
|
}
|
|
@@ -141,6 +143,8 @@ declare namespace Dispatcher {
|
|
|
141
143
|
signal?: AbortSignal | EventEmitter | null;
|
|
142
144
|
/** Default: 0 */
|
|
143
145
|
maxRedirections?: number;
|
|
146
|
+
/** Default: false */
|
|
147
|
+
redirectionLimitReached?: boolean;
|
|
144
148
|
/** Default: `null` */
|
|
145
149
|
onInfo?: (info: { statusCode: number, headers: Record<string, string | string[]> }) => void;
|
|
146
150
|
/** Default: `null` */
|
|
@@ -164,6 +168,8 @@ declare namespace Dispatcher {
|
|
|
164
168
|
signal?: AbortSignal | EventEmitter | null;
|
|
165
169
|
/** Default: 0 */
|
|
166
170
|
maxRedirections?: number;
|
|
171
|
+
/** Default: false */
|
|
172
|
+
redirectionLimitReached?: boolean;
|
|
167
173
|
/** Default: `null` */
|
|
168
174
|
responseHeader?: 'raw' | null;
|
|
169
175
|
}
|
|
@@ -229,7 +235,7 @@ declare namespace Dispatcher {
|
|
|
229
235
|
* @link https://fetch.spec.whatwg.org/#body-mixin
|
|
230
236
|
*/
|
|
231
237
|
interface BodyMixin {
|
|
232
|
-
readonly body?: never;
|
|
238
|
+
readonly body?: never;
|
|
233
239
|
readonly bodyUsed: boolean;
|
|
234
240
|
arrayBuffer(): Promise<ArrayBuffer>;
|
|
235
241
|
blob(): Promise<Blob>;
|
package/types/handlers.d.ts
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
import Dispatcher from "./dispatcher";
|
|
2
2
|
|
|
3
|
-
export declare class RedirectHandler implements Dispatcher.DispatchHandlers{
|
|
4
|
-
constructor
|
|
3
|
+
export declare class RedirectHandler implements Dispatcher.DispatchHandlers {
|
|
4
|
+
constructor(
|
|
5
|
+
dispatch: Dispatcher,
|
|
6
|
+
maxRedirections: number,
|
|
7
|
+
opts: Dispatcher.DispatchOptions,
|
|
8
|
+
handler: Dispatcher.DispatchHandlers,
|
|
9
|
+
redirectionLimitReached: boolean
|
|
10
|
+
);
|
|
5
11
|
}
|
|
6
12
|
|
|
7
|
-
export declare class DecoratorHandler implements Dispatcher.DispatchHandlers{
|
|
8
|
-
constructor
|
|
13
|
+
export declare class DecoratorHandler implements Dispatcher.DispatchHandlers {
|
|
14
|
+
constructor(handler: Dispatcher.DispatchHandlers);
|
|
9
15
|
}
|
package/types/readable.d.ts
CHANGED
|
@@ -44,9 +44,8 @@ declare class BodyReadable extends Readable {
|
|
|
44
44
|
*/
|
|
45
45
|
readonly bodyUsed: boolean
|
|
46
46
|
|
|
47
|
-
/**
|
|
48
|
-
*
|
|
49
|
-
* If body is null, it should return null as the body
|
|
47
|
+
/**
|
|
48
|
+
* If body is null, it should return null as the body
|
|
50
49
|
*
|
|
51
50
|
* If body is not null, should return the body as a ReadableStream
|
|
52
51
|
*
|