@nxtedition/lib 14.2.1 → 15.0.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/app.js +47 -47
- package/errors.js +7 -7
- package/package.json +13 -10
- package/undici/index.js +192 -0
- package/undici/interceptor/abort.js +61 -0
- package/undici/interceptor/catch.js +55 -0
- package/undici/interceptor/content.js +137 -0
- package/undici/interceptor/log.js +57 -0
- package/{proxy.js → undici/interceptor/proxy.js} +97 -23
- package/undici/interceptor/redirect.js +179 -0
- package/undici/interceptor/response-body-dump.js +46 -0
- package/undici/interceptor/response-body-retry.js +155 -0
- package/undici/interceptor/response-retry.js +84 -0
- package/undici/interceptor/response-status-retry.js +96 -0
- package/undici/interceptor/signal.js +47 -0
- package/undici/utils.js +171 -0
- package/undici.js +0 -160
package/app.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
const
|
|
2
|
-
const
|
|
1
|
+
const os = require('node:os')
|
|
2
|
+
const net = require('node:net')
|
|
3
|
+
const assert = require('node:assert')
|
|
3
4
|
const stream = require('node:stream')
|
|
4
5
|
const { Buffer } = require('node:buffer')
|
|
5
|
-
const
|
|
6
|
+
const { getDockerSecretsSync } = require('./docker-secrets')
|
|
7
|
+
const { getGlobalDispatcher } = require('undici')
|
|
6
8
|
const fp = require('lodash/fp.js')
|
|
7
|
-
const assert = require('node:assert')
|
|
8
9
|
|
|
9
10
|
module.exports = function (appConfig, onTerminate) {
|
|
10
11
|
let ds
|
|
@@ -90,14 +91,21 @@ module.exports = function (appConfig, onTerminate) {
|
|
|
90
91
|
|
|
91
92
|
const destroyers = []
|
|
92
93
|
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
const serviceName = appConfig.name + (instanceId && instanceId !== '0' ? `-${instanceId}` : '')
|
|
94
|
+
const serviceName = appConfig.name
|
|
95
|
+
const serviceModule = appConfig.module ?? 'main'
|
|
96
96
|
const serviceVersion = appConfig.version
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
appConfig.
|
|
100
|
-
|
|
97
|
+
const serviceInstanceId =
|
|
98
|
+
// process.env.name is the pm2 name of the process
|
|
99
|
+
appConfig.instanceId ?? appConfig.containerId ?? process.env.name ?? os.hostname()
|
|
100
|
+
|
|
101
|
+
const userAgent = globalThis.userAgent = (
|
|
102
|
+
appConfig.userAgent ??
|
|
103
|
+
(serviceName &&
|
|
104
|
+
`${serviceName}/${
|
|
105
|
+
serviceVersion || '*'
|
|
106
|
+
} (module:${serviceModule}; instance:${serviceInstanceId}) Node/${process.version}`) ??
|
|
107
|
+
null
|
|
108
|
+
)
|
|
101
109
|
|
|
102
110
|
const terminate = async (finalLogger) => {
|
|
103
111
|
finalLogger ??= logger
|
|
@@ -137,9 +145,10 @@ module.exports = function (appConfig, onTerminate) {
|
|
|
137
145
|
{
|
|
138
146
|
...loggerConfig,
|
|
139
147
|
name: serviceName,
|
|
148
|
+
module: serviceModule,
|
|
140
149
|
base: loggerConfig?.base ? { ...loggerConfig.base } : {},
|
|
141
150
|
},
|
|
142
|
-
terminate
|
|
151
|
+
terminate,
|
|
143
152
|
)
|
|
144
153
|
}
|
|
145
154
|
|
|
@@ -166,12 +175,7 @@ module.exports = function (appConfig, onTerminate) {
|
|
|
166
175
|
}
|
|
167
176
|
|
|
168
177
|
if (appConfig.perf && process.platform === 'linux') {
|
|
169
|
-
const
|
|
170
|
-
|
|
171
|
-
const containerId = appConfig.containerId ?? os.hostname()
|
|
172
|
-
const hostname = process.env.NODE_ENV === 'production' ? containerId : serviceName
|
|
173
|
-
|
|
174
|
-
const perfName = typeof appConfig.perf === 'string' ? appConfig.perf : hostname
|
|
178
|
+
const perfName = typeof appConfig.perf === 'string' ? appConfig.perf : serviceInstanceId
|
|
175
179
|
|
|
176
180
|
try {
|
|
177
181
|
const linuxPerf = require('linux-perf')
|
|
@@ -225,7 +229,7 @@ module.exports = function (appConfig, onTerminate) {
|
|
|
225
229
|
} else {
|
|
226
230
|
return {}
|
|
227
231
|
}
|
|
228
|
-
})
|
|
232
|
+
}),
|
|
229
233
|
)
|
|
230
234
|
|
|
231
235
|
if (couchConfig.url) {
|
|
@@ -253,7 +257,7 @@ module.exports = function (appConfig, onTerminate) {
|
|
|
253
257
|
} else {
|
|
254
258
|
return {}
|
|
255
259
|
}
|
|
256
|
-
})
|
|
260
|
+
}),
|
|
257
261
|
)
|
|
258
262
|
|
|
259
263
|
if (!dsConfig.credentials || !dsConfig.url) {
|
|
@@ -307,7 +311,7 @@ module.exports = function (appConfig, onTerminate) {
|
|
|
307
311
|
|
|
308
312
|
logger[level](
|
|
309
313
|
{ ds: { connectionState, username: userName, url: dsConfig.url } },
|
|
310
|
-
'Deepstream Connection State Changed.'
|
|
314
|
+
'Deepstream Connection State Changed.',
|
|
311
315
|
)
|
|
312
316
|
|
|
313
317
|
prevConnectionState = connectionState
|
|
@@ -348,7 +352,7 @@ module.exports = function (appConfig, onTerminate) {
|
|
|
348
352
|
rx.exhaustMap(() => {
|
|
349
353
|
const ret = appConfig.stats({ ds, couch, logger })
|
|
350
354
|
return ret?.then || ret?.subscribe ? ret : rxjs.of(ret)
|
|
351
|
-
})
|
|
355
|
+
}),
|
|
352
356
|
)
|
|
353
357
|
} else if (typeof appConfig.stats === 'object') {
|
|
354
358
|
stats$ = rxjs.timer(0, 10e3).pipe(rx.map(() => appConfig.stats))
|
|
@@ -366,12 +370,12 @@ module.exports = function (appConfig, onTerminate) {
|
|
|
366
370
|
rx.retryWhen((err$) =>
|
|
367
371
|
err$.pipe(
|
|
368
372
|
rx.tap((err) => logger.error({ err }, 'monitor.stats')),
|
|
369
|
-
rx.delay(10e3)
|
|
370
|
-
)
|
|
373
|
+
rx.delay(10e3),
|
|
374
|
+
),
|
|
371
375
|
),
|
|
372
376
|
rx.startWith({}),
|
|
373
377
|
rx.publishReplay(1),
|
|
374
|
-
rx.refCount()
|
|
378
|
+
rx.refCount(),
|
|
375
379
|
)
|
|
376
380
|
|
|
377
381
|
monitorProviders.stats$ = stats$
|
|
@@ -390,7 +394,7 @@ module.exports = function (appConfig, onTerminate) {
|
|
|
390
394
|
heap: v8.getHeapStatistics(),
|
|
391
395
|
...stats,
|
|
392
396
|
},
|
|
393
|
-
'STATS'
|
|
397
|
+
'STATS',
|
|
394
398
|
)
|
|
395
399
|
elu1 = elu2
|
|
396
400
|
}
|
|
@@ -419,7 +423,7 @@ module.exports = function (appConfig, onTerminate) {
|
|
|
419
423
|
})
|
|
420
424
|
.pipe(
|
|
421
425
|
rx.catchError((err) => rxjs.of({ warnings: [err.message] })),
|
|
422
|
-
rx.repeatWhen(() => rxjs.timer(10e3))
|
|
426
|
+
rx.repeatWhen(() => rxjs.timer(10e3)),
|
|
423
427
|
)
|
|
424
428
|
} else if (appConfig.status && typeof appConfig.status === 'object') {
|
|
425
429
|
status$ = rxjs.timer(0, 10e3).pipe(rx.exhaustMap(() => appConfig.status))
|
|
@@ -445,7 +449,7 @@ module.exports = function (appConfig, onTerminate) {
|
|
|
445
449
|
}),
|
|
446
450
|
rx.startWith([]),
|
|
447
451
|
rx.distinctUntilChanged(fp.isEqual),
|
|
448
|
-
rx.repeatWhen((complete$) => complete$.pipe(rx.delay(10e3)))
|
|
452
|
+
rx.repeatWhen((complete$) => complete$.pipe(rx.delay(10e3))),
|
|
449
453
|
),
|
|
450
454
|
toobusy
|
|
451
455
|
? rxjs.timer(0, 1e3).pipe(
|
|
@@ -459,10 +463,10 @@ module.exports = function (appConfig, onTerminate) {
|
|
|
459
463
|
msg: `lag: ${toobusy.lag()}`,
|
|
460
464
|
},
|
|
461
465
|
]
|
|
462
|
-
: []
|
|
466
|
+
: [],
|
|
463
467
|
),
|
|
464
468
|
rx.startWith([]),
|
|
465
|
-
rx.distinctUntilChanged(fp.isEqual)
|
|
469
|
+
rx.distinctUntilChanged(fp.isEqual),
|
|
466
470
|
)
|
|
467
471
|
: rxjs.of({}),
|
|
468
472
|
couch
|
|
@@ -482,7 +486,7 @@ module.exports = function (appConfig, onTerminate) {
|
|
|
482
486
|
}
|
|
483
487
|
}),
|
|
484
488
|
rx.startWith([]),
|
|
485
|
-
rx.distinctUntilChanged(fp.isEqual)
|
|
489
|
+
rx.distinctUntilChanged(fp.isEqual),
|
|
486
490
|
)
|
|
487
491
|
: rxjs.of({}),
|
|
488
492
|
ds
|
|
@@ -559,7 +563,7 @@ module.exports = function (appConfig, onTerminate) {
|
|
|
559
563
|
}
|
|
560
564
|
|
|
561
565
|
return messages
|
|
562
|
-
})
|
|
566
|
+
}),
|
|
563
567
|
)
|
|
564
568
|
.subscribe(o)
|
|
565
569
|
|
|
@@ -570,7 +574,7 @@ module.exports = function (appConfig, onTerminate) {
|
|
|
570
574
|
}).pipe(rx.startWith([]), rx.distinctUntilChanged(fp.isEqual))
|
|
571
575
|
: rxjs.of({}),
|
|
572
576
|
rxjs.timer(0, 10e3),
|
|
573
|
-
].filter(Boolean)
|
|
577
|
+
].filter(Boolean),
|
|
574
578
|
)
|
|
575
579
|
.pipe(
|
|
576
580
|
rx.auditTime(1e3),
|
|
@@ -594,7 +598,7 @@ module.exports = function (appConfig, onTerminate) {
|
|
|
594
598
|
...message,
|
|
595
599
|
message: undefined,
|
|
596
600
|
msg: message.message,
|
|
597
|
-
}
|
|
601
|
+
},
|
|
598
602
|
)
|
|
599
603
|
.map((message) =>
|
|
600
604
|
message.id
|
|
@@ -602,9 +606,9 @@ module.exports = function (appConfig, onTerminate) {
|
|
|
602
606
|
: {
|
|
603
607
|
...message,
|
|
604
608
|
id: hashString(
|
|
605
|
-
[message.msg, message].find(fp.isString) ?? JSON.stringify(message)
|
|
609
|
+
[message.msg, message].find(fp.isString) ?? JSON.stringify(message),
|
|
606
610
|
),
|
|
607
|
-
}
|
|
611
|
+
},
|
|
608
612
|
)
|
|
609
613
|
|
|
610
614
|
return { ...status, messages, timestamp: Date.now() }
|
|
@@ -619,7 +623,7 @@ module.exports = function (appConfig, onTerminate) {
|
|
|
619
623
|
rx.startWith({}),
|
|
620
624
|
rx.distinctUntilChanged(fp.isEqual),
|
|
621
625
|
rx.publishReplay(1),
|
|
622
|
-
rx.refCount()
|
|
626
|
+
rx.refCount(),
|
|
623
627
|
)
|
|
624
628
|
|
|
625
629
|
const loggerSubscription = status$
|
|
@@ -648,21 +652,15 @@ module.exports = function (appConfig, onTerminate) {
|
|
|
648
652
|
const { isMainThread } = require('node:worker_threads')
|
|
649
653
|
|
|
650
654
|
if (isMainThread) {
|
|
651
|
-
const os = require('os')
|
|
652
|
-
const rx = require('rxjs/operators')
|
|
653
|
-
|
|
654
|
-
const containerId = appConfig.containerId ?? os.hostname()
|
|
655
|
-
const hostname = process.env.NODE_ENV === 'production' ? containerId : serviceName
|
|
656
|
-
|
|
657
655
|
const unprovide = ds.record.provide(`^([^:]+):monitor\\.([^?]+)[?]?`, (key) => {
|
|
658
656
|
const [, id, prop] = key.match(/^([^:]+):monitor\.([^?]+)[?]?/)
|
|
659
657
|
|
|
660
658
|
if (id === serviceName) {
|
|
661
659
|
// TODO (fix): If id === serviceName check if there are multiple instances.
|
|
662
|
-
return monitorProviders[prop + '$']
|
|
660
|
+
return monitorProviders[prop + '$']
|
|
663
661
|
}
|
|
664
662
|
|
|
665
|
-
if (id ===
|
|
663
|
+
if (id === serviceInstanceId) {
|
|
666
664
|
return monitorProviders[prop + '$']
|
|
667
665
|
}
|
|
668
666
|
})
|
|
@@ -750,13 +748,13 @@ module.exports = function (appConfig, onTerminate) {
|
|
|
750
748
|
},
|
|
751
749
|
]
|
|
752
750
|
.flat()
|
|
753
|
-
.filter(Boolean)
|
|
751
|
+
.filter(Boolean),
|
|
754
752
|
)
|
|
755
753
|
|
|
756
754
|
server = createServer(
|
|
757
755
|
typeof appConfig.http === 'object' ? appConfig.http : {},
|
|
758
756
|
{ ds, couch, config: httpConfig, logger },
|
|
759
|
-
middleware
|
|
757
|
+
middleware,
|
|
760
758
|
)
|
|
761
759
|
|
|
762
760
|
if (httpConfig.keepAlive != null) {
|
|
@@ -794,7 +792,9 @@ module.exports = function (appConfig, onTerminate) {
|
|
|
794
792
|
tracer: trace,
|
|
795
793
|
userAgent,
|
|
796
794
|
serviceName,
|
|
795
|
+
serviceModule,
|
|
797
796
|
serviceVersion,
|
|
797
|
+
serviceInstanceId,
|
|
798
798
|
signal: ac.signal,
|
|
799
799
|
}
|
|
800
800
|
}
|
package/errors.js
CHANGED
|
@@ -3,8 +3,8 @@ const fp = require('lodash/fp.js')
|
|
|
3
3
|
const { toString } = Object.prototype
|
|
4
4
|
|
|
5
5
|
module.exports.AbortError = class AbortError extends Error {
|
|
6
|
-
constructor() {
|
|
7
|
-
super('The operation was aborted')
|
|
6
|
+
constructor(message) {
|
|
7
|
+
super(message ?? 'The operation was aborted')
|
|
8
8
|
this.code = 'ABORT_ERR'
|
|
9
9
|
this.name = 'AbortError'
|
|
10
10
|
}
|
|
@@ -34,7 +34,7 @@ module.exports.parseError = function parseError(error) {
|
|
|
34
34
|
{
|
|
35
35
|
...properties,
|
|
36
36
|
cause: cause ? parseError(error.cause) : undefined,
|
|
37
|
-
}
|
|
37
|
+
},
|
|
38
38
|
)
|
|
39
39
|
}
|
|
40
40
|
|
|
@@ -101,7 +101,7 @@ module.exports.serializeError = function serializeError(error) {
|
|
|
101
101
|
data,
|
|
102
102
|
cause,
|
|
103
103
|
errors,
|
|
104
|
-
})
|
|
104
|
+
}),
|
|
105
105
|
)
|
|
106
106
|
}
|
|
107
107
|
|
|
@@ -115,7 +115,7 @@ module.exports.makeMessages = function makeMessages(error, options) {
|
|
|
115
115
|
return fp.pipe(
|
|
116
116
|
fp.flattenDeep,
|
|
117
117
|
fp.flatMap((x) => makeMessages(x, null)),
|
|
118
|
-
fp.uniqBy('id')
|
|
118
|
+
fp.uniqBy('id'),
|
|
119
119
|
)(error)
|
|
120
120
|
} else if (Array.isArray(error.messages)) {
|
|
121
121
|
return makeMessages(error.messages, null)
|
|
@@ -127,11 +127,11 @@ module.exports.makeMessages = function makeMessages(error, options) {
|
|
|
127
127
|
const level = parseInt(error.level) || options?.level || 50
|
|
128
128
|
const code =
|
|
129
129
|
[error?.code, options?.codes?.[error?.code]].find(
|
|
130
|
-
(x) => typeof x === 'string' && x.length > 0
|
|
130
|
+
(x) => typeof x === 'string' && x.length > 0,
|
|
131
131
|
) ?? undefined
|
|
132
132
|
const msg =
|
|
133
133
|
[error.msg, error.message, code?.toLowerCase().replace('_', ' ')].find(
|
|
134
|
-
(x) => typeof x === 'string' && x.length > 0
|
|
134
|
+
(x) => typeof x === 'string' && x.length > 0,
|
|
135
135
|
) || 'unknown error'
|
|
136
136
|
|
|
137
137
|
let data = error.data
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nxtedition/lib",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "15.0.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Robert Nagy <robert.nagy@boffins.se>",
|
|
6
6
|
"files": [
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
"ass.js",
|
|
9
9
|
"rxjs/*",
|
|
10
10
|
"util/*",
|
|
11
|
+
"undici/*",
|
|
11
12
|
"subtract-ranges.js",
|
|
12
13
|
"serializers.js",
|
|
13
14
|
"elasticsearch.js",
|
|
@@ -22,9 +23,7 @@
|
|
|
22
23
|
"app.js",
|
|
23
24
|
"errors.js",
|
|
24
25
|
"worker.js",
|
|
25
|
-
"proxy.js",
|
|
26
26
|
"stream.js",
|
|
27
|
-
"undici.js",
|
|
28
27
|
"timeline.js",
|
|
29
28
|
"docker-secrets.js"
|
|
30
29
|
],
|
|
@@ -71,6 +70,7 @@
|
|
|
71
70
|
"/__tests__"
|
|
72
71
|
],
|
|
73
72
|
"dependencies": {
|
|
73
|
+
"cache-control-parser": "^2.0.4",
|
|
74
74
|
"date-fns": "^2.29.3",
|
|
75
75
|
"fast-querystring": "^1.1.1",
|
|
76
76
|
"hasha": "^5.2.2",
|
|
@@ -78,6 +78,7 @@
|
|
|
78
78
|
"json5": "^2.2.3",
|
|
79
79
|
"koa-compose": "^4.1.0",
|
|
80
80
|
"lodash": "^4.17.21",
|
|
81
|
+
"lru-cache": "^10.0.1",
|
|
81
82
|
"mime": "^3.0.0",
|
|
82
83
|
"moment-timezone": "^0.5.43",
|
|
83
84
|
"nconf": "^0.12.0",
|
|
@@ -90,23 +91,25 @@
|
|
|
90
91
|
"smpte-timecode": "^1.3.3",
|
|
91
92
|
"split-string": "^6.0.0",
|
|
92
93
|
"toobusy-js": "^0.5.1",
|
|
93
|
-
"undici": "^5.
|
|
94
|
+
"undici": "^5.25.2",
|
|
94
95
|
"url-join": "^4.0.0",
|
|
95
96
|
"xuid": "^4.1.2"
|
|
96
97
|
},
|
|
97
98
|
"devDependencies": {
|
|
98
|
-
"
|
|
99
|
-
"eslint
|
|
99
|
+
"@types/node": "^20.6.3",
|
|
100
|
+
"eslint": "^8.50.0",
|
|
101
|
+
"eslint-config-prettier": "^9.0.0",
|
|
100
102
|
"eslint-config-standard": "^17.0.0",
|
|
101
|
-
"eslint-plugin-import": "^2.
|
|
102
|
-
"eslint-plugin-n": "^
|
|
103
|
+
"eslint-plugin-import": "^2.28.1",
|
|
104
|
+
"eslint-plugin-n": "^16.1.0",
|
|
103
105
|
"eslint-plugin-node": "^11.1.0",
|
|
104
106
|
"eslint-plugin-promise": "^6.0.0",
|
|
105
107
|
"husky": "^8.0.3",
|
|
106
|
-
"lint-staged": "^
|
|
108
|
+
"lint-staged": "^14.0.1",
|
|
107
109
|
"pinst": "^3.0.0",
|
|
108
|
-
"prettier": "^
|
|
110
|
+
"prettier": "^3.0.3",
|
|
109
111
|
"rxjs": "^7.5.6",
|
|
112
|
+
"send": "^0.18.0",
|
|
110
113
|
"tap": "^16.3.4"
|
|
111
114
|
},
|
|
112
115
|
"peerDependencies": {
|
package/undici/index.js
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
const assert = require('assert')
|
|
2
|
+
const createError = require('http-errors')
|
|
3
|
+
const xuid = require('xuid')
|
|
4
|
+
const undici = require('undici')
|
|
5
|
+
const stream = require('stream')
|
|
6
|
+
const { parseHeaders } = require('../http')
|
|
7
|
+
|
|
8
|
+
class Readable extends stream.Readable {
|
|
9
|
+
constructor({ statusCode, statusMessage, headers, ...opts }) {
|
|
10
|
+
super(opts)
|
|
11
|
+
this.statusCode = statusCode
|
|
12
|
+
this.statusMessage = statusMessage
|
|
13
|
+
this.headers = headers
|
|
14
|
+
this.body = this
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async text() {
|
|
18
|
+
const dec = new TextDecoder()
|
|
19
|
+
let str = ''
|
|
20
|
+
for await (const chunk of this) {
|
|
21
|
+
if (typeof chunk === 'string') {
|
|
22
|
+
str += chunk
|
|
23
|
+
} else {
|
|
24
|
+
str += dec.decode(chunk, { stream: true })
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
// Flush the streaming TextDecoder so that any pending
|
|
28
|
+
// incomplete multibyte characters are handled.
|
|
29
|
+
str += dec.decode(undefined, { stream: false })
|
|
30
|
+
return str
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async json() {
|
|
34
|
+
return JSON.parse(await this.text())
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async arrayBuffer() {
|
|
38
|
+
const buffers = []
|
|
39
|
+
for await (const chunk of this) {
|
|
40
|
+
buffers.push(chunk)
|
|
41
|
+
}
|
|
42
|
+
return Buffer.concat(buffers)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async buffer() {
|
|
46
|
+
return Buffer.from(await this.arrayBuffer())
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async dump() {
|
|
50
|
+
let n = 0
|
|
51
|
+
try {
|
|
52
|
+
for await (const chunk of this) {
|
|
53
|
+
// do nothing
|
|
54
|
+
n += chunk.length
|
|
55
|
+
if (n > 128 * 1024) {
|
|
56
|
+
break
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
} catch {
|
|
60
|
+
this.destroy()
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const dispatchers = {
|
|
66
|
+
abort: require('./interceptor/abort.js'),
|
|
67
|
+
catch: require('./interceptor/catch.js'),
|
|
68
|
+
content: require('./interceptor/content.js'),
|
|
69
|
+
responseBodyDump: require('./interceptor/response-body-dump.js'),
|
|
70
|
+
log: require('./interceptor/log.js'),
|
|
71
|
+
redirect: require('./interceptor/redirect.js'),
|
|
72
|
+
responseBodyRetry: require('./interceptor/response-body-retry.js'),
|
|
73
|
+
responseStatusRetry: require('./interceptor/response-status-retry.js'),
|
|
74
|
+
responseRetry: require('./interceptor/response-retry.js'),
|
|
75
|
+
signal: require('./interceptor/signal.js'),
|
|
76
|
+
proxy: require('./interceptor/proxy.js'),
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function request(urlOrOpts, opts = {}) {
|
|
80
|
+
let url
|
|
81
|
+
if (typeof urlOrOpts === 'string') {
|
|
82
|
+
url = new URL(urlOrOpts)
|
|
83
|
+
} else if (urlOrOpts instanceof URL) {
|
|
84
|
+
url = urlOrOpts
|
|
85
|
+
} else if (typeof urlOrOpts?.origin === 'string' && typeof urlOrOpts?.path === 'string') {
|
|
86
|
+
url = urlOrOpts
|
|
87
|
+
} else if (typeof urlOrOpts === 'object' && urlOrOpts != null) {
|
|
88
|
+
opts = urlOrOpts
|
|
89
|
+
url = opts.url
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const method = opts.method ?? (opts.body ? 'POST' : 'GET')
|
|
93
|
+
const idempotent = opts.idempotent ?? (method === 'GET' || method === 'HEAD')
|
|
94
|
+
const dump = opts.dump ?? method === 'HEAD'
|
|
95
|
+
|
|
96
|
+
let headers
|
|
97
|
+
if (Array.isArray(opts.headers)) {
|
|
98
|
+
headers = parseHeaders(opts.headers)
|
|
99
|
+
} else {
|
|
100
|
+
headers = opts.headers
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
opts = {
|
|
104
|
+
url,
|
|
105
|
+
method,
|
|
106
|
+
body: opts.body,
|
|
107
|
+
headers: {
|
|
108
|
+
'request-id': xuid(),
|
|
109
|
+
'user-agent': opts.userAgent ?? globalThis.userAgent,
|
|
110
|
+
...headers,
|
|
111
|
+
},
|
|
112
|
+
origin: opts.origin ?? url.origin,
|
|
113
|
+
path: opts.path ?? url.search ? `${url.pathname}${url.search ?? ''}` : url.pathname,
|
|
114
|
+
reset: opts.reset ?? false,
|
|
115
|
+
headersTimeout: opts.headersTimeout,
|
|
116
|
+
bodyTimeout: opts.bodyTimeout,
|
|
117
|
+
idempotent,
|
|
118
|
+
signal: opts.signal,
|
|
119
|
+
retry: opts.retry ?? 8,
|
|
120
|
+
follow: { count: opts.maxRedirections ?? 8, ...opts.redirect, ...opts.follow },
|
|
121
|
+
dump,
|
|
122
|
+
logger: opts.logger,
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const dispatcher = opts.dispatcher ?? undici.getGlobalDispatcher()
|
|
126
|
+
|
|
127
|
+
return new Promise((resolve) => {
|
|
128
|
+
let dispatch = (opts, handler) => dispatcher.dispatch(opts, handler)
|
|
129
|
+
|
|
130
|
+
dispatch = dispatchers.catch(dispatch)
|
|
131
|
+
dispatch = dispatchers.abort(dispatch)
|
|
132
|
+
dispatch = dispatchers.log(dispatch)
|
|
133
|
+
dispatch = dispatchers.responseRetry(dispatch)
|
|
134
|
+
dispatch = dispatchers.responseStatusRetry(dispatch)
|
|
135
|
+
dispatch = dispatchers.responseBodyRetry(dispatch)
|
|
136
|
+
dispatch = dispatchers.content(dispatch)
|
|
137
|
+
dispatch = dispatchers.responseBodyDump(dispatch)
|
|
138
|
+
dispatch = dispatchers.redirect(dispatch)
|
|
139
|
+
dispatch = dispatchers.signal(dispatch)
|
|
140
|
+
dispatch = dispatchers.proxy(dispatch)
|
|
141
|
+
|
|
142
|
+
dispatch(opts, {
|
|
143
|
+
resolve,
|
|
144
|
+
/** @type {Function | null} */ abort: null,
|
|
145
|
+
/** @type {stream.Readable | null} */ body: null,
|
|
146
|
+
onConnect(abort) {
|
|
147
|
+
this.abort = abort
|
|
148
|
+
},
|
|
149
|
+
onHeaders(statusCode, rawHeaders, resume, statusMessage) {
|
|
150
|
+
assert(this.abort)
|
|
151
|
+
|
|
152
|
+
const headers = parseHeaders(rawHeaders)
|
|
153
|
+
|
|
154
|
+
if (statusCode >= 400) {
|
|
155
|
+
this.abort(createError(statusCode, { headers }))
|
|
156
|
+
} else {
|
|
157
|
+
assert(statusCode >= 200)
|
|
158
|
+
|
|
159
|
+
this.body = new Readable({
|
|
160
|
+
read: resume,
|
|
161
|
+
highWaterMark: 128 * 1024,
|
|
162
|
+
statusCode,
|
|
163
|
+
statusMessage,
|
|
164
|
+
headers,
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
this.resolve(this.body)
|
|
168
|
+
this.resolve = null
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return false
|
|
172
|
+
},
|
|
173
|
+
onData(chunk) {
|
|
174
|
+
assert(this.body)
|
|
175
|
+
return this.body.push(chunk)
|
|
176
|
+
},
|
|
177
|
+
onComplete() {
|
|
178
|
+
assert(this.body)
|
|
179
|
+
this.body.push(null)
|
|
180
|
+
},
|
|
181
|
+
onError(err) {
|
|
182
|
+
if (this.body) {
|
|
183
|
+
this.body.destroy(err)
|
|
184
|
+
} else {
|
|
185
|
+
this.resolve(Promise.reject(err))
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
})
|
|
189
|
+
})
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
module.exports = { request }
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
const { AbortError } = require('../../errors')
|
|
2
|
+
|
|
3
|
+
class Handler {
|
|
4
|
+
constructor(opts, { handler }) {
|
|
5
|
+
this.handler = handler
|
|
6
|
+
this.pos = 0
|
|
7
|
+
this.reason = null
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
onConnect(abort) {
|
|
11
|
+
this.abort = abort
|
|
12
|
+
this.handler.onConnect((reason) => {
|
|
13
|
+
this.reason = reason ?? new AbortError()
|
|
14
|
+
})
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
onBodySent(chunk) {
|
|
18
|
+
return this.handler.onBodySent(chunk)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
onHeaders(statusCode, rawHeaders, resume, statusMessage) {
|
|
22
|
+
if (this.reason == null) {
|
|
23
|
+
const ret = this.handler.onHeaders(statusCode, rawHeaders, resume, statusMessage)
|
|
24
|
+
if (this.reason == null) {
|
|
25
|
+
return ret
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return true
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
onData(chunk) {
|
|
33
|
+
if (this.reason == null) {
|
|
34
|
+
const ret = this.handler.onData(chunk)
|
|
35
|
+
if (this.reason == null) {
|
|
36
|
+
return ret
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
this.pos += chunk.length
|
|
41
|
+
if (this.pos < 128 * 1024) {
|
|
42
|
+
return true
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
this.abort(this.reason)
|
|
46
|
+
|
|
47
|
+
return false
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
onComplete(rawTrailers) {
|
|
51
|
+
return this.reason == null
|
|
52
|
+
? this.handler.onComplete(rawTrailers)
|
|
53
|
+
: this.handler.onError(this.reason)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
onError(err) {
|
|
57
|
+
return this.handler.onError(err)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
module.exports = (dispatch) => (opts, handler) => dispatch(opts, new Handler(opts, { handler }))
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
class Handler {
|
|
2
|
+
constructor(opts, { handler }) {
|
|
3
|
+
this.handler = handler
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
onConnect(abort) {
|
|
7
|
+
this.abort = abort
|
|
8
|
+
try {
|
|
9
|
+
return this.handler.onConnect(abort)
|
|
10
|
+
} catch (err) {
|
|
11
|
+
this.abort(err)
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
onBodySent(chunk) {
|
|
16
|
+
try {
|
|
17
|
+
return this.handler.onBodySent(chunk)
|
|
18
|
+
} catch (err) {
|
|
19
|
+
this.abort(err)
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
onHeaders(statusCode, rawHeaders, resume, statusMessage) {
|
|
24
|
+
try {
|
|
25
|
+
return this.handler.onHeaders(statusCode, rawHeaders, resume, statusMessage)
|
|
26
|
+
} catch (err) {
|
|
27
|
+
this.abort(err)
|
|
28
|
+
return false
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
onData(chunk) {
|
|
33
|
+
try {
|
|
34
|
+
return this.handler.onData(chunk)
|
|
35
|
+
} catch (err) {
|
|
36
|
+
this.abort(err)
|
|
37
|
+
return false
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
onComplete(rawTrailers) {
|
|
42
|
+
try {
|
|
43
|
+
return this.handler.onComplete(rawTrailers)
|
|
44
|
+
} catch (err) {
|
|
45
|
+
this.abort(err)
|
|
46
|
+
return false
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
onError(err) {
|
|
51
|
+
return this.handler.onError(err)
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
module.exports = (dispatch) => (opts, handler) => dispatch(opts, new Handler(opts, { handler }))
|