@nxtedition/nxt-undici 1.0.8 → 1.0.10
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 +56 -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,55 @@ 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
|
+
if (err) {
|
|
67
|
+
this[kAbort](err)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
callback(err)
|
|
30
71
|
}
|
|
31
72
|
|
|
32
73
|
async text() {
|
|
@@ -65,14 +106,13 @@ class Readable extends stream.Readable {
|
|
|
65
106
|
let n = 0
|
|
66
107
|
try {
|
|
67
108
|
for await (const chunk of this) {
|
|
68
|
-
// do nothing
|
|
69
109
|
n += chunk.length
|
|
70
110
|
if (n > 128 * 1024) {
|
|
71
111
|
break
|
|
72
112
|
}
|
|
73
113
|
}
|
|
74
114
|
} catch {
|
|
75
|
-
|
|
115
|
+
// Do nothing...
|
|
76
116
|
}
|
|
77
117
|
}
|
|
78
118
|
}
|
|
@@ -80,7 +120,8 @@ class Readable extends stream.Readable {
|
|
|
80
120
|
const dispatchers = {
|
|
81
121
|
abort: require('./interceptor/abort.js'),
|
|
82
122
|
catch: require('./interceptor/catch.js'),
|
|
83
|
-
|
|
123
|
+
responseContent: require('./interceptor/response-content.js'),
|
|
124
|
+
requestContent: require('./interceptor/request-content.js'),
|
|
84
125
|
log: require('./interceptor/log.js'),
|
|
85
126
|
redirect: require('./interceptor/redirect.js'),
|
|
86
127
|
responseBodyRetry: require('./interceptor/response-body-retry.js'),
|
|
@@ -159,7 +200,8 @@ async function request(url, opts) {
|
|
|
159
200
|
dispatch = dispatchers.responseRetry(dispatch)
|
|
160
201
|
dispatch = dispatchers.responseStatusRetry(dispatch)
|
|
161
202
|
dispatch = dispatchers.responseBodyRetry(dispatch)
|
|
162
|
-
dispatch = dispatchers.
|
|
203
|
+
dispatch = dispatchers.responseContent(dispatch)
|
|
204
|
+
dispatch = dispatchers.requestContent(dispatch)
|
|
163
205
|
dispatch = dispatchers.redirect(dispatch)
|
|
164
206
|
dispatch = dispatchers.signal(dispatch)
|
|
165
207
|
dispatch = dispatchers.cache(dispatch)
|
|
@@ -220,16 +262,13 @@ async function request(url, opts) {
|
|
|
220
262
|
const contentLength = Number(headers['content-length'] ?? headers['Content-Length'])
|
|
221
263
|
|
|
222
264
|
this.body = new Readable({
|
|
223
|
-
|
|
265
|
+
resume,
|
|
266
|
+
abort: this.abort,
|
|
224
267
|
highWaterMark: 128 * 1024,
|
|
225
268
|
statusCode,
|
|
226
269
|
statusMessage,
|
|
227
270
|
headers,
|
|
228
271
|
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
272
|
})
|
|
234
273
|
|
|
235
274
|
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
|
-
}
|