undici 7.0.0-alpha.7 → 7.0.0-alpha.9

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.
@@ -3,9 +3,9 @@ const assert = require('node:assert')
3
3
 
4
4
  const { kRetryHandlerDefaultRetry } = require('../core/symbols')
5
5
  const { RequestRetryError } = require('../core/errors')
6
+ const WrapHandler = require('./wrap-handler')
6
7
  const {
7
8
  isDisturbed,
8
- parseHeaders,
9
9
  parseRangeHeader,
10
10
  wrapRequestBody
11
11
  } = require('../core/util')
@@ -16,7 +16,7 @@ function calculateRetryAfterHeader (retryAfter) {
16
16
  }
17
17
 
18
18
  class RetryHandler {
19
- constructor (opts, handlers) {
19
+ constructor (opts, { dispatch, handler }) {
20
20
  const { retryOptions, ...dispatchOpts } = opts
21
21
  const {
22
22
  // Retry scoped
@@ -32,12 +32,9 @@ class RetryHandler {
32
32
  statusCodes
33
33
  } = retryOptions ?? {}
34
34
 
35
- this.dispatch = handlers.dispatch
36
- this.handler = handlers.handler
35
+ this.dispatch = dispatch
36
+ this.handler = WrapHandler.wrap(handler)
37
37
  this.opts = { ...dispatchOpts, body: wrapRequestBody(opts.body) }
38
- this.abort = null
39
- this.aborted = false
40
- this.connectCalled = false
41
38
  this.retryOpts = {
42
39
  retry: retryFn ?? RetryHandler[kRetryHandlerDefaultRetry],
43
40
  retryAfter: retryAfter ?? true,
@@ -65,37 +62,20 @@ class RetryHandler {
65
62
 
66
63
  this.retryCount = 0
67
64
  this.retryCountCheckpoint = 0
65
+ this.headersSent = false
68
66
  this.start = 0
69
67
  this.end = null
70
68
  this.etag = null
71
- this.resume = null
72
69
  }
73
70
 
74
- onRequestSent () {
75
- if (this.handler.onRequestSent) {
76
- this.handler.onRequestSent()
71
+ onRequestStart (controller, context) {
72
+ if (!this.headersSent) {
73
+ this.handler.onRequestStart?.(controller, context)
77
74
  }
78
75
  }
79
76
 
80
- onUpgrade (statusCode, headers, socket) {
81
- if (this.handler.onUpgrade) {
82
- this.handler.onUpgrade(statusCode, headers, socket)
83
- }
84
- }
85
-
86
- onConnect (abort, context) {
87
- this.abort = abort
88
- if (!this.connectCalled) {
89
- this.connectCalled = true
90
- this.handler.onConnect(reason => {
91
- this.aborted = true
92
- this.abort(reason)
93
- }, context)
94
- }
95
- }
96
-
97
- onBodySent (chunk) {
98
- if (this.handler.onBodySent) return this.handler.onBodySent(chunk)
77
+ onRequestUpgrade (controller, statusCode, headers, socket) {
78
+ this.handler.onRequestUpgrade?.(controller, statusCode, headers, socket)
99
79
  }
100
80
 
101
81
  static [kRetryHandlerDefaultRetry] (err, { state, opts }, cb) {
@@ -153,83 +133,68 @@ class RetryHandler {
153
133
  ? Math.min(retryAfterHeader, maxTimeout)
154
134
  : Math.min(minTimeout * timeoutFactor ** (counter - 1), maxTimeout)
155
135
 
156
- setTimeout(() => cb(null), retryTimeout)
136
+ setTimeout(() => cb(null), retryTimeout).unref()
157
137
  }
158
138
 
159
- onHeaders (statusCode, rawHeaders, resume, statusMessage) {
160
- const headers = parseHeaders(rawHeaders)
161
-
139
+ onResponseStart (controller, statusCode, headers, statusMessage) {
162
140
  this.retryCount += 1
163
141
 
164
142
  if (statusCode >= 300) {
165
143
  if (this.retryOpts.statusCodes.includes(statusCode) === false) {
166
- return this.handler.onHeaders(
144
+ this.headersSent = true
145
+ this.handler.onResponseStart?.(
146
+ controller,
167
147
  statusCode,
168
- rawHeaders,
169
- resume,
148
+ headers,
170
149
  statusMessage
171
150
  )
151
+ return
172
152
  } else {
173
- this.abort(
174
- new RequestRetryError('Request failed', statusCode, {
175
- headers,
176
- data: {
177
- count: this.retryCount
178
- }
179
- })
180
- )
181
- return false
153
+ throw new RequestRetryError('Request failed', statusCode, {
154
+ headers,
155
+ data: {
156
+ count: this.retryCount
157
+ }
158
+ })
182
159
  }
183
160
  }
184
161
 
185
162
  // Checkpoint for resume from where we left it
186
- if (this.resume != null) {
187
- this.resume = null
188
-
163
+ if (this.headersSent) {
189
164
  // Only Partial Content 206 supposed to provide Content-Range,
190
165
  // any other status code that partially consumed the payload
191
166
  // should not be retried because it would result in downstream
192
167
  // wrongly concatenate multiple responses.
193
168
  if (statusCode !== 206 && (this.start > 0 || statusCode !== 200)) {
194
- this.abort(
195
- new RequestRetryError('server does not support the range header and the payload was partially consumed', statusCode, {
196
- headers,
197
- data: { count: this.retryCount }
198
- })
199
- )
200
- return false
169
+ throw new RequestRetryError('server does not support the range header and the payload was partially consumed', statusCode, {
170
+ headers,
171
+ data: { count: this.retryCount }
172
+ })
201
173
  }
202
174
 
203
175
  const contentRange = parseRangeHeader(headers['content-range'])
204
176
  // If no content range
205
177
  if (!contentRange) {
206
- this.abort(
207
- new RequestRetryError('Content-Range mismatch', statusCode, {
208
- headers,
209
- data: { count: this.retryCount }
210
- })
211
- )
212
- return false
178
+ throw new RequestRetryError('Content-Range mismatch', statusCode, {
179
+ headers,
180
+ data: { count: this.retryCount }
181
+ })
213
182
  }
214
183
 
215
184
  // Let's start with a weak etag check
216
185
  if (this.etag != null && this.etag !== headers.etag) {
217
- this.abort(
218
- new RequestRetryError('ETag mismatch', statusCode, {
219
- headers,
220
- data: { count: this.retryCount }
221
- })
222
- )
223
- return false
186
+ throw new RequestRetryError('ETag mismatch', statusCode, {
187
+ headers,
188
+ data: { count: this.retryCount }
189
+ })
224
190
  }
225
191
 
226
- const { start, size, end = size - 1 } = contentRange
192
+ const { start, size, end = size ? size - 1 : null } = contentRange
227
193
 
228
194
  assert(this.start === start, 'content-range mismatch')
229
195
  assert(this.end == null || this.end === end, 'content-range mismatch')
230
196
 
231
- this.resume = resume
232
- return true
197
+ return
233
198
  }
234
199
 
235
200
  if (this.end == null) {
@@ -238,15 +203,17 @@ class RetryHandler {
238
203
  const range = parseRangeHeader(headers['content-range'])
239
204
 
240
205
  if (range == null) {
241
- return this.handler.onHeaders(
206
+ this.headersSent = true
207
+ this.handler.onResponseStart?.(
208
+ controller,
242
209
  statusCode,
243
- rawHeaders,
244
- resume,
210
+ headers,
245
211
  statusMessage
246
212
  )
213
+ return
247
214
  }
248
215
 
249
- const { start, size, end = size - 1 } = range
216
+ const { start, size, end = size ? size - 1 : null } = range
250
217
  assert(
251
218
  start != null && Number.isFinite(start),
252
219
  'content-range mismatch'
@@ -269,7 +236,7 @@ class RetryHandler {
269
236
  'invalid content-length'
270
237
  )
271
238
 
272
- this.resume = resume
239
+ this.resume = true
273
240
  this.etag = headers.etag != null ? headers.etag : null
274
241
 
275
242
  // Weak etags are not useful for comparison nor cache
@@ -283,38 +250,36 @@ class RetryHandler {
283
250
  this.etag = null
284
251
  }
285
252
 
286
- return this.handler.onHeaders(
253
+ this.headersSent = true
254
+ this.handler.onResponseStart?.(
255
+ controller,
287
256
  statusCode,
288
- rawHeaders,
289
- resume,
257
+ headers,
290
258
  statusMessage
291
259
  )
260
+ } else {
261
+ throw new RequestRetryError('Request failed', statusCode, {
262
+ headers,
263
+ data: { count: this.retryCount }
264
+ })
292
265
  }
293
-
294
- const err = new RequestRetryError('Request failed', statusCode, {
295
- headers,
296
- data: { count: this.retryCount }
297
- })
298
-
299
- this.abort(err)
300
-
301
- return false
302
266
  }
303
267
 
304
- onData (chunk) {
268
+ onResponseData (controller, chunk) {
305
269
  this.start += chunk.length
306
270
 
307
- return this.handler.onData(chunk)
271
+ this.handler.onResponseData?.(controller, chunk)
308
272
  }
309
273
 
310
- onComplete (rawTrailers) {
274
+ onResponseEnd (controller, trailers) {
311
275
  this.retryCount = 0
312
- return this.handler.onComplete(rawTrailers)
276
+ return this.handler.onResponseEnd?.(controller, trailers)
313
277
  }
314
278
 
315
- onError (err) {
316
- if (this.aborted || isDisturbed(this.opts.body)) {
317
- return this.handler.onError(err)
279
+ onResponseError (controller, err) {
280
+ if (!controller || controller.aborted || isDisturbed(this.opts.body)) {
281
+ this.handler.onResponseError?.(controller, err)
282
+ return
318
283
  }
319
284
 
320
285
  // We reconcile in case of a mix between network errors
@@ -343,8 +308,8 @@ class RetryHandler {
343
308
  * @returns
344
309
  */
345
310
  function onRetry (err) {
346
- if (err != null || this.aborted || isDisturbed(this.opts.body)) {
347
- return this.handler.onError(err)
311
+ if (err != null || controller?.aborted || isDisturbed(this.opts.body)) {
312
+ return this.handler.onResponseError?.(controller, err)
348
313
  }
349
314
 
350
315
  if (this.start !== 0) {
@@ -368,7 +333,7 @@ class RetryHandler {
368
333
  this.retryCountCheckpoint = this.retryCount
369
334
  this.dispatch(this.opts, this)
370
335
  } catch (err) {
371
- this.handler.onError(err)
336
+ this.handler.onResponseError?.(controller, err)
372
337
  }
373
338
  }
374
339
  }
@@ -73,7 +73,7 @@ module.exports = class UnwrapHandler {
73
73
 
74
74
  onHeaders (statusCode, rawHeaders, resume, statusMessage) {
75
75
  this.#controller[kResume] = resume
76
- this.#handler.onResponseStart?.(this.#controller, statusCode, statusMessage, parseHeaders(rawHeaders))
76
+ this.#handler.onResponseStart?.(this.#controller, statusCode, parseHeaders(rawHeaders), statusMessage)
77
77
  return !this.#controller.paused
78
78
  }
79
79
 
@@ -87,7 +87,7 @@ module.exports = class UnwrapHandler {
87
87
  }
88
88
 
89
89
  onError (err) {
90
- if (!this.#handler.onError) {
90
+ if (!this.#handler.onResponseError) {
91
91
  throw new InvalidArgumentError('invalid onError method')
92
92
  }
93
93
 
@@ -38,7 +38,7 @@ module.exports = class WrapHandler {
38
38
 
39
39
  onError (err) {
40
40
  if (!this.#handler.onError) {
41
- throw new InvalidArgumentError('invalid onError method')
41
+ throw err
42
42
  }
43
43
 
44
44
  return this.#handler.onError?.(err)
@@ -60,7 +60,7 @@ module.exports = class WrapHandler {
60
60
  this.#handler.onUpgrade?.(statusCode, rawHeaders, socket)
61
61
  }
62
62
 
63
- onResponseStart (controller, statusCode, statusMessage, headers) {
63
+ onResponseStart (controller, statusCode, headers, statusMessage) {
64
64
  const rawHeaders = []
65
65
  for (const [key, val] of Object.entries(headers)) {
66
66
  // TODO (fix): What if val is Array