undici 6.20.0 → 7.0.0-alpha.2
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 +6 -10
- package/docs/docs/api/Agent.md +0 -3
- package/docs/docs/api/Client.md +1 -3
- package/docs/docs/api/Debug.md +1 -1
- package/docs/docs/api/Dispatcher.md +60 -8
- package/docs/docs/api/EnvHttpProxyAgent.md +0 -1
- package/docs/docs/api/Fetch.md +1 -0
- package/docs/docs/api/MockAgent.md +2 -0
- package/docs/docs/api/MockPool.md +2 -1
- package/docs/docs/api/Pool.md +0 -1
- package/docs/docs/api/RetryAgent.md +1 -1
- package/docs/docs/api/RetryHandler.md +1 -1
- package/docs/docs/api/WebSocket.md +45 -3
- package/index.js +6 -6
- 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 +32 -47
- package/lib/api/api-stream.js +39 -50
- package/lib/api/api-upgrade.js +5 -3
- package/lib/api/readable.js +261 -64
- package/lib/api/util.js +2 -0
- package/lib/core/constants.js +11 -9
- package/lib/core/diagnostics.js +122 -128
- package/lib/core/errors.js +4 -4
- package/lib/core/request.js +11 -9
- package/lib/core/symbols.js +2 -1
- package/lib/core/tree.js +9 -1
- package/lib/core/util.js +219 -48
- package/lib/dispatcher/agent.js +3 -17
- package/lib/dispatcher/balanced-pool.js +5 -8
- package/lib/dispatcher/client-h1.js +278 -54
- package/lib/dispatcher/client-h2.js +1 -1
- package/lib/dispatcher/client.js +23 -34
- package/lib/dispatcher/dispatcher-base.js +2 -34
- package/lib/dispatcher/dispatcher.js +3 -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 +6 -7
- package/lib/handler/decorator-handler.js +24 -0
- package/lib/handler/redirect-handler.js +11 -2
- package/lib/handler/retry-handler.js +12 -3
- package/lib/interceptor/dns.js +346 -0
- package/lib/interceptor/dump.js +2 -2
- package/lib/interceptor/redirect.js +11 -14
- package/lib/interceptor/response-error.js +4 -1
- 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/web/cache/cache.js +24 -21
- package/lib/web/cache/cachestorage.js +1 -1
- package/lib/web/cookies/index.js +17 -13
- package/lib/web/cookies/parse.js +2 -2
- package/lib/web/eventsource/eventsource-stream.js +9 -8
- package/lib/web/eventsource/eventsource.js +10 -6
- package/lib/web/fetch/body.js +42 -36
- package/lib/web/fetch/constants.js +35 -26
- package/lib/web/fetch/data-url.js +1 -1
- package/lib/web/fetch/formdata-parser.js +2 -2
- package/lib/web/fetch/formdata.js +65 -54
- package/lib/web/fetch/headers.js +117 -85
- package/lib/web/fetch/index.js +55 -62
- package/lib/web/fetch/request.js +135 -77
- package/lib/web/fetch/response.js +86 -56
- package/lib/web/fetch/util.js +90 -64
- package/lib/web/fetch/webidl.js +99 -64
- package/lib/web/websocket/connection.js +76 -147
- package/lib/web/websocket/constants.js +3 -4
- 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 +20 -33
- 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/client.d.ts +11 -12
- package/types/diagnostics-channel.d.ts +10 -10
- package/types/dispatcher.d.ts +96 -97
- package/types/env-http-proxy-agent.d.ts +2 -2
- package/types/errors.d.ts +53 -47
- package/types/fetch.d.ts +8 -8
- 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 +4 -4
- package/types/header.d.ts +157 -1
- package/types/index.d.ts +42 -46
- package/types/interceptors.d.ts +22 -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 +22 -14
- package/types/retry-agent.d.ts +1 -1
- package/types/retry-handler.d.ts +8 -8
- 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,38 +158,84 @@ class BodyReadable extends Readable {
|
|
|
106
158
|
return super.push(chunk)
|
|
107
159
|
}
|
|
108
160
|
|
|
109
|
-
|
|
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
|
+
*/
|
|
110
167
|
async text () {
|
|
111
168
|
return consume(this, 'text')
|
|
112
169
|
}
|
|
113
170
|
|
|
114
|
-
|
|
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
|
+
*/
|
|
115
177
|
async json () {
|
|
116
178
|
return consume(this, 'json')
|
|
117
179
|
}
|
|
118
180
|
|
|
119
|
-
|
|
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
|
+
*/
|
|
120
187
|
async blob () {
|
|
121
188
|
return consume(this, 'blob')
|
|
122
189
|
}
|
|
123
190
|
|
|
124
|
-
|
|
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
|
+
async bytes () {
|
|
198
|
+
return consume(this, 'bytes')
|
|
199
|
+
}
|
|
200
|
+
|
|
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
|
+
*/
|
|
125
207
|
async arrayBuffer () {
|
|
126
208
|
return consume(this, 'arrayBuffer')
|
|
127
209
|
}
|
|
128
210
|
|
|
129
|
-
|
|
211
|
+
/**
|
|
212
|
+
* Not implemented
|
|
213
|
+
*
|
|
214
|
+
* @see https://fetch.spec.whatwg.org/#dom-body-formdata
|
|
215
|
+
* @throws {NotSupportedError}
|
|
216
|
+
*/
|
|
130
217
|
async formData () {
|
|
131
218
|
// TODO: Implement.
|
|
132
219
|
throw new NotSupportedError()
|
|
133
220
|
}
|
|
134
221
|
|
|
135
|
-
|
|
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
|
+
*/
|
|
136
230
|
get bodyUsed () {
|
|
137
231
|
return util.isDisturbed(this)
|
|
138
232
|
}
|
|
139
233
|
|
|
140
|
-
|
|
234
|
+
/**
|
|
235
|
+
* @see https://fetch.spec.whatwg.org/#dom-body-body
|
|
236
|
+
* @readonly
|
|
237
|
+
* @returns {ReadableStream}
|
|
238
|
+
*/
|
|
141
239
|
get body () {
|
|
142
240
|
if (!this[kBody]) {
|
|
143
241
|
this[kBody] = ReadableStreamFrom(this)
|
|
@@ -150,14 +248,24 @@ class BodyReadable extends Readable {
|
|
|
150
248
|
return this[kBody]
|
|
151
249
|
}
|
|
152
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
|
+
*/
|
|
153
258
|
async dump (opts) {
|
|
154
|
-
let limit = Number.isFinite(opts?.limit) ? opts.limit : 128 * 1024
|
|
155
259
|
const signal = opts?.signal
|
|
156
260
|
|
|
157
261
|
if (signal != null && (typeof signal !== 'object' || !('aborted' in signal))) {
|
|
158
262
|
throw new InvalidArgumentError('signal must be an AbortSignal')
|
|
159
263
|
}
|
|
160
264
|
|
|
265
|
+
const limit = opts?.limit && Number.isFinite(opts.limit)
|
|
266
|
+
? opts.limit
|
|
267
|
+
: 128 * 1024
|
|
268
|
+
|
|
161
269
|
signal?.throwIfAborted()
|
|
162
270
|
|
|
163
271
|
if (this._readableState.closeEmitted) {
|
|
@@ -165,47 +273,88 @@ class BodyReadable extends Readable {
|
|
|
165
273
|
}
|
|
166
274
|
|
|
167
275
|
return await new Promise((resolve, reject) => {
|
|
168
|
-
if (
|
|
276
|
+
if (
|
|
277
|
+
(this[kContentLength] && (this[kContentLength] > limit)) ||
|
|
278
|
+
this[kBytesRead] > limit
|
|
279
|
+
) {
|
|
169
280
|
this.destroy(new AbortError())
|
|
170
281
|
}
|
|
171
282
|
|
|
172
|
-
|
|
173
|
-
|
|
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)
|
|
174
299
|
}
|
|
175
|
-
signal?.addEventListener('abort', onAbort)
|
|
176
300
|
|
|
177
301
|
this
|
|
178
|
-
.on('close', function () {
|
|
179
|
-
signal?.removeEventListener('abort', onAbort)
|
|
180
|
-
if (signal?.aborted) {
|
|
181
|
-
reject(signal.reason ?? new AbortError())
|
|
182
|
-
} else {
|
|
183
|
-
resolve(null)
|
|
184
|
-
}
|
|
185
|
-
})
|
|
186
302
|
.on('error', noop)
|
|
187
|
-
.on('data',
|
|
188
|
-
limit
|
|
189
|
-
if (limit <= 0) {
|
|
303
|
+
.on('data', () => {
|
|
304
|
+
if (this[kBytesRead] > limit) {
|
|
190
305
|
this.destroy()
|
|
191
306
|
}
|
|
192
307
|
})
|
|
193
308
|
.resume()
|
|
194
309
|
})
|
|
195
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
|
+
}
|
|
196
322
|
}
|
|
197
323
|
|
|
198
|
-
|
|
199
|
-
|
|
324
|
+
/**
|
|
325
|
+
* @see https://streams.spec.whatwg.org/#readablestream-locked
|
|
326
|
+
* @param {BodyReadable} bodyReadable
|
|
327
|
+
* @returns {boolean}
|
|
328
|
+
*/
|
|
329
|
+
function isLocked (bodyReadable) {
|
|
200
330
|
// Consume is an implicit lock.
|
|
201
|
-
return
|
|
331
|
+
return bodyReadable[kBody]?.locked === true || bodyReadable[kConsume] !== null
|
|
202
332
|
}
|
|
203
333
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
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)
|
|
207
341
|
}
|
|
208
342
|
|
|
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
|
+
*/
|
|
209
358
|
async function consume (stream, type) {
|
|
210
359
|
assert(!stream[kConsume])
|
|
211
360
|
|
|
@@ -250,6 +399,10 @@ async function consume (stream, type) {
|
|
|
250
399
|
})
|
|
251
400
|
}
|
|
252
401
|
|
|
402
|
+
/**
|
|
403
|
+
* @param {Consume} consume
|
|
404
|
+
* @returns {void}
|
|
405
|
+
*/
|
|
253
406
|
function consumeStart (consume) {
|
|
254
407
|
if (consume.body === null) {
|
|
255
408
|
return
|
|
@@ -270,10 +423,10 @@ function consumeStart (consume) {
|
|
|
270
423
|
}
|
|
271
424
|
|
|
272
425
|
if (state.endEmitted) {
|
|
273
|
-
consumeEnd(this[kConsume])
|
|
426
|
+
consumeEnd(this[kConsume], this._readableState.encoding)
|
|
274
427
|
} else {
|
|
275
428
|
consume.stream.on('end', function () {
|
|
276
|
-
consumeEnd(this[kConsume])
|
|
429
|
+
consumeEnd(this[kConsume], this._readableState.encoding)
|
|
277
430
|
})
|
|
278
431
|
}
|
|
279
432
|
|
|
@@ -287,8 +440,10 @@ function consumeStart (consume) {
|
|
|
287
440
|
/**
|
|
288
441
|
* @param {Buffer[]} chunks
|
|
289
442
|
* @param {number} length
|
|
443
|
+
* @param {BufferEncoding} encoding
|
|
444
|
+
* @returns {string}
|
|
290
445
|
*/
|
|
291
|
-
function chunksDecode (chunks, length) {
|
|
446
|
+
function chunksDecode (chunks, length, encoding) {
|
|
292
447
|
if (chunks.length === 0 || length === 0) {
|
|
293
448
|
return ''
|
|
294
449
|
}
|
|
@@ -303,29 +458,57 @@ function chunksDecode (chunks, length) {
|
|
|
303
458
|
buffer[2] === 0xbf
|
|
304
459
|
? 3
|
|
305
460
|
: 0
|
|
306
|
-
|
|
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
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* @param {Buffer[]} chunks
|
|
470
|
+
* @param {number} length
|
|
471
|
+
* @returns {Uint8Array}
|
|
472
|
+
*/
|
|
473
|
+
function chunksConcat (chunks, length) {
|
|
474
|
+
if (chunks.length === 0 || length === 0) {
|
|
475
|
+
return new Uint8Array(0)
|
|
476
|
+
}
|
|
477
|
+
if (chunks.length === 1) {
|
|
478
|
+
// fast-path
|
|
479
|
+
return new Uint8Array(chunks[0])
|
|
480
|
+
}
|
|
481
|
+
const buffer = new Uint8Array(Buffer.allocUnsafeSlow(length).buffer)
|
|
482
|
+
|
|
483
|
+
let offset = 0
|
|
484
|
+
for (let i = 0; i < chunks.length; ++i) {
|
|
485
|
+
const chunk = chunks[i]
|
|
486
|
+
buffer.set(chunk, offset)
|
|
487
|
+
offset += chunk.length
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
return buffer
|
|
307
491
|
}
|
|
308
492
|
|
|
309
|
-
|
|
493
|
+
/**
|
|
494
|
+
* @param {Consume} consume
|
|
495
|
+
* @param {BufferEncoding} encoding
|
|
496
|
+
* @returns {void}
|
|
497
|
+
*/
|
|
498
|
+
function consumeEnd (consume, encoding) {
|
|
310
499
|
const { type, body, resolve, stream, length } = consume
|
|
311
500
|
|
|
312
501
|
try {
|
|
313
502
|
if (type === 'text') {
|
|
314
|
-
resolve(chunksDecode(body, length))
|
|
503
|
+
resolve(chunksDecode(body, length, encoding))
|
|
315
504
|
} else if (type === 'json') {
|
|
316
|
-
resolve(JSON.parse(chunksDecode(body, length)))
|
|
505
|
+
resolve(JSON.parse(chunksDecode(body, length, encoding)))
|
|
317
506
|
} else if (type === 'arrayBuffer') {
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
let pos = 0
|
|
321
|
-
for (const buf of body) {
|
|
322
|
-
dst.set(buf, pos)
|
|
323
|
-
pos += buf.byteLength
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
resolve(dst.buffer)
|
|
507
|
+
resolve(chunksConcat(body, length).buffer)
|
|
327
508
|
} else if (type === 'blob') {
|
|
328
509
|
resolve(new Blob(body, { type: stream[kContentType] }))
|
|
510
|
+
} else if (type === 'bytes') {
|
|
511
|
+
resolve(chunksConcat(body, length))
|
|
329
512
|
}
|
|
330
513
|
|
|
331
514
|
consumeFinish(consume)
|
|
@@ -334,11 +517,21 @@ function consumeEnd (consume) {
|
|
|
334
517
|
}
|
|
335
518
|
}
|
|
336
519
|
|
|
520
|
+
/**
|
|
521
|
+
* @param {Consume} consume
|
|
522
|
+
* @param {Buffer} chunk
|
|
523
|
+
* @returns {void}
|
|
524
|
+
*/
|
|
337
525
|
function consumePush (consume, chunk) {
|
|
338
526
|
consume.length += chunk.length
|
|
339
527
|
consume.body.push(chunk)
|
|
340
528
|
}
|
|
341
529
|
|
|
530
|
+
/**
|
|
531
|
+
* @param {Consume} consume
|
|
532
|
+
* @param {Error} [err]
|
|
533
|
+
* @returns {void}
|
|
534
|
+
*/
|
|
342
535
|
function consumeFinish (consume, err) {
|
|
343
536
|
if (consume.body === null) {
|
|
344
537
|
return
|
|
@@ -350,6 +543,7 @@ function consumeFinish (consume, err) {
|
|
|
350
543
|
consume.resolve()
|
|
351
544
|
}
|
|
352
545
|
|
|
546
|
+
// Reset the consume object to allow for garbage collection.
|
|
353
547
|
consume.type = null
|
|
354
548
|
consume.stream = null
|
|
355
549
|
consume.resolve = null
|
|
@@ -358,4 +552,7 @@ function consumeFinish (consume, err) {
|
|
|
358
552
|
consume.body = null
|
|
359
553
|
}
|
|
360
554
|
|
|
361
|
-
module.exports = {
|
|
555
|
+
module.exports = {
|
|
556
|
+
Readable: BodyReadable,
|
|
557
|
+
chunksDecode
|
|
558
|
+
}
|