@nxtedition/nxt-undici 1.0.8 → 1.0.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.
- package/lib/index.js +54 -17
- package/lib/interceptor/abort.js +4 -0
- package/lib/interceptor/cache.js +8 -0
- package/lib/interceptor/catch.js +8 -0
- package/lib/interceptor/log.js +4 -0
- package/lib/interceptor/proxy.js +4 -0
- package/lib/interceptor/redirect.js +4 -0
- package/lib/interceptor/request-content.js +76 -0
- package/lib/interceptor/response-body-retry.js +4 -0
- package/lib/interceptor/response-content.js +69 -0
- package/lib/interceptor/response-retry.js +4 -0
- package/lib/interceptor/response-status-retry.js +4 -0
- package/package.json +1 -1
- package/lib/interceptor/content.js +0 -141
package/lib/index.js
CHANGED
|
@@ -2,7 +2,7 @@ const assert = require('assert')
|
|
|
2
2
|
const createError = require('http-errors')
|
|
3
3
|
const undici = require('undici')
|
|
4
4
|
const stream = require('stream')
|
|
5
|
-
const { parseHeaders } = require('./utils')
|
|
5
|
+
const { parseHeaders, AbortError } = require('./utils')
|
|
6
6
|
|
|
7
7
|
const dispatcherCache = new WeakMap()
|
|
8
8
|
|
|
@@ -19,14 +19,53 @@ function genReqId() {
|
|
|
19
19
|
return `req-${nextReqId.toString(36)}`
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
const kAbort = Symbol('abort')
|
|
23
|
+
const kStatusCode = Symbol('statusCode')
|
|
24
|
+
const kStatusMessage = Symbol('statusMessage')
|
|
25
|
+
const kHeaders = Symbol('headers')
|
|
26
|
+
const kSize = Symbol('size')
|
|
27
|
+
|
|
22
28
|
class Readable extends stream.Readable {
|
|
23
|
-
constructor({ statusCode, statusMessage, headers, size,
|
|
24
|
-
super(
|
|
25
|
-
|
|
26
|
-
this
|
|
27
|
-
this
|
|
28
|
-
this
|
|
29
|
-
this
|
|
29
|
+
constructor({ statusCode, statusMessage, headers, size, abort, highWaterMark, resume }) {
|
|
30
|
+
super(highWaterMark ? { highWaterMark } : undefined)
|
|
31
|
+
|
|
32
|
+
this[kStatusCode] = statusCode
|
|
33
|
+
this[kStatusMessage] = statusMessage
|
|
34
|
+
this[kHeaders] = headers
|
|
35
|
+
this[kSize] = size
|
|
36
|
+
this[kAbort] = abort
|
|
37
|
+
|
|
38
|
+
this._read = resume
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
get statusCode() {
|
|
42
|
+
return this[kStatusCode]
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
get statusMessage() {
|
|
46
|
+
return this[kStatusMessage]
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
get headers() {
|
|
50
|
+
return this[kHeaders]
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
get size() {
|
|
54
|
+
return this[kSize]
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
get body() {
|
|
58
|
+
return this
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
_destroy(err, callback) {
|
|
62
|
+
if (err == null && !this.readableEnded) {
|
|
63
|
+
err = new AbortError()
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
this[kAbort](err)
|
|
67
|
+
|
|
68
|
+
callback(err)
|
|
30
69
|
}
|
|
31
70
|
|
|
32
71
|
async text() {
|
|
@@ -65,14 +104,13 @@ class Readable extends stream.Readable {
|
|
|
65
104
|
let n = 0
|
|
66
105
|
try {
|
|
67
106
|
for await (const chunk of this) {
|
|
68
|
-
// do nothing
|
|
69
107
|
n += chunk.length
|
|
70
108
|
if (n > 128 * 1024) {
|
|
71
109
|
break
|
|
72
110
|
}
|
|
73
111
|
}
|
|
74
112
|
} catch {
|
|
75
|
-
|
|
113
|
+
// Do nothing...
|
|
76
114
|
}
|
|
77
115
|
}
|
|
78
116
|
}
|
|
@@ -80,7 +118,8 @@ class Readable extends stream.Readable {
|
|
|
80
118
|
const dispatchers = {
|
|
81
119
|
abort: require('./interceptor/abort.js'),
|
|
82
120
|
catch: require('./interceptor/catch.js'),
|
|
83
|
-
|
|
121
|
+
responseContent: require('./interceptor/response-content.js'),
|
|
122
|
+
requestContent: require('./interceptor/request-content.js'),
|
|
84
123
|
log: require('./interceptor/log.js'),
|
|
85
124
|
redirect: require('./interceptor/redirect.js'),
|
|
86
125
|
responseBodyRetry: require('./interceptor/response-body-retry.js'),
|
|
@@ -159,7 +198,8 @@ async function request(url, opts) {
|
|
|
159
198
|
dispatch = dispatchers.responseRetry(dispatch)
|
|
160
199
|
dispatch = dispatchers.responseStatusRetry(dispatch)
|
|
161
200
|
dispatch = dispatchers.responseBodyRetry(dispatch)
|
|
162
|
-
dispatch = dispatchers.
|
|
201
|
+
dispatch = dispatchers.responseContent(dispatch)
|
|
202
|
+
dispatch = dispatchers.requestContent(dispatch)
|
|
163
203
|
dispatch = dispatchers.redirect(dispatch)
|
|
164
204
|
dispatch = dispatchers.signal(dispatch)
|
|
165
205
|
dispatch = dispatchers.cache(dispatch)
|
|
@@ -220,16 +260,13 @@ async function request(url, opts) {
|
|
|
220
260
|
const contentLength = Number(headers['content-length'] ?? headers['Content-Length'])
|
|
221
261
|
|
|
222
262
|
this.body = new Readable({
|
|
223
|
-
|
|
263
|
+
resume,
|
|
264
|
+
abort: this.abort,
|
|
224
265
|
highWaterMark: 128 * 1024,
|
|
225
266
|
statusCode,
|
|
226
267
|
statusMessage,
|
|
227
268
|
headers,
|
|
228
269
|
size: Number.isFinite(contentLength) ? contentLength : null,
|
|
229
|
-
}).on('error', (err) => {
|
|
230
|
-
if (this.logger && this.body?.listenerCount('error') === 1) {
|
|
231
|
-
this.logger.error({ err }, 'unhandled response body error')
|
|
232
|
-
}
|
|
233
270
|
})
|
|
234
271
|
|
|
235
272
|
this.resolve(this.body)
|
package/lib/interceptor/abort.js
CHANGED
package/lib/interceptor/cache.js
CHANGED
|
@@ -18,6 +18,14 @@ class CacheHandler {
|
|
|
18
18
|
return this.handler.onUpgrade(statusCode, rawHeaders, socket)
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
onBodySent(chunk) {
|
|
22
|
+
return this.handler.onBodySent(chunk)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
onRequestSent() {
|
|
26
|
+
return this.handler.onRequestSent()
|
|
27
|
+
}
|
|
28
|
+
|
|
21
29
|
onHeaders(statusCode, rawHeaders, resume, statusMessage) {
|
|
22
30
|
// NOTE: Only cache 307 respones for now...
|
|
23
31
|
if (statusCode !== 307) {
|
package/lib/interceptor/catch.js
CHANGED
|
@@ -28,6 +28,14 @@ class Handler {
|
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
onRequestSent() {
|
|
32
|
+
try {
|
|
33
|
+
return this.handler.onRequestSent()
|
|
34
|
+
} catch (err) {
|
|
35
|
+
this.abort(err)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
31
39
|
onHeaders(statusCode, rawHeaders, resume, statusMessage) {
|
|
32
40
|
try {
|
|
33
41
|
return this.handler.onHeaders(statusCode, rawHeaders, resume, statusMessage)
|
package/lib/interceptor/log.js
CHANGED
package/lib/interceptor/proxy.js
CHANGED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
const crypto = require('node:crypto')
|
|
2
|
+
|
|
3
|
+
class Handler {
|
|
4
|
+
constructor(opts, { handler, md5, length }) {
|
|
5
|
+
this.handler = handler
|
|
6
|
+
this.md5 = md5
|
|
7
|
+
this.length = length
|
|
8
|
+
this.hasher = this.md5 ? crypto.createHash('md5') : null
|
|
9
|
+
this.pos = 0
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
onConnect(abort) {
|
|
13
|
+
return this.handler.onConnect(abort)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
onUpgrade(statusCode, rawHeaders, socket) {
|
|
17
|
+
return this.handler.onUpgrade(statusCode, rawHeaders, socket)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
onBodySent(chunk) {
|
|
21
|
+
this.pos += chunk.length
|
|
22
|
+
this.hasher?.update(chunk)
|
|
23
|
+
return this.handler.onBodySent(chunk)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
onRequestSent() {
|
|
27
|
+
const hash = this.hasher?.digest('base64')
|
|
28
|
+
if (this.md5 != null && hash !== this.md5) {
|
|
29
|
+
this.handler.onError(
|
|
30
|
+
Object.assign(new Error('Request Content-Length mismatch'), {
|
|
31
|
+
expected: this.md5,
|
|
32
|
+
actual: hash,
|
|
33
|
+
}),
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
if (this.length != null && this.pos !== Number(this.length)) {
|
|
37
|
+
return this.handler.onError(
|
|
38
|
+
Object.assign(new Error('Request Content-Length mismatch'), {
|
|
39
|
+
expected: Number(this.length),
|
|
40
|
+
actual: this.pos,
|
|
41
|
+
}),
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
return this.handler.onRequestSent()
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
onHeaders(statusCode, rawHeaders, resume, statusMessage) {
|
|
48
|
+
return this.handler.onHeaders(statusCode, rawHeaders, resume, statusMessage)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
onData(chunk) {
|
|
52
|
+
return this.handler.onData(chunk)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
onComplete(rawTrailers) {
|
|
56
|
+
return this.handler.onComplete(rawTrailers)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
onError(err) {
|
|
60
|
+
this.handler.onError(err)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
module.exports = (dispatch) => (opts, handler) => {
|
|
65
|
+
if (opts.upgrade) {
|
|
66
|
+
return dispatch(opts, handler)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// TODO (fix): case-insensitive check?
|
|
70
|
+
const md5 = opts.headers?.['content-md5'] ?? opts.headers?.['Content-MD5']
|
|
71
|
+
const length = opts.headers?.['content-lenght'] ?? opts.headers?.['Content-Length']
|
|
72
|
+
|
|
73
|
+
return md5 != null || length != null
|
|
74
|
+
? dispatch(opts, new Handler(opts, { handler, md5, length }))
|
|
75
|
+
: dispatch(opts, handler)
|
|
76
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
const crypto = require('node:crypto')
|
|
2
|
+
const { findHeader } = require('../utils')
|
|
3
|
+
|
|
4
|
+
class Handler {
|
|
5
|
+
constructor(opts, { handler }) {
|
|
6
|
+
this.handler = handler
|
|
7
|
+
this.md5 = null
|
|
8
|
+
this.length = null
|
|
9
|
+
this.hasher = null
|
|
10
|
+
this.pos = 0
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
onConnect(abort) {
|
|
14
|
+
return this.handler.onConnect(abort)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
onUpgrade(statusCode, rawHeaders, socket) {
|
|
18
|
+
return this.handler.onUpgrade(statusCode, rawHeaders, socket)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
onBodySent(chunk) {
|
|
22
|
+
return this.handler.onBodySent(chunk)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
onRequestSent() {
|
|
26
|
+
return this.handler.onRequestSent()
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
onHeaders(statusCode, rawHeaders, resume, statusMessage) {
|
|
30
|
+
this.md5 = findHeader(rawHeaders, 'content-md5')
|
|
31
|
+
this.length = findHeader(rawHeaders, 'content-length')
|
|
32
|
+
this.hasher = this.md5 != null ? crypto.createHash('md5') : null
|
|
33
|
+
return this.handler.onHeaders(statusCode, rawHeaders, resume, statusMessage)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
onData(chunk) {
|
|
37
|
+
this.pos += chunk.length
|
|
38
|
+
this.hasher?.update(chunk)
|
|
39
|
+
return this.handler.onData(chunk)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
onComplete(rawTrailers) {
|
|
43
|
+
const hash = this.hasher?.digest('base64')
|
|
44
|
+
if (this.md5 != null && hash !== this.md5) {
|
|
45
|
+
this.handler.onError(
|
|
46
|
+
Object.assign(new Error('Request Content-Length mismatch'), {
|
|
47
|
+
expected: this.md5,
|
|
48
|
+
actual: hash,
|
|
49
|
+
}),
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
if (this.length != null && this.pos !== Number(this.length)) {
|
|
53
|
+
return this.handler.onError(
|
|
54
|
+
Object.assign(new Error('Request Content-Length mismatch'), {
|
|
55
|
+
expected: Number(this.length),
|
|
56
|
+
actual: this.pos,
|
|
57
|
+
}),
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
return this.handler.onComplete(rawTrailers)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
onError(err) {
|
|
64
|
+
this.handler.onError(err)
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
module.exports = (dispatch) => (opts, handler) =>
|
|
69
|
+
!opts.upgrade ? dispatch(opts, new Handler(opts, { handler })) : dispatch(opts, handler)
|
|
@@ -39,6 +39,10 @@ class Handler {
|
|
|
39
39
|
return this.handler.onBodySent(chunk)
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
onRequestSent() {
|
|
43
|
+
return this.handler.onRequestSent()
|
|
44
|
+
}
|
|
45
|
+
|
|
42
46
|
onHeaders(statusCode, rawHeaders, resume, statusMessage) {
|
|
43
47
|
this.statusCode = statusCode
|
|
44
48
|
return this.handler.onHeaders(statusCode, rawHeaders, resume, statusMessage)
|
|
@@ -39,6 +39,10 @@ class Handler {
|
|
|
39
39
|
return this.handler.onBodySent(chunk)
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
onRequestSent() {
|
|
43
|
+
return this.handler.onRequestSent()
|
|
44
|
+
}
|
|
45
|
+
|
|
42
46
|
onHeaders(statusCode, rawHeaders, resume, statusMessage) {
|
|
43
47
|
if (statusCode < 400) {
|
|
44
48
|
return this.handler.onHeaders(statusCode, rawHeaders, resume, statusMessage)
|
package/package.json
CHANGED
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
const crypto = require('node:crypto')
|
|
2
|
-
const stream = require('node:stream')
|
|
3
|
-
const { findHeader, isStream } = require('../utils')
|
|
4
|
-
|
|
5
|
-
class Handler {
|
|
6
|
-
constructor(opts, { handler }) {
|
|
7
|
-
this.handler = handler
|
|
8
|
-
this.md5 = null
|
|
9
|
-
this.length = null
|
|
10
|
-
this.hasher = null
|
|
11
|
-
this.pos = 0
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
onConnect(abort) {
|
|
15
|
-
return this.handler.onConnect(abort)
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
onUpgrade(statusCode, rawHeaders, socket) {
|
|
19
|
-
return this.handler.onUpgrade(statusCode, rawHeaders, socket)
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
onBodySent(chunk) {
|
|
23
|
-
return this.handler.onBodySent(chunk)
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
onHeaders(statusCode, rawHeaders, resume, statusMessage) {
|
|
27
|
-
this.md5 = findHeader(rawHeaders, 'content-md5')
|
|
28
|
-
this.length = findHeader(rawHeaders, 'content-length')
|
|
29
|
-
this.hasher = this.md5 != null ? crypto.createHash('md5') : null
|
|
30
|
-
return this.handler.onHeaders(statusCode, rawHeaders, resume, statusMessage)
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
onData(chunk) {
|
|
34
|
-
this.pos += chunk.length
|
|
35
|
-
this.hasher?.update(chunk)
|
|
36
|
-
return this.handler.onData(chunk)
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
onComplete(rawTrailers) {
|
|
40
|
-
const hash = this.hasher?.digest('base64')
|
|
41
|
-
if (this.md5 != null && hash !== this.md5) {
|
|
42
|
-
this.handler.onError(
|
|
43
|
-
Object.assign(new Error('Request Content-Length mismatch'), {
|
|
44
|
-
expected: this.md5,
|
|
45
|
-
actual: hash,
|
|
46
|
-
}),
|
|
47
|
-
)
|
|
48
|
-
}
|
|
49
|
-
if (this.length != null && this.pos !== Number(this.length)) {
|
|
50
|
-
return this.handler.onError(
|
|
51
|
-
Object.assign(new Error('Request Content-Length mismatch'), {
|
|
52
|
-
expected: Number(this.length),
|
|
53
|
-
actual: this.pos,
|
|
54
|
-
}),
|
|
55
|
-
)
|
|
56
|
-
}
|
|
57
|
-
return this.handler.onComplete(rawTrailers)
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
onError(err) {
|
|
61
|
-
this.handler.onError(err)
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
module.exports = (dispatch) => (opts, handler) => {
|
|
66
|
-
if (opts.upgrade) {
|
|
67
|
-
return dispatch(opts, handler)
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// TODO (fix): case-insensitive check?
|
|
71
|
-
const md5 = opts.headers?.['content-md5'] ?? opts.headers?.['Content-MD5']
|
|
72
|
-
const length = opts.headers?.['content-lenght'] ?? opts.headers?.['Content-Length']
|
|
73
|
-
|
|
74
|
-
if (md5 == null && length == null) {
|
|
75
|
-
return dispatch(opts, new Handler(opts, { handler }))
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
if (isStream(opts.body)) {
|
|
79
|
-
const hasher = md5 ? crypto.createHash('md5') : null
|
|
80
|
-
let pos = 0
|
|
81
|
-
|
|
82
|
-
opts = {
|
|
83
|
-
...opts,
|
|
84
|
-
body: stream.pipeline(
|
|
85
|
-
opts.body,
|
|
86
|
-
new stream.Transform({
|
|
87
|
-
transform(chunk, encoding, callback) {
|
|
88
|
-
pos += chunk.length
|
|
89
|
-
hasher?.update(chunk)
|
|
90
|
-
callback(null, chunk)
|
|
91
|
-
},
|
|
92
|
-
final(callback) {
|
|
93
|
-
const hash = hasher?.digest('base64')
|
|
94
|
-
if (md5 != null && hash !== md5) {
|
|
95
|
-
callback(
|
|
96
|
-
Object.assign(new Error('Request Content-MD5 mismatch'), {
|
|
97
|
-
expected: md5,
|
|
98
|
-
actual: hash,
|
|
99
|
-
}),
|
|
100
|
-
)
|
|
101
|
-
} else if (length != null && pos !== Number(length)) {
|
|
102
|
-
callback(
|
|
103
|
-
Object.assign(new Error('Request Content-Length mismatch'), {
|
|
104
|
-
expected: Number(length),
|
|
105
|
-
actual: pos,
|
|
106
|
-
}),
|
|
107
|
-
)
|
|
108
|
-
} else {
|
|
109
|
-
callback(null)
|
|
110
|
-
}
|
|
111
|
-
},
|
|
112
|
-
}),
|
|
113
|
-
() => {},
|
|
114
|
-
),
|
|
115
|
-
}
|
|
116
|
-
} else if (opts.body instanceof Buffer || typeof opts.body === 'string') {
|
|
117
|
-
const buf = Buffer.from(opts.body)
|
|
118
|
-
const hasher = md5 ? crypto.createHash('md5') : null
|
|
119
|
-
|
|
120
|
-
const hash = hasher?.update(buf).digest('base64')
|
|
121
|
-
const pos = buf.length
|
|
122
|
-
|
|
123
|
-
if (md5 != null && hash !== md5) {
|
|
124
|
-
throw Object.assign(new Error('Request Content-MD5 mismatch'), {
|
|
125
|
-
expected: md5,
|
|
126
|
-
actual: hash,
|
|
127
|
-
})
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
if (length != null && pos !== Number(length)) {
|
|
131
|
-
throw Object.assign(new Error('Request Content-Length mismatch'), {
|
|
132
|
-
expected: Number(length),
|
|
133
|
-
actual: pos,
|
|
134
|
-
})
|
|
135
|
-
}
|
|
136
|
-
} else {
|
|
137
|
-
throw new Error('not implemented')
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
return dispatch(opts, new Handler(opts, { handler }))
|
|
141
|
-
}
|