undici 6.19.7 → 7.0.0-alpha.1

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 (115) hide show
  1. package/README.md +5 -9
  2. package/docs/docs/api/Agent.md +0 -3
  3. package/docs/docs/api/Client.md +0 -2
  4. package/docs/docs/api/Dispatcher.md +204 -6
  5. package/docs/docs/api/EnvHttpProxyAgent.md +0 -1
  6. package/docs/docs/api/Fetch.md +1 -0
  7. package/docs/docs/api/Pool.md +0 -1
  8. package/docs/docs/api/RetryHandler.md +1 -1
  9. package/index.js +0 -4
  10. package/lib/api/api-connect.js +3 -1
  11. package/lib/api/api-pipeline.js +3 -4
  12. package/lib/api/api-request.js +29 -46
  13. package/lib/api/api-stream.js +36 -49
  14. package/lib/api/api-upgrade.js +5 -3
  15. package/lib/api/readable.js +71 -27
  16. package/lib/core/connect.js +39 -24
  17. package/lib/core/errors.js +17 -4
  18. package/lib/core/request.js +7 -5
  19. package/lib/core/symbols.js +0 -1
  20. package/lib/core/tree.js +6 -0
  21. package/lib/core/util.js +1 -11
  22. package/lib/dispatcher/agent.js +3 -17
  23. package/lib/dispatcher/balanced-pool.js +27 -11
  24. package/lib/dispatcher/client-h1.js +44 -39
  25. package/lib/dispatcher/client.js +3 -27
  26. package/lib/dispatcher/dispatcher-base.js +2 -34
  27. package/lib/dispatcher/dispatcher.js +3 -24
  28. package/lib/dispatcher/pool.js +3 -6
  29. package/lib/dispatcher/proxy-agent.js +3 -6
  30. package/lib/handler/decorator-handler.js +24 -0
  31. package/lib/handler/redirect-handler.js +9 -0
  32. package/lib/handler/retry-handler.js +22 -3
  33. package/lib/interceptor/dump.js +2 -2
  34. package/lib/interceptor/redirect.js +11 -14
  35. package/lib/interceptor/response-error.js +89 -0
  36. package/lib/llhttp/constants.d.ts +97 -0
  37. package/lib/llhttp/constants.js +412 -192
  38. package/lib/llhttp/constants.js.map +1 -0
  39. package/lib/llhttp/llhttp-wasm.js +11 -1
  40. package/lib/llhttp/llhttp_simd-wasm.js +11 -1
  41. package/lib/llhttp/utils.d.ts +2 -0
  42. package/lib/llhttp/utils.js +9 -9
  43. package/lib/llhttp/utils.js.map +1 -0
  44. package/lib/mock/mock-client.js +2 -2
  45. package/lib/mock/mock-pool.js +2 -2
  46. package/lib/mock/mock-symbols.js +1 -0
  47. package/lib/util/timers.js +324 -44
  48. package/lib/web/cookies/index.js +15 -13
  49. package/lib/web/cookies/parse.js +2 -2
  50. package/lib/web/eventsource/eventsource-stream.js +9 -8
  51. package/lib/web/eventsource/eventsource.js +10 -6
  52. package/lib/web/fetch/body.js +31 -11
  53. package/lib/web/fetch/data-url.js +1 -1
  54. package/lib/web/fetch/formdata-parser.js +1 -2
  55. package/lib/web/fetch/formdata.js +28 -37
  56. package/lib/web/fetch/headers.js +1 -1
  57. package/lib/web/fetch/index.js +7 -8
  58. package/lib/web/fetch/request.js +11 -28
  59. package/lib/web/fetch/response.js +13 -41
  60. package/lib/web/fetch/symbols.js +0 -1
  61. package/lib/web/fetch/util.js +3 -12
  62. package/lib/web/fetch/webidl.js +73 -62
  63. package/lib/web/websocket/connection.js +26 -174
  64. package/lib/web/websocket/constants.js +1 -1
  65. package/lib/web/websocket/frame.js +45 -3
  66. package/lib/web/websocket/receiver.js +28 -26
  67. package/lib/web/websocket/sender.js +18 -13
  68. package/lib/web/websocket/util.js +20 -74
  69. package/lib/web/websocket/websocket.js +294 -70
  70. package/package.json +16 -29
  71. package/scripts/strip-comments.js +3 -1
  72. package/types/agent.d.ts +7 -7
  73. package/types/api.d.ts +24 -24
  74. package/types/balanced-pool.d.ts +11 -11
  75. package/types/client.d.ts +11 -12
  76. package/types/diagnostics-channel.d.ts +10 -10
  77. package/types/dispatcher.d.ts +96 -97
  78. package/types/env-http-proxy-agent.d.ts +2 -2
  79. package/types/errors.d.ts +53 -47
  80. package/types/eventsource.d.ts +0 -2
  81. package/types/fetch.d.ts +8 -8
  82. package/types/formdata.d.ts +7 -7
  83. package/types/global-dispatcher.d.ts +4 -4
  84. package/types/global-origin.d.ts +5 -5
  85. package/types/handlers.d.ts +4 -4
  86. package/types/header.d.ts +157 -1
  87. package/types/index.d.ts +42 -46
  88. package/types/interceptors.d.ts +10 -8
  89. package/types/mock-agent.d.ts +18 -18
  90. package/types/mock-client.d.ts +4 -4
  91. package/types/mock-errors.d.ts +3 -3
  92. package/types/mock-interceptor.d.ts +19 -19
  93. package/types/mock-pool.d.ts +4 -4
  94. package/types/patch.d.ts +0 -42
  95. package/types/pool-stats.d.ts +8 -8
  96. package/types/pool.d.ts +12 -12
  97. package/types/proxy-agent.d.ts +4 -4
  98. package/types/readable.d.ts +14 -9
  99. package/types/retry-agent.d.ts +1 -1
  100. package/types/retry-handler.d.ts +8 -8
  101. package/types/util.d.ts +3 -3
  102. package/types/utility.d.ts +7 -0
  103. package/types/webidl.d.ts +22 -4
  104. package/types/websocket.d.ts +1 -3
  105. package/docs/docs/api/DispatchInterceptor.md +0 -60
  106. package/lib/interceptor/redirect-interceptor.js +0 -21
  107. package/lib/web/fetch/file.js +0 -126
  108. package/lib/web/fileapi/encoding.js +0 -290
  109. package/lib/web/fileapi/filereader.js +0 -344
  110. package/lib/web/fileapi/progressevent.js +0 -78
  111. package/lib/web/fileapi/symbols.js +0 -10
  112. package/lib/web/fileapi/util.js +0 -391
  113. package/lib/web/websocket/symbols.js +0 -12
  114. package/types/file.d.ts +0 -39
  115. package/types/filereader.d.ts +0 -54
@@ -1,11 +1,10 @@
1
1
  'use strict'
2
2
 
3
3
  const assert = require('node:assert')
4
- const { finished, PassThrough } = require('node:stream')
4
+ const { finished } = require('node:stream')
5
+ const { AsyncResource } = require('node:async_hooks')
5
6
  const { InvalidArgumentError, InvalidReturnValueError } = require('../core/errors')
6
7
  const util = require('../core/util')
7
- const { getResolveErrorBodyCallback } = require('./util')
8
- const { AsyncResource } = require('node:async_hooks')
9
8
  const { addSignal, removeSignal } = require('./abort-signal')
10
9
 
11
10
  class StreamHandler extends AsyncResource {
@@ -14,7 +13,7 @@ class StreamHandler extends AsyncResource {
14
13
  throw new InvalidArgumentError('invalid opts')
15
14
  }
16
15
 
17
- const { signal, method, opaque, body, onInfo, responseHeaders, throwOnError } = opts
16
+ const { signal, method, opaque, body, onInfo, responseHeaders } = opts
18
17
 
19
18
  try {
20
19
  if (typeof callback !== 'function') {
@@ -55,7 +54,6 @@ class StreamHandler extends AsyncResource {
55
54
  this.trailers = null
56
55
  this.body = body
57
56
  this.onInfo = onInfo || null
58
- this.throwOnError = throwOnError || false
59
57
 
60
58
  if (util.isStream(body)) {
61
59
  body.on('error', (err) => {
@@ -79,7 +77,7 @@ class StreamHandler extends AsyncResource {
79
77
  }
80
78
 
81
79
  onHeaders (statusCode, rawHeaders, resume, statusMessage) {
82
- const { factory, opaque, context, callback, responseHeaders } = this
80
+ const { factory, opaque, context, responseHeaders } = this
83
81
 
84
82
  const headers = responseHeaders === 'raw' ? util.parseRawHeaders(rawHeaders) : util.parseHeaders(rawHeaders)
85
83
 
@@ -92,55 +90,42 @@ class StreamHandler extends AsyncResource {
92
90
 
93
91
  this.factory = null
94
92
 
95
- let res
93
+ if (factory === null) {
94
+ return
95
+ }
96
96
 
97
- if (this.throwOnError && statusCode >= 400) {
98
- const parsedHeaders = responseHeaders === 'raw' ? util.parseHeaders(rawHeaders) : headers
99
- const contentType = parsedHeaders['content-type']
100
- res = new PassThrough()
97
+ const res = this.runInAsyncScope(factory, null, {
98
+ statusCode,
99
+ headers,
100
+ opaque,
101
+ context
102
+ })
101
103
 
102
- this.callback = null
103
- this.runInAsyncScope(getResolveErrorBodyCallback, null,
104
- { callback, body: res, contentType, statusCode, statusMessage, headers }
105
- )
106
- } else {
107
- if (factory === null) {
108
- return
109
- }
104
+ if (
105
+ !res ||
106
+ typeof res.write !== 'function' ||
107
+ typeof res.end !== 'function' ||
108
+ typeof res.on !== 'function'
109
+ ) {
110
+ throw new InvalidReturnValueError('expected Writable')
111
+ }
110
112
 
111
- res = this.runInAsyncScope(factory, null, {
112
- statusCode,
113
- headers,
114
- opaque,
115
- context
116
- })
113
+ // TODO: Avoid finished. It registers an unnecessary amount of listeners.
114
+ finished(res, { readable: false }, (err) => {
115
+ const { callback, res, opaque, trailers, abort } = this
117
116
 
118
- if (
119
- !res ||
120
- typeof res.write !== 'function' ||
121
- typeof res.end !== 'function' ||
122
- typeof res.on !== 'function'
123
- ) {
124
- throw new InvalidReturnValueError('expected Writable')
117
+ this.res = null
118
+ if (err || !res.readable) {
119
+ util.destroy(res, err)
125
120
  }
126
121
 
127
- // TODO: Avoid finished. It registers an unnecessary amount of listeners.
128
- finished(res, { readable: false }, (err) => {
129
- const { callback, res, opaque, trailers, abort } = this
130
-
131
- this.res = null
132
- if (err || !res.readable) {
133
- util.destroy(res, err)
134
- }
135
-
136
- this.callback = null
137
- this.runInAsyncScope(callback, null, err || null, { opaque, trailers })
122
+ this.callback = null
123
+ this.runInAsyncScope(callback, null, err || null, { opaque, trailers })
138
124
 
139
- if (err) {
140
- abort()
141
- }
142
- })
143
- }
125
+ if (err) {
126
+ abort()
127
+ }
128
+ })
144
129
 
145
130
  res.on('drain', resume)
146
131
 
@@ -207,7 +192,9 @@ function stream (opts, factory, callback) {
207
192
  }
208
193
 
209
194
  try {
210
- this.dispatch(opts, new StreamHandler(opts, factory, callback))
195
+ const handler = new StreamHandler(opts, factory, callback)
196
+
197
+ this.dispatch(opts, handler)
211
198
  } catch (err) {
212
199
  if (typeof callback !== 'function') {
213
200
  throw err
@@ -2,9 +2,9 @@
2
2
 
3
3
  const { InvalidArgumentError, SocketError } = require('../core/errors')
4
4
  const { AsyncResource } = require('node:async_hooks')
5
+ const assert = require('node:assert')
5
6
  const util = require('../core/util')
6
7
  const { addSignal, removeSignal } = require('./abort-signal')
7
- const assert = require('node:assert')
8
8
 
9
9
  class UpgradeHandler extends AsyncResource {
10
10
  constructor (opts, callback) {
@@ -91,11 +91,13 @@ function upgrade (opts, callback) {
91
91
 
92
92
  try {
93
93
  const upgradeHandler = new UpgradeHandler(opts, callback)
94
- this.dispatch({
94
+ const upgradeOpts = {
95
95
  ...opts,
96
96
  method: opts.method || 'GET',
97
97
  upgrade: opts.protocol || 'Websocket'
98
- }, upgradeHandler)
98
+ }
99
+
100
+ this.dispatch(upgradeOpts, upgradeHandler)
99
101
  } catch (err) {
100
102
  if (typeof callback !== 'function') {
101
103
  throw err
@@ -14,6 +14,8 @@ const kBody = Symbol('kBody')
14
14
  const kAbort = Symbol('kAbort')
15
15
  const kContentType = Symbol('kContentType')
16
16
  const kContentLength = Symbol('kContentLength')
17
+ const kUsed = Symbol('kUsed')
18
+ const kBytesRead = Symbol('kBytesRead')
17
19
 
18
20
  const noop = () => {}
19
21
 
@@ -35,9 +37,11 @@ class BodyReadable extends Readable {
35
37
 
36
38
  this[kAbort] = abort
37
39
  this[kConsume] = null
40
+ this[kBytesRead] = 0
38
41
  this[kBody] = null
42
+ this[kUsed] = false
39
43
  this[kContentType] = contentType
40
- this[kContentLength] = contentLength
44
+ this[kContentLength] = Number.isFinite(contentLength) ? contentLength : null
41
45
 
42
46
  // Is stream being consumed through Readable API?
43
47
  // This is an optimization so that we avoid checking
@@ -46,7 +50,7 @@ class BodyReadable extends Readable {
46
50
  this[kReading] = false
47
51
  }
48
52
 
49
- destroy (err) {
53
+ _destroy (err, callback) {
50
54
  if (!err && !this._readableState.endEmitted) {
51
55
  err = new RequestAbortedError()
52
56
  }
@@ -55,15 +59,11 @@ class BodyReadable extends Readable {
55
59
  this[kAbort]()
56
60
  }
57
61
 
58
- return super.destroy(err)
59
- }
60
-
61
- _destroy (err, callback) {
62
62
  // Workaround for Node "bug". If the stream is destroyed in same
63
63
  // tick as it is created, then a user who is waiting for a
64
64
  // promise (i.e micro tick) for installing a 'error' listener will
65
65
  // never get a chance and will always encounter an unhandled exception.
66
- if (!this[kReading]) {
66
+ if (!this[kUsed]) {
67
67
  setImmediate(() => {
68
68
  callback(err)
69
69
  })
@@ -75,6 +75,7 @@ class BodyReadable extends Readable {
75
75
  on (ev, ...args) {
76
76
  if (ev === 'data' || ev === 'readable') {
77
77
  this[kReading] = true
78
+ this[kUsed] = true
78
79
  }
79
80
  return super.on(ev, ...args)
80
81
  }
@@ -99,6 +100,8 @@ class BodyReadable extends Readable {
99
100
  }
100
101
 
101
102
  push (chunk) {
103
+ this[kBytesRead] += chunk ? chunk.length : 0
104
+
102
105
  if (this[kConsume] && chunk !== null) {
103
106
  consumePush(this[kConsume], chunk)
104
107
  return this[kReading] ? super.push(chunk) : true
@@ -121,6 +124,11 @@ class BodyReadable extends Readable {
121
124
  return consume(this, 'blob')
122
125
  }
123
126
 
127
+ // https://fetch.spec.whatwg.org/#dom-body-bytes
128
+ async bytes () {
129
+ return consume(this, 'bytes')
130
+ }
131
+
124
132
  // https://fetch.spec.whatwg.org/#dom-body-arraybuffer
125
133
  async arrayBuffer () {
126
134
  return consume(this, 'arrayBuffer')
@@ -151,13 +159,14 @@ class BodyReadable extends Readable {
151
159
  }
152
160
 
153
161
  async dump (opts) {
154
- let limit = Number.isFinite(opts?.limit) ? opts.limit : 128 * 1024
155
162
  const signal = opts?.signal
156
163
 
157
164
  if (signal != null && (typeof signal !== 'object' || !('aborted' in signal))) {
158
165
  throw new InvalidArgumentError('signal must be an AbortSignal')
159
166
  }
160
167
 
168
+ const limit = Number.isFinite(opts?.limit) ? opts.limit : 128 * 1024
169
+
161
170
  signal?.throwIfAborted()
162
171
 
163
172
  if (this._readableState.closeEmitted) {
@@ -165,7 +174,7 @@ class BodyReadable extends Readable {
165
174
  }
166
175
 
167
176
  return await new Promise((resolve, reject) => {
168
- if (this[kContentLength] > limit) {
177
+ if (this[kContentLength] > limit || this[kBytesRead] > limit) {
169
178
  this.destroy(new AbortError())
170
179
  }
171
180
 
@@ -185,14 +194,24 @@ class BodyReadable extends Readable {
185
194
  })
186
195
  .on('error', noop)
187
196
  .on('data', function (chunk) {
188
- limit -= chunk.length
189
- if (limit <= 0) {
197
+ if (this[kBytesRead] > limit) {
190
198
  this.destroy()
191
199
  }
192
200
  })
193
201
  .resume()
194
202
  })
195
203
  }
204
+
205
+ /**
206
+ * @param {BufferEncoding} encoding
207
+ * @returns {BodyReadable}
208
+ */
209
+ setEncoding (encoding) {
210
+ if (Buffer.isEncoding(encoding)) {
211
+ this._readableState.encoding = encoding
212
+ }
213
+ return this
214
+ }
196
215
  }
197
216
 
198
217
  // https://streams.spec.whatwg.org/#readablestream-locked
@@ -270,10 +289,10 @@ function consumeStart (consume) {
270
289
  }
271
290
 
272
291
  if (state.endEmitted) {
273
- consumeEnd(this[kConsume])
292
+ consumeEnd(this[kConsume], this._readableState.encoding)
274
293
  } else {
275
294
  consume.stream.on('end', function () {
276
- consumeEnd(this[kConsume])
295
+ consumeEnd(this[kConsume], this._readableState.encoding)
277
296
  })
278
297
  }
279
298
 
@@ -287,8 +306,10 @@ function consumeStart (consume) {
287
306
  /**
288
307
  * @param {Buffer[]} chunks
289
308
  * @param {number} length
309
+ * @param {BufferEncoding} encoding
310
+ * @returns {string}
290
311
  */
291
- function chunksDecode (chunks, length) {
312
+ function chunksDecode (chunks, length, encoding) {
292
313
  if (chunks.length === 0 || length === 0) {
293
314
  return ''
294
315
  }
@@ -303,29 +324,52 @@ function chunksDecode (chunks, length) {
303
324
  buffer[2] === 0xbf
304
325
  ? 3
305
326
  : 0
306
- return buffer.utf8Slice(start, bufferLength)
327
+ if (!encoding || encoding === 'utf8' || encoding === 'utf-8') {
328
+ return buffer.utf8Slice(start, bufferLength)
329
+ } else {
330
+ return buffer.subarray(start, bufferLength).toString(encoding)
331
+ }
332
+ }
333
+
334
+ /**
335
+ * @param {Buffer[]} chunks
336
+ * @param {number} length
337
+ * @returns {Uint8Array}
338
+ */
339
+ function chunksConcat (chunks, length) {
340
+ if (chunks.length === 0 || length === 0) {
341
+ return new Uint8Array(0)
342
+ }
343
+ if (chunks.length === 1) {
344
+ // fast-path
345
+ return new Uint8Array(chunks[0])
346
+ }
347
+ const buffer = new Uint8Array(Buffer.allocUnsafeSlow(length).buffer)
348
+
349
+ let offset = 0
350
+ for (let i = 0; i < chunks.length; ++i) {
351
+ const chunk = chunks[i]
352
+ buffer.set(chunk, offset)
353
+ offset += chunk.length
354
+ }
355
+
356
+ return buffer
307
357
  }
308
358
 
309
- function consumeEnd (consume) {
359
+ function consumeEnd (consume, encoding) {
310
360
  const { type, body, resolve, stream, length } = consume
311
361
 
312
362
  try {
313
363
  if (type === 'text') {
314
- resolve(chunksDecode(body, length))
364
+ resolve(chunksDecode(body, length, encoding))
315
365
  } else if (type === 'json') {
316
- resolve(JSON.parse(chunksDecode(body, length)))
366
+ resolve(JSON.parse(chunksDecode(body, length, encoding)))
317
367
  } else if (type === 'arrayBuffer') {
318
- const dst = new Uint8Array(length)
319
-
320
- let pos = 0
321
- for (const buf of body) {
322
- dst.set(buf, pos)
323
- pos += buf.byteLength
324
- }
325
-
326
- resolve(dst.buffer)
368
+ resolve(chunksConcat(body, length).buffer)
327
369
  } else if (type === 'blob') {
328
370
  resolve(new Blob(body, { type: stream[kContentType] }))
371
+ } else if (type === 'bytes') {
372
+ resolve(chunksConcat(body, length))
329
373
  }
330
374
 
331
375
  consumeFinish(consume)
@@ -4,6 +4,7 @@ const net = require('node:net')
4
4
  const assert = require('node:assert')
5
5
  const util = require('./util')
6
6
  const { InvalidArgumentError, ConnectTimeoutError } = require('./errors')
7
+ const timers = require('../util/timers')
7
8
 
8
9
  let tls // include tls conditionally since it is not always available
9
10
 
@@ -130,12 +131,12 @@ function buildConnector ({ allowH2, maxCachedSessions, socketPath, timeout, sess
130
131
  socket.setKeepAlive(true, keepAliveInitialDelay)
131
132
  }
132
133
 
133
- const cancelTimeout = setupTimeout(() => onConnectTimeout(socket), timeout)
134
+ const cancelConnectTimeout = setupConnectTimeout(new WeakRef(socket), timeout)
134
135
 
135
136
  socket
136
137
  .setNoDelay(true)
137
138
  .once(protocol === 'https:' ? 'secureConnect' : 'connect', function () {
138
- cancelTimeout()
139
+ cancelConnectTimeout()
139
140
 
140
141
  if (callback) {
141
142
  const cb = callback
@@ -144,7 +145,7 @@ function buildConnector ({ allowH2, maxCachedSessions, socketPath, timeout, sess
144
145
  }
145
146
  })
146
147
  .on('error', function (err) {
147
- cancelTimeout()
148
+ cancelConnectTimeout()
148
149
 
149
150
  if (callback) {
150
151
  const cb = callback
@@ -157,30 +158,44 @@ function buildConnector ({ allowH2, maxCachedSessions, socketPath, timeout, sess
157
158
  }
158
159
  }
159
160
 
160
- function setupTimeout (onConnectTimeout, timeout) {
161
- if (!timeout) {
162
- return () => {}
163
- }
161
+ const setupConnectTimeout = process.platform === 'win32'
162
+ ? (socket, timeout) => {
163
+ if (!timeout) {
164
+ return () => { }
165
+ }
164
166
 
165
- let s1 = null
166
- let s2 = null
167
- const timeoutId = setTimeout(() => {
168
- // setImmediate is added to make sure that we prioritize socket error events over timeouts
169
- s1 = setImmediate(() => {
170
- if (process.platform === 'win32') {
167
+ let s1 = null
168
+ let s2 = null
169
+ const timer = timers.setTimeout(() => {
170
+ // setImmediate is added to make sure that we prioritize socket error events over timeouts
171
+ s1 = setImmediate(() => {
171
172
  // Windows needs an extra setImmediate probably due to implementation differences in the socket logic
172
- s2 = setImmediate(() => onConnectTimeout())
173
- } else {
174
- onConnectTimeout()
173
+ s2 = setImmediate(() => onConnectTimeout(socket.deref()))
174
+ })
175
+ }, timeout)
176
+ return () => {
177
+ timers.clearTimeout(timer)
178
+ clearImmediate(s1)
179
+ clearImmediate(s2)
175
180
  }
176
- })
177
- }, timeout)
178
- return () => {
179
- clearTimeout(timeoutId)
180
- clearImmediate(s1)
181
- clearImmediate(s2)
182
- }
183
- }
181
+ }
182
+ : (socket, timeout) => {
183
+ if (!timeout) {
184
+ return () => { }
185
+ }
186
+
187
+ let s1 = null
188
+ const timer = timers.setTimeout(() => {
189
+ // setImmediate is added to make sure that we prioritize socket error events over timeouts
190
+ s1 = setImmediate(() => {
191
+ onConnectTimeout(socket.deref())
192
+ })
193
+ }, timeout)
194
+ return () => {
195
+ timers.clearTimeout(timer)
196
+ clearImmediate(s1)
197
+ }
198
+ }
184
199
 
185
200
  function onConnectTimeout (socket) {
186
201
  let message = 'Connect Timeout Error'
@@ -1,8 +1,8 @@
1
1
  'use strict'
2
2
 
3
3
  class UndiciError extends Error {
4
- constructor (message) {
5
- super(message)
4
+ constructor (message, options) {
5
+ super(message, options)
6
6
  this.name = 'UndiciError'
7
7
  this.code = 'UND_ERR'
8
8
  }
@@ -195,9 +195,21 @@ class RequestRetryError extends UndiciError {
195
195
  }
196
196
  }
197
197
 
198
+ class ResponseError extends UndiciError {
199
+ constructor (message, code, { headers, data }) {
200
+ super(message)
201
+ this.name = 'ResponseError'
202
+ this.message = message || 'Response error'
203
+ this.code = 'UND_ERR_RESPONSE'
204
+ this.statusCode = code
205
+ this.data = data
206
+ this.headers = headers
207
+ }
208
+ }
209
+
198
210
  class SecureProxyConnectionError extends UndiciError {
199
- constructor (cause, message, options) {
200
- super(message, { cause, ...(options ?? {}) })
211
+ constructor (cause, message, options = {}) {
212
+ super(message, { cause, ...options })
201
213
  this.name = 'SecureProxyConnectionError'
202
214
  this.message = message || 'Secure Proxy Connection failed'
203
215
  this.code = 'UND_ERR_PRX_TLS'
@@ -227,5 +239,6 @@ module.exports = {
227
239
  BalancedPoolMissingUpstreamError,
228
240
  ResponseExceededMaxSizeError,
229
241
  RequestRetryError,
242
+ ResponseError,
230
243
  SecureProxyConnectionError
231
244
  }
@@ -40,9 +40,9 @@ class Request {
40
40
  headersTimeout,
41
41
  bodyTimeout,
42
42
  reset,
43
- throwOnError,
44
43
  expectContinue,
45
- servername
44
+ servername,
45
+ throwOnError
46
46
  }, handler) {
47
47
  if (typeof path !== 'string') {
48
48
  throw new InvalidArgumentError('path must be a string')
@@ -82,12 +82,14 @@ class Request {
82
82
  throw new InvalidArgumentError('invalid expectContinue')
83
83
  }
84
84
 
85
+ if (throwOnError != null) {
86
+ throw new InvalidArgumentError('invalid throwOnError')
87
+ }
88
+
85
89
  this.headersTimeout = headersTimeout
86
90
 
87
91
  this.bodyTimeout = bodyTimeout
88
92
 
89
- this.throwOnError = throwOnError === true
90
-
91
93
  this.method = method
92
94
 
93
95
  this.abort = null
@@ -183,7 +185,7 @@ class Request {
183
185
 
184
186
  validateHandler(handler, method, upgrade)
185
187
 
186
- this.servername = servername || getServerName(this.host)
188
+ this.servername = servername || getServerName(this.host) || null
187
189
 
188
190
  this[kHandler] = handler
189
191
 
@@ -52,7 +52,6 @@ module.exports = {
52
52
  kMaxRequests: Symbol('maxRequestsPerClient'),
53
53
  kProxy: Symbol('proxy agent options'),
54
54
  kCounter: Symbol('socket request counter'),
55
- kInterceptors: Symbol('dispatch interceptors'),
56
55
  kMaxResponseSize: Symbol('max response size'),
57
56
  kHTTP2Session: Symbol('http2Session'),
58
57
  kHTTP2SessionState: Symbol('http2Session state'),
package/lib/core/tree.js CHANGED
@@ -47,6 +47,9 @@ class TstNode {
47
47
  throw new TypeError('Unreachable')
48
48
  }
49
49
  let index = 0
50
+ /**
51
+ * @type {TstNode}
52
+ */
50
53
  let node = this
51
54
  while (true) {
52
55
  const code = key.charCodeAt(index)
@@ -87,6 +90,9 @@ class TstNode {
87
90
  search (key) {
88
91
  const keylength = key.length
89
92
  let index = 0
93
+ /**
94
+ * @type {TstNode}
95
+ */
90
96
  let node = this
91
97
  while (node !== null && index < keylength) {
92
98
  let code = key[index]
package/lib/core/util.js CHANGED
@@ -443,14 +443,6 @@ function isDisturbed (body) {
443
443
  return !!(body && (stream.isDisturbed(body) || body[kBodyUsed]))
444
444
  }
445
445
 
446
- function isErrored (body) {
447
- return !!(body && stream.isErrored(body))
448
- }
449
-
450
- function isReadable (body) {
451
- return !!(body && stream.isReadable(body))
452
- }
453
-
454
446
  function getSocketInfo (socket) {
455
447
  return {
456
448
  localAddress: socket.localAddress,
@@ -518,7 +510,7 @@ function addAbortListener (signal, listener) {
518
510
  signal.addEventListener('abort', listener, { once: true })
519
511
  return () => signal.removeEventListener('abort', listener)
520
512
  }
521
- signal.addListener('abort', listener)
513
+ signal.once('abort', listener)
522
514
  return () => signal.removeListener('abort', listener)
523
515
  }
524
516
 
@@ -674,8 +666,6 @@ module.exports = {
674
666
  kEnumerableProperty,
675
667
  nop,
676
668
  isDisturbed,
677
- isErrored,
678
- isReadable,
679
669
  toUSVString,
680
670
  isUSVString,
681
671
  isBlobLike,
@@ -1,17 +1,15 @@
1
1
  'use strict'
2
2
 
3
3
  const { InvalidArgumentError } = require('../core/errors')
4
- const { kClients, kRunning, kClose, kDestroy, kDispatch, kInterceptors } = require('../core/symbols')
4
+ const { kClients, kRunning, kClose, kDestroy, kDispatch } = require('../core/symbols')
5
5
  const DispatcherBase = require('./dispatcher-base')
6
6
  const Pool = require('./pool')
7
7
  const Client = require('./client')
8
8
  const util = require('../core/util')
9
- const createRedirectInterceptor = require('../interceptor/redirect-interceptor')
10
9
 
11
10
  const kOnConnect = Symbol('onConnect')
12
11
  const kOnDisconnect = Symbol('onDisconnect')
13
12
  const kOnConnectionError = Symbol('onConnectionError')
14
- const kMaxRedirections = Symbol('maxRedirections')
15
13
  const kOnDrain = Symbol('onDrain')
16
14
  const kFactory = Symbol('factory')
17
15
  const kOptions = Symbol('options')
@@ -23,9 +21,7 @@ function defaultFactory (origin, opts) {
23
21
  }
24
22
 
25
23
  class Agent extends DispatcherBase {
26
- constructor ({ factory = defaultFactory, maxRedirections = 0, connect, ...options } = {}) {
27
- super()
28
-
24
+ constructor ({ factory = defaultFactory, connect, ...options } = {}) {
29
25
  if (typeof factory !== 'function') {
30
26
  throw new InvalidArgumentError('factory must be a function.')
31
27
  }
@@ -34,23 +30,13 @@ class Agent extends DispatcherBase {
34
30
  throw new InvalidArgumentError('connect must be a function or an object')
35
31
  }
36
32
 
37
- if (!Number.isInteger(maxRedirections) || maxRedirections < 0) {
38
- throw new InvalidArgumentError('maxRedirections must be a positive number')
39
- }
33
+ super()
40
34
 
41
35
  if (connect && typeof connect !== 'function') {
42
36
  connect = { ...connect }
43
37
  }
44
38
 
45
- this[kInterceptors] = options.interceptors?.Agent && Array.isArray(options.interceptors.Agent)
46
- ? options.interceptors.Agent
47
- : [createRedirectInterceptor({ maxRedirections })]
48
-
49
39
  this[kOptions] = { ...util.deepClone(options), connect }
50
- this[kOptions].interceptors = options.interceptors
51
- ? { ...options.interceptors }
52
- : undefined
53
- this[kMaxRedirections] = maxRedirections
54
40
  this[kFactory] = factory
55
41
  this[kClients] = new Map()
56
42