@nxtedition/nxt-undici 6.2.12 → 6.2.13
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/lib/interceptor/dns.js +3 -2
- package/lib/interceptor/response-error.js +29 -71
- package/lib/interceptor/response-retry.js +43 -35
- package/lib/utils.js +62 -1
- package/package.json +1 -1
package/lib/interceptor/dns.js
CHANGED
|
@@ -42,8 +42,11 @@ export default () => (dispatch) => {
|
|
|
42
42
|
|
|
43
43
|
if (err) {
|
|
44
44
|
logger?.error({ err, dns: { hostname } }, 'lookup failed')
|
|
45
|
+
|
|
45
46
|
resolve([err, null])
|
|
46
47
|
} else {
|
|
48
|
+
logger?.debug({ dns: { hostname, records } }, 'lookup completed')
|
|
49
|
+
|
|
47
50
|
const now = getFastNow()
|
|
48
51
|
const val = records.map(({ address, ttl }) => ({
|
|
49
52
|
address,
|
|
@@ -53,8 +56,6 @@ export default () => (dispatch) => {
|
|
|
53
56
|
counter: 0,
|
|
54
57
|
}))
|
|
55
58
|
|
|
56
|
-
logger?.debug({ err, dns: { hostname, records } }, 'lookup completed')
|
|
57
|
-
|
|
58
59
|
cache.set(hostname, val)
|
|
59
60
|
|
|
60
61
|
resolve([null, val])
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { DecoratorHandler } from '../utils.js'
|
|
1
|
+
import { DecoratorHandler, decorateError } from '../utils.js'
|
|
3
2
|
|
|
4
3
|
class Handler extends DecoratorHandler {
|
|
5
4
|
#statusCode = 0
|
|
6
|
-
#contentType
|
|
7
5
|
#decoder
|
|
8
6
|
#headers
|
|
7
|
+
#trailers
|
|
9
8
|
#body = ''
|
|
10
9
|
#opts
|
|
11
10
|
|
|
@@ -15,13 +14,8 @@ class Handler extends DecoratorHandler {
|
|
|
15
14
|
this.#opts = opts
|
|
16
15
|
}
|
|
17
16
|
|
|
18
|
-
#checkContentType(contentType) {
|
|
19
|
-
return (this.#contentType ?? '').indexOf(contentType) === 0
|
|
20
|
-
}
|
|
21
|
-
|
|
22
17
|
onConnect(abort) {
|
|
23
18
|
this.#statusCode = 0
|
|
24
|
-
this.#contentType = null
|
|
25
19
|
this.#decoder = null
|
|
26
20
|
this.#headers = null
|
|
27
21
|
this.#body = ''
|
|
@@ -32,14 +26,17 @@ class Handler extends DecoratorHandler {
|
|
|
32
26
|
onHeaders(statusCode, headers, resume) {
|
|
33
27
|
this.#statusCode = statusCode
|
|
34
28
|
this.#headers = headers
|
|
35
|
-
this.#contentType = headers['content-type']
|
|
36
29
|
|
|
37
30
|
if (this.#statusCode < 400) {
|
|
38
31
|
return super.onHeaders(statusCode, headers, resume)
|
|
39
32
|
}
|
|
40
33
|
|
|
41
|
-
if (
|
|
34
|
+
if (
|
|
35
|
+
this.#headers['content-type']?.startsWith('application/json') ||
|
|
36
|
+
this.#headers['content-type']?.startsWith('text/plain')
|
|
37
|
+
) {
|
|
42
38
|
this.#decoder = new TextDecoder('utf-8')
|
|
39
|
+
this.#body = ''
|
|
43
40
|
}
|
|
44
41
|
}
|
|
45
42
|
|
|
@@ -52,72 +49,33 @@ class Handler extends DecoratorHandler {
|
|
|
52
49
|
}
|
|
53
50
|
|
|
54
51
|
onComplete(trailers) {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
try {
|
|
60
|
-
this.#body = JSON.parse(this.#body)
|
|
61
|
-
} catch {
|
|
62
|
-
// Do nothing...
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
let err
|
|
67
|
-
const stackTraceLimit = Error.stackTraceLimit
|
|
68
|
-
Error.stackTraceLimit = 0
|
|
69
|
-
try {
|
|
70
|
-
err = createHttpError(this.#statusCode)
|
|
71
|
-
} finally {
|
|
72
|
-
Error.stackTraceLimit = stackTraceLimit
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
super.onError(this.#decorateError(err))
|
|
76
|
-
} else {
|
|
77
|
-
super.onComplete(trailers)
|
|
52
|
+
this.#trailers = trailers
|
|
53
|
+
|
|
54
|
+
if (this.#statusCode < 400) {
|
|
55
|
+
return super.onComplete(trailers)
|
|
78
56
|
}
|
|
79
|
-
}
|
|
80
57
|
|
|
81
|
-
|
|
82
|
-
|
|
58
|
+
this.#body += this.#decoder?.decode(undefined, { stream: false }) ?? ''
|
|
59
|
+
|
|
60
|
+
super.onError(
|
|
61
|
+
decorateError(null, this.#opts, {
|
|
62
|
+
statusCode: this.#statusCode,
|
|
63
|
+
headers: this.#headers,
|
|
64
|
+
trailers: this.#trailers,
|
|
65
|
+
body: this.#body,
|
|
66
|
+
}),
|
|
67
|
+
)
|
|
83
68
|
}
|
|
84
69
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
err
|
|
88
|
-
|
|
89
|
-
err.req = {
|
|
90
|
-
method: this.#opts?.method,
|
|
91
|
-
headers: this.#opts?.headers,
|
|
92
|
-
body:
|
|
93
|
-
// TODO (fix): JSON.stringify POJO
|
|
94
|
-
typeof this.#opts?.body !== 'string' || this.#opts.body.length > 1024
|
|
95
|
-
? undefined
|
|
96
|
-
: this.#opts.body,
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
err.res = {
|
|
70
|
+
onError(err) {
|
|
71
|
+
super.onError(
|
|
72
|
+
decorateError(err, this.#opts, {
|
|
73
|
+
statusCode: this.#statusCode,
|
|
100
74
|
headers: this.#headers,
|
|
101
|
-
|
|
102
|
-
body:
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
if (this.#body) {
|
|
106
|
-
if (this.#body.reason != null) {
|
|
107
|
-
err.reason ??= this.#body.reason
|
|
108
|
-
}
|
|
109
|
-
if (this.#body.code != null) {
|
|
110
|
-
err.code ??= this.#body.code
|
|
111
|
-
}
|
|
112
|
-
if (this.#body.error != null) {
|
|
113
|
-
err.error ??= this.#body.error
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
return err
|
|
118
|
-
} catch (er) {
|
|
119
|
-
return new AggregateError([er, err])
|
|
120
|
-
}
|
|
75
|
+
trailers: this.#trailers,
|
|
76
|
+
body: null,
|
|
77
|
+
}),
|
|
78
|
+
)
|
|
121
79
|
}
|
|
122
80
|
}
|
|
123
81
|
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import assert from 'node:assert'
|
|
2
2
|
import tp from 'node:timers/promises'
|
|
3
|
-
import { DecoratorHandler, isDisturbed, parseRangeHeader } from '../utils.js'
|
|
4
|
-
import createHttpError from 'http-errors'
|
|
3
|
+
import { DecoratorHandler, isDisturbed, decorateError, parseRangeHeader } from '../utils.js'
|
|
5
4
|
|
|
6
5
|
function noop() {}
|
|
7
6
|
|
|
@@ -15,8 +14,10 @@ class Handler extends DecoratorHandler {
|
|
|
15
14
|
#errorSent = false
|
|
16
15
|
|
|
17
16
|
#statusCode = 0
|
|
18
|
-
#headers
|
|
19
|
-
#trailers
|
|
17
|
+
#headers
|
|
18
|
+
#trailers
|
|
19
|
+
#body = ''
|
|
20
|
+
#decoder
|
|
20
21
|
|
|
21
22
|
#abort
|
|
22
23
|
#aborted = false
|
|
@@ -48,6 +49,8 @@ class Handler extends DecoratorHandler {
|
|
|
48
49
|
this.#statusCode = 0
|
|
49
50
|
this.#headers = null
|
|
50
51
|
this.#trailers = null
|
|
52
|
+
this.#body = ''
|
|
53
|
+
this.#decoder = null
|
|
51
54
|
|
|
52
55
|
if (!this.#headersSent) {
|
|
53
56
|
this.#pos = null
|
|
@@ -117,6 +120,13 @@ class Handler extends DecoratorHandler {
|
|
|
117
120
|
this.#end = contentLength
|
|
118
121
|
this.#etag = headers.etag
|
|
119
122
|
} else if (statusCode >= 400) {
|
|
123
|
+
if (
|
|
124
|
+
this.#headers['content-type']?.startsWith('application/json') ||
|
|
125
|
+
this.#headers['content-type']?.startsWith('text/plain')
|
|
126
|
+
) {
|
|
127
|
+
this.#decoder = new TextDecoder('utf-8')
|
|
128
|
+
this.#body = ''
|
|
129
|
+
}
|
|
120
130
|
return true
|
|
121
131
|
} else {
|
|
122
132
|
return this.#onHeaders(statusCode, headers, resume)
|
|
@@ -161,29 +171,30 @@ class Handler extends DecoratorHandler {
|
|
|
161
171
|
}
|
|
162
172
|
|
|
163
173
|
onData(chunk) {
|
|
164
|
-
if (this.#statusCode >= 400) {
|
|
165
|
-
// TODO (fix): Limit the amount of data we read?
|
|
166
|
-
return true
|
|
167
|
-
}
|
|
168
|
-
|
|
169
174
|
if (this.#pos != null) {
|
|
170
175
|
this.#pos += chunk.byteLength
|
|
171
176
|
}
|
|
172
|
-
return super.onData(chunk)
|
|
173
|
-
}
|
|
174
177
|
|
|
175
|
-
|
|
176
|
-
|
|
178
|
+
if (this.#statusCode < 400) {
|
|
179
|
+
return super.onData(chunk)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
this.#body += this.#decoder?.decode(chunk, { stream: true }) ?? ''
|
|
177
183
|
}
|
|
178
184
|
|
|
179
185
|
onComplete(trailers) {
|
|
180
186
|
this.#trailers = trailers
|
|
181
187
|
|
|
182
|
-
if (this.#statusCode
|
|
183
|
-
|
|
184
|
-
} else {
|
|
185
|
-
super.onComplete(trailers)
|
|
188
|
+
if (this.#statusCode < 400) {
|
|
189
|
+
return super.onComplete(trailers)
|
|
186
190
|
}
|
|
191
|
+
|
|
192
|
+
this.#body += this.#decoder?.decode(undefined, { stream: false }) ?? ''
|
|
193
|
+
this.#maybeRetry(null)
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
onError(err) {
|
|
197
|
+
this.#maybeRetry(err)
|
|
187
198
|
}
|
|
188
199
|
|
|
189
200
|
#maybeError(err) {
|
|
@@ -202,26 +213,29 @@ class Handler extends DecoratorHandler {
|
|
|
202
213
|
}
|
|
203
214
|
}
|
|
204
215
|
|
|
205
|
-
#maybeRetry(err
|
|
216
|
+
#maybeRetry(err) {
|
|
206
217
|
if (this.#aborted || isDisturbed(this.#opts.body) || (this.#pos && !this.#etag)) {
|
|
207
218
|
this.#maybeError(err)
|
|
208
219
|
return
|
|
209
220
|
}
|
|
210
221
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
}
|
|
220
|
-
}
|
|
222
|
+
this.#error =
|
|
223
|
+
err ??
|
|
224
|
+
decorateError(null, this.#opts, {
|
|
225
|
+
statusCode: this.#statusCode,
|
|
226
|
+
headers: this.#headers,
|
|
227
|
+
trailers: this.#trailers,
|
|
228
|
+
body: this.#body,
|
|
229
|
+
})
|
|
221
230
|
|
|
222
231
|
let retryPromise
|
|
223
232
|
try {
|
|
224
|
-
retryPromise =
|
|
233
|
+
retryPromise =
|
|
234
|
+
typeof this.#opts?.retry === 'function'
|
|
235
|
+
? this.#opts?.retry(this.#error, this.#retryCount, this.#opts, () =>
|
|
236
|
+
retryFn(this.#error, this.#retryCount, this.#opts),
|
|
237
|
+
)
|
|
238
|
+
: retryFn(this.#error, this.#retryCount, this.#opts)
|
|
225
239
|
} catch (err) {
|
|
226
240
|
retryPromise = Promise.reject(err)
|
|
227
241
|
}
|
|
@@ -231,8 +245,6 @@ class Handler extends DecoratorHandler {
|
|
|
231
245
|
return
|
|
232
246
|
}
|
|
233
247
|
|
|
234
|
-
this.#error = err
|
|
235
|
-
|
|
236
248
|
retryPromise
|
|
237
249
|
.then((opts) => {
|
|
238
250
|
if (this.#aborted) {
|
|
@@ -301,10 +313,6 @@ async function retryFn(err, retryCount, opts) {
|
|
|
301
313
|
throw err
|
|
302
314
|
}
|
|
303
315
|
|
|
304
|
-
if (typeof retryOpts === 'function') {
|
|
305
|
-
return retryOpts(err, retryCount, opts, () => retryFn(err, retryCount, opts))
|
|
306
|
-
}
|
|
307
|
-
|
|
308
316
|
if (typeof retryOpts === 'number') {
|
|
309
317
|
retryOpts = { count: retryOpts }
|
|
310
318
|
}
|
package/lib/utils.js
CHANGED
|
@@ -2,6 +2,7 @@ import cacheControlParser from 'cache-control-parser'
|
|
|
2
2
|
import stream from 'node:stream'
|
|
3
3
|
import assert from 'node:assert'
|
|
4
4
|
import { util } from '@nxtedition/undici'
|
|
5
|
+
import createHttpError from 'http-errors'
|
|
5
6
|
|
|
6
7
|
let fastNow = Date.now()
|
|
7
8
|
|
|
@@ -351,9 +352,69 @@ export function parseHeaders(headers, obj) {
|
|
|
351
352
|
}
|
|
352
353
|
|
|
353
354
|
// See https://github.com/nodejs/node/pull/46528
|
|
354
|
-
if ('content-length' in obj && 'content-disposition' in obj) {
|
|
355
|
+
if (obj != null && 'content-length' in obj && 'content-disposition' in obj) {
|
|
355
356
|
obj['content-disposition'] = Buffer.from(obj['content-disposition']).toString('latin1')
|
|
356
357
|
}
|
|
357
358
|
|
|
358
359
|
return obj
|
|
359
360
|
}
|
|
361
|
+
|
|
362
|
+
export function decorateError(err, opts, { statusCode, headers, trailers, body }) {
|
|
363
|
+
try {
|
|
364
|
+
if (err == null) {
|
|
365
|
+
const stackTraceLimit = Error.stackTraceLimit
|
|
366
|
+
Error.stackTraceLimit = 0
|
|
367
|
+
try {
|
|
368
|
+
err = createHttpError(statusCode)
|
|
369
|
+
} finally {
|
|
370
|
+
Error.stackTraceLimit = stackTraceLimit
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
if (statusCode != null) {
|
|
375
|
+
err.statusCode = statusCode
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
err.url ??= opts.origin ? new URL(opts.path, opts.origin).href : null
|
|
379
|
+
|
|
380
|
+
err.req = {
|
|
381
|
+
method: opts?.method,
|
|
382
|
+
headers: opts?.headers,
|
|
383
|
+
body:
|
|
384
|
+
// TODO (fix): JSON.stringify POJO
|
|
385
|
+
typeof opts?.body !== 'string' || opts.body.length > 1024 ? undefined : opts.body,
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
if (headers?.['content-type']?.startsWith('application/json') && typeof body === 'string') {
|
|
389
|
+
try {
|
|
390
|
+
body = JSON.parse(body)
|
|
391
|
+
} catch {
|
|
392
|
+
// Do nothing...
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
err.res = {
|
|
397
|
+
headers,
|
|
398
|
+
trailers,
|
|
399
|
+
// TODO (fix): JSON.stringify POJO
|
|
400
|
+
body: typeof body !== 'string' || body.length < 1024 ? undefined : body,
|
|
401
|
+
statusCode,
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
if (body) {
|
|
405
|
+
if (body.reason != null) {
|
|
406
|
+
err.reason ??= body.reason
|
|
407
|
+
}
|
|
408
|
+
if (body.code != null) {
|
|
409
|
+
err.code ??= body.code
|
|
410
|
+
}
|
|
411
|
+
if (body.error != null) {
|
|
412
|
+
err.error ??= body.error
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
return err
|
|
417
|
+
} catch (er) {
|
|
418
|
+
return new AggregateError([er, err])
|
|
419
|
+
}
|
|
420
|
+
}
|