undici 5.28.2 → 6.0.1
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 +0 -2
- package/docs/api/BalancedPool.md +1 -1
- package/index.js +36 -40
- package/lib/api/readable.js +46 -23
- package/lib/client.js +18 -12
- package/lib/compat/dispatcher-weakref.js +1 -6
- package/lib/core/connect.js +5 -1
- package/lib/core/constants.js +116 -0
- package/lib/core/request.js +0 -4
- package/lib/core/util.js +18 -24
- package/lib/fetch/body.js +14 -19
- package/lib/fetch/constants.js +0 -36
- package/lib/fetch/dataURL.js +8 -2
- package/lib/fetch/index.js +241 -113
- package/lib/fetch/request.js +0 -6
- package/lib/fetch/response.js +20 -10
- package/lib/fetch/util.js +169 -12
- package/lib/fetch/webidl.js +3 -3
- package/lib/fileapi/util.js +0 -1
- package/lib/handler/RedirectHandler.js +2 -2
- package/lib/handler/RetryHandler.js +16 -7
- package/lib/websocket/connection.js +1 -0
- package/lib/websocket/util.js +1 -0
- package/lib/websocket/websocket.js +0 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -180,8 +180,6 @@ Implements [fetch](https://fetch.spec.whatwg.org/#fetch-method).
|
|
|
180
180
|
* https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch
|
|
181
181
|
* https://fetch.spec.whatwg.org/#fetch-method
|
|
182
182
|
|
|
183
|
-
Only supported on Node 16.8+.
|
|
184
|
-
|
|
185
183
|
Basic usage example:
|
|
186
184
|
|
|
187
185
|
```js
|
package/docs/api/BalancedPool.md
CHANGED
package/index.js
CHANGED
|
@@ -98,58 +98,54 @@ function makeDispatcher (fn) {
|
|
|
98
98
|
module.exports.setGlobalDispatcher = setGlobalDispatcher
|
|
99
99
|
module.exports.getGlobalDispatcher = getGlobalDispatcher
|
|
100
100
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
try {
|
|
109
|
-
return await fetchImpl(...arguments)
|
|
110
|
-
} catch (err) {
|
|
111
|
-
if (typeof err === 'object') {
|
|
112
|
-
Error.captureStackTrace(err, this)
|
|
113
|
-
}
|
|
101
|
+
let fetchImpl = null
|
|
102
|
+
module.exports.fetch = async function fetch (resource) {
|
|
103
|
+
if (!fetchImpl) {
|
|
104
|
+
fetchImpl = require('./lib/fetch').fetch
|
|
105
|
+
}
|
|
114
106
|
|
|
115
|
-
|
|
107
|
+
try {
|
|
108
|
+
return await fetchImpl(...arguments)
|
|
109
|
+
} catch (err) {
|
|
110
|
+
if (typeof err === 'object') {
|
|
111
|
+
Error.captureStackTrace(err, this)
|
|
116
112
|
}
|
|
113
|
+
|
|
114
|
+
throw err
|
|
117
115
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
116
|
+
}
|
|
117
|
+
module.exports.Headers = require('./lib/fetch/headers').Headers
|
|
118
|
+
module.exports.Response = require('./lib/fetch/response').Response
|
|
119
|
+
module.exports.Request = require('./lib/fetch/request').Request
|
|
120
|
+
module.exports.FormData = require('./lib/fetch/formdata').FormData
|
|
121
|
+
module.exports.File = require('./lib/fetch/file').File
|
|
122
|
+
module.exports.FileReader = require('./lib/fileapi/filereader').FileReader
|
|
124
123
|
|
|
125
|
-
|
|
124
|
+
const { setGlobalOrigin, getGlobalOrigin } = require('./lib/fetch/global')
|
|
126
125
|
|
|
127
|
-
|
|
128
|
-
|
|
126
|
+
module.exports.setGlobalOrigin = setGlobalOrigin
|
|
127
|
+
module.exports.getGlobalOrigin = getGlobalOrigin
|
|
129
128
|
|
|
130
|
-
|
|
131
|
-
|
|
129
|
+
const { CacheStorage } = require('./lib/cache/cachestorage')
|
|
130
|
+
const { kConstruct } = require('./lib/cache/symbols')
|
|
132
131
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
}
|
|
132
|
+
// Cache & CacheStorage are tightly coupled with fetch. Even if it may run
|
|
133
|
+
// in an older version of Node, it doesn't have any use without fetch.
|
|
134
|
+
module.exports.caches = new CacheStorage(kConstruct)
|
|
137
135
|
|
|
138
|
-
|
|
139
|
-
const { deleteCookie, getCookies, getSetCookies, setCookie } = require('./lib/cookies')
|
|
136
|
+
const { deleteCookie, getCookies, getSetCookies, setCookie } = require('./lib/cookies')
|
|
140
137
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
138
|
+
module.exports.deleteCookie = deleteCookie
|
|
139
|
+
module.exports.getCookies = getCookies
|
|
140
|
+
module.exports.getSetCookies = getSetCookies
|
|
141
|
+
module.exports.setCookie = setCookie
|
|
145
142
|
|
|
146
|
-
|
|
143
|
+
const { parseMIMEType, serializeAMimeType } = require('./lib/fetch/dataURL')
|
|
147
144
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
}
|
|
145
|
+
module.exports.parseMIMEType = parseMIMEType
|
|
146
|
+
module.exports.serializeAMimeType = serializeAMimeType
|
|
151
147
|
|
|
152
|
-
if (
|
|
148
|
+
if (hasCrypto) {
|
|
153
149
|
const { WebSocket } = require('./lib/websocket/websocket')
|
|
154
150
|
|
|
155
151
|
module.exports.WebSocket = WebSocket
|
package/lib/api/readable.js
CHANGED
|
@@ -62,6 +62,18 @@ module.exports = class BodyReadable extends Readable {
|
|
|
62
62
|
return super.destroy(err)
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
_destroy (err, callback) {
|
|
66
|
+
// Workaround for Node "bug". If the stream is destroyed in same
|
|
67
|
+
// tick as it is created, then a user who is waiting for a
|
|
68
|
+
// promise (i.e micro tick) for installing a 'error' listener will
|
|
69
|
+
// never get a chance and will always encounter an unhandled exception.
|
|
70
|
+
// - tick => process.nextTick(fn)
|
|
71
|
+
// - micro tick => queueMicrotask(fn)
|
|
72
|
+
queueMicrotask(() => {
|
|
73
|
+
callback(err)
|
|
74
|
+
})
|
|
75
|
+
}
|
|
76
|
+
|
|
65
77
|
emit (ev, ...args) {
|
|
66
78
|
if (ev === 'data') {
|
|
67
79
|
// Node < 16.7
|
|
@@ -166,7 +178,7 @@ module.exports = class BodyReadable extends Readable {
|
|
|
166
178
|
}
|
|
167
179
|
}
|
|
168
180
|
|
|
169
|
-
if (this.
|
|
181
|
+
if (this._readableState.closeEmitted) {
|
|
170
182
|
return Promise.resolve(null)
|
|
171
183
|
}
|
|
172
184
|
|
|
@@ -210,33 +222,44 @@ function isUnusable (self) {
|
|
|
210
222
|
}
|
|
211
223
|
|
|
212
224
|
async function consume (stream, type) {
|
|
213
|
-
if (isUnusable(stream)) {
|
|
214
|
-
throw new TypeError('unusable')
|
|
215
|
-
}
|
|
216
|
-
|
|
217
225
|
assert(!stream[kConsume])
|
|
218
226
|
|
|
219
227
|
return new Promise((resolve, reject) => {
|
|
220
|
-
stream
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
+
if (isUnusable(stream)) {
|
|
229
|
+
const rState = stream._readableState
|
|
230
|
+
if (rState.destroyed && rState.closeEmitted === false) {
|
|
231
|
+
stream
|
|
232
|
+
.on('error', err => {
|
|
233
|
+
reject(err)
|
|
234
|
+
})
|
|
235
|
+
.on('close', () => {
|
|
236
|
+
reject(new TypeError('unusable'))
|
|
237
|
+
})
|
|
238
|
+
} else {
|
|
239
|
+
reject(rState.errored ?? new TypeError('unusable'))
|
|
240
|
+
}
|
|
241
|
+
} else {
|
|
242
|
+
stream[kConsume] = {
|
|
243
|
+
type,
|
|
244
|
+
stream,
|
|
245
|
+
resolve,
|
|
246
|
+
reject,
|
|
247
|
+
length: 0,
|
|
248
|
+
body: []
|
|
249
|
+
}
|
|
228
250
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
251
|
+
stream
|
|
252
|
+
.on('error', function (err) {
|
|
253
|
+
consumeFinish(this[kConsume], err)
|
|
254
|
+
})
|
|
255
|
+
.on('close', function () {
|
|
256
|
+
if (this[kConsume].body !== null) {
|
|
257
|
+
consumeFinish(this[kConsume], new RequestAbortedError())
|
|
258
|
+
}
|
|
259
|
+
})
|
|
238
260
|
|
|
239
|
-
|
|
261
|
+
queueMicrotask(() => consumeStart(stream[kConsume]))
|
|
262
|
+
}
|
|
240
263
|
})
|
|
241
264
|
}
|
|
242
265
|
|
package/lib/client.js
CHANGED
|
@@ -249,7 +249,7 @@ class Client extends DispatcherBase {
|
|
|
249
249
|
}
|
|
250
250
|
|
|
251
251
|
if (maxConcurrentStreams != null && (typeof maxConcurrentStreams !== 'number' || maxConcurrentStreams < 1)) {
|
|
252
|
-
throw new InvalidArgumentError('maxConcurrentStreams must be a
|
|
252
|
+
throw new InvalidArgumentError('maxConcurrentStreams must be a positive integer, greater than 0')
|
|
253
253
|
}
|
|
254
254
|
|
|
255
255
|
if (typeof connect !== 'function') {
|
|
@@ -296,7 +296,7 @@ class Client extends DispatcherBase {
|
|
|
296
296
|
? null
|
|
297
297
|
: {
|
|
298
298
|
// streams: null, // Fixed queue of streams - For future support of `push`
|
|
299
|
-
openStreams: 0, // Keep track of them to decide
|
|
299
|
+
openStreams: 0, // Keep track of them to decide whether or not unref the session
|
|
300
300
|
maxConcurrentStreams: maxConcurrentStreams != null ? maxConcurrentStreams : 100 // Max peerConcurrentStreams for a Node h2 server
|
|
301
301
|
}
|
|
302
302
|
this[kHost] = `${this[kUrl].hostname}${this[kUrl].port ? `:${this[kUrl].port}` : ''}`
|
|
@@ -1706,7 +1706,7 @@ function writeH2 (client, session, request) {
|
|
|
1706
1706
|
}
|
|
1707
1707
|
|
|
1708
1708
|
// https://tools.ietf.org/html/rfc7540#section-8.3
|
|
1709
|
-
// :path and :scheme headers must be
|
|
1709
|
+
// :path and :scheme headers must be omitted when sending CONNECT
|
|
1710
1710
|
|
|
1711
1711
|
headers[HTTP2_HEADER_PATH] = path
|
|
1712
1712
|
headers[HTTP2_HEADER_SCHEME] = 'https'
|
|
@@ -1833,7 +1833,7 @@ function writeH2 (client, session, request) {
|
|
|
1833
1833
|
// })
|
|
1834
1834
|
|
|
1835
1835
|
// stream.on('push', headers => {
|
|
1836
|
-
// // TODO(HTTP/2):
|
|
1836
|
+
// // TODO(HTTP/2): Support push
|
|
1837
1837
|
// })
|
|
1838
1838
|
|
|
1839
1839
|
// stream.on('trailers', headers => {
|
|
@@ -1963,12 +1963,19 @@ function writeStream ({ h2stream, body, client, request, socket, contentLength,
|
|
|
1963
1963
|
body.resume()
|
|
1964
1964
|
}
|
|
1965
1965
|
}
|
|
1966
|
-
const
|
|
1967
|
-
|
|
1968
|
-
|
|
1966
|
+
const onClose = function () {
|
|
1967
|
+
// 'close' might be emitted *before* 'error' for
|
|
1968
|
+
// broken streams. Wait a tick to avoid this case.
|
|
1969
|
+
queueMicrotask(() => {
|
|
1970
|
+
// It's only safe to remove 'error' listener after
|
|
1971
|
+
// 'close'.
|
|
1972
|
+
body.removeListener('error', onFinished)
|
|
1973
|
+
})
|
|
1974
|
+
|
|
1975
|
+
if (!finished) {
|
|
1976
|
+
const err = new RequestAbortedError()
|
|
1977
|
+
queueMicrotask(() => onFinished(err))
|
|
1969
1978
|
}
|
|
1970
|
-
const err = new RequestAbortedError()
|
|
1971
|
-
queueMicrotask(() => onFinished(err))
|
|
1972
1979
|
}
|
|
1973
1980
|
const onFinished = function (err) {
|
|
1974
1981
|
if (finished) {
|
|
@@ -1986,8 +1993,7 @@ function writeStream ({ h2stream, body, client, request, socket, contentLength,
|
|
|
1986
1993
|
body
|
|
1987
1994
|
.removeListener('data', onData)
|
|
1988
1995
|
.removeListener('end', onFinished)
|
|
1989
|
-
.removeListener('
|
|
1990
|
-
.removeListener('close', onAbort)
|
|
1996
|
+
.removeListener('close', onClose)
|
|
1991
1997
|
|
|
1992
1998
|
if (!err) {
|
|
1993
1999
|
try {
|
|
@@ -2010,7 +2016,7 @@ function writeStream ({ h2stream, body, client, request, socket, contentLength,
|
|
|
2010
2016
|
.on('data', onData)
|
|
2011
2017
|
.on('end', onFinished)
|
|
2012
2018
|
.on('error', onFinished)
|
|
2013
|
-
.on('close',
|
|
2019
|
+
.on('close', onClose)
|
|
2014
2020
|
|
|
2015
2021
|
if (body.resume) {
|
|
2016
2022
|
body.resume()
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
/* istanbul ignore file: only for Node 12 */
|
|
4
|
-
|
|
5
3
|
const { kConnected, kSize } = require('../core/symbols')
|
|
6
4
|
|
|
7
5
|
class CompatWeakRef {
|
|
@@ -41,8 +39,5 @@ module.exports = function () {
|
|
|
41
39
|
FinalizationRegistry: CompatFinalizer
|
|
42
40
|
}
|
|
43
41
|
}
|
|
44
|
-
return {
|
|
45
|
-
WeakRef: global.WeakRef || CompatWeakRef,
|
|
46
|
-
FinalizationRegistry: global.FinalizationRegistry || CompatFinalizer
|
|
47
|
-
}
|
|
42
|
+
return { WeakRef, FinalizationRegistry }
|
|
48
43
|
}
|
package/lib/core/connect.js
CHANGED
|
@@ -183,7 +183,11 @@ function setupTimeout (onConnectTimeout, timeout) {
|
|
|
183
183
|
}
|
|
184
184
|
|
|
185
185
|
function onConnectTimeout (socket) {
|
|
186
|
-
|
|
186
|
+
let message = 'Connect Timeout Error'
|
|
187
|
+
if (Array.isArray(socket.autoSelectFamilyAttemptedAddresses)) {
|
|
188
|
+
message = +` (attempted addresses: ${socket.autoSelectFamilyAttemptedAddresses.join(', ')})`
|
|
189
|
+
}
|
|
190
|
+
util.destroy(socket, new ConnectTimeoutError(message))
|
|
187
191
|
}
|
|
188
192
|
|
|
189
193
|
module.exports = buildConnector
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/** @type {Record<string, string | undefined>} */
|
|
2
|
+
const headerNameLowerCasedRecord = {}
|
|
3
|
+
|
|
4
|
+
// https://developer.mozilla.org/docs/Web/HTTP/Headers
|
|
5
|
+
const wellknownHeaderNames = [
|
|
6
|
+
'Accept',
|
|
7
|
+
'Accept-Encoding',
|
|
8
|
+
'Accept-Language',
|
|
9
|
+
'Accept-Ranges',
|
|
10
|
+
'Access-Control-Allow-Credentials',
|
|
11
|
+
'Access-Control-Allow-Headers',
|
|
12
|
+
'Access-Control-Allow-Methods',
|
|
13
|
+
'Access-Control-Allow-Origin',
|
|
14
|
+
'Access-Control-Expose-Headers',
|
|
15
|
+
'Access-Control-Max-Age',
|
|
16
|
+
'Access-Control-Request-Headers',
|
|
17
|
+
'Access-Control-Request-Method',
|
|
18
|
+
'Age',
|
|
19
|
+
'Allow',
|
|
20
|
+
'Alt-Svc',
|
|
21
|
+
'Alt-Used',
|
|
22
|
+
'Authorization',
|
|
23
|
+
'Cache-Control',
|
|
24
|
+
'Clear-Site-Data',
|
|
25
|
+
'Connection',
|
|
26
|
+
'Content-Disposition',
|
|
27
|
+
'Content-Encoding',
|
|
28
|
+
'Content-Language',
|
|
29
|
+
'Content-Length',
|
|
30
|
+
'Content-Location',
|
|
31
|
+
'Content-Range',
|
|
32
|
+
'Content-Security-Policy',
|
|
33
|
+
'Content-Security-Policy-Report-Only',
|
|
34
|
+
'Content-Type',
|
|
35
|
+
'Cookie',
|
|
36
|
+
'Cross-Origin-Embedder-Policy',
|
|
37
|
+
'Cross-Origin-Opener-Policy',
|
|
38
|
+
'Cross-Origin-Resource-Policy',
|
|
39
|
+
'Date',
|
|
40
|
+
'Device-Memory',
|
|
41
|
+
'Downlink',
|
|
42
|
+
'ECT',
|
|
43
|
+
'ETag',
|
|
44
|
+
'Expect',
|
|
45
|
+
'Expect-CT',
|
|
46
|
+
'Expires',
|
|
47
|
+
'Forwarded',
|
|
48
|
+
'From',
|
|
49
|
+
'Host',
|
|
50
|
+
'If-Match',
|
|
51
|
+
'If-Modified-Since',
|
|
52
|
+
'If-None-Match',
|
|
53
|
+
'If-Range',
|
|
54
|
+
'If-Unmodified-Since',
|
|
55
|
+
'Keep-Alive',
|
|
56
|
+
'Last-Modified',
|
|
57
|
+
'Link',
|
|
58
|
+
'Location',
|
|
59
|
+
'Max-Forwards',
|
|
60
|
+
'Origin',
|
|
61
|
+
'Permissions-Policy',
|
|
62
|
+
'Pragma',
|
|
63
|
+
'Proxy-Authenticate',
|
|
64
|
+
'Proxy-Authorization',
|
|
65
|
+
'RTT',
|
|
66
|
+
'Range',
|
|
67
|
+
'Referer',
|
|
68
|
+
'Referrer-Policy',
|
|
69
|
+
'Refresh',
|
|
70
|
+
'Retry-After',
|
|
71
|
+
'Sec-WebSocket-Accept',
|
|
72
|
+
'Sec-WebSocket-Extensions',
|
|
73
|
+
'Sec-WebSocket-Key',
|
|
74
|
+
'Sec-WebSocket-Protocol',
|
|
75
|
+
'Sec-WebSocket-Version',
|
|
76
|
+
'Server',
|
|
77
|
+
'Server-Timing',
|
|
78
|
+
'Service-Worker-Allowed',
|
|
79
|
+
'Service-Worker-Navigation-Preload',
|
|
80
|
+
'Set-Cookie',
|
|
81
|
+
'SourceMap',
|
|
82
|
+
'Strict-Transport-Security',
|
|
83
|
+
'Supports-Loading-Mode',
|
|
84
|
+
'TE',
|
|
85
|
+
'Timing-Allow-Origin',
|
|
86
|
+
'Trailer',
|
|
87
|
+
'Transfer-Encoding',
|
|
88
|
+
'Upgrade',
|
|
89
|
+
'Upgrade-Insecure-Requests',
|
|
90
|
+
'User-Agent',
|
|
91
|
+
'Vary',
|
|
92
|
+
'Via',
|
|
93
|
+
'WWW-Authenticate',
|
|
94
|
+
'X-Content-Type-Options',
|
|
95
|
+
'X-DNS-Prefetch-Control',
|
|
96
|
+
'X-Frame-Options',
|
|
97
|
+
'X-Permitted-Cross-Domain-Policies',
|
|
98
|
+
'X-Powered-By',
|
|
99
|
+
'X-Requested-With',
|
|
100
|
+
'X-XSS-Protection'
|
|
101
|
+
]
|
|
102
|
+
|
|
103
|
+
for (let i = 0; i < wellknownHeaderNames.length; ++i) {
|
|
104
|
+
const key = wellknownHeaderNames[i]
|
|
105
|
+
const lowerCasedKey = key.toLowerCase()
|
|
106
|
+
headerNameLowerCasedRecord[key] = headerNameLowerCasedRecord[lowerCasedKey] =
|
|
107
|
+
lowerCasedKey
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Note: object prototypes should not be able to be referenced. e.g. `Object#hasOwnProperty`.
|
|
111
|
+
Object.setPrototypeOf(headerNameLowerCasedRecord, null)
|
|
112
|
+
|
|
113
|
+
module.exports = {
|
|
114
|
+
wellknownHeaderNames,
|
|
115
|
+
headerNameLowerCasedRecord
|
|
116
|
+
}
|
package/lib/core/request.js
CHANGED
|
@@ -196,10 +196,6 @@ class Request {
|
|
|
196
196
|
}
|
|
197
197
|
|
|
198
198
|
if (util.isFormDataLike(this.body)) {
|
|
199
|
-
if (util.nodeMajor < 16 || (util.nodeMajor === 16 && util.nodeMinor < 8)) {
|
|
200
|
-
throw new InvalidArgumentError('Form-Data bodies are only supported in node v16.8 and newer.')
|
|
201
|
-
}
|
|
202
|
-
|
|
203
199
|
if (!extractBody) {
|
|
204
200
|
extractBody = require('../fetch/body.js').extractBody
|
|
205
201
|
}
|
package/lib/core/util.js
CHANGED
|
@@ -9,6 +9,7 @@ const { InvalidArgumentError } = require('./errors')
|
|
|
9
9
|
const { Blob } = require('buffer')
|
|
10
10
|
const nodeUtil = require('util')
|
|
11
11
|
const { stringify } = require('querystring')
|
|
12
|
+
const { headerNameLowerCasedRecord } = require('./constants')
|
|
12
13
|
|
|
13
14
|
const [nodeMajor, nodeMinor] = process.versions.node.split('.').map(v => Number(v))
|
|
14
15
|
|
|
@@ -223,19 +224,21 @@ function parseHeaders (headers, obj = {}) {
|
|
|
223
224
|
if (!Array.isArray(headers)) return headers
|
|
224
225
|
|
|
225
226
|
for (let i = 0; i < headers.length; i += 2) {
|
|
226
|
-
const key = headers[i].toString()
|
|
227
|
-
|
|
227
|
+
const key = headers[i].toString()
|
|
228
|
+
const lowerCasedKey = headerNameLowerCasedRecord[key] ?? key.toLowerCase()
|
|
229
|
+
let val = obj[lowerCasedKey]
|
|
228
230
|
|
|
229
231
|
if (!val) {
|
|
230
|
-
|
|
231
|
-
|
|
232
|
+
const headersValue = headers[i + 1]
|
|
233
|
+
if (typeof headersValue === 'string') {
|
|
234
|
+
obj[lowerCasedKey] = headersValue
|
|
232
235
|
} else {
|
|
233
|
-
obj[
|
|
236
|
+
obj[lowerCasedKey] = Array.isArray(headersValue) ? headersValue.map(x => x.toString('utf8')) : headersValue.toString('utf8')
|
|
234
237
|
}
|
|
235
238
|
} else {
|
|
236
239
|
if (!Array.isArray(val)) {
|
|
237
240
|
val = [val]
|
|
238
|
-
obj[
|
|
241
|
+
obj[lowerCasedKey] = val
|
|
239
242
|
}
|
|
240
243
|
val.push(headers[i + 1].toString('utf8'))
|
|
241
244
|
}
|
|
@@ -359,21 +362,9 @@ function getSocketInfo (socket) {
|
|
|
359
362
|
}
|
|
360
363
|
}
|
|
361
364
|
|
|
362
|
-
|
|
363
|
-
for await (const chunk of iterable) {
|
|
364
|
-
yield Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
let ReadableStream
|
|
365
|
+
/** @type {globalThis['ReadableStream']} */
|
|
369
366
|
function ReadableStreamFrom (iterable) {
|
|
370
|
-
|
|
371
|
-
ReadableStream = require('stream/web').ReadableStream
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
if (ReadableStream.from) {
|
|
375
|
-
return ReadableStream.from(convertIterableToBuffer(iterable))
|
|
376
|
-
}
|
|
367
|
+
// We cannot use ReadableStream.from here because it does not return a byte stream.
|
|
377
368
|
|
|
378
369
|
let iterator
|
|
379
370
|
return new ReadableStream(
|
|
@@ -386,18 +377,21 @@ function ReadableStreamFrom (iterable) {
|
|
|
386
377
|
if (done) {
|
|
387
378
|
queueMicrotask(() => {
|
|
388
379
|
controller.close()
|
|
380
|
+
controller.byobRequest?.respond(0)
|
|
389
381
|
})
|
|
390
382
|
} else {
|
|
391
383
|
const buf = Buffer.isBuffer(value) ? value : Buffer.from(value)
|
|
392
|
-
|
|
384
|
+
if (buf.byteLength) {
|
|
385
|
+
controller.enqueue(new Uint8Array(buf))
|
|
386
|
+
}
|
|
393
387
|
}
|
|
394
388
|
return controller.desiredSize > 0
|
|
395
389
|
},
|
|
396
390
|
async cancel (reason) {
|
|
397
391
|
await iterator.return()
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
|
|
392
|
+
},
|
|
393
|
+
type: 'bytes'
|
|
394
|
+
}
|
|
401
395
|
)
|
|
402
396
|
}
|
|
403
397
|
|
package/lib/fetch/body.js
CHANGED
|
@@ -13,7 +13,6 @@ const {
|
|
|
13
13
|
const { FormData } = require('./formdata')
|
|
14
14
|
const { kState } = require('./symbols')
|
|
15
15
|
const { webidl } = require('./webidl')
|
|
16
|
-
const { DOMException, structuredClone } = require('./constants')
|
|
17
16
|
const { Blob, File: NativeFile } = require('buffer')
|
|
18
17
|
const { kBodyUsed } = require('../core/symbols')
|
|
19
18
|
const assert = require('assert')
|
|
@@ -22,8 +21,6 @@ const { isUint8Array, isArrayBuffer } = require('util/types')
|
|
|
22
21
|
const { File: UndiciFile } = require('./file')
|
|
23
22
|
const { parseMIMEType, serializeAMimeType } = require('./dataURL')
|
|
24
23
|
|
|
25
|
-
let ReadableStream = globalThis.ReadableStream
|
|
26
|
-
|
|
27
24
|
/** @type {globalThis['File']} */
|
|
28
25
|
const File = NativeFile ?? UndiciFile
|
|
29
26
|
const textEncoder = new TextEncoder()
|
|
@@ -31,10 +28,6 @@ const textDecoder = new TextDecoder()
|
|
|
31
28
|
|
|
32
29
|
// https://fetch.spec.whatwg.org/#concept-bodyinit-extract
|
|
33
30
|
function extractBody (object, keepalive = false) {
|
|
34
|
-
if (!ReadableStream) {
|
|
35
|
-
ReadableStream = require('stream/web').ReadableStream
|
|
36
|
-
}
|
|
37
|
-
|
|
38
31
|
// 1. Let stream be null.
|
|
39
32
|
let stream = null
|
|
40
33
|
|
|
@@ -47,16 +40,19 @@ function extractBody (object, keepalive = false) {
|
|
|
47
40
|
stream = object.stream()
|
|
48
41
|
} else {
|
|
49
42
|
// 4. Otherwise, set stream to a new ReadableStream object, and set
|
|
50
|
-
// up stream.
|
|
43
|
+
// up stream with byte reading support.
|
|
51
44
|
stream = new ReadableStream({
|
|
52
45
|
async pull (controller) {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
)
|
|
46
|
+
const buffer = typeof source === 'string' ? textEncoder.encode(source) : source
|
|
47
|
+
|
|
48
|
+
if (buffer.byteLength) {
|
|
49
|
+
controller.enqueue(buffer)
|
|
50
|
+
}
|
|
51
|
+
|
|
56
52
|
queueMicrotask(() => readableStreamClose(controller))
|
|
57
53
|
},
|
|
58
54
|
start () {},
|
|
59
|
-
type:
|
|
55
|
+
type: 'bytes'
|
|
60
56
|
})
|
|
61
57
|
}
|
|
62
58
|
|
|
@@ -223,13 +219,17 @@ function extractBody (object, keepalive = false) {
|
|
|
223
219
|
// When running action is done, close stream.
|
|
224
220
|
queueMicrotask(() => {
|
|
225
221
|
controller.close()
|
|
222
|
+
controller.byobRequest?.respond(0)
|
|
226
223
|
})
|
|
227
224
|
} else {
|
|
228
225
|
// Whenever one or more bytes are available and stream is not errored,
|
|
229
226
|
// enqueue a Uint8Array wrapping an ArrayBuffer containing the available
|
|
230
227
|
// bytes into stream.
|
|
231
228
|
if (!isErrored(stream)) {
|
|
232
|
-
|
|
229
|
+
const buffer = new Uint8Array(value)
|
|
230
|
+
if (buffer.byteLength) {
|
|
231
|
+
controller.enqueue(buffer)
|
|
232
|
+
}
|
|
233
233
|
}
|
|
234
234
|
}
|
|
235
235
|
return controller.desiredSize > 0
|
|
@@ -237,7 +237,7 @@ function extractBody (object, keepalive = false) {
|
|
|
237
237
|
async cancel (reason) {
|
|
238
238
|
await iterator.return()
|
|
239
239
|
},
|
|
240
|
-
type:
|
|
240
|
+
type: 'bytes'
|
|
241
241
|
})
|
|
242
242
|
}
|
|
243
243
|
|
|
@@ -251,11 +251,6 @@ function extractBody (object, keepalive = false) {
|
|
|
251
251
|
|
|
252
252
|
// https://fetch.spec.whatwg.org/#bodyinit-safely-extract
|
|
253
253
|
function safelyExtractBody (object, keepalive = false) {
|
|
254
|
-
if (!ReadableStream) {
|
|
255
|
-
// istanbul ignore next
|
|
256
|
-
ReadableStream = require('stream/web').ReadableStream
|
|
257
|
-
}
|
|
258
|
-
|
|
259
254
|
// To safely extract a body and a `Content-Type` value from
|
|
260
255
|
// a byte sequence or BodyInit object object, run these steps:
|
|
261
256
|
|
package/lib/fetch/constants.js
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const { MessageChannel, receiveMessageOnPort } = require('worker_threads')
|
|
4
|
-
|
|
5
3
|
const corsSafeListedMethods = ['GET', 'HEAD', 'POST']
|
|
6
4
|
const corsSafeListedMethodsSet = new Set(corsSafeListedMethods)
|
|
7
5
|
|
|
@@ -92,41 +90,7 @@ const subresource = [
|
|
|
92
90
|
]
|
|
93
91
|
const subresourceSet = new Set(subresource)
|
|
94
92
|
|
|
95
|
-
/** @type {globalThis['DOMException']} */
|
|
96
|
-
const DOMException = globalThis.DOMException ?? (() => {
|
|
97
|
-
// DOMException was only made a global in Node v17.0.0,
|
|
98
|
-
// but fetch supports >= v16.8.
|
|
99
|
-
try {
|
|
100
|
-
atob('~')
|
|
101
|
-
} catch (err) {
|
|
102
|
-
return Object.getPrototypeOf(err).constructor
|
|
103
|
-
}
|
|
104
|
-
})()
|
|
105
|
-
|
|
106
|
-
let channel
|
|
107
|
-
|
|
108
|
-
/** @type {globalThis['structuredClone']} */
|
|
109
|
-
const structuredClone =
|
|
110
|
-
globalThis.structuredClone ??
|
|
111
|
-
// https://github.com/nodejs/node/blob/b27ae24dcc4251bad726d9d84baf678d1f707fed/lib/internal/structured_clone.js
|
|
112
|
-
// structuredClone was added in v17.0.0, but fetch supports v16.8
|
|
113
|
-
function structuredClone (value, options = undefined) {
|
|
114
|
-
if (arguments.length === 0) {
|
|
115
|
-
throw new TypeError('missing argument')
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
if (!channel) {
|
|
119
|
-
channel = new MessageChannel()
|
|
120
|
-
}
|
|
121
|
-
channel.port1.unref()
|
|
122
|
-
channel.port2.unref()
|
|
123
|
-
channel.port1.postMessage(value, options?.transfer)
|
|
124
|
-
return receiveMessageOnPort(channel.port2).message
|
|
125
|
-
}
|
|
126
|
-
|
|
127
93
|
module.exports = {
|
|
128
|
-
DOMException,
|
|
129
|
-
structuredClone,
|
|
130
94
|
subresource,
|
|
131
95
|
forbiddenMethods,
|
|
132
96
|
requestBodyHeader,
|