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.
Files changed (93) hide show
  1. package/README.md +48 -2
  2. package/docs/docs/api/Agent.md +1 -0
  3. package/docs/docs/api/Client.md +1 -0
  4. package/docs/docs/api/DiagnosticsChannel.md +57 -0
  5. package/docs/docs/api/Dispatcher.md +86 -0
  6. package/docs/docs/api/Errors.md +0 -1
  7. package/docs/docs/api/RoundRobinPool.md +145 -0
  8. package/docs/docs/api/WebSocket.md +21 -0
  9. package/docs/docs/best-practices/crawling.md +58 -0
  10. package/index-fetch.js +2 -2
  11. package/index.js +8 -9
  12. package/lib/api/api-request.js +22 -8
  13. package/lib/api/api-upgrade.js +2 -1
  14. package/lib/api/readable.js +7 -5
  15. package/lib/core/connect.js +4 -1
  16. package/lib/core/diagnostics.js +28 -1
  17. package/lib/core/errors.js +217 -13
  18. package/lib/core/request.js +5 -1
  19. package/lib/core/symbols.js +3 -0
  20. package/lib/core/util.js +61 -41
  21. package/lib/dispatcher/agent.js +19 -7
  22. package/lib/dispatcher/balanced-pool.js +10 -0
  23. package/lib/dispatcher/client-h1.js +18 -23
  24. package/lib/dispatcher/client-h2.js +166 -26
  25. package/lib/dispatcher/client.js +64 -59
  26. package/lib/dispatcher/dispatcher-base.js +20 -16
  27. package/lib/dispatcher/env-http-proxy-agent.js +12 -16
  28. package/lib/dispatcher/fixed-queue.js +15 -39
  29. package/lib/dispatcher/h2c-client.js +7 -78
  30. package/lib/dispatcher/pool-base.js +60 -43
  31. package/lib/dispatcher/pool.js +2 -2
  32. package/lib/dispatcher/proxy-agent.js +27 -11
  33. package/lib/dispatcher/round-robin-pool.js +137 -0
  34. package/lib/encoding/index.js +33 -0
  35. package/lib/global.js +19 -1
  36. package/lib/handler/cache-handler.js +84 -27
  37. package/lib/handler/deduplication-handler.js +216 -0
  38. package/lib/handler/retry-handler.js +0 -2
  39. package/lib/interceptor/cache.js +94 -15
  40. package/lib/interceptor/decompress.js +2 -1
  41. package/lib/interceptor/deduplicate.js +109 -0
  42. package/lib/interceptor/dns.js +55 -13
  43. package/lib/mock/mock-agent.js +4 -4
  44. package/lib/mock/mock-errors.js +10 -0
  45. package/lib/mock/mock-utils.js +13 -12
  46. package/lib/mock/snapshot-agent.js +11 -5
  47. package/lib/mock/snapshot-recorder.js +12 -4
  48. package/lib/mock/snapshot-utils.js +4 -4
  49. package/lib/util/cache.js +29 -1
  50. package/lib/util/date.js +534 -140
  51. package/lib/util/runtime-features.js +124 -0
  52. package/lib/web/cookies/index.js +1 -1
  53. package/lib/web/cookies/parse.js +1 -1
  54. package/lib/web/eventsource/eventsource-stream.js +2 -2
  55. package/lib/web/eventsource/eventsource.js +34 -29
  56. package/lib/web/eventsource/util.js +1 -9
  57. package/lib/web/fetch/body.js +45 -61
  58. package/lib/web/fetch/data-url.js +12 -160
  59. package/lib/web/fetch/formdata-parser.js +204 -127
  60. package/lib/web/fetch/index.js +21 -19
  61. package/lib/web/fetch/request.js +6 -0
  62. package/lib/web/fetch/response.js +4 -7
  63. package/lib/web/fetch/util.js +10 -79
  64. package/lib/web/infra/index.js +229 -0
  65. package/lib/web/subresource-integrity/subresource-integrity.js +6 -5
  66. package/lib/web/webidl/index.js +207 -44
  67. package/lib/web/websocket/connection.js +33 -22
  68. package/lib/web/websocket/events.js +1 -1
  69. package/lib/web/websocket/frame.js +9 -15
  70. package/lib/web/websocket/stream/websocketerror.js +22 -1
  71. package/lib/web/websocket/stream/websocketstream.js +17 -8
  72. package/lib/web/websocket/util.js +2 -1
  73. package/lib/web/websocket/websocket.js +32 -42
  74. package/package.json +9 -7
  75. package/types/agent.d.ts +2 -1
  76. package/types/api.d.ts +2 -2
  77. package/types/balanced-pool.d.ts +2 -1
  78. package/types/cache-interceptor.d.ts +1 -0
  79. package/types/client.d.ts +1 -1
  80. package/types/connector.d.ts +2 -2
  81. package/types/diagnostics-channel.d.ts +2 -2
  82. package/types/dispatcher.d.ts +12 -12
  83. package/types/errors.d.ts +5 -15
  84. package/types/fetch.d.ts +4 -4
  85. package/types/formdata.d.ts +1 -1
  86. package/types/h2c-client.d.ts +1 -1
  87. package/types/index.d.ts +9 -1
  88. package/types/interceptors.d.ts +36 -2
  89. package/types/pool.d.ts +1 -1
  90. package/types/readable.d.ts +2 -2
  91. package/types/round-robin-pool.d.ts +41 -0
  92. package/types/webidl.d.ts +82 -21
  93. package/types/websocket.d.ts +9 -9
@@ -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
@@ -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',
@@ -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
- class ResponseStatusCodeError extends UndiciError {
48
- constructor (message, statusCode, headers, body) {
49
- super(message)
50
- this.name = 'ResponseStatusCodeError'
51
- this.message = message || 'Response Status Code Error'
52
- this.code = 'UND_ERR_RESPONSE_STATUS_CODE'
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
  }
@@ -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
@@ -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
- async start () {
612
+ start () {
613
613
  iterator = iterable[Symbol.asyncIterator]()
614
614
  },
615
615
  pull (controller) {
616
- async function pull () {
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 await pull()
627
+ return this.pull(controller)
629
628
  }
630
629
  }
631
- }
632
-
633
- return pull()
630
+ })
634
631
  },
635
- async cancel () {
636
- await iterator.return()
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
- switch (c) {
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 === 0) {
710
- return false
711
- }
712
- for (let i = 0; i < characters.length; ++i) {
713
- if (!isTokenCharCode(characters.charCodeAt(i))) {
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
  }