undici 7.14.0 → 7.16.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 (63) hide show
  1. package/README.md +1 -1
  2. package/docs/docs/api/Agent.md +1 -0
  3. package/docs/docs/api/Dispatcher.md +59 -0
  4. package/docs/docs/api/Errors.md +0 -1
  5. package/index-fetch.js +2 -2
  6. package/index.js +6 -9
  7. package/lib/api/api-request.js +22 -8
  8. package/lib/api/readable.js +7 -5
  9. package/lib/core/errors.js +217 -13
  10. package/lib/core/request.js +5 -1
  11. package/lib/core/util.js +45 -11
  12. package/lib/dispatcher/agent.js +44 -23
  13. package/lib/dispatcher/client-h1.js +20 -9
  14. package/lib/dispatcher/client-h2.js +13 -3
  15. package/lib/dispatcher/client.js +57 -57
  16. package/lib/dispatcher/dispatcher-base.js +12 -7
  17. package/lib/dispatcher/env-http-proxy-agent.js +12 -16
  18. package/lib/dispatcher/fixed-queue.js +15 -39
  19. package/lib/dispatcher/h2c-client.js +6 -6
  20. package/lib/dispatcher/pool-base.js +60 -43
  21. package/lib/dispatcher/pool.js +2 -2
  22. package/lib/dispatcher/proxy-agent.js +14 -9
  23. package/lib/global.js +19 -1
  24. package/lib/interceptor/cache.js +61 -0
  25. package/lib/interceptor/decompress.js +253 -0
  26. package/lib/llhttp/constants.d.ts +99 -1
  27. package/lib/llhttp/constants.js +34 -1
  28. package/lib/llhttp/llhttp-wasm.js +1 -1
  29. package/lib/llhttp/llhttp_simd-wasm.js +1 -1
  30. package/lib/llhttp/utils.d.ts +2 -2
  31. package/lib/llhttp/utils.js +3 -6
  32. package/lib/mock/mock-agent.js +4 -4
  33. package/lib/mock/mock-errors.js +10 -0
  34. package/lib/mock/mock-utils.js +12 -10
  35. package/lib/util/cache.js +6 -7
  36. package/lib/util/date.js +534 -140
  37. package/lib/web/cookies/index.js +1 -1
  38. package/lib/web/cookies/parse.js +2 -2
  39. package/lib/web/eventsource/eventsource-stream.js +2 -2
  40. package/lib/web/eventsource/eventsource.js +34 -29
  41. package/lib/web/eventsource/util.js +1 -9
  42. package/lib/web/fetch/body.js +20 -26
  43. package/lib/web/fetch/index.js +15 -16
  44. package/lib/web/fetch/response.js +2 -4
  45. package/lib/web/fetch/util.js +8 -230
  46. package/lib/web/subresource-integrity/Readme.md +9 -0
  47. package/lib/web/subresource-integrity/subresource-integrity.js +306 -0
  48. package/lib/web/webidl/index.js +203 -42
  49. package/lib/web/websocket/connection.js +4 -3
  50. package/lib/web/websocket/events.js +1 -1
  51. package/lib/web/websocket/stream/websocketerror.js +22 -1
  52. package/lib/web/websocket/stream/websocketstream.js +16 -7
  53. package/lib/web/websocket/websocket.js +32 -42
  54. package/package.json +9 -7
  55. package/types/agent.d.ts +1 -0
  56. package/types/diagnostics-channel.d.ts +0 -1
  57. package/types/errors.d.ts +5 -15
  58. package/types/interceptors.d.ts +5 -0
  59. package/types/snapshot-agent.d.ts +5 -3
  60. package/types/webidl.d.ts +82 -21
  61. package/lib/api/util.js +0 -95
  62. package/lib/llhttp/constants.js.map +0 -1
  63. package/lib/llhttp/utils.js.map +0 -1
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # undici
2
2
 
3
- [![Node CI](https://github.com/nodejs/undici/actions/workflows/nodejs.yml/badge.svg)](https://github.com/nodejs/undici/actions/workflows/nodejs.yml) [![neostandard javascript style](https://img.shields.io/badge/neo-standard-7fffff?style=flat\&labelColor=ff80ff)](https://github.com/neostandard/neostandard) [![npm version](https://badge.fury.io/js/undici.svg)](https://badge.fury.io/js/undici) [![codecov](https://codecov.io/gh/nodejs/undici/branch/main/graph/badge.svg?token=yZL6LtXkOA)](https://codecov.io/gh/nodejs/undici)
3
+ [![Node CI](https://github.com/nodejs/undici/actions/workflows/ci.yml/badge.svg)](https://github.com/nodejs/undici/actions/workflows/nodejs.yml) [![neostandard javascript style](https://img.shields.io/badge/neo-standard-7fffff?style=flat\&labelColor=ff80ff)](https://github.com/neostandard/neostandard) [![npm version](https://badge.fury.io/js/undici.svg)](https://badge.fury.io/js/undici) [![codecov](https://codecov.io/gh/nodejs/undici/branch/main/graph/badge.svg?token=yZL6LtXkOA)](https://codecov.io/gh/nodejs/undici)
4
4
 
5
5
  An HTTP/1.1 client, written from scratch for Node.js.
6
6
 
@@ -19,6 +19,7 @@ Returns: `Agent`
19
19
  Extends: [`PoolOptions`](/docs/docs/api/Pool.md#parameter-pooloptions)
20
20
 
21
21
  * **factory** `(origin: URL, opts: Object) => Dispatcher` - Default: `(origin, opts) => new Pool(origin, opts)`
22
+ * **maxOrigins** `number` (optional) - Default: `Infinity` - Limits the total number of origins that can receive requests at a time, throwing an `MaxOriginsReachedError` error when attempting to dispatch when the max is reached. If `Infinity`, no limit is enforced.
22
23
 
23
24
  ## Instance Properties
24
25
 
@@ -1094,6 +1094,65 @@ await client.request({
1094
1094
  });
1095
1095
  ```
1096
1096
 
1097
+ ##### `decompress`
1098
+
1099
+ ⚠️ The decompress interceptor is experimental and subject to change.
1100
+
1101
+ The `decompress` interceptor automatically decompresses response bodies that are compressed with gzip, deflate, brotli, or zstd compression. It removes the `content-encoding` and `content-length` headers from decompressed responses and supports RFC-9110 compliant multiple encodings.
1102
+
1103
+ **Options**
1104
+
1105
+ - `skipErrorResponses` - Whether to skip decompression for error responses (status codes >= 400). Default: `true`.
1106
+ - `skipStatusCodes` - Array of status codes to skip decompression for. Default: `[204, 304]`.
1107
+
1108
+ **Example - Basic Decompress Interceptor**
1109
+
1110
+ ```js
1111
+ const { Client, interceptors } = require("undici");
1112
+ const { decompress } = interceptors;
1113
+
1114
+ const client = new Client("http://example.com").compose(
1115
+ decompress()
1116
+ );
1117
+
1118
+ // Automatically decompresses gzip/deflate/brotli/zstd responses
1119
+ const response = await client.request({
1120
+ method: "GET",
1121
+ path: "/"
1122
+ });
1123
+ ```
1124
+
1125
+ **Example - Custom Options**
1126
+
1127
+ ```js
1128
+ const { Client, interceptors } = require("undici");
1129
+ const { decompress } = interceptors;
1130
+
1131
+ const client = new Client("http://example.com").compose(
1132
+ decompress({
1133
+ skipErrorResponses: false, // Decompress 5xx responses
1134
+ skipStatusCodes: [204, 304, 201] // Skip these status codes
1135
+ })
1136
+ );
1137
+ ```
1138
+
1139
+ **Supported Encodings**
1140
+
1141
+ - `gzip` / `x-gzip` - GZIP compression
1142
+ - `deflate` / `x-compress` - DEFLATE compression
1143
+ - `br` - Brotli compression
1144
+ - `zstd` - Zstandard compression
1145
+ - Multiple encodings (e.g., `gzip, deflate`) are supported per RFC-9110
1146
+
1147
+ **Behavior**
1148
+
1149
+ - Skips decompression for status codes < 200 or >= 400 (configurable)
1150
+ - Skips decompression for 204 No Content and 304 Not Modified by default
1151
+ - Removes `content-encoding` and `content-length` headers when decompressing
1152
+ - Passes through unsupported encodings unchanged
1153
+ - Handles case-insensitive encoding names
1154
+ - Supports streaming decompression without buffering
1155
+
1097
1156
  ##### `Cache Interceptor`
1098
1157
 
1099
1158
  The `cache` interceptor implements client-side response caching as described in
@@ -14,7 +14,6 @@ import { errors } from 'undici'
14
14
  | `HeadersTimeoutError` | `UND_ERR_HEADERS_TIMEOUT` | socket is destroyed due to headers timeout. |
15
15
  | `HeadersOverflowError` | `UND_ERR_HEADERS_OVERFLOW` | socket is destroyed due to headers' max size being exceeded. |
16
16
  | `BodyTimeoutError` | `UND_ERR_BODY_TIMEOUT` | socket is destroyed due to body timeout. |
17
- | `ResponseStatusCodeError` | `UND_ERR_RESPONSE_STATUS_CODE` | an error is thrown when `throwOnError` is `true` for status codes >= 400. |
18
17
  | `InvalidArgumentError` | `UND_ERR_INVALID_ARG` | passed an invalid argument. |
19
18
  | `InvalidReturnValueError` | `UND_ERR_INVALID_RETURN_VALUE` | returned an invalid value. |
20
19
  | `RequestAbortedError` | `UND_ERR_ABORTED` | the request has been aborted by the user |
package/index-fetch.js CHANGED
@@ -4,8 +4,8 @@ const { getGlobalDispatcher, setGlobalDispatcher } = require('./lib/global')
4
4
  const EnvHttpProxyAgent = require('./lib/dispatcher/env-http-proxy-agent')
5
5
  const fetchImpl = require('./lib/web/fetch').fetch
6
6
 
7
- module.exports.fetch = function fetch (resource, init = undefined) {
8
- return fetchImpl(resource, init).catch((err) => {
7
+ module.exports.fetch = function fetch (init, options = undefined) {
8
+ return fetchImpl(init, options).catch(err => {
9
9
  if (err && typeof err === 'object') {
10
10
  Error.captureStackTrace(err)
11
11
  }
package/index.js CHANGED
@@ -46,7 +46,8 @@ module.exports.interceptors = {
46
46
  retry: require('./lib/interceptor/retry'),
47
47
  dump: require('./lib/interceptor/dump'),
48
48
  dns: require('./lib/interceptor/dns'),
49
- cache: require('./lib/interceptor/cache')
49
+ cache: require('./lib/interceptor/cache'),
50
+ decompress: require('./lib/interceptor/decompress')
50
51
  }
51
52
 
52
53
  module.exports.cacheStores = {
@@ -116,16 +117,14 @@ module.exports.setGlobalDispatcher = setGlobalDispatcher
116
117
  module.exports.getGlobalDispatcher = getGlobalDispatcher
117
118
 
118
119
  const fetchImpl = require('./lib/web/fetch').fetch
119
- module.exports.fetch = async function fetch (init, options = undefined) {
120
- try {
121
- return await fetchImpl(init, options)
122
- } catch (err) {
120
+
121
+ module.exports.fetch = function fetch (init, options = undefined) {
122
+ return fetchImpl(init, options).catch(err => {
123
123
  if (err && typeof err === 'object') {
124
124
  Error.captureStackTrace(err)
125
125
  }
126
-
127
126
  throw err
128
- }
127
+ })
129
128
  }
130
129
  module.exports.Headers = require('./lib/web/fetch/headers').Headers
131
130
  module.exports.Response = require('./lib/web/fetch/response').Response
@@ -140,8 +139,6 @@ module.exports.getGlobalOrigin = getGlobalOrigin
140
139
  const { CacheStorage } = require('./lib/web/cache/cachestorage')
141
140
  const { kConstruct } = require('./lib/core/symbols')
142
141
 
143
- // Cache & CacheStorage are tightly coupled with fetch. Even if it may run
144
- // in an older version of Node, it doesn't have any use without fetch.
145
142
  module.exports.caches = new CacheStorage(kConstruct)
146
143
 
147
144
  const { deleteCookie, getCookies, getSetCookies, setCookie, parseCookie } = require('./lib/web/cookies')
@@ -118,14 +118,28 @@ class RequestHandler extends AsyncResource {
118
118
  this.callback = null
119
119
  this.res = res
120
120
  if (callback !== null) {
121
- this.runInAsyncScope(callback, null, null, {
122
- statusCode,
123
- headers,
124
- trailers: this.trailers,
125
- opaque,
126
- body: res,
127
- context
128
- })
121
+ try {
122
+ this.runInAsyncScope(callback, null, null, {
123
+ statusCode,
124
+ headers,
125
+ trailers: this.trailers,
126
+ opaque,
127
+ body: res,
128
+ context
129
+ })
130
+ } catch (err) {
131
+ // If the callback throws synchronously, we need to handle it
132
+ // Remove reference to res to allow res being garbage collected
133
+ this.res = null
134
+
135
+ // Destroy the response stream
136
+ util.destroy(res.on('error', noop), err)
137
+
138
+ // Use queueMicrotask to re-throw the error so it reaches uncaughtException
139
+ queueMicrotask(() => {
140
+ throw err
141
+ })
142
+ }
129
143
  }
130
144
  }
131
145
 
@@ -262,24 +262,26 @@ class BodyReadable extends Readable {
262
262
  * @param {AbortSignal} [opts.signal] An AbortSignal to cancel the dump.
263
263
  * @returns {Promise<null>}
264
264
  */
265
- async dump (opts) {
265
+ dump (opts) {
266
266
  const signal = opts?.signal
267
267
 
268
268
  if (signal != null && (typeof signal !== 'object' || !('aborted' in signal))) {
269
- throw new InvalidArgumentError('signal must be an AbortSignal')
269
+ return Promise.reject(new InvalidArgumentError('signal must be an AbortSignal'))
270
270
  }
271
271
 
272
272
  const limit = opts?.limit && Number.isFinite(opts.limit)
273
273
  ? opts.limit
274
274
  : 128 * 1024
275
275
 
276
- signal?.throwIfAborted()
276
+ if (signal?.aborted) {
277
+ return Promise.reject(signal.reason ?? new AbortError())
278
+ }
277
279
 
278
280
  if (this._readableState.closeEmitted) {
279
- return null
281
+ return Promise.resolve(null)
280
282
  }
281
283
 
282
- return await new Promise((resolve, reject) => {
284
+ return new Promise((resolve, reject) => {
283
285
  if (
284
286
  (this[kContentLength] && (this[kContentLength] > limit)) ||
285
287
  this[kBytesRead] > limit
@@ -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