undici 7.0.0-alpha.9 → 7.1.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 CHANGED
@@ -434,6 +434,8 @@ and `undici.Agent`) which will enable the family autoselection algorithm when es
434
434
  * [__Matthew Aitken__](https://github.com/KhafraDev), <https://www.npmjs.com/~khaf>
435
435
  * [__Robert Nagy__](https://github.com/ronag), <https://www.npmjs.com/~ronag>
436
436
  * [__Szymon Marczak__](https://github.com/szmarczak), <https://www.npmjs.com/~szmarczak>
437
+
438
+ ## Past Collaborators
437
439
  * [__Tomas Della Vedova__](https://github.com/delvedor), <https://www.npmjs.com/~delvedor>
438
440
 
439
441
  ### Releasers
@@ -17,8 +17,6 @@ Returns: `Client`
17
17
 
18
18
  ### Parameter: `ClientOptions`
19
19
 
20
- > ⚠️ Warning: The `H2` support is experimental.
21
-
22
20
  * **bodyTimeout** `number | null` (optional) - Default: `300e3` - The timeout after which a request will time out, in milliseconds. Monitors time between receiving body data. Use `0` to disable it entirely. Defaults to 300 seconds. Please note the `timeout` will be reset if you keep writing data to the socket everytime.
23
21
  * **headersTimeout** `number | null` (optional) - Default: `300e3` - The amount of time, in milliseconds, the parser will wait to receive the complete HTTP headers while not sending the request. Defaults to 300 seconds.
24
22
  * **keepAliveMaxTimeout** `number | null` (optional) - Default: `600e3` - The maximum allowed `keepAliveTimeout`, in milliseconds, when overridden by *keep-alive* hints from the server. Defaults to 10 minutes.
@@ -34,6 +32,17 @@ Returns: `Client`
34
32
  * **allowH2**: `boolean` - Default: `false`. Enables support for H2 if the server has assigned bigger priority to it through ALPN negotiation.
35
33
  * **maxConcurrentStreams**: `number` - Default: `100`. Dictates the maximum number of concurrent streams for a single H2 session. It can be overridden by a SETTINGS remote frame.
36
34
 
35
+ > **Notes about HTTP/2**
36
+ > - It only works under TLS connections. h2c is not supported.
37
+ > - The server must support HTTP/2 and choose it as the protocol during the ALPN negotiation.
38
+ > - The server must not have a bigger priority for HTTP/1.1 than HTTP/2.
39
+ > - Pseudo headers are automatically attached to the request. If you try to set them, they will be overwritten.
40
+ > - The `:path` header is automatically set to the request path.
41
+ > - The `:method` header is automatically set to the request method.
42
+ > - The `:scheme` header is automatically set to the request scheme.
43
+ > - The `:authority` header is automatically set to the request `host[:port]`.
44
+ > - `PUSH` frames are yet not supported.
45
+
37
46
  #### Parameter: `ConnectOptions`
38
47
 
39
48
  Every Tls option, see [here](https://nodejs.org/api/tls.html#tls_tls_connect_options_callback).
@@ -128,3 +128,91 @@ Implements [`Agent.dispatch(options, handlers)`](/docs/docs/api/Agent.md#paramet
128
128
  ### `ProxyAgent.request(options[, callback])`
129
129
 
130
130
  See [`Dispatcher.request(options [, callback])`](/docs/docs/api/Dispatcher.md#dispatcherrequestoptions-callback).
131
+
132
+
133
+ #### Example - ProxyAgent with Fetch
134
+
135
+ This example demonstrates how to use `fetch` with a proxy via `ProxyAgent`. It is particularly useful for scenarios requiring proxy tunneling.
136
+
137
+ ```javascript
138
+ import { ProxyAgent, fetch } from 'undici';
139
+
140
+ // Define the ProxyAgent
141
+ const proxyAgent = new ProxyAgent('http://localhost:8000');
142
+
143
+ // Make a GET request through the proxy
144
+ const response = await fetch('http://localhost:3000/foo', {
145
+ dispatcher: proxyAgent,
146
+ method: 'GET',
147
+ });
148
+
149
+ console.log('Response status:', response.status);
150
+ console.log('Response data:', await response.text());
151
+ ```
152
+
153
+ ---
154
+
155
+ #### Example - ProxyAgent with a Custom Proxy Server
156
+
157
+ This example shows how to create a custom proxy server and use it with `ProxyAgent`.
158
+
159
+ ```javascript
160
+ import * as http from 'node:http';
161
+ import { createProxy } from 'proxy';
162
+ import { ProxyAgent, fetch } from 'undici';
163
+
164
+ // Create a proxy server
165
+ const proxyServer = createProxy(http.createServer());
166
+ proxyServer.listen(8000, () => {
167
+ console.log('Proxy server running on port 8000');
168
+ });
169
+
170
+ // Define and use the ProxyAgent
171
+ const proxyAgent = new ProxyAgent('http://localhost:8000');
172
+
173
+ const response = await fetch('http://example.com', {
174
+ dispatcher: proxyAgent,
175
+ method: 'GET',
176
+ });
177
+
178
+ console.log('Response status:', response.status);
179
+ console.log('Response data:', await response.text());
180
+ ```
181
+
182
+ ---
183
+
184
+ #### Example - ProxyAgent with HTTPS Tunneling
185
+
186
+ This example demonstrates how to perform HTTPS tunneling using a proxy.
187
+
188
+ ```javascript
189
+ import { ProxyAgent, fetch } from 'undici';
190
+
191
+ // Define a ProxyAgent for HTTPS proxy
192
+ const proxyAgent = new ProxyAgent('https://secure.proxy.server');
193
+
194
+ // Make a request to an HTTPS endpoint via the proxy
195
+ const response = await fetch('https://secure.endpoint.com/api/data', {
196
+ dispatcher: proxyAgent,
197
+ method: 'GET',
198
+ });
199
+
200
+ console.log('Response status:', response.status);
201
+ console.log('Response data:', await response.json());
202
+ ```
203
+
204
+ #### Example - ProxyAgent as a Global Dispatcher
205
+
206
+ `ProxyAgent` can be configured as a global dispatcher, making it available for all requests without explicitly passing it. This simplifies code and is useful when a single proxy configuration applies to all requests.
207
+
208
+ ```javascript
209
+ import { ProxyAgent, setGlobalDispatcher, fetch } from 'undici';
210
+
211
+ // Define and configure the ProxyAgent
212
+ const proxyAgent = new ProxyAgent('http://localhost:8000');
213
+ setGlobalDispatcher(proxyAgent);
214
+
215
+ // Make requests without specifying the dispatcher
216
+ const response = await fetch('http://example.com');
217
+ console.log('Response status:', response.status);
218
+ console.log('Response data:', await response.text());
package/index.js CHANGED
@@ -53,9 +53,10 @@ try {
53
53
  const SqliteCacheStore = require('./lib/cache/sqlite-cache-store')
54
54
  module.exports.cacheStores.SqliteCacheStore = SqliteCacheStore
55
55
  } catch (err) {
56
- if (err.code !== 'ERR_UNKNOWN_BUILTIN_MODULE') {
57
- throw err
58
- }
56
+ // Most likely node:sqlite was not present, since SqliteCacheStore is
57
+ // optional, don't throw. Don't check specific error codes here because while
58
+ // ERR_UNKNOWN_BUILTIN_MODULE is expected, users have seen other codes like
59
+ // MODULE_NOT_FOUND
59
60
  }
60
61
 
61
62
  module.exports.buildConnector = buildConnector
@@ -27,16 +27,14 @@ const {
27
27
  kResume,
28
28
  kSize,
29
29
  kHTTPContext,
30
- kClosed
30
+ kClosed,
31
+ kBodyTimeout
31
32
  } = require('../core/symbols.js')
32
33
 
33
34
  const kOpenStreams = Symbol('open streams')
34
35
 
35
36
  let extractBody
36
37
 
37
- // Experimental
38
- let h2ExperimentalWarned = false
39
-
40
38
  /** @type {import('http2')} */
41
39
  let http2
42
40
  try {
@@ -81,16 +79,13 @@ function parseH2Headers (headers) {
81
79
  async function connectH2 (client, socket) {
82
80
  client[kSocket] = socket
83
81
 
84
- if (!h2ExperimentalWarned) {
85
- h2ExperimentalWarned = true
86
- process.emitWarning('H2 support is experimental, expect them to change at any time.', {
87
- code: 'UNDICI-H2'
88
- })
89
- }
90
-
91
82
  const session = http2.connect(client[kUrl], {
92
83
  createConnection: () => socket,
93
- peerMaxConcurrentStreams: client[kMaxConcurrentStreams]
84
+ peerMaxConcurrentStreams: client[kMaxConcurrentStreams],
85
+ settings: {
86
+ // TODO(metcoder95): add support for PUSH
87
+ enablePush: false
88
+ }
94
89
  })
95
90
 
96
91
  session[kOpenStreams] = 0
@@ -129,7 +124,6 @@ async function connectH2 (client, socket) {
129
124
  if (socket[kClosed]) {
130
125
  queueMicrotask(callback)
131
126
  } else {
132
- // Destroying the socket will trigger the session close
133
127
  socket.destroy(err).on('close', callback)
134
128
  }
135
129
  },
@@ -281,6 +275,7 @@ function shouldSendContentLength (method) {
281
275
  }
282
276
 
283
277
  function writeH2 (client, request) {
278
+ const requestTimeout = request.bodyTimeout ?? client[kBodyTimeout]
284
279
  const session = client[kHTTP2Session]
285
280
  const { method, path, host, upgrade, expectContinue, signal, headers: reqHeaders } = request
286
281
  let { body } = request
@@ -379,6 +374,7 @@ function writeH2 (client, request) {
379
374
  session[kOpenStreams] -= 1
380
375
  if (session[kOpenStreams] === 0) session.unref()
381
376
  })
377
+ stream.setTimeout(requestTimeout)
382
378
 
383
379
  return true
384
380
  }
@@ -452,6 +448,7 @@ function writeH2 (client, request) {
452
448
 
453
449
  session.ref()
454
450
 
451
+ // TODO(metcoder95): add support for sending trailers
455
452
  const shouldEndStream = method === 'GET' || method === 'HEAD' || body === null
456
453
  if (expectContinue) {
457
454
  headers[HTTP2_HEADER_EXPECT] = '100-continue'
@@ -469,6 +466,7 @@ function writeH2 (client, request) {
469
466
 
470
467
  // Increment counter as we have new streams open
471
468
  ++session[kOpenStreams]
469
+ stream.setTimeout(requestTimeout)
472
470
 
473
471
  stream.once('response', headers => {
474
472
  const { [HTTP2_HEADER_STATUS]: statusCode, ...realHeaders } = headers
@@ -502,8 +500,9 @@ function writeH2 (client, request) {
502
500
  // Present specially when using pipeline or stream
503
501
  if (stream.state?.state == null || stream.state.state < 6) {
504
502
  // Do not complete the request if it was aborted
505
- if (!request.aborted) {
506
- request.onComplete([])
503
+ // Not prone to happen for as safety net to avoid race conditions with 'trailers'
504
+ if (!request.aborted && !request.completed) {
505
+ request.onComplete({})
507
506
  }
508
507
 
509
508
  client[kQueue][client[kRunningIdx]++] = null
@@ -546,17 +545,25 @@ function writeH2 (client, request) {
546
545
  stream.removeAllListeners('data')
547
546
  })
548
547
 
549
- // stream.on('timeout', () => {
550
- // // TODO(HTTP/2): Support timeout
551
- // })
548
+ stream.on('timeout', () => {
549
+ const err = new InformationalError(`HTTP/2: "stream timeout after ${requestTimeout}"`)
550
+ stream.removeAllListeners('data')
551
+ session[kOpenStreams] -= 1
552
+
553
+ if (session[kOpenStreams] === 0) {
554
+ session.unref()
555
+ }
556
+
557
+ abort(err)
558
+ })
552
559
 
553
- // stream.on('push', headers => {
554
- // // TODO(HTTP/2): Support push
555
- // })
560
+ stream.once('trailers', trailers => {
561
+ if (request.aborted || request.completed) {
562
+ return
563
+ }
556
564
 
557
- // stream.on('trailers', headers => {
558
- // // TODO(HTTP/2): Support trailers
559
- // })
565
+ request.onComplete(trailers)
566
+ })
560
567
 
561
568
  return true
562
569
 
@@ -1,6 +1,7 @@
1
1
  'use strict'
2
2
 
3
3
  const assert = require('node:assert')
4
+ const WrapHandler = require('./wrap-handler')
4
5
 
5
6
  /**
6
7
  * @deprecated
@@ -9,63 +10,58 @@ module.exports = class DecoratorHandler {
9
10
  #handler
10
11
  #onCompleteCalled = false
11
12
  #onErrorCalled = false
13
+ #onResponseStartCalled = false
12
14
 
13
15
  constructor (handler) {
14
16
  if (typeof handler !== 'object' || handler === null) {
15
17
  throw new TypeError('handler must be an object')
16
18
  }
17
- this.#handler = handler
19
+ this.#handler = WrapHandler.wrap(handler)
18
20
  }
19
21
 
20
- onConnect (...args) {
21
- return this.#handler.onConnect?.(...args)
22
+ onRequestStart (...args) {
23
+ this.#handler.onRequestStart?.(...args)
22
24
  }
23
25
 
24
- onError (...args) {
25
- this.#onErrorCalled = true
26
- return this.#handler.onError?.(...args)
27
- }
28
-
29
- onUpgrade (...args) {
26
+ onRequestUpgrade (...args) {
30
27
  assert(!this.#onCompleteCalled)
31
28
  assert(!this.#onErrorCalled)
32
29
 
33
- return this.#handler.onUpgrade?.(...args)
30
+ return this.#handler.onRequestUpgrade?.(...args)
34
31
  }
35
32
 
36
- onResponseStarted (...args) {
33
+ onResponseStart (...args) {
37
34
  assert(!this.#onCompleteCalled)
38
35
  assert(!this.#onErrorCalled)
36
+ assert(!this.#onResponseStartCalled)
39
37
 
40
- return this.#handler.onResponseStarted?.(...args)
41
- }
42
-
43
- onHeaders (...args) {
44
- assert(!this.#onCompleteCalled)
45
- assert(!this.#onErrorCalled)
38
+ this.#onResponseStartCalled = true
46
39
 
47
- return this.#handler.onHeaders?.(...args)
40
+ return this.#handler.onResponseStart?.(...args)
48
41
  }
49
42
 
50
- onData (...args) {
43
+ onResponseData (...args) {
51
44
  assert(!this.#onCompleteCalled)
52
45
  assert(!this.#onErrorCalled)
53
46
 
54
- return this.#handler.onData?.(...args)
47
+ return this.#handler.onResponseData?.(...args)
55
48
  }
56
49
 
57
- onComplete (...args) {
50
+ onResponseEnd (...args) {
58
51
  assert(!this.#onCompleteCalled)
59
52
  assert(!this.#onErrorCalled)
60
53
 
61
54
  this.#onCompleteCalled = true
62
- return this.#handler.onComplete?.(...args)
55
+ return this.#handler.onResponseEnd?.(...args)
63
56
  }
64
57
 
65
- onBodySent (...args) {
66
- assert(!this.#onCompleteCalled)
67
- assert(!this.#onErrorCalled)
68
-
69
- return this.#handler.onBodySent?.(...args)
58
+ onResponseError (...args) {
59
+ this.#onErrorCalled = true
60
+ return this.#handler.onResponseError?.(...args)
70
61
  }
62
+
63
+ /**
64
+ * @deprecated
65
+ */
66
+ onBodySent () {}
71
67
  }
@@ -222,19 +222,18 @@ class DNSDispatchHandler extends DecoratorHandler {
222
222
  #state = null
223
223
  #opts = null
224
224
  #dispatch = null
225
- #handler = null
226
225
  #origin = null
226
+ #controller = null
227
227
 
228
228
  constructor (state, { origin, handler, dispatch }, opts) {
229
229
  super(handler)
230
230
  this.#origin = origin
231
- this.#handler = handler
232
231
  this.#opts = { ...opts }
233
232
  this.#state = state
234
233
  this.#dispatch = dispatch
235
234
  }
236
235
 
237
- onError (err) {
236
+ onResponseError (controller, err) {
238
237
  switch (err.code) {
239
238
  case 'ETIMEDOUT':
240
239
  case 'ECONNREFUSED': {
@@ -242,7 +241,8 @@ class DNSDispatchHandler extends DecoratorHandler {
242
241
  // We delete the record and retry
243
242
  this.#state.runLookup(this.#origin, this.#opts, (err, newOrigin) => {
244
243
  if (err) {
245
- return this.#handler.onError(err)
244
+ super.onResponseError(controller, err)
245
+ return
246
246
  }
247
247
 
248
248
  const dispatchOpts = {
@@ -253,18 +253,18 @@ class DNSDispatchHandler extends DecoratorHandler {
253
253
  this.#dispatch(dispatchOpts, this)
254
254
  })
255
255
 
256
- // if dual-stack disabled, we error out
257
256
  return
258
257
  }
259
258
 
260
- this.#handler.onError(err)
261
- return
259
+ // if dual-stack disabled, we error out
260
+ super.onResponseError(controller, err)
261
+ break
262
262
  }
263
263
  case 'ENOTFOUND':
264
264
  this.#state.deleteRecord(this.#origin)
265
265
  // eslint-disable-next-line no-fallthrough
266
266
  default:
267
- this.#handler.onError(err)
267
+ super.onResponseError(controller, err)
268
268
  break
269
269
  }
270
270
  }
@@ -1,19 +1,17 @@
1
1
  'use strict'
2
2
 
3
- const util = require('../core/util')
4
3
  const { InvalidArgumentError, RequestAbortedError } = require('../core/errors')
5
4
  const DecoratorHandler = require('../handler/decorator-handler')
6
5
 
7
6
  class DumpHandler extends DecoratorHandler {
8
7
  #maxSize = 1024 * 1024
9
- #abort = null
10
8
  #dumped = false
11
- #aborted = false
12
9
  #size = 0
13
- #reason = null
14
- #handler = null
10
+ #controller = null
11
+ aborted = false
12
+ reason = false
15
13
 
16
- constructor ({ maxSize }, handler) {
14
+ constructor ({ maxSize, signal }, handler) {
17
15
  if (maxSize != null && (!Number.isFinite(maxSize) || maxSize < 1)) {
18
16
  throw new InvalidArgumentError('maxSize must be a number greater than 0')
19
17
  }
@@ -21,23 +19,22 @@ class DumpHandler extends DecoratorHandler {
21
19
  super(handler)
22
20
 
23
21
  this.#maxSize = maxSize ?? this.#maxSize
24
- this.#handler = handler
22
+ // this.#handler = handler
25
23
  }
26
24
 
27
- onConnect (abort) {
28
- this.#abort = abort
29
-
30
- this.#handler.onConnect(this.#customAbort.bind(this))
25
+ #abort (reason) {
26
+ this.aborted = true
27
+ this.reason = reason
31
28
  }
32
29
 
33
- #customAbort (reason) {
34
- this.#aborted = true
35
- this.#reason = reason
30
+ onRequestStart (controller, context) {
31
+ controller.abort = this.#abort.bind(this)
32
+ this.#controller = controller
33
+
34
+ return super.onRequestStart(controller, context)
36
35
  }
37
36
 
38
- // TODO: will require adjustment after new hooks are out
39
- onHeaders (statusCode, rawHeaders, resume, statusMessage) {
40
- const headers = util.parseHeaders(rawHeaders)
37
+ onResponseStart (controller, statusCode, headers, statusMessage) {
41
38
  const contentLength = headers['content-length']
42
39
 
43
40
  if (contentLength != null && contentLength > this.#maxSize) {
@@ -48,55 +45,50 @@ class DumpHandler extends DecoratorHandler {
48
45
  )
49
46
  }
50
47
 
51
- if (this.#aborted) {
48
+ if (this.aborted === true) {
52
49
  return true
53
50
  }
54
51
 
55
- return this.#handler.onHeaders(
56
- statusCode,
57
- rawHeaders,
58
- resume,
59
- statusMessage
60
- )
52
+ return super.onResponseStart(controller, statusCode, headers, statusMessage)
61
53
  }
62
54
 
63
- onError (err) {
55
+ onResponseError (controller, err) {
64
56
  if (this.#dumped) {
65
57
  return
66
58
  }
67
59
 
68
- err = this.#reason ?? err
60
+ err = this.#controller.reason ?? err
69
61
 
70
- this.#handler.onError(err)
62
+ super.onResponseError(controller, err)
71
63
  }
72
64
 
73
- onData (chunk) {
65
+ onResponseData (controller, chunk) {
74
66
  this.#size = this.#size + chunk.length
75
67
 
76
68
  if (this.#size >= this.#maxSize) {
77
69
  this.#dumped = true
78
70
 
79
- if (this.#aborted) {
80
- this.#handler.onError(this.#reason)
71
+ if (this.aborted === true) {
72
+ super.onResponseError(controller, this.reason)
81
73
  } else {
82
- this.#handler.onComplete([])
74
+ super.onResponseEnd(controller, {})
83
75
  }
84
76
  }
85
77
 
86
78
  return true
87
79
  }
88
80
 
89
- onComplete (trailers) {
81
+ onResponseEnd (controller, trailers) {
90
82
  if (this.#dumped) {
91
83
  return
92
84
  }
93
85
 
94
- if (this.#aborted) {
95
- this.#handler.onError(this.reason)
86
+ if (this.#controller.aborted === true) {
87
+ super.onResponseError(controller, this.reason)
96
88
  return
97
89
  }
98
90
 
99
- this.#handler.onComplete(trailers)
91
+ super.onResponseEnd(controller, trailers)
100
92
  }
101
93
  }
102
94
 
@@ -107,13 +99,9 @@ function createDumpInterceptor (
107
99
  ) {
108
100
  return dispatch => {
109
101
  return function Intercept (opts, handler) {
110
- const { dumpMaxSize = defaultMaxSize } =
111
- opts
102
+ const { dumpMaxSize = defaultMaxSize } = opts
112
103
 
113
- const dumpHandler = new DumpHandler(
114
- { maxSize: dumpMaxSize },
115
- handler
116
- )
104
+ const dumpHandler = new DumpHandler({ maxSize: dumpMaxSize, signal: opts.signal }, handler)
117
105
 
118
106
  return dispatch(opts, dumpHandler)
119
107
  }
@@ -1,59 +1,61 @@
1
1
  'use strict'
2
2
 
3
- const { parseHeaders } = require('../core/util')
3
+ // const { parseHeaders } = require('../core/util')
4
4
  const DecoratorHandler = require('../handler/decorator-handler')
5
5
  const { ResponseError } = require('../core/errors')
6
6
 
7
7
  class ResponseErrorHandler extends DecoratorHandler {
8
- #handler
9
8
  #statusCode
10
9
  #contentType
11
10
  #decoder
12
11
  #headers
13
12
  #body
14
13
 
15
- constructor (opts, { handler }) {
14
+ constructor (_opts, { handler }) {
16
15
  super(handler)
17
- this.#handler = handler
18
16
  }
19
17
 
20
- onConnect (abort) {
18
+ #checkContentType (contentType) {
19
+ return this.#contentType.indexOf(contentType) === 0
20
+ }
21
+
22
+ onRequestStart (controller, context) {
21
23
  this.#statusCode = 0
22
24
  this.#contentType = null
23
25
  this.#decoder = null
24
26
  this.#headers = null
25
27
  this.#body = ''
26
28
 
27
- return this.#handler.onConnect(abort)
29
+ return super.onRequestStart(controller, context)
28
30
  }
29
31
 
30
- onHeaders (statusCode, rawHeaders, resume, statusMessage, headers = parseHeaders(rawHeaders)) {
32
+ onResponseStart (controller, statusCode, headers, statusMessage) {
31
33
  this.#statusCode = statusCode
32
34
  this.#headers = headers
33
35
  this.#contentType = headers['content-type']
34
36
 
35
37
  if (this.#statusCode < 400) {
36
- return this.#handler.onHeaders(statusCode, rawHeaders, resume, statusMessage, headers)
38
+ return super.onResponseStart(controller, statusCode, headers, statusMessage)
37
39
  }
38
40
 
39
- if (this.#contentType === 'application/json' || this.#contentType === 'text/plain') {
41
+ if (this.#checkContentType('application/json') || this.#checkContentType('text/plain')) {
40
42
  this.#decoder = new TextDecoder('utf-8')
41
43
  }
42
44
  }
43
45
 
44
- onData (chunk) {
46
+ onResponseData (controller, chunk) {
45
47
  if (this.#statusCode < 400) {
46
- return this.#handler.onData(chunk)
48
+ return super.onResponseData(controller, chunk)
47
49
  }
48
50
 
49
51
  this.#body += this.#decoder?.decode(chunk, { stream: true }) ?? ''
50
52
  }
51
53
 
52
- onComplete (rawTrailers) {
54
+ onResponseEnd (controller, trailers) {
53
55
  if (this.#statusCode >= 400) {
54
56
  this.#body += this.#decoder?.decode(undefined, { stream: false }) ?? ''
55
57
 
56
- if (this.#contentType === 'application/json') {
58
+ if (this.#checkContentType('application/json')) {
57
59
  try {
58
60
  this.#body = JSON.parse(this.#body)
59
61
  } catch {
@@ -73,14 +75,14 @@ class ResponseErrorHandler extends DecoratorHandler {
73
75
  Error.stackTraceLimit = stackTraceLimit
74
76
  }
75
77
 
76
- this.#handler.onError(err)
78
+ super.onResponseError(controller, err)
77
79
  } else {
78
- this.#handler.onComplete(rawTrailers)
80
+ super.onResponseEnd(controller, trailers)
79
81
  }
80
82
  }
81
83
 
82
- onError (err) {
83
- this.#handler.onError(err)
84
+ onResponseError (err) {
85
+ super.onResponseError(err)
84
86
  }
85
87
  }
86
88
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "undici",
3
- "version": "7.0.0-alpha.9",
3
+ "version": "7.1.0",
4
4
  "description": "An HTTP/1.1 client, written from scratch for Node.js",
5
5
  "homepage": "https://undici.nodejs.org",
6
6
  "bugs": {