@nxtedition/nxt-undici 1.0.7 → 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 +61 -26
- 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 -1
- package/lib/interceptor/response-content.js +69 -0
- package/lib/interceptor/response-retry.js +7 -3
- package/lib/interceptor/response-status-retry.js +4 -1
- 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() {
|
|
@@ -50,6 +89,10 @@ class Readable extends stream.Readable {
|
|
|
50
89
|
}
|
|
51
90
|
|
|
52
91
|
async arrayBuffer() {
|
|
92
|
+
return (await this.buffer()).buffer
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async buffer() {
|
|
53
96
|
const buffers = []
|
|
54
97
|
for await (const chunk of this) {
|
|
55
98
|
buffers.push(chunk)
|
|
@@ -57,22 +100,17 @@ class Readable extends stream.Readable {
|
|
|
57
100
|
return Buffer.concat(buffers)
|
|
58
101
|
}
|
|
59
102
|
|
|
60
|
-
async buffer() {
|
|
61
|
-
return Buffer.from(await this.arrayBuffer())
|
|
62
|
-
}
|
|
63
|
-
|
|
64
103
|
async dump() {
|
|
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'),
|
|
@@ -154,12 +193,13 @@ async function request(url, opts) {
|
|
|
154
193
|
dispatch = (opts, handler) => dispatcher.dispatch(opts, handler)
|
|
155
194
|
dispatch = dispatchers.catch(dispatch)
|
|
156
195
|
dispatch = dispatchers.abort(dispatch)
|
|
157
|
-
dispatch = dispatchers.log(dispatch)
|
|
158
196
|
dispatch = dispatchers.requestId(dispatch)
|
|
197
|
+
dispatch = dispatchers.log(dispatch)
|
|
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)
|
|
@@ -210,8 +250,6 @@ async function request(url, opts) {
|
|
|
210
250
|
},
|
|
211
251
|
onBodySent(chunk) {},
|
|
212
252
|
onHeaders(statusCode, rawHeaders, resume, statusMessage) {
|
|
213
|
-
assert(this.abort)
|
|
214
|
-
|
|
215
253
|
const headers = parseHeaders(rawHeaders)
|
|
216
254
|
|
|
217
255
|
if (statusCode >= 400) {
|
|
@@ -222,30 +260,26 @@ async function request(url, opts) {
|
|
|
222
260
|
const contentLength = Number(headers['content-length'] ?? headers['Content-Length'])
|
|
223
261
|
|
|
224
262
|
this.body = new Readable({
|
|
225
|
-
|
|
263
|
+
resume,
|
|
264
|
+
abort: this.abort,
|
|
226
265
|
highWaterMark: 128 * 1024,
|
|
227
266
|
statusCode,
|
|
228
267
|
statusMessage,
|
|
229
268
|
headers,
|
|
230
269
|
size: Number.isFinite(contentLength) ? contentLength : null,
|
|
231
|
-
}).on('error', (err) => {
|
|
232
|
-
if (this.logger && this.body?.listenerCount('error') === 1) {
|
|
233
|
-
this.logger.error({ err }, 'unhandled response body error')
|
|
234
|
-
}
|
|
235
270
|
})
|
|
236
271
|
|
|
237
272
|
this.resolve(this.body)
|
|
238
273
|
this.resolve = null
|
|
274
|
+
this.reject = null
|
|
239
275
|
}
|
|
240
276
|
|
|
241
277
|
return false
|
|
242
278
|
},
|
|
243
279
|
onData(chunk) {
|
|
244
|
-
assert(this.body)
|
|
245
280
|
return this.body.push(chunk)
|
|
246
281
|
},
|
|
247
282
|
onComplete() {
|
|
248
|
-
assert(this.body)
|
|
249
283
|
this.body.push(null)
|
|
250
284
|
},
|
|
251
285
|
onError(err) {
|
|
@@ -253,6 +287,7 @@ async function request(url, opts) {
|
|
|
253
287
|
this.body.destroy(err)
|
|
254
288
|
} else {
|
|
255
289
|
this.reject(err)
|
|
290
|
+
this.resolve = null
|
|
256
291
|
this.reject = null
|
|
257
292
|
}
|
|
258
293
|
},
|
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
|
+
}
|
|
@@ -44,6 +44,10 @@ class Handler {
|
|
|
44
44
|
return this.handler.onBodySent(chunk)
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
onRequestSent() {
|
|
48
|
+
return this.handler.onRequestSent()
|
|
49
|
+
}
|
|
50
|
+
|
|
47
51
|
onHeaders(statusCode, rawHeaders, resume, statusMessage) {
|
|
48
52
|
const etag = findHeader(rawHeaders, 'etag')
|
|
49
53
|
|
|
@@ -116,7 +120,6 @@ class Handler {
|
|
|
116
120
|
return this.handler.onError(err)
|
|
117
121
|
}
|
|
118
122
|
|
|
119
|
-
// TODO (fix): abort signal?
|
|
120
123
|
const retryPromise = retryFn(err, this.retryCount++, this.opts)
|
|
121
124
|
if (retryPromise == null) {
|
|
122
125
|
return this.handler.onError(err)
|
|
@@ -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)
|
|
@@ -8,6 +8,7 @@ class Handler {
|
|
|
8
8
|
this.abort = null
|
|
9
9
|
this.aborted = false
|
|
10
10
|
this.reason = null
|
|
11
|
+
this.statusCode = 0
|
|
11
12
|
|
|
12
13
|
this.retryCount = 0
|
|
13
14
|
this.retryPromise = null
|
|
@@ -38,8 +39,12 @@ class Handler {
|
|
|
38
39
|
return this.handler.onBodySent(chunk)
|
|
39
40
|
}
|
|
40
41
|
|
|
42
|
+
onRequestSent() {
|
|
43
|
+
return this.handler.onRequestSent()
|
|
44
|
+
}
|
|
45
|
+
|
|
41
46
|
onHeaders(statusCode, rawHeaders, resume, statusMessage) {
|
|
42
|
-
this.
|
|
47
|
+
this.statusCode = statusCode
|
|
43
48
|
return this.handler.onHeaders(statusCode, rawHeaders, resume, statusMessage)
|
|
44
49
|
}
|
|
45
50
|
|
|
@@ -52,11 +57,10 @@ class Handler {
|
|
|
52
57
|
}
|
|
53
58
|
|
|
54
59
|
onError(err) {
|
|
55
|
-
if (this.aborted || isDisturbed(this.opts.body)) {
|
|
60
|
+
if (this.aborted || this.statusCode || isDisturbed(this.opts.body)) {
|
|
56
61
|
return this.handler.onError(err)
|
|
57
62
|
}
|
|
58
63
|
|
|
59
|
-
// TODO (fix): abort signal?
|
|
60
64
|
const retryPromise = retryFn(err, this.retryCount++, this.opts)
|
|
61
65
|
if (retryPromise == null) {
|
|
62
66
|
return this.handler.onError(err)
|
|
@@ -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)
|
|
@@ -46,7 +50,6 @@ class Handler {
|
|
|
46
50
|
|
|
47
51
|
const err = createError(statusCode, { headers: parseHeaders(rawHeaders) })
|
|
48
52
|
|
|
49
|
-
// TODO (fix): abort signal?
|
|
50
53
|
const retryPromise = retryFn(err, this.retryCount++, this.opts)
|
|
51
54
|
if (retryPromise == null) {
|
|
52
55
|
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
|
-
}
|