undici 6.19.7 → 7.0.0-alpha.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 +5 -9
- package/docs/docs/api/Agent.md +0 -3
- package/docs/docs/api/Client.md +0 -2
- package/docs/docs/api/Dispatcher.md +204 -6
- package/docs/docs/api/EnvHttpProxyAgent.md +0 -1
- package/docs/docs/api/Fetch.md +1 -0
- package/docs/docs/api/Pool.md +0 -1
- package/docs/docs/api/RetryHandler.md +1 -1
- package/index.js +0 -4
- package/lib/api/api-connect.js +3 -1
- package/lib/api/api-pipeline.js +3 -4
- package/lib/api/api-request.js +29 -46
- package/lib/api/api-stream.js +36 -49
- package/lib/api/api-upgrade.js +5 -3
- package/lib/api/readable.js +71 -27
- package/lib/core/connect.js +39 -24
- package/lib/core/errors.js +17 -4
- package/lib/core/request.js +7 -5
- package/lib/core/symbols.js +0 -1
- package/lib/core/tree.js +6 -0
- package/lib/core/util.js +1 -11
- package/lib/dispatcher/agent.js +3 -17
- package/lib/dispatcher/balanced-pool.js +27 -11
- package/lib/dispatcher/client-h1.js +44 -39
- package/lib/dispatcher/client.js +3 -27
- package/lib/dispatcher/dispatcher-base.js +2 -34
- package/lib/dispatcher/dispatcher.js +3 -24
- package/lib/dispatcher/pool.js +3 -6
- package/lib/dispatcher/proxy-agent.js +3 -6
- package/lib/handler/decorator-handler.js +24 -0
- package/lib/handler/redirect-handler.js +9 -0
- package/lib/handler/retry-handler.js +22 -3
- package/lib/interceptor/dump.js +2 -2
- package/lib/interceptor/redirect.js +11 -14
- package/lib/interceptor/response-error.js +89 -0
- 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-client.js +2 -2
- package/lib/mock/mock-pool.js +2 -2
- package/lib/mock/mock-symbols.js +1 -0
- package/lib/util/timers.js +324 -44
- package/lib/web/cookies/index.js +15 -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 +31 -11
- package/lib/web/fetch/data-url.js +1 -1
- package/lib/web/fetch/formdata-parser.js +1 -2
- package/lib/web/fetch/formdata.js +28 -37
- package/lib/web/fetch/headers.js +1 -1
- package/lib/web/fetch/index.js +7 -8
- package/lib/web/fetch/request.js +11 -28
- package/lib/web/fetch/response.js +13 -41
- package/lib/web/fetch/symbols.js +0 -1
- package/lib/web/fetch/util.js +3 -12
- package/lib/web/fetch/webidl.js +73 -62
- package/lib/web/websocket/connection.js +26 -174
- package/lib/web/websocket/constants.js +1 -1
- package/lib/web/websocket/frame.js +45 -3
- package/lib/web/websocket/receiver.js +28 -26
- package/lib/web/websocket/sender.js +18 -13
- package/lib/web/websocket/util.js +20 -74
- package/lib/web/websocket/websocket.js +294 -70
- package/package.json +16 -29
- 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/eventsource.d.ts +0 -2
- 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 +10 -8
- package/types/mock-agent.d.ts +18 -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 -42
- 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 +14 -9
- 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 +22 -4
- package/types/websocket.d.ts +1 -3
- package/docs/docs/api/DispatchInterceptor.md +0 -60
- package/lib/interceptor/redirect-interceptor.js +0 -21
- package/lib/web/fetch/file.js +0 -126
- 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/web/fetch/body.js
CHANGED
|
@@ -3,8 +3,6 @@
|
|
|
3
3
|
const util = require('../../core/util')
|
|
4
4
|
const {
|
|
5
5
|
ReadableStreamFrom,
|
|
6
|
-
isBlobLike,
|
|
7
|
-
isReadableStreamLike,
|
|
8
6
|
readableStreamClose,
|
|
9
7
|
createDeferredPromise,
|
|
10
8
|
fullyReadBody,
|
|
@@ -16,12 +14,25 @@ const { kState } = require('./symbols')
|
|
|
16
14
|
const { webidl } = require('./webidl')
|
|
17
15
|
const { Blob } = require('node:buffer')
|
|
18
16
|
const assert = require('node:assert')
|
|
19
|
-
const { isErrored } = require('
|
|
17
|
+
const { isErrored, isDisturbed } = require('node:stream')
|
|
20
18
|
const { isArrayBuffer } = require('node:util/types')
|
|
21
19
|
const { serializeAMimeType } = require('./data-url')
|
|
22
20
|
const { multipartFormDataParser } = require('./formdata-parser')
|
|
23
21
|
|
|
24
22
|
const textEncoder = new TextEncoder()
|
|
23
|
+
function noop () {}
|
|
24
|
+
|
|
25
|
+
const hasFinalizationRegistry = globalThis.FinalizationRegistry && process.version.indexOf('v18') !== 0
|
|
26
|
+
let streamRegistry
|
|
27
|
+
|
|
28
|
+
if (hasFinalizationRegistry) {
|
|
29
|
+
streamRegistry = new FinalizationRegistry((weakRef) => {
|
|
30
|
+
const stream = weakRef.deref()
|
|
31
|
+
if (stream && !stream.locked && !isDisturbed(stream) && !isErrored(stream)) {
|
|
32
|
+
stream.cancel('Response object has been garbage collected').catch(noop)
|
|
33
|
+
}
|
|
34
|
+
})
|
|
35
|
+
}
|
|
25
36
|
|
|
26
37
|
// https://fetch.spec.whatwg.org/#concept-bodyinit-extract
|
|
27
38
|
function extractBody (object, keepalive = false) {
|
|
@@ -31,7 +42,7 @@ function extractBody (object, keepalive = false) {
|
|
|
31
42
|
// 2. If object is a ReadableStream object, then set stream to object.
|
|
32
43
|
if (object instanceof ReadableStream) {
|
|
33
44
|
stream = object
|
|
34
|
-
} else if (
|
|
45
|
+
} else if (object instanceof Blob) {
|
|
35
46
|
// 3. Otherwise, if object is a Blob object, set stream to the
|
|
36
47
|
// result of running object’s get stream.
|
|
37
48
|
stream = object.stream()
|
|
@@ -54,7 +65,7 @@ function extractBody (object, keepalive = false) {
|
|
|
54
65
|
}
|
|
55
66
|
|
|
56
67
|
// 5. Assert: stream is a ReadableStream object.
|
|
57
|
-
assert(
|
|
68
|
+
assert(stream instanceof ReadableStream)
|
|
58
69
|
|
|
59
70
|
// 6. Let action be null.
|
|
60
71
|
let action = null
|
|
@@ -99,7 +110,7 @@ function extractBody (object, keepalive = false) {
|
|
|
99
110
|
|
|
100
111
|
// Set source to a copy of the bytes held by object.
|
|
101
112
|
source = new Uint8Array(object.buffer.slice(object.byteOffset, object.byteOffset + object.byteLength))
|
|
102
|
-
} else if (
|
|
113
|
+
} else if (object instanceof FormData) {
|
|
103
114
|
const boundary = `----formdata-undici-0${`${Math.floor(Math.random() * 1e11)}`.padStart(11, '0')}`
|
|
104
115
|
const prefix = `--${boundary}\r\nContent-Disposition: form-data`
|
|
105
116
|
|
|
@@ -165,7 +176,7 @@ function extractBody (object, keepalive = false) {
|
|
|
165
176
|
// followed by the multipart/form-data boundary string generated
|
|
166
177
|
// by the multipart/form-data encoding algorithm.
|
|
167
178
|
type = `multipart/form-data; boundary=${boundary}`
|
|
168
|
-
} else if (
|
|
179
|
+
} else if (object instanceof Blob) {
|
|
169
180
|
// Blob
|
|
170
181
|
|
|
171
182
|
// Set source to object.
|
|
@@ -264,7 +275,7 @@ function safelyExtractBody (object, keepalive = false) {
|
|
|
264
275
|
return extractBody(object, keepalive)
|
|
265
276
|
}
|
|
266
277
|
|
|
267
|
-
function cloneBody (body) {
|
|
278
|
+
function cloneBody (instance, body) {
|
|
268
279
|
// To clone a body body, run these steps:
|
|
269
280
|
|
|
270
281
|
// https://fetch.spec.whatwg.org/#concept-body-clone
|
|
@@ -272,6 +283,10 @@ function cloneBody (body) {
|
|
|
272
283
|
// 1. Let « out1, out2 » be the result of teeing body’s stream.
|
|
273
284
|
const [out1, out2] = body.stream.tee()
|
|
274
285
|
|
|
286
|
+
if (hasFinalizationRegistry) {
|
|
287
|
+
streamRegistry.register(instance, new WeakRef(out1))
|
|
288
|
+
}
|
|
289
|
+
|
|
275
290
|
// 2. Set body’s stream to out1.
|
|
276
291
|
body.stream = out1
|
|
277
292
|
|
|
@@ -414,7 +429,7 @@ async function consumeBody (object, convertBytesToJSValue, instance) {
|
|
|
414
429
|
|
|
415
430
|
// 1. If object is unusable, then return a promise rejected
|
|
416
431
|
// with a TypeError.
|
|
417
|
-
if (bodyUnusable(object
|
|
432
|
+
if (bodyUnusable(object)) {
|
|
418
433
|
throw new TypeError('Body is unusable: Body has already been read')
|
|
419
434
|
}
|
|
420
435
|
|
|
@@ -454,7 +469,9 @@ async function consumeBody (object, convertBytesToJSValue, instance) {
|
|
|
454
469
|
}
|
|
455
470
|
|
|
456
471
|
// https://fetch.spec.whatwg.org/#body-unusable
|
|
457
|
-
function bodyUnusable (
|
|
472
|
+
function bodyUnusable (object) {
|
|
473
|
+
const body = object[kState].body
|
|
474
|
+
|
|
458
475
|
// An object including the Body interface mixin is
|
|
459
476
|
// said to be unusable if its body is non-null and
|
|
460
477
|
// its body’s stream is disturbed or locked.
|
|
@@ -496,5 +513,8 @@ module.exports = {
|
|
|
496
513
|
extractBody,
|
|
497
514
|
safelyExtractBody,
|
|
498
515
|
cloneBody,
|
|
499
|
-
mixinBody
|
|
516
|
+
mixinBody,
|
|
517
|
+
streamRegistry,
|
|
518
|
+
hasFinalizationRegistry,
|
|
519
|
+
bodyUnusable
|
|
500
520
|
}
|
|
@@ -431,7 +431,7 @@ function parseMIMEType (input) {
|
|
|
431
431
|
/** @param {string} data */
|
|
432
432
|
function forgivingBase64 (data) {
|
|
433
433
|
// 1. Remove all ASCII whitespace from data.
|
|
434
|
-
data = data.replace(ASCII_WHITESPACE_REPLACE_REGEX, '')
|
|
434
|
+
data = data.replace(ASCII_WHITESPACE_REPLACE_REGEX, '')
|
|
435
435
|
|
|
436
436
|
let dataLength = data.length
|
|
437
437
|
// 2. If data’s code point length divides by 4 leaving
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
const { isUSVString, bufferToLowerCasedHeaderName } = require('../../core/util')
|
|
4
4
|
const { utf8DecodeBytes } = require('./util')
|
|
5
5
|
const { HTTP_TOKEN_CODEPOINTS, isomorphicDecode } = require('./data-url')
|
|
6
|
-
const { isFileLike } = require('./file')
|
|
7
6
|
const { makeEntry } = require('./formdata')
|
|
8
7
|
const assert = require('node:assert')
|
|
9
8
|
const { File: NodeFile } = require('node:buffer')
|
|
@@ -195,7 +194,7 @@ function multipartFormDataParser (input, mimeType) {
|
|
|
195
194
|
|
|
196
195
|
// 5.12. Assert: name is a scalar value string and value is either a scalar value string or a File object.
|
|
197
196
|
assert(isUSVString(name))
|
|
198
|
-
assert((typeof value === 'string' && isUSVString(value)) ||
|
|
197
|
+
assert((typeof value === 'string' && isUSVString(value)) || value instanceof File)
|
|
199
198
|
|
|
200
199
|
// 5.13. Create an entry with name and value, and append it to entry list.
|
|
201
200
|
entryList.push(makeEntry(name, value, filename))
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const {
|
|
3
|
+
const { iteratorMixin } = require('./util')
|
|
4
4
|
const { kState } = require('./symbols')
|
|
5
5
|
const { kEnumerableProperty } = require('../../core/util')
|
|
6
|
-
const { FileLike, isFileLike } = require('./file')
|
|
7
6
|
const { webidl } = require('./webidl')
|
|
8
7
|
const { File: NativeFile } = require('node:buffer')
|
|
9
8
|
const nodeUtil = require('node:util')
|
|
@@ -31,22 +30,20 @@ class FormData {
|
|
|
31
30
|
const prefix = 'FormData.append'
|
|
32
31
|
webidl.argumentLengthCheck(arguments, 2, prefix)
|
|
33
32
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
)
|
|
33
|
+
name = webidl.converters.USVString(name)
|
|
34
|
+
|
|
35
|
+
if (arguments.length === 3 || value instanceof Blob) {
|
|
36
|
+
value = webidl.converters.Blob(value, prefix, 'value')
|
|
37
|
+
|
|
38
|
+
if (filename !== undefined) {
|
|
39
|
+
filename = webidl.converters.USVString(filename)
|
|
40
|
+
}
|
|
41
|
+
} else {
|
|
42
|
+
value = webidl.converters.USVString(value)
|
|
38
43
|
}
|
|
39
44
|
|
|
40
45
|
// 1. Let value be value if given; otherwise blobValue.
|
|
41
46
|
|
|
42
|
-
name = webidl.converters.USVString(name, prefix, 'name')
|
|
43
|
-
value = isBlobLike(value)
|
|
44
|
-
? webidl.converters.Blob(value, prefix, 'value', { strict: false })
|
|
45
|
-
: webidl.converters.USVString(value, prefix, 'value')
|
|
46
|
-
filename = arguments.length === 3
|
|
47
|
-
? webidl.converters.USVString(filename, prefix, 'filename')
|
|
48
|
-
: undefined
|
|
49
|
-
|
|
50
47
|
// 2. Let entry be the result of creating an entry with
|
|
51
48
|
// name, value, and filename if given.
|
|
52
49
|
const entry = makeEntry(name, value, filename)
|
|
@@ -61,7 +58,7 @@ class FormData {
|
|
|
61
58
|
const prefix = 'FormData.delete'
|
|
62
59
|
webidl.argumentLengthCheck(arguments, 1, prefix)
|
|
63
60
|
|
|
64
|
-
name = webidl.converters.USVString(name
|
|
61
|
+
name = webidl.converters.USVString(name)
|
|
65
62
|
|
|
66
63
|
// The delete(name) method steps are to remove all entries whose name
|
|
67
64
|
// is name from this’s entry list.
|
|
@@ -74,7 +71,7 @@ class FormData {
|
|
|
74
71
|
const prefix = 'FormData.get'
|
|
75
72
|
webidl.argumentLengthCheck(arguments, 1, prefix)
|
|
76
73
|
|
|
77
|
-
name = webidl.converters.USVString(name
|
|
74
|
+
name = webidl.converters.USVString(name)
|
|
78
75
|
|
|
79
76
|
// 1. If there is no entry whose name is name in this’s entry list,
|
|
80
77
|
// then return null.
|
|
@@ -94,7 +91,7 @@ class FormData {
|
|
|
94
91
|
const prefix = 'FormData.getAll'
|
|
95
92
|
webidl.argumentLengthCheck(arguments, 1, prefix)
|
|
96
93
|
|
|
97
|
-
name = webidl.converters.USVString(name
|
|
94
|
+
name = webidl.converters.USVString(name)
|
|
98
95
|
|
|
99
96
|
// 1. If there is no entry whose name is name in this’s entry list,
|
|
100
97
|
// then return the empty list.
|
|
@@ -111,7 +108,7 @@ class FormData {
|
|
|
111
108
|
const prefix = 'FormData.has'
|
|
112
109
|
webidl.argumentLengthCheck(arguments, 1, prefix)
|
|
113
110
|
|
|
114
|
-
name = webidl.converters.USVString(name
|
|
111
|
+
name = webidl.converters.USVString(name)
|
|
115
112
|
|
|
116
113
|
// The has(name) method steps are to return true if there is an entry
|
|
117
114
|
// whose name is name in this’s entry list; otherwise false.
|
|
@@ -124,10 +121,16 @@ class FormData {
|
|
|
124
121
|
const prefix = 'FormData.set'
|
|
125
122
|
webidl.argumentLengthCheck(arguments, 2, prefix)
|
|
126
123
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
)
|
|
124
|
+
name = webidl.converters.USVString(name)
|
|
125
|
+
|
|
126
|
+
if (arguments.length === 3 || value instanceof Blob) {
|
|
127
|
+
value = webidl.converters.Blob(value, prefix, 'value')
|
|
128
|
+
|
|
129
|
+
if (filename !== undefined) {
|
|
130
|
+
filename = webidl.converters.USVString(filename)
|
|
131
|
+
}
|
|
132
|
+
} else {
|
|
133
|
+
value = webidl.converters.USVString(value)
|
|
131
134
|
}
|
|
132
135
|
|
|
133
136
|
// The set(name, value) and set(name, blobValue, filename) method steps
|
|
@@ -135,14 +138,6 @@ class FormData {
|
|
|
135
138
|
|
|
136
139
|
// 1. Let value be value if given; otherwise blobValue.
|
|
137
140
|
|
|
138
|
-
name = webidl.converters.USVString(name, prefix, 'name')
|
|
139
|
-
value = isBlobLike(value)
|
|
140
|
-
? webidl.converters.Blob(value, prefix, 'name', { strict: false })
|
|
141
|
-
: webidl.converters.USVString(value, prefix, 'name')
|
|
142
|
-
filename = arguments.length === 3
|
|
143
|
-
? webidl.converters.USVString(filename, prefix, 'name')
|
|
144
|
-
: undefined
|
|
145
|
-
|
|
146
141
|
// 2. Let entry be the result of creating an entry with name, value, and
|
|
147
142
|
// filename if given.
|
|
148
143
|
const entry = makeEntry(name, value, filename)
|
|
@@ -222,10 +217,8 @@ function makeEntry (name, value, filename) {
|
|
|
222
217
|
|
|
223
218
|
// 1. If value is not a File object, then set value to a new File object,
|
|
224
219
|
// representing the same bytes, whose name attribute value is "blob"
|
|
225
|
-
if (!
|
|
226
|
-
value = value
|
|
227
|
-
? new File([value], 'blob', { type: value.type })
|
|
228
|
-
: new FileLike(value, 'blob', { type: value.type })
|
|
220
|
+
if (!(value instanceof File)) {
|
|
221
|
+
value = new File([value], 'blob', { type: value.type })
|
|
229
222
|
}
|
|
230
223
|
|
|
231
224
|
// 2. If filename is given, then set value to a new File object,
|
|
@@ -237,9 +230,7 @@ function makeEntry (name, value, filename) {
|
|
|
237
230
|
lastModified: value.lastModified
|
|
238
231
|
}
|
|
239
232
|
|
|
240
|
-
value = value
|
|
241
|
-
? new File([value], filename, options)
|
|
242
|
-
: new FileLike(value, filename, options)
|
|
233
|
+
value = new File([value], filename, options)
|
|
243
234
|
}
|
|
244
235
|
}
|
|
245
236
|
|
package/lib/web/fetch/headers.js
CHANGED
|
@@ -645,7 +645,7 @@ Object.defineProperties(Headers.prototype, {
|
|
|
645
645
|
})
|
|
646
646
|
|
|
647
647
|
webidl.converters.HeadersInit = function (V, prefix, argument) {
|
|
648
|
-
if (webidl.util.Type(V) ===
|
|
648
|
+
if (webidl.util.Type(V) === webidl.util.Types.OBJECT) {
|
|
649
649
|
const iterator = Reflect.get(V, Symbol.iterator)
|
|
650
650
|
|
|
651
651
|
// A work-around to ensure we send the properly-cased Headers when V is a Headers object.
|
package/lib/web/fetch/index.js
CHANGED
|
@@ -30,7 +30,6 @@ const {
|
|
|
30
30
|
determineRequestsReferrer,
|
|
31
31
|
coarsenedSharedCurrentTime,
|
|
32
32
|
createDeferredPromise,
|
|
33
|
-
isBlobLike,
|
|
34
33
|
sameOrigin,
|
|
35
34
|
isCancelled,
|
|
36
35
|
isAborted,
|
|
@@ -58,8 +57,8 @@ const {
|
|
|
58
57
|
subresourceSet
|
|
59
58
|
} = require('./constants')
|
|
60
59
|
const EE = require('node:events')
|
|
61
|
-
const { Readable, pipeline, finished } = require('node:stream')
|
|
62
|
-
const { addAbortListener,
|
|
60
|
+
const { Readable, pipeline, finished, isErrored, isReadable } = require('node:stream')
|
|
61
|
+
const { addAbortListener, bufferToLowerCasedHeaderName } = require('../../core/util')
|
|
63
62
|
const { dataURLProcessor, serializeAMimeType, minimizeSupportedMimeType } = require('./data-url')
|
|
64
63
|
const { getGlobalDispatcher } = require('../../global')
|
|
65
64
|
const { webidl } = require('./webidl')
|
|
@@ -327,7 +326,7 @@ function abortFetch (p, request, responseObject, error) {
|
|
|
327
326
|
|
|
328
327
|
// 2. If request’s body is not null and is readable, then cancel request’s
|
|
329
328
|
// body with error.
|
|
330
|
-
if (request.body != null && isReadable(request.body
|
|
329
|
+
if (request.body?.stream != null && isReadable(request.body.stream)) {
|
|
331
330
|
request.body.stream.cancel(error).catch((err) => {
|
|
332
331
|
if (err.code === 'ERR_INVALID_STATE') {
|
|
333
332
|
// Node bug?
|
|
@@ -347,7 +346,7 @@ function abortFetch (p, request, responseObject, error) {
|
|
|
347
346
|
|
|
348
347
|
// 5. If response’s body is not null and is readable, then error response’s
|
|
349
348
|
// body with error.
|
|
350
|
-
if (response.body != null && isReadable(response.body
|
|
349
|
+
if (response.body?.stream != null && isReadable(response.body.stream)) {
|
|
351
350
|
response.body.stream.cancel(error).catch((err) => {
|
|
352
351
|
if (err.code === 'ERR_INVALID_STATE') {
|
|
353
352
|
// Node bug?
|
|
@@ -810,7 +809,7 @@ function schemeFetch (fetchParams) {
|
|
|
810
809
|
|
|
811
810
|
// 2. If request’s method is not `GET`, blobURLEntry is null, or blobURLEntry’s
|
|
812
811
|
// object is not a Blob object, then return a network error.
|
|
813
|
-
if (request.method !== 'GET' || !
|
|
812
|
+
if (request.method !== 'GET' || !(blob instanceof Blob)) {
|
|
814
813
|
return Promise.resolve(makeNetworkError('invalid method'))
|
|
815
814
|
}
|
|
816
815
|
|
|
@@ -1460,7 +1459,7 @@ async function httpNetworkOrCacheFetch (
|
|
|
1460
1459
|
// user agents should append `User-Agent`/default `User-Agent` value to
|
|
1461
1460
|
// httpRequest’s header list.
|
|
1462
1461
|
if (!httpRequest.headersList.contains('user-agent', true)) {
|
|
1463
|
-
httpRequest.headersList.append('user-agent', defaultUserAgent)
|
|
1462
|
+
httpRequest.headersList.append('user-agent', defaultUserAgent, true)
|
|
1464
1463
|
}
|
|
1465
1464
|
|
|
1466
1465
|
// 15. If httpRequest’s cache mode is "default" and httpRequest’s header
|
|
@@ -2137,7 +2136,7 @@ async function httpNetworkFetch (
|
|
|
2137
2136
|
|
|
2138
2137
|
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding
|
|
2139
2138
|
if (codings.length !== 0 && request.method !== 'HEAD' && request.method !== 'CONNECT' && !nullBodyStatus.includes(status) && !willFollow) {
|
|
2140
|
-
for (let i =
|
|
2139
|
+
for (let i = codings.length - 1; i >= 0; --i) {
|
|
2141
2140
|
const coding = codings[i]
|
|
2142
2141
|
// https://www.rfc-editor.org/rfc/rfc9112.html#section-7.2
|
|
2143
2142
|
if (coding === 'x-gzip' || coding === 'gzip') {
|
package/lib/web/fetch/request.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
'use strict'
|
|
4
4
|
|
|
5
|
-
const { extractBody, mixinBody, cloneBody } = require('./body')
|
|
5
|
+
const { extractBody, mixinBody, cloneBody, bodyUnusable } = require('./body')
|
|
6
6
|
const { Headers, fill: fillHeaders, HeadersList, setHeadersGuard, getHeadersGuard, setHeadersList, getHeadersList } = require('./headers')
|
|
7
7
|
const { FinalizationRegistry } = require('./dispatcher-weakref')()
|
|
8
8
|
const util = require('../../core/util')
|
|
@@ -28,7 +28,7 @@ const { webidl } = require('./webidl')
|
|
|
28
28
|
const { URLSerializer } = require('./data-url')
|
|
29
29
|
const { kConstruct } = require('../../core/symbols')
|
|
30
30
|
const assert = require('node:assert')
|
|
31
|
-
const { getMaxListeners, setMaxListeners,
|
|
31
|
+
const { getMaxListeners, setMaxListeners, defaultMaxListeners } = require('node:events')
|
|
32
32
|
|
|
33
33
|
const kAbortController = Symbol('abortController')
|
|
34
34
|
|
|
@@ -81,7 +81,7 @@ let patchMethodWarning = false
|
|
|
81
81
|
// https://fetch.spec.whatwg.org/#request-class
|
|
82
82
|
class Request {
|
|
83
83
|
// https://fetch.spec.whatwg.org/#dom-request
|
|
84
|
-
constructor (input, init =
|
|
84
|
+
constructor (input, init = undefined) {
|
|
85
85
|
if (input === kConstruct) {
|
|
86
86
|
return
|
|
87
87
|
}
|
|
@@ -400,16 +400,6 @@ class Request {
|
|
|
400
400
|
|
|
401
401
|
// 29. If signal is not null, then make this’s signal follow signal.
|
|
402
402
|
if (signal != null) {
|
|
403
|
-
if (
|
|
404
|
-
!signal ||
|
|
405
|
-
typeof signal.aborted !== 'boolean' ||
|
|
406
|
-
typeof signal.addEventListener !== 'function'
|
|
407
|
-
) {
|
|
408
|
-
throw new TypeError(
|
|
409
|
-
"Failed to construct 'Request': member signal is not of type AbortSignal."
|
|
410
|
-
)
|
|
411
|
-
}
|
|
412
|
-
|
|
413
403
|
if (signal.aborted) {
|
|
414
404
|
ac.abort(signal.reason)
|
|
415
405
|
} else {
|
|
@@ -429,8 +419,6 @@ class Request {
|
|
|
429
419
|
// This is only available in node >= v19.9.0
|
|
430
420
|
if (typeof getMaxListeners === 'function' && getMaxListeners(signal) === defaultMaxListeners) {
|
|
431
421
|
setMaxListeners(1500, signal)
|
|
432
|
-
} else if (getEventListeners(signal, 'abort').length >= defaultMaxListeners) {
|
|
433
|
-
setMaxListeners(1500, signal)
|
|
434
422
|
}
|
|
435
423
|
} catch {}
|
|
436
424
|
|
|
@@ -522,7 +510,7 @@ class Request {
|
|
|
522
510
|
// not contain `Content-Type`, then append `Content-Type`/Content-Type to
|
|
523
511
|
// this’s headers.
|
|
524
512
|
if (contentType && !getHeadersList(this[kHeaders]).contains('content-type', true)) {
|
|
525
|
-
this[kHeaders].append('content-type', contentType)
|
|
513
|
+
this[kHeaders].append('content-type', contentType, true)
|
|
526
514
|
}
|
|
527
515
|
}
|
|
528
516
|
|
|
@@ -557,7 +545,7 @@ class Request {
|
|
|
557
545
|
// 40. If initBody is null and inputBody is non-null, then:
|
|
558
546
|
if (initBody == null && inputBody != null) {
|
|
559
547
|
// 1. If input is unusable, then throw a TypeError.
|
|
560
|
-
if (
|
|
548
|
+
if (bodyUnusable(input)) {
|
|
561
549
|
throw new TypeError(
|
|
562
550
|
'Cannot construct a Request with a Request object that has already been used.'
|
|
563
551
|
)
|
|
@@ -759,7 +747,7 @@ class Request {
|
|
|
759
747
|
webidl.brandCheck(this, Request)
|
|
760
748
|
|
|
761
749
|
// 1. If this is unusable, then throw a TypeError.
|
|
762
|
-
if (this
|
|
750
|
+
if (bodyUnusable(this)) {
|
|
763
751
|
throw new TypeError('unusable')
|
|
764
752
|
}
|
|
765
753
|
|
|
@@ -877,7 +865,7 @@ function cloneRequest (request) {
|
|
|
877
865
|
// 2. If request’s body is non-null, set newRequest’s body to the
|
|
878
866
|
// result of cloning request’s body.
|
|
879
867
|
if (request.body != null) {
|
|
880
|
-
newRequest.body = cloneBody(request.body)
|
|
868
|
+
newRequest.body = cloneBody(newRequest, request.body)
|
|
881
869
|
}
|
|
882
870
|
|
|
883
871
|
// 3. Return newRequest.
|
|
@@ -928,21 +916,17 @@ Object.defineProperties(Request.prototype, {
|
|
|
928
916
|
}
|
|
929
917
|
})
|
|
930
918
|
|
|
931
|
-
webidl.converters.Request = webidl.interfaceConverter(
|
|
932
|
-
Request
|
|
933
|
-
)
|
|
934
|
-
|
|
935
919
|
// https://fetch.spec.whatwg.org/#requestinfo
|
|
936
920
|
webidl.converters.RequestInfo = function (V, prefix, argument) {
|
|
937
921
|
if (typeof V === 'string') {
|
|
938
|
-
return webidl.converters.USVString(V
|
|
922
|
+
return webidl.converters.USVString(V)
|
|
939
923
|
}
|
|
940
924
|
|
|
941
925
|
if (V instanceof Request) {
|
|
942
|
-
return
|
|
926
|
+
return V
|
|
943
927
|
}
|
|
944
928
|
|
|
945
|
-
return webidl.converters.USVString(V
|
|
929
|
+
return webidl.converters.USVString(V)
|
|
946
930
|
}
|
|
947
931
|
|
|
948
932
|
webidl.converters.AbortSignal = webidl.interfaceConverter(
|
|
@@ -1013,8 +997,7 @@ webidl.converters.RequestInit = webidl.dictionaryConverter([
|
|
|
1013
997
|
(signal) => webidl.converters.AbortSignal(
|
|
1014
998
|
signal,
|
|
1015
999
|
'RequestInit',
|
|
1016
|
-
'signal'
|
|
1017
|
-
{ strict: false }
|
|
1000
|
+
'signal'
|
|
1018
1001
|
)
|
|
1019
1002
|
)
|
|
1020
1003
|
},
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const { Headers, HeadersList, fill, getHeadersGuard, setHeadersGuard, setHeadersList } = require('./headers')
|
|
4
|
-
const { extractBody, cloneBody, mixinBody } = require('./body')
|
|
4
|
+
const { extractBody, cloneBody, mixinBody, hasFinalizationRegistry, streamRegistry, bodyUnusable } = require('./body')
|
|
5
5
|
const util = require('../../core/util')
|
|
6
6
|
const nodeUtil = require('node:util')
|
|
7
7
|
const { kEnumerableProperty } = util
|
|
@@ -9,7 +9,6 @@ const {
|
|
|
9
9
|
isValidReasonPhrase,
|
|
10
10
|
isCancelled,
|
|
11
11
|
isAborted,
|
|
12
|
-
isBlobLike,
|
|
13
12
|
serializeJavascriptValueToJSONString,
|
|
14
13
|
isErrorLike,
|
|
15
14
|
isomorphicEncode,
|
|
@@ -26,24 +25,9 @@ const { URLSerializer } = require('./data-url')
|
|
|
26
25
|
const { kConstruct } = require('../../core/symbols')
|
|
27
26
|
const assert = require('node:assert')
|
|
28
27
|
const { types } = require('node:util')
|
|
29
|
-
const { isDisturbed, isErrored } = require('node:stream')
|
|
30
28
|
|
|
31
29
|
const textEncoder = new TextEncoder('utf-8')
|
|
32
30
|
|
|
33
|
-
const hasFinalizationRegistry = globalThis.FinalizationRegistry && process.version.indexOf('v18') !== 0
|
|
34
|
-
let registry
|
|
35
|
-
|
|
36
|
-
if (hasFinalizationRegistry) {
|
|
37
|
-
registry = new FinalizationRegistry((weakRef) => {
|
|
38
|
-
const stream = weakRef.deref()
|
|
39
|
-
if (stream && !stream.locked && !isDisturbed(stream) && !isErrored(stream)) {
|
|
40
|
-
stream.cancel('Response object has been garbage collected').catch(noop)
|
|
41
|
-
}
|
|
42
|
-
})
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function noop () {}
|
|
46
|
-
|
|
47
31
|
// https://fetch.spec.whatwg.org/#response-class
|
|
48
32
|
class Response {
|
|
49
33
|
// Creates network error Response.
|
|
@@ -57,7 +41,7 @@ class Response {
|
|
|
57
41
|
}
|
|
58
42
|
|
|
59
43
|
// https://fetch.spec.whatwg.org/#dom-response-json
|
|
60
|
-
static json (data, init =
|
|
44
|
+
static json (data, init = undefined) {
|
|
61
45
|
webidl.argumentLengthCheck(arguments, 1, 'Response.json')
|
|
62
46
|
|
|
63
47
|
if (init !== null) {
|
|
@@ -124,7 +108,7 @@ class Response {
|
|
|
124
108
|
}
|
|
125
109
|
|
|
126
110
|
// https://fetch.spec.whatwg.org/#dom-response
|
|
127
|
-
constructor (body = null, init =
|
|
111
|
+
constructor (body = null, init = undefined) {
|
|
128
112
|
if (body === kConstruct) {
|
|
129
113
|
return
|
|
130
114
|
}
|
|
@@ -244,7 +228,7 @@ class Response {
|
|
|
244
228
|
webidl.brandCheck(this, Response)
|
|
245
229
|
|
|
246
230
|
// 1. If this is unusable, then throw a TypeError.
|
|
247
|
-
if (this
|
|
231
|
+
if (bodyUnusable(this)) {
|
|
248
232
|
throw webidl.errors.exception({
|
|
249
233
|
header: 'Response.clone',
|
|
250
234
|
message: 'Body has already been consumed.'
|
|
@@ -327,7 +311,7 @@ function cloneResponse (response) {
|
|
|
327
311
|
// 3. If response’s body is non-null, then set newResponse’s body to the
|
|
328
312
|
// result of cloning response’s body.
|
|
329
313
|
if (response.body != null) {
|
|
330
|
-
newResponse.body = cloneBody(response.body)
|
|
314
|
+
newResponse.body = cloneBody(newResponse, response.body)
|
|
331
315
|
}
|
|
332
316
|
|
|
333
317
|
// 4. Return newResponse.
|
|
@@ -532,44 +516,32 @@ function fromInnerResponse (innerResponse, guard) {
|
|
|
532
516
|
// a primitive or an object, even undefined. If the held value is an object, the registry keeps
|
|
533
517
|
// a strong reference to it (so it can pass it to the cleanup callback later). Reworded from
|
|
534
518
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/FinalizationRegistry
|
|
535
|
-
|
|
519
|
+
streamRegistry.register(response, new WeakRef(innerResponse.body.stream))
|
|
536
520
|
}
|
|
537
521
|
|
|
538
522
|
return response
|
|
539
523
|
}
|
|
540
524
|
|
|
541
|
-
webidl.converters.ReadableStream = webidl.interfaceConverter(
|
|
542
|
-
ReadableStream
|
|
543
|
-
)
|
|
544
|
-
|
|
545
|
-
webidl.converters.FormData = webidl.interfaceConverter(
|
|
546
|
-
FormData
|
|
547
|
-
)
|
|
548
|
-
|
|
549
|
-
webidl.converters.URLSearchParams = webidl.interfaceConverter(
|
|
550
|
-
URLSearchParams
|
|
551
|
-
)
|
|
552
|
-
|
|
553
525
|
// https://fetch.spec.whatwg.org/#typedefdef-xmlhttprequestbodyinit
|
|
554
526
|
webidl.converters.XMLHttpRequestBodyInit = function (V, prefix, name) {
|
|
555
527
|
if (typeof V === 'string') {
|
|
556
528
|
return webidl.converters.USVString(V, prefix, name)
|
|
557
529
|
}
|
|
558
530
|
|
|
559
|
-
if (
|
|
560
|
-
return
|
|
531
|
+
if (V instanceof Blob) {
|
|
532
|
+
return V
|
|
561
533
|
}
|
|
562
534
|
|
|
563
535
|
if (ArrayBuffer.isView(V) || types.isArrayBuffer(V)) {
|
|
564
|
-
return
|
|
536
|
+
return V
|
|
565
537
|
}
|
|
566
538
|
|
|
567
|
-
if (
|
|
568
|
-
return
|
|
539
|
+
if (V instanceof FormData) {
|
|
540
|
+
return V
|
|
569
541
|
}
|
|
570
542
|
|
|
571
543
|
if (V instanceof URLSearchParams) {
|
|
572
|
-
return
|
|
544
|
+
return V
|
|
573
545
|
}
|
|
574
546
|
|
|
575
547
|
return webidl.converters.DOMString(V, prefix, name)
|
|
@@ -578,7 +550,7 @@ webidl.converters.XMLHttpRequestBodyInit = function (V, prefix, name) {
|
|
|
578
550
|
// https://fetch.spec.whatwg.org/#bodyinit
|
|
579
551
|
webidl.converters.BodyInit = function (V, prefix, argument) {
|
|
580
552
|
if (V instanceof ReadableStream) {
|
|
581
|
-
return
|
|
553
|
+
return V
|
|
582
554
|
}
|
|
583
555
|
|
|
584
556
|
// Note: the spec doesn't include async iterables,
|
package/lib/web/fetch/symbols.js
CHANGED
package/lib/web/fetch/util.js
CHANGED
|
@@ -6,7 +6,7 @@ const { redirectStatusSet, referrerPolicySet: referrerPolicyTokens, badPortsSet
|
|
|
6
6
|
const { getGlobalOrigin } = require('./global')
|
|
7
7
|
const { collectASequenceOfCodePoints, collectAnHTTPQuotedString, removeChars, parseMIMEType } = require('./data-url')
|
|
8
8
|
const { performance } = require('node:perf_hooks')
|
|
9
|
-
const {
|
|
9
|
+
const { ReadableStreamFrom, isValidHTTPToken, normalizedMethodRecordsBase } = require('../../core/util')
|
|
10
10
|
const assert = require('node:assert')
|
|
11
11
|
const { isUint8Array } = require('node:util/types')
|
|
12
12
|
const { webidl } = require('./webidl')
|
|
@@ -449,7 +449,7 @@ function determineRequestsReferrer (request) {
|
|
|
449
449
|
// 3. Return referrerOrigin.
|
|
450
450
|
return referrerOrigin
|
|
451
451
|
}
|
|
452
|
-
case 'strict-origin':
|
|
452
|
+
case 'strict-origin':
|
|
453
453
|
/**
|
|
454
454
|
* 1. If referrerURL is a potentially trustworthy URL and
|
|
455
455
|
* request’s current URL is not a potentially trustworthy URL,
|
|
@@ -472,7 +472,7 @@ function determineRequestsReferrer (request) {
|
|
|
472
472
|
/**
|
|
473
473
|
* @see https://w3c.github.io/webappsec-referrer-policy/#strip-url
|
|
474
474
|
* @param {URL} url
|
|
475
|
-
* @param {boolean
|
|
475
|
+
* @param {boolean} [originOnly]
|
|
476
476
|
*/
|
|
477
477
|
function stripURLForReferrer (url, originOnly) {
|
|
478
478
|
// 1. Assert: url is a URL.
|
|
@@ -1061,13 +1061,6 @@ async function fullyReadBody (body, processBody, processBodyError) {
|
|
|
1061
1061
|
}
|
|
1062
1062
|
}
|
|
1063
1063
|
|
|
1064
|
-
function isReadableStreamLike (stream) {
|
|
1065
|
-
return stream instanceof ReadableStream || (
|
|
1066
|
-
stream[Symbol.toStringTag] === 'ReadableStream' &&
|
|
1067
|
-
typeof stream.tee === 'function'
|
|
1068
|
-
)
|
|
1069
|
-
}
|
|
1070
|
-
|
|
1071
1064
|
/**
|
|
1072
1065
|
* @param {ReadableStreamController<Uint8Array>} controller
|
|
1073
1066
|
*/
|
|
@@ -1589,7 +1582,6 @@ module.exports = {
|
|
|
1589
1582
|
requestCurrentURL,
|
|
1590
1583
|
responseURL,
|
|
1591
1584
|
responseLocationURL,
|
|
1592
|
-
isBlobLike,
|
|
1593
1585
|
isURLPotentiallyTrustworthy,
|
|
1594
1586
|
isValidReasonPhrase,
|
|
1595
1587
|
sameOrigin,
|
|
@@ -1602,7 +1594,6 @@ module.exports = {
|
|
|
1602
1594
|
isErrorLike,
|
|
1603
1595
|
fullyReadBody,
|
|
1604
1596
|
bytesMatch,
|
|
1605
|
-
isReadableStreamLike,
|
|
1606
1597
|
readableStreamClose,
|
|
1607
1598
|
isomorphicEncode,
|
|
1608
1599
|
urlIsLocal,
|