undici 7.15.0 → 7.17.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 +48 -2
- package/docs/docs/api/Agent.md +1 -0
- package/docs/docs/api/Client.md +1 -0
- package/docs/docs/api/DiagnosticsChannel.md +57 -0
- package/docs/docs/api/Dispatcher.md +86 -0
- package/docs/docs/api/Errors.md +0 -1
- package/docs/docs/api/RoundRobinPool.md +145 -0
- package/docs/docs/api/WebSocket.md +21 -0
- package/docs/docs/best-practices/crawling.md +58 -0
- package/index-fetch.js +2 -2
- package/index.js +8 -9
- package/lib/api/api-request.js +22 -8
- package/lib/api/api-upgrade.js +2 -1
- package/lib/api/readable.js +7 -5
- package/lib/core/connect.js +4 -1
- package/lib/core/diagnostics.js +28 -1
- package/lib/core/errors.js +217 -13
- package/lib/core/request.js +5 -1
- package/lib/core/symbols.js +3 -0
- package/lib/core/util.js +61 -41
- package/lib/dispatcher/agent.js +19 -7
- package/lib/dispatcher/balanced-pool.js +10 -0
- package/lib/dispatcher/client-h1.js +18 -23
- package/lib/dispatcher/client-h2.js +166 -26
- package/lib/dispatcher/client.js +64 -59
- package/lib/dispatcher/dispatcher-base.js +20 -16
- package/lib/dispatcher/env-http-proxy-agent.js +12 -16
- package/lib/dispatcher/fixed-queue.js +15 -39
- package/lib/dispatcher/h2c-client.js +7 -78
- package/lib/dispatcher/pool-base.js +60 -43
- package/lib/dispatcher/pool.js +2 -2
- package/lib/dispatcher/proxy-agent.js +27 -11
- package/lib/dispatcher/round-robin-pool.js +137 -0
- package/lib/encoding/index.js +33 -0
- package/lib/global.js +19 -1
- package/lib/handler/cache-handler.js +84 -27
- package/lib/handler/deduplication-handler.js +216 -0
- package/lib/handler/retry-handler.js +0 -2
- package/lib/interceptor/cache.js +94 -15
- package/lib/interceptor/decompress.js +2 -1
- package/lib/interceptor/deduplicate.js +109 -0
- package/lib/interceptor/dns.js +55 -13
- package/lib/mock/mock-agent.js +4 -4
- package/lib/mock/mock-errors.js +10 -0
- package/lib/mock/mock-utils.js +13 -12
- package/lib/mock/snapshot-agent.js +11 -5
- package/lib/mock/snapshot-recorder.js +12 -4
- package/lib/mock/snapshot-utils.js +4 -4
- package/lib/util/cache.js +29 -1
- package/lib/util/date.js +534 -140
- package/lib/util/runtime-features.js +124 -0
- package/lib/web/cookies/index.js +1 -1
- package/lib/web/cookies/parse.js +1 -1
- package/lib/web/eventsource/eventsource-stream.js +2 -2
- package/lib/web/eventsource/eventsource.js +34 -29
- package/lib/web/eventsource/util.js +1 -9
- package/lib/web/fetch/body.js +45 -61
- package/lib/web/fetch/data-url.js +12 -160
- package/lib/web/fetch/formdata-parser.js +204 -127
- package/lib/web/fetch/index.js +21 -19
- package/lib/web/fetch/request.js +6 -0
- package/lib/web/fetch/response.js +4 -7
- package/lib/web/fetch/util.js +10 -79
- package/lib/web/infra/index.js +229 -0
- package/lib/web/subresource-integrity/subresource-integrity.js +6 -5
- package/lib/web/webidl/index.js +207 -44
- package/lib/web/websocket/connection.js +33 -22
- package/lib/web/websocket/events.js +1 -1
- package/lib/web/websocket/frame.js +9 -15
- package/lib/web/websocket/stream/websocketerror.js +22 -1
- package/lib/web/websocket/stream/websocketstream.js +17 -8
- package/lib/web/websocket/util.js +2 -1
- package/lib/web/websocket/websocket.js +32 -42
- package/package.json +9 -7
- package/types/agent.d.ts +2 -1
- package/types/api.d.ts +2 -2
- package/types/balanced-pool.d.ts +2 -1
- package/types/cache-interceptor.d.ts +1 -0
- package/types/client.d.ts +1 -1
- package/types/connector.d.ts +2 -2
- package/types/diagnostics-channel.d.ts +2 -2
- package/types/dispatcher.d.ts +12 -12
- package/types/errors.d.ts +5 -15
- package/types/fetch.d.ts +4 -4
- package/types/formdata.d.ts +1 -1
- package/types/h2c-client.d.ts +1 -1
- package/types/index.d.ts +9 -1
- package/types/interceptors.d.ts +36 -2
- package/types/pool.d.ts +1 -1
- package/types/readable.d.ts +2 -2
- package/types/round-robin-pool.d.ts +41 -0
- package/types/webidl.d.ts +82 -21
- package/types/websocket.d.ts +9 -9
package/lib/core/connect.js
CHANGED
|
@@ -43,7 +43,7 @@ const SessionCache = class WeakSessionCache {
|
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
function buildConnector ({ allowH2, maxCachedSessions, socketPath, timeout, session: customSession, ...opts }) {
|
|
46
|
+
function buildConnector ({ allowH2, useH2c, maxCachedSessions, socketPath, timeout, session: customSession, ...opts }) {
|
|
47
47
|
if (maxCachedSessions != null && (!Number.isInteger(maxCachedSessions) || maxCachedSessions < 0)) {
|
|
48
48
|
throw new InvalidArgumentError('maxCachedSessions must be a positive integer or zero')
|
|
49
49
|
}
|
|
@@ -96,6 +96,9 @@ function buildConnector ({ allowH2, maxCachedSessions, socketPath, timeout, sess
|
|
|
96
96
|
port,
|
|
97
97
|
host: hostname
|
|
98
98
|
})
|
|
99
|
+
if (useH2c === true) {
|
|
100
|
+
socket.alpnProtocol = 'h2'
|
|
101
|
+
}
|
|
99
102
|
}
|
|
100
103
|
|
|
101
104
|
// Set TCP keep alive options on the socket here instead of in connect() for the case of assigning the socket
|
package/lib/core/diagnostics.js
CHANGED
|
@@ -26,7 +26,9 @@ const channels = {
|
|
|
26
26
|
close: diagnosticsChannel.channel('undici:websocket:close'),
|
|
27
27
|
socketError: diagnosticsChannel.channel('undici:websocket:socket_error'),
|
|
28
28
|
ping: diagnosticsChannel.channel('undici:websocket:ping'),
|
|
29
|
-
pong: diagnosticsChannel.channel('undici:websocket:pong')
|
|
29
|
+
pong: diagnosticsChannel.channel('undici:websocket:pong'),
|
|
30
|
+
// ProxyAgent
|
|
31
|
+
proxyConnected: diagnosticsChannel.channel('undici:proxy:connected')
|
|
30
32
|
}
|
|
31
33
|
|
|
32
34
|
let isTrackingClientEvents = false
|
|
@@ -36,6 +38,14 @@ function trackClientEvents (debugLog = undiciDebugLog) {
|
|
|
36
38
|
return
|
|
37
39
|
}
|
|
38
40
|
|
|
41
|
+
// Check if any of the channels already have subscribers to prevent duplicate subscriptions
|
|
42
|
+
// This can happen when both Node.js built-in undici and undici as a dependency are present
|
|
43
|
+
if (channels.beforeConnect.hasSubscribers || channels.connected.hasSubscribers ||
|
|
44
|
+
channels.connectError.hasSubscribers || channels.sendHeaders.hasSubscribers) {
|
|
45
|
+
isTrackingClientEvents = true
|
|
46
|
+
return
|
|
47
|
+
}
|
|
48
|
+
|
|
39
49
|
isTrackingClientEvents = true
|
|
40
50
|
|
|
41
51
|
diagnosticsChannel.subscribe('undici:client:beforeConnect',
|
|
@@ -98,6 +108,14 @@ function trackRequestEvents (debugLog = undiciDebugLog) {
|
|
|
98
108
|
return
|
|
99
109
|
}
|
|
100
110
|
|
|
111
|
+
// Check if any of the channels already have subscribers to prevent duplicate subscriptions
|
|
112
|
+
// This can happen when both Node.js built-in undici and undici as a dependency are present
|
|
113
|
+
if (channels.headers.hasSubscribers || channels.trailers.hasSubscribers ||
|
|
114
|
+
channels.error.hasSubscribers) {
|
|
115
|
+
isTrackingRequestEvents = true
|
|
116
|
+
return
|
|
117
|
+
}
|
|
118
|
+
|
|
101
119
|
isTrackingRequestEvents = true
|
|
102
120
|
|
|
103
121
|
diagnosticsChannel.subscribe('undici:request:headers',
|
|
@@ -146,6 +164,15 @@ function trackWebSocketEvents (debugLog = websocketDebuglog) {
|
|
|
146
164
|
return
|
|
147
165
|
}
|
|
148
166
|
|
|
167
|
+
// Check if any of the channels already have subscribers to prevent duplicate subscriptions
|
|
168
|
+
// This can happen when both Node.js built-in undici and undici as a dependency are present
|
|
169
|
+
if (channels.open.hasSubscribers || channels.close.hasSubscribers ||
|
|
170
|
+
channels.socketError.hasSubscribers || channels.ping.hasSubscribers ||
|
|
171
|
+
channels.pong.hasSubscribers) {
|
|
172
|
+
isTrackingWebSocketEvents = true
|
|
173
|
+
return
|
|
174
|
+
}
|
|
175
|
+
|
|
149
176
|
isTrackingWebSocketEvents = true
|
|
150
177
|
|
|
151
178
|
diagnosticsChannel.subscribe('undici:websocket:open',
|
package/lib/core/errors.js
CHANGED
|
@@ -1,13 +1,23 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const kUndiciError = Symbol.for('undici.error.UND_ERR')
|
|
3
4
|
class UndiciError extends Error {
|
|
4
5
|
constructor (message, options) {
|
|
5
6
|
super(message, options)
|
|
6
7
|
this.name = 'UndiciError'
|
|
7
8
|
this.code = 'UND_ERR'
|
|
8
9
|
}
|
|
10
|
+
|
|
11
|
+
static [Symbol.hasInstance] (instance) {
|
|
12
|
+
return instance && instance[kUndiciError] === true
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
get [kUndiciError] () {
|
|
16
|
+
return true
|
|
17
|
+
}
|
|
9
18
|
}
|
|
10
19
|
|
|
20
|
+
const kConnectTimeoutError = Symbol.for('undici.error.UND_ERR_CONNECT_TIMEOUT')
|
|
11
21
|
class ConnectTimeoutError extends UndiciError {
|
|
12
22
|
constructor (message) {
|
|
13
23
|
super(message)
|
|
@@ -15,8 +25,17 @@ class ConnectTimeoutError extends UndiciError {
|
|
|
15
25
|
this.message = message || 'Connect Timeout Error'
|
|
16
26
|
this.code = 'UND_ERR_CONNECT_TIMEOUT'
|
|
17
27
|
}
|
|
28
|
+
|
|
29
|
+
static [Symbol.hasInstance] (instance) {
|
|
30
|
+
return instance && instance[kConnectTimeoutError] === true
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
get [kConnectTimeoutError] () {
|
|
34
|
+
return true
|
|
35
|
+
}
|
|
18
36
|
}
|
|
19
37
|
|
|
38
|
+
const kHeadersTimeoutError = Symbol.for('undici.error.UND_ERR_HEADERS_TIMEOUT')
|
|
20
39
|
class HeadersTimeoutError extends UndiciError {
|
|
21
40
|
constructor (message) {
|
|
22
41
|
super(message)
|
|
@@ -24,8 +43,17 @@ class HeadersTimeoutError extends UndiciError {
|
|
|
24
43
|
this.message = message || 'Headers Timeout Error'
|
|
25
44
|
this.code = 'UND_ERR_HEADERS_TIMEOUT'
|
|
26
45
|
}
|
|
46
|
+
|
|
47
|
+
static [Symbol.hasInstance] (instance) {
|
|
48
|
+
return instance && instance[kHeadersTimeoutError] === true
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
get [kHeadersTimeoutError] () {
|
|
52
|
+
return true
|
|
53
|
+
}
|
|
27
54
|
}
|
|
28
55
|
|
|
56
|
+
const kHeadersOverflowError = Symbol.for('undici.error.UND_ERR_HEADERS_OVERFLOW')
|
|
29
57
|
class HeadersOverflowError extends UndiciError {
|
|
30
58
|
constructor (message) {
|
|
31
59
|
super(message)
|
|
@@ -33,8 +61,17 @@ class HeadersOverflowError extends UndiciError {
|
|
|
33
61
|
this.message = message || 'Headers Overflow Error'
|
|
34
62
|
this.code = 'UND_ERR_HEADERS_OVERFLOW'
|
|
35
63
|
}
|
|
64
|
+
|
|
65
|
+
static [Symbol.hasInstance] (instance) {
|
|
66
|
+
return instance && instance[kHeadersOverflowError] === true
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
get [kHeadersOverflowError] () {
|
|
70
|
+
return true
|
|
71
|
+
}
|
|
36
72
|
}
|
|
37
73
|
|
|
74
|
+
const kBodyTimeoutError = Symbol.for('undici.error.UND_ERR_BODY_TIMEOUT')
|
|
38
75
|
class BodyTimeoutError extends UndiciError {
|
|
39
76
|
constructor (message) {
|
|
40
77
|
super(message)
|
|
@@ -42,21 +79,17 @@ class BodyTimeoutError extends UndiciError {
|
|
|
42
79
|
this.message = message || 'Body Timeout Error'
|
|
43
80
|
this.code = 'UND_ERR_BODY_TIMEOUT'
|
|
44
81
|
}
|
|
45
|
-
}
|
|
46
82
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
this.body = body
|
|
54
|
-
this.status = statusCode
|
|
55
|
-
this.statusCode = statusCode
|
|
56
|
-
this.headers = headers
|
|
83
|
+
static [Symbol.hasInstance] (instance) {
|
|
84
|
+
return instance && instance[kBodyTimeoutError] === true
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
get [kBodyTimeoutError] () {
|
|
88
|
+
return true
|
|
57
89
|
}
|
|
58
90
|
}
|
|
59
91
|
|
|
92
|
+
const kInvalidArgumentError = Symbol.for('undici.error.UND_ERR_INVALID_ARG')
|
|
60
93
|
class InvalidArgumentError extends UndiciError {
|
|
61
94
|
constructor (message) {
|
|
62
95
|
super(message)
|
|
@@ -64,8 +97,17 @@ class InvalidArgumentError extends UndiciError {
|
|
|
64
97
|
this.message = message || 'Invalid Argument Error'
|
|
65
98
|
this.code = 'UND_ERR_INVALID_ARG'
|
|
66
99
|
}
|
|
100
|
+
|
|
101
|
+
static [Symbol.hasInstance] (instance) {
|
|
102
|
+
return instance && instance[kInvalidArgumentError] === true
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
get [kInvalidArgumentError] () {
|
|
106
|
+
return true
|
|
107
|
+
}
|
|
67
108
|
}
|
|
68
109
|
|
|
110
|
+
const kInvalidReturnValueError = Symbol.for('undici.error.UND_ERR_INVALID_RETURN_VALUE')
|
|
69
111
|
class InvalidReturnValueError extends UndiciError {
|
|
70
112
|
constructor (message) {
|
|
71
113
|
super(message)
|
|
@@ -73,16 +115,35 @@ class InvalidReturnValueError extends UndiciError {
|
|
|
73
115
|
this.message = message || 'Invalid Return Value Error'
|
|
74
116
|
this.code = 'UND_ERR_INVALID_RETURN_VALUE'
|
|
75
117
|
}
|
|
118
|
+
|
|
119
|
+
static [Symbol.hasInstance] (instance) {
|
|
120
|
+
return instance && instance[kInvalidReturnValueError] === true
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
get [kInvalidReturnValueError] () {
|
|
124
|
+
return true
|
|
125
|
+
}
|
|
76
126
|
}
|
|
77
127
|
|
|
128
|
+
const kAbortError = Symbol.for('undici.error.UND_ERR_ABORT')
|
|
78
129
|
class AbortError extends UndiciError {
|
|
79
130
|
constructor (message) {
|
|
80
131
|
super(message)
|
|
81
132
|
this.name = 'AbortError'
|
|
82
133
|
this.message = message || 'The operation was aborted'
|
|
134
|
+
this.code = 'UND_ERR_ABORT'
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
static [Symbol.hasInstance] (instance) {
|
|
138
|
+
return instance && instance[kAbortError] === true
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
get [kAbortError] () {
|
|
142
|
+
return true
|
|
83
143
|
}
|
|
84
144
|
}
|
|
85
145
|
|
|
146
|
+
const kRequestAbortedError = Symbol.for('undici.error.UND_ERR_ABORTED')
|
|
86
147
|
class RequestAbortedError extends AbortError {
|
|
87
148
|
constructor (message) {
|
|
88
149
|
super(message)
|
|
@@ -90,8 +151,17 @@ class RequestAbortedError extends AbortError {
|
|
|
90
151
|
this.message = message || 'Request aborted'
|
|
91
152
|
this.code = 'UND_ERR_ABORTED'
|
|
92
153
|
}
|
|
154
|
+
|
|
155
|
+
static [Symbol.hasInstance] (instance) {
|
|
156
|
+
return instance && instance[kRequestAbortedError] === true
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
get [kRequestAbortedError] () {
|
|
160
|
+
return true
|
|
161
|
+
}
|
|
93
162
|
}
|
|
94
163
|
|
|
164
|
+
const kInformationalError = Symbol.for('undici.error.UND_ERR_INFO')
|
|
95
165
|
class InformationalError extends UndiciError {
|
|
96
166
|
constructor (message) {
|
|
97
167
|
super(message)
|
|
@@ -99,8 +169,17 @@ class InformationalError extends UndiciError {
|
|
|
99
169
|
this.message = message || 'Request information'
|
|
100
170
|
this.code = 'UND_ERR_INFO'
|
|
101
171
|
}
|
|
172
|
+
|
|
173
|
+
static [Symbol.hasInstance] (instance) {
|
|
174
|
+
return instance && instance[kInformationalError] === true
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
get [kInformationalError] () {
|
|
178
|
+
return true
|
|
179
|
+
}
|
|
102
180
|
}
|
|
103
181
|
|
|
182
|
+
const kRequestContentLengthMismatchError = Symbol.for('undici.error.UND_ERR_REQ_CONTENT_LENGTH_MISMATCH')
|
|
104
183
|
class RequestContentLengthMismatchError extends UndiciError {
|
|
105
184
|
constructor (message) {
|
|
106
185
|
super(message)
|
|
@@ -108,8 +187,17 @@ class RequestContentLengthMismatchError extends UndiciError {
|
|
|
108
187
|
this.message = message || 'Request body length does not match content-length header'
|
|
109
188
|
this.code = 'UND_ERR_REQ_CONTENT_LENGTH_MISMATCH'
|
|
110
189
|
}
|
|
190
|
+
|
|
191
|
+
static [Symbol.hasInstance] (instance) {
|
|
192
|
+
return instance && instance[kRequestContentLengthMismatchError] === true
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
get [kRequestContentLengthMismatchError] () {
|
|
196
|
+
return true
|
|
197
|
+
}
|
|
111
198
|
}
|
|
112
199
|
|
|
200
|
+
const kResponseContentLengthMismatchError = Symbol.for('undici.error.UND_ERR_RES_CONTENT_LENGTH_MISMATCH')
|
|
113
201
|
class ResponseContentLengthMismatchError extends UndiciError {
|
|
114
202
|
constructor (message) {
|
|
115
203
|
super(message)
|
|
@@ -117,8 +205,17 @@ class ResponseContentLengthMismatchError extends UndiciError {
|
|
|
117
205
|
this.message = message || 'Response body length does not match content-length header'
|
|
118
206
|
this.code = 'UND_ERR_RES_CONTENT_LENGTH_MISMATCH'
|
|
119
207
|
}
|
|
208
|
+
|
|
209
|
+
static [Symbol.hasInstance] (instance) {
|
|
210
|
+
return instance && instance[kResponseContentLengthMismatchError] === true
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
get [kResponseContentLengthMismatchError] () {
|
|
214
|
+
return true
|
|
215
|
+
}
|
|
120
216
|
}
|
|
121
217
|
|
|
218
|
+
const kClientDestroyedError = Symbol.for('undici.error.UND_ERR_DESTROYED')
|
|
122
219
|
class ClientDestroyedError extends UndiciError {
|
|
123
220
|
constructor (message) {
|
|
124
221
|
super(message)
|
|
@@ -126,8 +223,17 @@ class ClientDestroyedError extends UndiciError {
|
|
|
126
223
|
this.message = message || 'The client is destroyed'
|
|
127
224
|
this.code = 'UND_ERR_DESTROYED'
|
|
128
225
|
}
|
|
226
|
+
|
|
227
|
+
static [Symbol.hasInstance] (instance) {
|
|
228
|
+
return instance && instance[kClientDestroyedError] === true
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
get [kClientDestroyedError] () {
|
|
232
|
+
return true
|
|
233
|
+
}
|
|
129
234
|
}
|
|
130
235
|
|
|
236
|
+
const kClientClosedError = Symbol.for('undici.error.UND_ERR_CLOSED')
|
|
131
237
|
class ClientClosedError extends UndiciError {
|
|
132
238
|
constructor (message) {
|
|
133
239
|
super(message)
|
|
@@ -135,8 +241,17 @@ class ClientClosedError extends UndiciError {
|
|
|
135
241
|
this.message = message || 'The client is closed'
|
|
136
242
|
this.code = 'UND_ERR_CLOSED'
|
|
137
243
|
}
|
|
244
|
+
|
|
245
|
+
static [Symbol.hasInstance] (instance) {
|
|
246
|
+
return instance && instance[kClientClosedError] === true
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
get [kClientClosedError] () {
|
|
250
|
+
return true
|
|
251
|
+
}
|
|
138
252
|
}
|
|
139
253
|
|
|
254
|
+
const kSocketError = Symbol.for('undici.error.UND_ERR_SOCKET')
|
|
140
255
|
class SocketError extends UndiciError {
|
|
141
256
|
constructor (message, socket) {
|
|
142
257
|
super(message)
|
|
@@ -145,8 +260,17 @@ class SocketError extends UndiciError {
|
|
|
145
260
|
this.code = 'UND_ERR_SOCKET'
|
|
146
261
|
this.socket = socket
|
|
147
262
|
}
|
|
263
|
+
|
|
264
|
+
static [Symbol.hasInstance] (instance) {
|
|
265
|
+
return instance && instance[kSocketError] === true
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
get [kSocketError] () {
|
|
269
|
+
return true
|
|
270
|
+
}
|
|
148
271
|
}
|
|
149
272
|
|
|
273
|
+
const kNotSupportedError = Symbol.for('undici.error.UND_ERR_NOT_SUPPORTED')
|
|
150
274
|
class NotSupportedError extends UndiciError {
|
|
151
275
|
constructor (message) {
|
|
152
276
|
super(message)
|
|
@@ -154,8 +278,17 @@ class NotSupportedError extends UndiciError {
|
|
|
154
278
|
this.message = message || 'Not supported error'
|
|
155
279
|
this.code = 'UND_ERR_NOT_SUPPORTED'
|
|
156
280
|
}
|
|
281
|
+
|
|
282
|
+
static [Symbol.hasInstance] (instance) {
|
|
283
|
+
return instance && instance[kNotSupportedError] === true
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
get [kNotSupportedError] () {
|
|
287
|
+
return true
|
|
288
|
+
}
|
|
157
289
|
}
|
|
158
290
|
|
|
291
|
+
const kBalancedPoolMissingUpstreamError = Symbol.for('undici.error.UND_ERR_BPL_MISSING_UPSTREAM')
|
|
159
292
|
class BalancedPoolMissingUpstreamError extends UndiciError {
|
|
160
293
|
constructor (message) {
|
|
161
294
|
super(message)
|
|
@@ -163,8 +296,17 @@ class BalancedPoolMissingUpstreamError extends UndiciError {
|
|
|
163
296
|
this.message = message || 'No upstream has been added to the BalancedPool'
|
|
164
297
|
this.code = 'UND_ERR_BPL_MISSING_UPSTREAM'
|
|
165
298
|
}
|
|
299
|
+
|
|
300
|
+
static [Symbol.hasInstance] (instance) {
|
|
301
|
+
return instance && instance[kBalancedPoolMissingUpstreamError] === true
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
get [kBalancedPoolMissingUpstreamError] () {
|
|
305
|
+
return true
|
|
306
|
+
}
|
|
166
307
|
}
|
|
167
308
|
|
|
309
|
+
const kHTTPParserError = Symbol.for('undici.error.UND_ERR_HTTP_PARSER')
|
|
168
310
|
class HTTPParserError extends Error {
|
|
169
311
|
constructor (message, code, data) {
|
|
170
312
|
super(message)
|
|
@@ -172,8 +314,17 @@ class HTTPParserError extends Error {
|
|
|
172
314
|
this.code = code ? `HPE_${code}` : undefined
|
|
173
315
|
this.data = data ? data.toString() : undefined
|
|
174
316
|
}
|
|
317
|
+
|
|
318
|
+
static [Symbol.hasInstance] (instance) {
|
|
319
|
+
return instance && instance[kHTTPParserError] === true
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
get [kHTTPParserError] () {
|
|
323
|
+
return true
|
|
324
|
+
}
|
|
175
325
|
}
|
|
176
326
|
|
|
327
|
+
const kResponseExceededMaxSizeError = Symbol.for('undici.error.UND_ERR_RES_EXCEEDED_MAX_SIZE')
|
|
177
328
|
class ResponseExceededMaxSizeError extends UndiciError {
|
|
178
329
|
constructor (message) {
|
|
179
330
|
super(message)
|
|
@@ -181,8 +332,17 @@ class ResponseExceededMaxSizeError extends UndiciError {
|
|
|
181
332
|
this.message = message || 'Response content exceeded max size'
|
|
182
333
|
this.code = 'UND_ERR_RES_EXCEEDED_MAX_SIZE'
|
|
183
334
|
}
|
|
335
|
+
|
|
336
|
+
static [Symbol.hasInstance] (instance) {
|
|
337
|
+
return instance && instance[kResponseExceededMaxSizeError] === true
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
get [kResponseExceededMaxSizeError] () {
|
|
341
|
+
return true
|
|
342
|
+
}
|
|
184
343
|
}
|
|
185
344
|
|
|
345
|
+
const kRequestRetryError = Symbol.for('undici.error.UND_ERR_REQ_RETRY')
|
|
186
346
|
class RequestRetryError extends UndiciError {
|
|
187
347
|
constructor (message, code, { headers, data }) {
|
|
188
348
|
super(message)
|
|
@@ -193,8 +353,17 @@ class RequestRetryError extends UndiciError {
|
|
|
193
353
|
this.data = data
|
|
194
354
|
this.headers = headers
|
|
195
355
|
}
|
|
356
|
+
|
|
357
|
+
static [Symbol.hasInstance] (instance) {
|
|
358
|
+
return instance && instance[kRequestRetryError] === true
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
get [kRequestRetryError] () {
|
|
362
|
+
return true
|
|
363
|
+
}
|
|
196
364
|
}
|
|
197
365
|
|
|
366
|
+
const kResponseError = Symbol.for('undici.error.UND_ERR_RESPONSE')
|
|
198
367
|
class ResponseError extends UndiciError {
|
|
199
368
|
constructor (message, code, { headers, body }) {
|
|
200
369
|
super(message)
|
|
@@ -205,8 +374,17 @@ class ResponseError extends UndiciError {
|
|
|
205
374
|
this.body = body
|
|
206
375
|
this.headers = headers
|
|
207
376
|
}
|
|
377
|
+
|
|
378
|
+
static [Symbol.hasInstance] (instance) {
|
|
379
|
+
return instance && instance[kResponseError] === true
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
get [kResponseError] () {
|
|
383
|
+
return true
|
|
384
|
+
}
|
|
208
385
|
}
|
|
209
386
|
|
|
387
|
+
const kSecureProxyConnectionError = Symbol.for('undici.error.UND_ERR_PRX_TLS')
|
|
210
388
|
class SecureProxyConnectionError extends UndiciError {
|
|
211
389
|
constructor (cause, message, options = {}) {
|
|
212
390
|
super(message, { cause, ...options })
|
|
@@ -215,6 +393,32 @@ class SecureProxyConnectionError extends UndiciError {
|
|
|
215
393
|
this.code = 'UND_ERR_PRX_TLS'
|
|
216
394
|
this.cause = cause
|
|
217
395
|
}
|
|
396
|
+
|
|
397
|
+
static [Symbol.hasInstance] (instance) {
|
|
398
|
+
return instance && instance[kSecureProxyConnectionError] === true
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
get [kSecureProxyConnectionError] () {
|
|
402
|
+
return true
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
const kMaxOriginsReachedError = Symbol.for('undici.error.UND_ERR_MAX_ORIGINS_REACHED')
|
|
407
|
+
class MaxOriginsReachedError extends UndiciError {
|
|
408
|
+
constructor (message) {
|
|
409
|
+
super(message)
|
|
410
|
+
this.name = 'MaxOriginsReachedError'
|
|
411
|
+
this.message = message || 'Maximum allowed origins reached'
|
|
412
|
+
this.code = 'UND_ERR_MAX_ORIGINS_REACHED'
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
static [Symbol.hasInstance] (instance) {
|
|
416
|
+
return instance && instance[kMaxOriginsReachedError] === true
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
get [kMaxOriginsReachedError] () {
|
|
420
|
+
return true
|
|
421
|
+
}
|
|
218
422
|
}
|
|
219
423
|
|
|
220
424
|
module.exports = {
|
|
@@ -226,7 +430,6 @@ module.exports = {
|
|
|
226
430
|
BodyTimeoutError,
|
|
227
431
|
RequestContentLengthMismatchError,
|
|
228
432
|
ConnectTimeoutError,
|
|
229
|
-
ResponseStatusCodeError,
|
|
230
433
|
InvalidArgumentError,
|
|
231
434
|
InvalidReturnValueError,
|
|
232
435
|
RequestAbortedError,
|
|
@@ -240,5 +443,6 @@ module.exports = {
|
|
|
240
443
|
ResponseExceededMaxSizeError,
|
|
241
444
|
RequestRetryError,
|
|
242
445
|
ResponseError,
|
|
243
|
-
SecureProxyConnectionError
|
|
446
|
+
SecureProxyConnectionError,
|
|
447
|
+
MaxOriginsReachedError
|
|
244
448
|
}
|
package/lib/core/request.js
CHANGED
|
@@ -17,7 +17,8 @@ const {
|
|
|
17
17
|
serializePathWithQuery,
|
|
18
18
|
assertRequestHandler,
|
|
19
19
|
getServerName,
|
|
20
|
-
normalizedMethodRecords
|
|
20
|
+
normalizedMethodRecords,
|
|
21
|
+
getProtocolFromUrlString
|
|
21
22
|
} = require('./util')
|
|
22
23
|
const { channels } = require('./diagnostics.js')
|
|
23
24
|
const { headerNameLowerCasedRecord } = require('./constants')
|
|
@@ -141,8 +142,11 @@ class Request {
|
|
|
141
142
|
|
|
142
143
|
this.path = query ? serializePathWithQuery(path, query) : path
|
|
143
144
|
|
|
145
|
+
// TODO: shall we maybe standardize it to an URL object?
|
|
144
146
|
this.origin = origin
|
|
145
147
|
|
|
148
|
+
this.protocol = getProtocolFromUrlString(origin)
|
|
149
|
+
|
|
146
150
|
this.idempotent = idempotent == null
|
|
147
151
|
? method === 'HEAD' || method === 'GET'
|
|
148
152
|
: idempotent
|
package/lib/core/symbols.js
CHANGED
|
@@ -62,6 +62,9 @@ module.exports = {
|
|
|
62
62
|
kListeners: Symbol('listeners'),
|
|
63
63
|
kHTTPContext: Symbol('http context'),
|
|
64
64
|
kMaxConcurrentStreams: Symbol('max concurrent streams'),
|
|
65
|
+
kEnableConnectProtocol: Symbol('http2session connect protocol'),
|
|
66
|
+
kRemoteSettings: Symbol('http2session remote settings'),
|
|
67
|
+
kHTTP2Stream: Symbol('http2session client stream'),
|
|
65
68
|
kNoProxyAgent: Symbol('no proxy agent'),
|
|
66
69
|
kHttpProxyAgent: Symbol('http proxy agent'),
|
|
67
70
|
kHttpsProxyAgent: Symbol('https proxy agent')
|
package/lib/core/util.js
CHANGED
|
@@ -609,31 +609,28 @@ function ReadableStreamFrom (iterable) {
|
|
|
609
609
|
let iterator
|
|
610
610
|
return new ReadableStream(
|
|
611
611
|
{
|
|
612
|
-
|
|
612
|
+
start () {
|
|
613
613
|
iterator = iterable[Symbol.asyncIterator]()
|
|
614
614
|
},
|
|
615
615
|
pull (controller) {
|
|
616
|
-
|
|
617
|
-
const { done, value } = await iterator.next()
|
|
616
|
+
return iterator.next().then(({ done, value }) => {
|
|
618
617
|
if (done) {
|
|
619
|
-
queueMicrotask(() => {
|
|
618
|
+
return queueMicrotask(() => {
|
|
620
619
|
controller.close()
|
|
621
620
|
controller.byobRequest?.respond(0)
|
|
622
621
|
})
|
|
623
622
|
} else {
|
|
624
623
|
const buf = Buffer.isBuffer(value) ? value : Buffer.from(value)
|
|
625
624
|
if (buf.byteLength) {
|
|
626
|
-
controller.enqueue(new Uint8Array(buf))
|
|
625
|
+
return controller.enqueue(new Uint8Array(buf))
|
|
627
626
|
} else {
|
|
628
|
-
return
|
|
627
|
+
return this.pull(controller)
|
|
629
628
|
}
|
|
630
629
|
}
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
return pull()
|
|
630
|
+
})
|
|
634
631
|
},
|
|
635
|
-
|
|
636
|
-
|
|
632
|
+
cancel () {
|
|
633
|
+
return iterator.return()
|
|
637
634
|
},
|
|
638
635
|
type: 'bytes'
|
|
639
636
|
}
|
|
@@ -669,48 +666,46 @@ function addAbortListener (signal, listener) {
|
|
|
669
666
|
return () => signal.removeListener('abort', listener)
|
|
670
667
|
}
|
|
671
668
|
|
|
669
|
+
const validTokenChars = new Uint8Array([
|
|
670
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0-15
|
|
671
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16-31
|
|
672
|
+
0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, // 32-47 (!"#$%&'()*+,-./)
|
|
673
|
+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 48-63 (0-9:;<=>?)
|
|
674
|
+
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64-79 (@A-O)
|
|
675
|
+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, // 80-95 (P-Z[\]^_)
|
|
676
|
+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96-111 (`a-o)
|
|
677
|
+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, // 112-127 (p-z{|}~)
|
|
678
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 128-143
|
|
679
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 144-159
|
|
680
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 160-175
|
|
681
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 176-191
|
|
682
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 192-207
|
|
683
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 208-223
|
|
684
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 224-239
|
|
685
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 240-255
|
|
686
|
+
])
|
|
687
|
+
|
|
672
688
|
/**
|
|
673
689
|
* @see https://tools.ietf.org/html/rfc7230#section-3.2.6
|
|
674
690
|
* @param {number} c
|
|
675
691
|
* @returns {boolean}
|
|
676
692
|
*/
|
|
677
693
|
function isTokenCharCode (c) {
|
|
678
|
-
|
|
679
|
-
case 0x22:
|
|
680
|
-
case 0x28:
|
|
681
|
-
case 0x29:
|
|
682
|
-
case 0x2c:
|
|
683
|
-
case 0x2f:
|
|
684
|
-
case 0x3a:
|
|
685
|
-
case 0x3b:
|
|
686
|
-
case 0x3c:
|
|
687
|
-
case 0x3d:
|
|
688
|
-
case 0x3e:
|
|
689
|
-
case 0x3f:
|
|
690
|
-
case 0x40:
|
|
691
|
-
case 0x5b:
|
|
692
|
-
case 0x5c:
|
|
693
|
-
case 0x5d:
|
|
694
|
-
case 0x7b:
|
|
695
|
-
case 0x7d:
|
|
696
|
-
// DQUOTE and "(),/:;<=>?@[\]{}"
|
|
697
|
-
return false
|
|
698
|
-
default:
|
|
699
|
-
// VCHAR %x21-7E
|
|
700
|
-
return c >= 0x21 && c <= 0x7e
|
|
701
|
-
}
|
|
694
|
+
return (validTokenChars[c] === 1)
|
|
702
695
|
}
|
|
703
696
|
|
|
697
|
+
const tokenRegExp = /^[\^_`a-zA-Z\-0-9!#$%&'*+.|~]+$/
|
|
698
|
+
|
|
704
699
|
/**
|
|
705
700
|
* @param {string} characters
|
|
706
701
|
* @returns {boolean}
|
|
707
702
|
*/
|
|
708
703
|
function isValidHTTPToken (characters) {
|
|
709
|
-
if (characters.length
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
for (let i = 0; i < characters.length; ++
|
|
713
|
-
if (
|
|
704
|
+
if (characters.length >= 12) return tokenRegExp.test(characters)
|
|
705
|
+
if (characters.length === 0) return false
|
|
706
|
+
|
|
707
|
+
for (let i = 0; i < characters.length; i++) {
|
|
708
|
+
if (validTokenChars[characters.charCodeAt(i)] !== 1) {
|
|
714
709
|
return false
|
|
715
710
|
}
|
|
716
711
|
}
|
|
@@ -879,6 +874,30 @@ function onConnectTimeout (socket, opts) {
|
|
|
879
874
|
destroy(socket, new ConnectTimeoutError(message))
|
|
880
875
|
}
|
|
881
876
|
|
|
877
|
+
/**
|
|
878
|
+
* @param {string} urlString
|
|
879
|
+
* @returns {string}
|
|
880
|
+
*/
|
|
881
|
+
function getProtocolFromUrlString (urlString) {
|
|
882
|
+
if (
|
|
883
|
+
urlString[0] === 'h' &&
|
|
884
|
+
urlString[1] === 't' &&
|
|
885
|
+
urlString[2] === 't' &&
|
|
886
|
+
urlString[3] === 'p'
|
|
887
|
+
) {
|
|
888
|
+
switch (urlString[4]) {
|
|
889
|
+
case ':':
|
|
890
|
+
return 'http:'
|
|
891
|
+
case 's':
|
|
892
|
+
if (urlString[5] === ':') {
|
|
893
|
+
return 'https:'
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
// fallback if none of the usual suspects
|
|
898
|
+
return urlString.slice(0, urlString.indexOf(':') + 1)
|
|
899
|
+
}
|
|
900
|
+
|
|
882
901
|
const kEnumerableProperty = Object.create(null)
|
|
883
902
|
kEnumerableProperty.enumerable = true
|
|
884
903
|
|
|
@@ -950,5 +969,6 @@ module.exports = {
|
|
|
950
969
|
nodeMinor,
|
|
951
970
|
safeHTTPMethods: Object.freeze(['GET', 'HEAD', 'OPTIONS', 'TRACE']),
|
|
952
971
|
wrapRequestBody,
|
|
953
|
-
setupConnectTimeout
|
|
972
|
+
setupConnectTimeout,
|
|
973
|
+
getProtocolFromUrlString
|
|
954
974
|
}
|