undici 6.21.0 → 7.0.0-alpha.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/README.md +27 -46
- package/docs/docs/api/Agent.md +14 -17
- package/docs/docs/api/BalancedPool.md +16 -16
- package/docs/docs/api/CacheStore.md +131 -0
- package/docs/docs/api/Client.md +12 -14
- package/docs/docs/api/Debug.md +1 -1
- package/docs/docs/api/Dispatcher.md +98 -194
- package/docs/docs/api/EnvHttpProxyAgent.md +12 -13
- package/docs/docs/api/MockAgent.md +5 -3
- package/docs/docs/api/MockClient.md +5 -5
- package/docs/docs/api/MockPool.md +4 -3
- package/docs/docs/api/Pool.md +15 -16
- package/docs/docs/api/PoolStats.md +1 -1
- package/docs/docs/api/ProxyAgent.md +3 -3
- package/docs/docs/api/RedirectHandler.md +1 -1
- package/docs/docs/api/RetryAgent.md +1 -1
- package/docs/docs/api/RetryHandler.md +4 -4
- package/docs/docs/api/WebSocket.md +46 -4
- package/docs/docs/api/api-lifecycle.md +11 -11
- package/docs/docs/best-practices/mocking-request.md +2 -2
- package/docs/docs/best-practices/proxy.md +1 -1
- package/index.d.ts +1 -1
- package/index.js +23 -7
- package/lib/api/abort-signal.js +2 -0
- package/lib/api/api-connect.js +3 -1
- package/lib/api/api-pipeline.js +7 -6
- package/lib/api/api-request.js +33 -48
- package/lib/api/api-stream.js +39 -50
- package/lib/api/api-upgrade.js +5 -3
- package/lib/api/readable.js +235 -62
- package/lib/api/util.js +2 -0
- package/lib/cache/memory-cache-store.js +177 -0
- package/lib/cache/sqlite-cache-store.js +446 -0
- package/lib/core/constants.js +35 -10
- package/lib/core/diagnostics.js +122 -128
- package/lib/core/errors.js +6 -6
- package/lib/core/request.js +13 -11
- package/lib/core/symbols.js +2 -1
- package/lib/core/tree.js +9 -1
- package/lib/core/util.js +237 -49
- package/lib/dispatcher/agent.js +3 -17
- package/lib/dispatcher/balanced-pool.js +5 -8
- package/lib/dispatcher/client-h1.js +379 -134
- package/lib/dispatcher/client-h2.js +173 -107
- package/lib/dispatcher/client.js +19 -32
- package/lib/dispatcher/dispatcher-base.js +6 -35
- package/lib/dispatcher/dispatcher.js +7 -24
- package/lib/dispatcher/fixed-queue.js +91 -49
- package/lib/dispatcher/pool-stats.js +2 -0
- package/lib/dispatcher/pool.js +3 -6
- package/lib/dispatcher/proxy-agent.js +3 -6
- package/lib/handler/cache-handler.js +393 -0
- package/lib/handler/cache-revalidation-handler.js +124 -0
- package/lib/handler/decorator-handler.js +27 -0
- package/lib/handler/redirect-handler.js +54 -59
- package/lib/handler/retry-handler.js +77 -109
- package/lib/handler/unwrap-handler.js +96 -0
- package/lib/handler/wrap-handler.js +98 -0
- package/lib/interceptor/cache.js +350 -0
- package/lib/interceptor/dns.js +375 -0
- package/lib/interceptor/dump.js +2 -2
- package/lib/interceptor/redirect.js +11 -14
- package/lib/interceptor/response-error.js +18 -7
- package/lib/llhttp/constants.d.ts +97 -0
- package/lib/llhttp/constants.js +412 -192
- package/lib/llhttp/constants.js.map +1 -0
- package/lib/llhttp/llhttp-wasm.js +11 -1
- package/lib/llhttp/llhttp_simd-wasm.js +11 -1
- package/lib/llhttp/utils.d.ts +2 -0
- package/lib/llhttp/utils.js +9 -9
- package/lib/llhttp/utils.js.map +1 -0
- package/lib/mock/mock-agent.js +5 -8
- package/lib/mock/mock-client.js +9 -4
- package/lib/mock/mock-errors.js +3 -1
- package/lib/mock/mock-interceptor.js +8 -6
- package/lib/mock/mock-pool.js +9 -4
- package/lib/mock/mock-symbols.js +3 -1
- package/lib/mock/mock-utils.js +29 -5
- package/lib/util/cache.js +360 -0
- package/lib/web/cache/cache.js +24 -21
- package/lib/web/cache/cachestorage.js +1 -1
- package/lib/web/cookies/index.js +29 -14
- package/lib/web/cookies/parse.js +8 -3
- package/lib/web/eventsource/eventsource-stream.js +9 -8
- package/lib/web/eventsource/eventsource.js +10 -6
- package/lib/web/fetch/body.js +43 -41
- package/lib/web/fetch/constants.js +12 -5
- package/lib/web/fetch/data-url.js +3 -3
- package/lib/web/fetch/formdata-parser.js +72 -45
- package/lib/web/fetch/formdata.js +65 -54
- package/lib/web/fetch/headers.js +118 -86
- package/lib/web/fetch/index.js +58 -67
- package/lib/web/fetch/request.js +136 -77
- package/lib/web/fetch/response.js +87 -56
- package/lib/web/fetch/util.js +259 -109
- package/lib/web/fetch/webidl.js +113 -68
- package/lib/web/websocket/connection.js +76 -147
- package/lib/web/websocket/constants.js +70 -10
- package/lib/web/websocket/events.js +4 -2
- package/lib/web/websocket/frame.js +45 -3
- package/lib/web/websocket/receiver.js +29 -33
- package/lib/web/websocket/sender.js +18 -13
- package/lib/web/websocket/stream/websocketerror.js +83 -0
- package/lib/web/websocket/stream/websocketstream.js +485 -0
- package/lib/web/websocket/util.js +128 -77
- package/lib/web/websocket/websocket.js +234 -135
- package/package.json +24 -36
- package/scripts/strip-comments.js +3 -1
- package/types/agent.d.ts +7 -7
- package/types/api.d.ts +24 -24
- package/types/balanced-pool.d.ts +11 -11
- package/types/cache-interceptor.d.ts +172 -0
- package/types/client.d.ts +11 -12
- package/types/cookies.d.ts +2 -0
- package/types/diagnostics-channel.d.ts +10 -10
- package/types/dispatcher.d.ts +113 -90
- package/types/env-http-proxy-agent.d.ts +2 -2
- package/types/errors.d.ts +53 -47
- package/types/fetch.d.ts +17 -16
- package/types/formdata.d.ts +7 -7
- package/types/global-dispatcher.d.ts +4 -4
- package/types/global-origin.d.ts +5 -5
- package/types/handlers.d.ts +7 -7
- package/types/header.d.ts +157 -1
- package/types/index.d.ts +44 -46
- package/types/interceptors.d.ts +25 -8
- package/types/mock-agent.d.ts +21 -18
- package/types/mock-client.d.ts +4 -4
- package/types/mock-errors.d.ts +3 -3
- package/types/mock-interceptor.d.ts +19 -19
- package/types/mock-pool.d.ts +4 -4
- package/types/patch.d.ts +0 -4
- package/types/pool-stats.d.ts +8 -8
- package/types/pool.d.ts +12 -12
- package/types/proxy-agent.d.ts +4 -4
- package/types/readable.d.ts +18 -15
- package/types/retry-agent.d.ts +1 -1
- package/types/retry-handler.d.ts +10 -10
- package/types/util.d.ts +3 -3
- package/types/utility.d.ts +7 -0
- package/types/webidl.d.ts +44 -6
- package/types/websocket.d.ts +34 -1
- package/docs/docs/api/DispatchInterceptor.md +0 -60
- package/lib/interceptor/redirect-interceptor.js +0 -21
- package/lib/mock/pluralizer.js +0 -29
- package/lib/web/cache/symbols.js +0 -5
- package/lib/web/fetch/file.js +0 -126
- package/lib/web/fetch/symbols.js +0 -9
- package/lib/web/fileapi/encoding.js +0 -290
- package/lib/web/fileapi/filereader.js +0 -344
- package/lib/web/fileapi/progressevent.js +0 -78
- package/lib/web/fileapi/symbols.js +0 -10
- package/lib/web/fileapi/util.js +0 -391
- package/lib/web/websocket/symbols.js +0 -12
- package/types/file.d.ts +0 -39
- package/types/filereader.d.ts +0 -54
package/lib/api/api-stream.js
CHANGED
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const assert = require('node:assert')
|
|
4
|
-
const { finished
|
|
4
|
+
const { finished } = require('node:stream')
|
|
5
|
+
const { AsyncResource } = require('node:async_hooks')
|
|
5
6
|
const { InvalidArgumentError, InvalidReturnValueError } = require('../core/errors')
|
|
6
7
|
const util = require('../core/util')
|
|
7
|
-
const { getResolveErrorBodyCallback } = require('./util')
|
|
8
|
-
const { AsyncResource } = require('node:async_hooks')
|
|
9
8
|
const { addSignal, removeSignal } = require('./abort-signal')
|
|
10
9
|
|
|
10
|
+
function noop () {}
|
|
11
|
+
|
|
11
12
|
class StreamHandler extends AsyncResource {
|
|
12
13
|
constructor (opts, factory, callback) {
|
|
13
14
|
if (!opts || typeof opts !== 'object') {
|
|
14
15
|
throw new InvalidArgumentError('invalid opts')
|
|
15
16
|
}
|
|
16
17
|
|
|
17
|
-
const { signal, method, opaque, body, onInfo, responseHeaders
|
|
18
|
+
const { signal, method, opaque, body, onInfo, responseHeaders } = opts
|
|
18
19
|
|
|
19
20
|
try {
|
|
20
21
|
if (typeof callback !== 'function') {
|
|
@@ -40,7 +41,7 @@ class StreamHandler extends AsyncResource {
|
|
|
40
41
|
super('UNDICI_STREAM')
|
|
41
42
|
} catch (err) {
|
|
42
43
|
if (util.isStream(body)) {
|
|
43
|
-
util.destroy(body.on('error',
|
|
44
|
+
util.destroy(body.on('error', noop), err)
|
|
44
45
|
}
|
|
45
46
|
throw err
|
|
46
47
|
}
|
|
@@ -55,7 +56,6 @@ class StreamHandler extends AsyncResource {
|
|
|
55
56
|
this.trailers = null
|
|
56
57
|
this.body = body
|
|
57
58
|
this.onInfo = onInfo || null
|
|
58
|
-
this.throwOnError = throwOnError || false
|
|
59
59
|
|
|
60
60
|
if (util.isStream(body)) {
|
|
61
61
|
body.on('error', (err) => {
|
|
@@ -79,7 +79,7 @@ class StreamHandler extends AsyncResource {
|
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
onHeaders (statusCode, rawHeaders, resume, statusMessage) {
|
|
82
|
-
const { factory, opaque, context,
|
|
82
|
+
const { factory, opaque, context, responseHeaders } = this
|
|
83
83
|
|
|
84
84
|
const headers = responseHeaders === 'raw' ? util.parseRawHeaders(rawHeaders) : util.parseHeaders(rawHeaders)
|
|
85
85
|
|
|
@@ -92,55 +92,42 @@ class StreamHandler extends AsyncResource {
|
|
|
92
92
|
|
|
93
93
|
this.factory = null
|
|
94
94
|
|
|
95
|
-
|
|
95
|
+
if (factory === null) {
|
|
96
|
+
return
|
|
97
|
+
}
|
|
96
98
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
99
|
+
const res = this.runInAsyncScope(factory, null, {
|
|
100
|
+
statusCode,
|
|
101
|
+
headers,
|
|
102
|
+
opaque,
|
|
103
|
+
context
|
|
104
|
+
})
|
|
101
105
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
106
|
+
if (
|
|
107
|
+
!res ||
|
|
108
|
+
typeof res.write !== 'function' ||
|
|
109
|
+
typeof res.end !== 'function' ||
|
|
110
|
+
typeof res.on !== 'function'
|
|
111
|
+
) {
|
|
112
|
+
throw new InvalidReturnValueError('expected Writable')
|
|
113
|
+
}
|
|
110
114
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
opaque,
|
|
115
|
-
context
|
|
116
|
-
})
|
|
115
|
+
// TODO: Avoid finished. It registers an unnecessary amount of listeners.
|
|
116
|
+
finished(res, { readable: false }, (err) => {
|
|
117
|
+
const { callback, res, opaque, trailers, abort } = this
|
|
117
118
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
typeof res.end !== 'function' ||
|
|
122
|
-
typeof res.on !== 'function'
|
|
123
|
-
) {
|
|
124
|
-
throw new InvalidReturnValueError('expected Writable')
|
|
119
|
+
this.res = null
|
|
120
|
+
if (err || !res.readable) {
|
|
121
|
+
util.destroy(res, err)
|
|
125
122
|
}
|
|
126
123
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
const { callback, res, opaque, trailers, abort } = this
|
|
130
|
-
|
|
131
|
-
this.res = null
|
|
132
|
-
if (err || !res.readable) {
|
|
133
|
-
util.destroy(res, err)
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
this.callback = null
|
|
137
|
-
this.runInAsyncScope(callback, null, err || null, { opaque, trailers })
|
|
124
|
+
this.callback = null
|
|
125
|
+
this.runInAsyncScope(callback, null, err || null, { opaque, trailers })
|
|
138
126
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
}
|
|
127
|
+
if (err) {
|
|
128
|
+
abort()
|
|
129
|
+
}
|
|
130
|
+
})
|
|
144
131
|
|
|
145
132
|
res.on('drain', resume)
|
|
146
133
|
|
|
@@ -207,7 +194,9 @@ function stream (opts, factory, callback) {
|
|
|
207
194
|
}
|
|
208
195
|
|
|
209
196
|
try {
|
|
210
|
-
|
|
197
|
+
const handler = new StreamHandler(opts, factory, callback)
|
|
198
|
+
|
|
199
|
+
this.dispatch(opts, handler)
|
|
211
200
|
} catch (err) {
|
|
212
201
|
if (typeof callback !== 'function') {
|
|
213
202
|
throw err
|
package/lib/api/api-upgrade.js
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
const { InvalidArgumentError, SocketError } = require('../core/errors')
|
|
4
4
|
const { AsyncResource } = require('node:async_hooks')
|
|
5
|
+
const assert = require('node:assert')
|
|
5
6
|
const util = require('../core/util')
|
|
6
7
|
const { addSignal, removeSignal } = require('./abort-signal')
|
|
7
|
-
const assert = require('node:assert')
|
|
8
8
|
|
|
9
9
|
class UpgradeHandler extends AsyncResource {
|
|
10
10
|
constructor (opts, callback) {
|
|
@@ -91,11 +91,13 @@ function upgrade (opts, callback) {
|
|
|
91
91
|
|
|
92
92
|
try {
|
|
93
93
|
const upgradeHandler = new UpgradeHandler(opts, callback)
|
|
94
|
-
|
|
94
|
+
const upgradeOpts = {
|
|
95
95
|
...opts,
|
|
96
96
|
method: opts.method || 'GET',
|
|
97
97
|
upgrade: opts.protocol || 'Websocket'
|
|
98
|
-
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
this.dispatch(upgradeOpts, upgradeHandler)
|
|
99
101
|
} catch (err) {
|
|
100
102
|
if (typeof callback !== 'function') {
|
|
101
103
|
throw err
|
package/lib/api/readable.js
CHANGED
|
@@ -14,10 +14,25 @@ const kBody = Symbol('kBody')
|
|
|
14
14
|
const kAbort = Symbol('kAbort')
|
|
15
15
|
const kContentType = Symbol('kContentType')
|
|
16
16
|
const kContentLength = Symbol('kContentLength')
|
|
17
|
+
const kUsed = Symbol('kUsed')
|
|
18
|
+
const kBytesRead = Symbol('kBytesRead')
|
|
17
19
|
|
|
18
20
|
const noop = () => {}
|
|
19
21
|
|
|
22
|
+
/**
|
|
23
|
+
* @class
|
|
24
|
+
* @extends {Readable}
|
|
25
|
+
* @see https://fetch.spec.whatwg.org/#body
|
|
26
|
+
*/
|
|
20
27
|
class BodyReadable extends Readable {
|
|
28
|
+
/**
|
|
29
|
+
* @param {object} opts
|
|
30
|
+
* @param {(this: Readable, size: number) => void} opts.resume
|
|
31
|
+
* @param {() => (void | null)} opts.abort
|
|
32
|
+
* @param {string} [opts.contentType = '']
|
|
33
|
+
* @param {number} [opts.contentLength]
|
|
34
|
+
* @param {number} [opts.highWaterMark = 64 * 1024]
|
|
35
|
+
*/
|
|
21
36
|
constructor ({
|
|
22
37
|
resume,
|
|
23
38
|
abort,
|
|
@@ -34,10 +49,19 @@ class BodyReadable extends Readable {
|
|
|
34
49
|
this._readableState.dataEmitted = false
|
|
35
50
|
|
|
36
51
|
this[kAbort] = abort
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* @type {Consume | null}
|
|
55
|
+
*/
|
|
37
56
|
this[kConsume] = null
|
|
57
|
+
this[kBytesRead] = 0
|
|
58
|
+
/**
|
|
59
|
+
* @type {ReadableStream|null}
|
|
60
|
+
*/
|
|
38
61
|
this[kBody] = null
|
|
62
|
+
this[kUsed] = false
|
|
39
63
|
this[kContentType] = contentType
|
|
40
|
-
this[kContentLength] = contentLength
|
|
64
|
+
this[kContentLength] = Number.isFinite(contentLength) ? contentLength : null
|
|
41
65
|
|
|
42
66
|
// Is stream being consumed through Readable API?
|
|
43
67
|
// This is an optimization so that we avoid checking
|
|
@@ -46,7 +70,12 @@ class BodyReadable extends Readable {
|
|
|
46
70
|
this[kReading] = false
|
|
47
71
|
}
|
|
48
72
|
|
|
49
|
-
|
|
73
|
+
/**
|
|
74
|
+
* @param {Error|null} err
|
|
75
|
+
* @param {(error:(Error|null)) => void} callback
|
|
76
|
+
* @returns {void}
|
|
77
|
+
*/
|
|
78
|
+
_destroy (err, callback) {
|
|
50
79
|
if (!err && !this._readableState.endEmitted) {
|
|
51
80
|
err = new RequestAbortedError()
|
|
52
81
|
}
|
|
@@ -55,15 +84,11 @@ class BodyReadable extends Readable {
|
|
|
55
84
|
this[kAbort]()
|
|
56
85
|
}
|
|
57
86
|
|
|
58
|
-
return super.destroy(err)
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
_destroy (err, callback) {
|
|
62
87
|
// Workaround for Node "bug". If the stream is destroyed in same
|
|
63
88
|
// tick as it is created, then a user who is waiting for a
|
|
64
|
-
// promise (i.e micro tick) for installing
|
|
89
|
+
// promise (i.e micro tick) for installing an 'error' listener will
|
|
65
90
|
// never get a chance and will always encounter an unhandled exception.
|
|
66
|
-
if (!this[
|
|
91
|
+
if (!this[kUsed]) {
|
|
67
92
|
setImmediate(() => {
|
|
68
93
|
callback(err)
|
|
69
94
|
})
|
|
@@ -72,20 +97,36 @@ class BodyReadable extends Readable {
|
|
|
72
97
|
}
|
|
73
98
|
}
|
|
74
99
|
|
|
75
|
-
|
|
76
|
-
|
|
100
|
+
/**
|
|
101
|
+
* @param {string} event
|
|
102
|
+
* @param {(...args: any[]) => void} listener
|
|
103
|
+
* @returns {this}
|
|
104
|
+
*/
|
|
105
|
+
on (event, listener) {
|
|
106
|
+
if (event === 'data' || event === 'readable') {
|
|
77
107
|
this[kReading] = true
|
|
108
|
+
this[kUsed] = true
|
|
78
109
|
}
|
|
79
|
-
return super.on(
|
|
110
|
+
return super.on(event, listener)
|
|
80
111
|
}
|
|
81
112
|
|
|
82
|
-
|
|
83
|
-
|
|
113
|
+
/**
|
|
114
|
+
* @param {string} event
|
|
115
|
+
* @param {(...args: any[]) => void} listener
|
|
116
|
+
* @returns {this}
|
|
117
|
+
*/
|
|
118
|
+
addListener (event, listener) {
|
|
119
|
+
return this.on(event, listener)
|
|
84
120
|
}
|
|
85
121
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
122
|
+
/**
|
|
123
|
+
* @param {string|symbol} event
|
|
124
|
+
* @param {(...args: any[]) => void} listener
|
|
125
|
+
* @returns {this}
|
|
126
|
+
*/
|
|
127
|
+
off (event, listener) {
|
|
128
|
+
const ret = super.off(event, listener)
|
|
129
|
+
if (event === 'data' || event === 'readable') {
|
|
89
130
|
this[kReading] = (
|
|
90
131
|
this.listenerCount('data') > 0 ||
|
|
91
132
|
this.listenerCount('readable') > 0
|
|
@@ -94,11 +135,22 @@ class BodyReadable extends Readable {
|
|
|
94
135
|
return ret
|
|
95
136
|
}
|
|
96
137
|
|
|
97
|
-
|
|
98
|
-
|
|
138
|
+
/**
|
|
139
|
+
* @param {string|symbol} event
|
|
140
|
+
* @param {(...args: any[]) => void} listener
|
|
141
|
+
* @returns {this}
|
|
142
|
+
*/
|
|
143
|
+
removeListener (event, listener) {
|
|
144
|
+
return this.off(event, listener)
|
|
99
145
|
}
|
|
100
146
|
|
|
147
|
+
/**
|
|
148
|
+
* @param {Buffer|null} chunk
|
|
149
|
+
* @returns {boolean}
|
|
150
|
+
*/
|
|
101
151
|
push (chunk) {
|
|
152
|
+
this[kBytesRead] += chunk ? chunk.length : 0
|
|
153
|
+
|
|
102
154
|
if (this[kConsume] && chunk !== null) {
|
|
103
155
|
consumePush(this[kConsume], chunk)
|
|
104
156
|
return this[kReading] ? super.push(chunk) : true
|
|
@@ -106,43 +158,84 @@ class BodyReadable extends Readable {
|
|
|
106
158
|
return super.push(chunk)
|
|
107
159
|
}
|
|
108
160
|
|
|
109
|
-
|
|
110
|
-
|
|
161
|
+
/**
|
|
162
|
+
* Consumes and returns the body as a string.
|
|
163
|
+
*
|
|
164
|
+
* @see https://fetch.spec.whatwg.org/#dom-body-text
|
|
165
|
+
* @returns {Promise<string>}
|
|
166
|
+
*/
|
|
167
|
+
text () {
|
|
111
168
|
return consume(this, 'text')
|
|
112
169
|
}
|
|
113
170
|
|
|
114
|
-
|
|
115
|
-
|
|
171
|
+
/**
|
|
172
|
+
* Consumes and returns the body as a JavaScript Object.
|
|
173
|
+
*
|
|
174
|
+
* @see https://fetch.spec.whatwg.org/#dom-body-json
|
|
175
|
+
* @returns {Promise<unknown>}
|
|
176
|
+
*/
|
|
177
|
+
json () {
|
|
116
178
|
return consume(this, 'json')
|
|
117
179
|
}
|
|
118
180
|
|
|
119
|
-
|
|
120
|
-
|
|
181
|
+
/**
|
|
182
|
+
* Consumes and returns the body as a Blob
|
|
183
|
+
*
|
|
184
|
+
* @see https://fetch.spec.whatwg.org/#dom-body-blob
|
|
185
|
+
* @returns {Promise<Blob>}
|
|
186
|
+
*/
|
|
187
|
+
blob () {
|
|
121
188
|
return consume(this, 'blob')
|
|
122
189
|
}
|
|
123
190
|
|
|
124
|
-
|
|
125
|
-
|
|
191
|
+
/**
|
|
192
|
+
* Consumes and returns the body as an Uint8Array.
|
|
193
|
+
*
|
|
194
|
+
* @see https://fetch.spec.whatwg.org/#dom-body-bytes
|
|
195
|
+
* @returns {Promise<Uint8Array>}
|
|
196
|
+
*/
|
|
197
|
+
bytes () {
|
|
126
198
|
return consume(this, 'bytes')
|
|
127
199
|
}
|
|
128
200
|
|
|
129
|
-
|
|
130
|
-
|
|
201
|
+
/**
|
|
202
|
+
* Consumes and returns the body as an ArrayBuffer.
|
|
203
|
+
*
|
|
204
|
+
* @see https://fetch.spec.whatwg.org/#dom-body-arraybuffer
|
|
205
|
+
* @returns {Promise<ArrayBuffer>}
|
|
206
|
+
*/
|
|
207
|
+
arrayBuffer () {
|
|
131
208
|
return consume(this, 'arrayBuffer')
|
|
132
209
|
}
|
|
133
210
|
|
|
134
|
-
|
|
211
|
+
/**
|
|
212
|
+
* Not implemented
|
|
213
|
+
*
|
|
214
|
+
* @see https://fetch.spec.whatwg.org/#dom-body-formdata
|
|
215
|
+
* @throws {NotSupportedError}
|
|
216
|
+
*/
|
|
135
217
|
async formData () {
|
|
136
218
|
// TODO: Implement.
|
|
137
219
|
throw new NotSupportedError()
|
|
138
220
|
}
|
|
139
221
|
|
|
140
|
-
|
|
222
|
+
/**
|
|
223
|
+
* Returns true if the body is not null and the body has been consumed.
|
|
224
|
+
* Otherwise, returns false.
|
|
225
|
+
*
|
|
226
|
+
* @see https://fetch.spec.whatwg.org/#dom-body-bodyused
|
|
227
|
+
* @readonly
|
|
228
|
+
* @returns {boolean}
|
|
229
|
+
*/
|
|
141
230
|
get bodyUsed () {
|
|
142
231
|
return util.isDisturbed(this)
|
|
143
232
|
}
|
|
144
233
|
|
|
145
|
-
|
|
234
|
+
/**
|
|
235
|
+
* @see https://fetch.spec.whatwg.org/#dom-body-body
|
|
236
|
+
* @readonly
|
|
237
|
+
* @returns {ReadableStream}
|
|
238
|
+
*/
|
|
146
239
|
get body () {
|
|
147
240
|
if (!this[kBody]) {
|
|
148
241
|
this[kBody] = ReadableStreamFrom(this)
|
|
@@ -155,14 +248,24 @@ class BodyReadable extends Readable {
|
|
|
155
248
|
return this[kBody]
|
|
156
249
|
}
|
|
157
250
|
|
|
251
|
+
/**
|
|
252
|
+
* Dumps the response body by reading `limit` number of bytes.
|
|
253
|
+
* @param {object} opts
|
|
254
|
+
* @param {number} [opts.limit = 131072] Number of bytes to read.
|
|
255
|
+
* @param {AbortSignal} [opts.signal] An AbortSignal to cancel the dump.
|
|
256
|
+
* @returns {Promise<null>}
|
|
257
|
+
*/
|
|
158
258
|
async dump (opts) {
|
|
159
|
-
let limit = Number.isFinite(opts?.limit) ? opts.limit : 128 * 1024
|
|
160
259
|
const signal = opts?.signal
|
|
161
260
|
|
|
162
261
|
if (signal != null && (typeof signal !== 'object' || !('aborted' in signal))) {
|
|
163
262
|
throw new InvalidArgumentError('signal must be an AbortSignal')
|
|
164
263
|
}
|
|
165
264
|
|
|
265
|
+
const limit = opts?.limit && Number.isFinite(opts.limit)
|
|
266
|
+
? opts.limit
|
|
267
|
+
: 128 * 1024
|
|
268
|
+
|
|
166
269
|
signal?.throwIfAborted()
|
|
167
270
|
|
|
168
271
|
if (this._readableState.closeEmitted) {
|
|
@@ -170,48 +273,89 @@ class BodyReadable extends Readable {
|
|
|
170
273
|
}
|
|
171
274
|
|
|
172
275
|
return await new Promise((resolve, reject) => {
|
|
173
|
-
if (
|
|
276
|
+
if (
|
|
277
|
+
(this[kContentLength] && (this[kContentLength] > limit)) ||
|
|
278
|
+
this[kBytesRead] > limit
|
|
279
|
+
) {
|
|
174
280
|
this.destroy(new AbortError())
|
|
175
281
|
}
|
|
176
282
|
|
|
177
|
-
|
|
178
|
-
|
|
283
|
+
if (signal) {
|
|
284
|
+
const onAbort = () => {
|
|
285
|
+
this.destroy(signal.reason ?? new AbortError())
|
|
286
|
+
}
|
|
287
|
+
signal.addEventListener('abort', onAbort)
|
|
288
|
+
this
|
|
289
|
+
.on('close', function () {
|
|
290
|
+
signal.removeEventListener('abort', onAbort)
|
|
291
|
+
if (signal.aborted) {
|
|
292
|
+
reject(signal.reason ?? new AbortError())
|
|
293
|
+
} else {
|
|
294
|
+
resolve(null)
|
|
295
|
+
}
|
|
296
|
+
})
|
|
297
|
+
} else {
|
|
298
|
+
this.on('close', resolve)
|
|
179
299
|
}
|
|
180
|
-
signal?.addEventListener('abort', onAbort)
|
|
181
300
|
|
|
182
301
|
this
|
|
183
|
-
.on('close', function () {
|
|
184
|
-
signal?.removeEventListener('abort', onAbort)
|
|
185
|
-
if (signal?.aborted) {
|
|
186
|
-
reject(signal.reason ?? new AbortError())
|
|
187
|
-
} else {
|
|
188
|
-
resolve(null)
|
|
189
|
-
}
|
|
190
|
-
})
|
|
191
302
|
.on('error', noop)
|
|
192
|
-
.on('data',
|
|
193
|
-
limit
|
|
194
|
-
if (limit <= 0) {
|
|
303
|
+
.on('data', () => {
|
|
304
|
+
if (this[kBytesRead] > limit) {
|
|
195
305
|
this.destroy()
|
|
196
306
|
}
|
|
197
307
|
})
|
|
198
308
|
.resume()
|
|
199
309
|
})
|
|
200
310
|
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* @param {BufferEncoding} encoding
|
|
314
|
+
* @returns {this}
|
|
315
|
+
*/
|
|
316
|
+
setEncoding (encoding) {
|
|
317
|
+
if (Buffer.isEncoding(encoding)) {
|
|
318
|
+
this._readableState.encoding = encoding
|
|
319
|
+
}
|
|
320
|
+
return this
|
|
321
|
+
}
|
|
201
322
|
}
|
|
202
323
|
|
|
203
|
-
|
|
204
|
-
|
|
324
|
+
/**
|
|
325
|
+
* @see https://streams.spec.whatwg.org/#readablestream-locked
|
|
326
|
+
* @param {BodyReadable} bodyReadable
|
|
327
|
+
* @returns {boolean}
|
|
328
|
+
*/
|
|
329
|
+
function isLocked (bodyReadable) {
|
|
205
330
|
// Consume is an implicit lock.
|
|
206
|
-
return
|
|
331
|
+
return bodyReadable[kBody]?.locked === true || bodyReadable[kConsume] !== null
|
|
207
332
|
}
|
|
208
333
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
334
|
+
/**
|
|
335
|
+
* @see https://fetch.spec.whatwg.org/#body-unusable
|
|
336
|
+
* @param {BodyReadable} bodyReadable
|
|
337
|
+
* @returns {boolean}
|
|
338
|
+
*/
|
|
339
|
+
function isUnusable (bodyReadable) {
|
|
340
|
+
return util.isDisturbed(bodyReadable) || isLocked(bodyReadable)
|
|
212
341
|
}
|
|
213
342
|
|
|
214
|
-
|
|
343
|
+
/**
|
|
344
|
+
* @typedef {object} Consume
|
|
345
|
+
* @property {string} type
|
|
346
|
+
* @property {BodyReadable} stream
|
|
347
|
+
* @property {((value?: any) => void)} resolve
|
|
348
|
+
* @property {((err: Error) => void)} reject
|
|
349
|
+
* @property {number} length
|
|
350
|
+
* @property {Buffer[]} body
|
|
351
|
+
*/
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* @param {BodyReadable} stream
|
|
355
|
+
* @param {string} type
|
|
356
|
+
* @returns {Promise<any>}
|
|
357
|
+
*/
|
|
358
|
+
function consume (stream, type) {
|
|
215
359
|
assert(!stream[kConsume])
|
|
216
360
|
|
|
217
361
|
return new Promise((resolve, reject) => {
|
|
@@ -255,6 +399,10 @@ async function consume (stream, type) {
|
|
|
255
399
|
})
|
|
256
400
|
}
|
|
257
401
|
|
|
402
|
+
/**
|
|
403
|
+
* @param {Consume} consume
|
|
404
|
+
* @returns {void}
|
|
405
|
+
*/
|
|
258
406
|
function consumeStart (consume) {
|
|
259
407
|
if (consume.body === null) {
|
|
260
408
|
return
|
|
@@ -275,10 +423,10 @@ function consumeStart (consume) {
|
|
|
275
423
|
}
|
|
276
424
|
|
|
277
425
|
if (state.endEmitted) {
|
|
278
|
-
consumeEnd(this[kConsume])
|
|
426
|
+
consumeEnd(this[kConsume], this._readableState.encoding)
|
|
279
427
|
} else {
|
|
280
428
|
consume.stream.on('end', function () {
|
|
281
|
-
consumeEnd(this[kConsume])
|
|
429
|
+
consumeEnd(this[kConsume], this._readableState.encoding)
|
|
282
430
|
})
|
|
283
431
|
}
|
|
284
432
|
|
|
@@ -292,8 +440,10 @@ function consumeStart (consume) {
|
|
|
292
440
|
/**
|
|
293
441
|
* @param {Buffer[]} chunks
|
|
294
442
|
* @param {number} length
|
|
443
|
+
* @param {BufferEncoding} encoding
|
|
444
|
+
* @returns {string}
|
|
295
445
|
*/
|
|
296
|
-
function chunksDecode (chunks, length) {
|
|
446
|
+
function chunksDecode (chunks, length, encoding) {
|
|
297
447
|
if (chunks.length === 0 || length === 0) {
|
|
298
448
|
return ''
|
|
299
449
|
}
|
|
@@ -308,7 +458,11 @@ function chunksDecode (chunks, length) {
|
|
|
308
458
|
buffer[2] === 0xbf
|
|
309
459
|
? 3
|
|
310
460
|
: 0
|
|
311
|
-
|
|
461
|
+
if (!encoding || encoding === 'utf8' || encoding === 'utf-8') {
|
|
462
|
+
return buffer.utf8Slice(start, bufferLength)
|
|
463
|
+
} else {
|
|
464
|
+
return buffer.subarray(start, bufferLength).toString(encoding)
|
|
465
|
+
}
|
|
312
466
|
}
|
|
313
467
|
|
|
314
468
|
/**
|
|
@@ -336,14 +490,19 @@ function chunksConcat (chunks, length) {
|
|
|
336
490
|
return buffer
|
|
337
491
|
}
|
|
338
492
|
|
|
339
|
-
|
|
493
|
+
/**
|
|
494
|
+
* @param {Consume} consume
|
|
495
|
+
* @param {BufferEncoding} encoding
|
|
496
|
+
* @returns {void}
|
|
497
|
+
*/
|
|
498
|
+
function consumeEnd (consume, encoding) {
|
|
340
499
|
const { type, body, resolve, stream, length } = consume
|
|
341
500
|
|
|
342
501
|
try {
|
|
343
502
|
if (type === 'text') {
|
|
344
|
-
resolve(chunksDecode(body, length))
|
|
503
|
+
resolve(chunksDecode(body, length, encoding))
|
|
345
504
|
} else if (type === 'json') {
|
|
346
|
-
resolve(JSON.parse(chunksDecode(body, length)))
|
|
505
|
+
resolve(JSON.parse(chunksDecode(body, length, encoding)))
|
|
347
506
|
} else if (type === 'arrayBuffer') {
|
|
348
507
|
resolve(chunksConcat(body, length).buffer)
|
|
349
508
|
} else if (type === 'blob') {
|
|
@@ -358,11 +517,21 @@ function consumeEnd (consume) {
|
|
|
358
517
|
}
|
|
359
518
|
}
|
|
360
519
|
|
|
520
|
+
/**
|
|
521
|
+
* @param {Consume} consume
|
|
522
|
+
* @param {Buffer} chunk
|
|
523
|
+
* @returns {void}
|
|
524
|
+
*/
|
|
361
525
|
function consumePush (consume, chunk) {
|
|
362
526
|
consume.length += chunk.length
|
|
363
527
|
consume.body.push(chunk)
|
|
364
528
|
}
|
|
365
529
|
|
|
530
|
+
/**
|
|
531
|
+
* @param {Consume} consume
|
|
532
|
+
* @param {Error} [err]
|
|
533
|
+
* @returns {void}
|
|
534
|
+
*/
|
|
366
535
|
function consumeFinish (consume, err) {
|
|
367
536
|
if (consume.body === null) {
|
|
368
537
|
return
|
|
@@ -374,6 +543,7 @@ function consumeFinish (consume, err) {
|
|
|
374
543
|
consume.resolve()
|
|
375
544
|
}
|
|
376
545
|
|
|
546
|
+
// Reset the consume object to allow for garbage collection.
|
|
377
547
|
consume.type = null
|
|
378
548
|
consume.stream = null
|
|
379
549
|
consume.resolve = null
|
|
@@ -382,4 +552,7 @@ function consumeFinish (consume, err) {
|
|
|
382
552
|
consume.body = null
|
|
383
553
|
}
|
|
384
554
|
|
|
385
|
-
module.exports = {
|
|
555
|
+
module.exports = {
|
|
556
|
+
Readable: BodyReadable,
|
|
557
|
+
chunksDecode
|
|
558
|
+
}
|