undici 4.11.0 → 4.12.0
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 +8 -8
- package/lib/api/readable.js +11 -10
- package/lib/balanced-pool.js +4 -4
- package/lib/core/util.js +22 -7
- package/lib/fetch/body.js +3 -9
- package/lib/fetch/file.js +1 -0
- package/lib/fetch/headers.js +1 -1
- package/lib/fetch/index.js +161 -180
- package/lib/fetch/request.js +64 -16
- package/lib/fetch/response.js +1 -1
- package/lib/fetch/util.js +6 -7
- package/lib/pool-base.js +2 -2
- package/lib/pool.js +3 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -203,13 +203,13 @@ not support or does not fully implement.
|
|
|
203
203
|
* https://fetch.spec.whatwg.org/#garbage-collection
|
|
204
204
|
|
|
205
205
|
The [Fetch Standard](https://fetch.spec.whatwg.org) allows users to skip consuming the response body by relying on
|
|
206
|
-
[garbage collection](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Memory_Management#garbage_collection) to release connection resources. Undici does the same.
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
206
|
+
[garbage collection](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Memory_Management#garbage_collection) to release connection resources. Undici does not do the same. Therefore, it is important to always either consume or cancel the response body.
|
|
207
|
+
|
|
208
|
+
Garbage collection in Node is less aggressive and deterministic
|
|
209
|
+
(due to the lack of clear idle periods that browser have through the rendering refresh rate)
|
|
210
|
+
which means that leaving the release of connection resources to the garbage collector can lead
|
|
211
|
+
to excessive connection usage, reduced performance (due to less connection re-use), and even
|
|
212
|
+
stalls or deadlocks when running out of connections.
|
|
213
213
|
|
|
214
214
|
```js
|
|
215
215
|
// Do
|
|
@@ -289,7 +289,7 @@ pipeline requests, without checking whether the connection is persistent.
|
|
|
289
289
|
Hence, automatic fallback to HTTP/1.0 or HTTP/1.1 without pipelining is
|
|
290
290
|
not supported.
|
|
291
291
|
|
|
292
|
-
Undici will immediately pipeline when retrying requests
|
|
292
|
+
Undici will immediately pipeline when retrying requests after a failed
|
|
293
293
|
connection. However, Undici will not retry the first remaining requests in
|
|
294
294
|
the prior pipeline and instead error the corresponding callback/promise/stream.
|
|
295
295
|
|
package/lib/api/readable.js
CHANGED
|
@@ -56,8 +56,8 @@ module.exports = class BodyReadable extends Readable {
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
emit (ev, ...args) {
|
|
59
|
-
// Waiting for: https://github.com/nodejs/node/pull/39589
|
|
60
59
|
if (ev === 'data') {
|
|
60
|
+
// Node < 16.7
|
|
61
61
|
this._readableState.dataEmitted = true
|
|
62
62
|
} else if (ev === 'error') {
|
|
63
63
|
// Node < 16
|
|
@@ -93,17 +93,16 @@ module.exports = class BodyReadable extends Readable {
|
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
push (chunk) {
|
|
96
|
-
if (this[kConsume] && chunk !== null
|
|
96
|
+
if (this[kConsume] && chunk !== null) {
|
|
97
97
|
consumePush(this[kConsume], chunk)
|
|
98
|
-
return true
|
|
99
|
-
} else {
|
|
100
|
-
return super.push(chunk)
|
|
98
|
+
return this[kReading] ? super.push(chunk) : true
|
|
101
99
|
}
|
|
100
|
+
return super.push(chunk)
|
|
102
101
|
}
|
|
103
102
|
|
|
104
103
|
// https://fetch.spec.whatwg.org/#dom-body-text
|
|
105
104
|
async text () {
|
|
106
|
-
return
|
|
105
|
+
return consume(this, 'text')
|
|
107
106
|
}
|
|
108
107
|
|
|
109
108
|
// https://fetch.spec.whatwg.org/#dom-body-json
|
|
@@ -145,7 +144,7 @@ module.exports = class BodyReadable extends Readable {
|
|
|
145
144
|
return this[kBody]
|
|
146
145
|
}
|
|
147
146
|
|
|
148
|
-
async dump(opts) {
|
|
147
|
+
async dump (opts) {
|
|
149
148
|
let limit = opts && Number.isFinite(opts.limit) ? opts.limit : 262144
|
|
150
149
|
try {
|
|
151
150
|
for await (const chunk of this) {
|
|
@@ -233,7 +232,7 @@ function consumeEnd (consume) {
|
|
|
233
232
|
|
|
234
233
|
try {
|
|
235
234
|
if (type === 'text') {
|
|
236
|
-
resolve(Buffer.concat(body))
|
|
235
|
+
resolve(toUSVString(Buffer.concat(body)))
|
|
237
236
|
} else if (type === 'json') {
|
|
238
237
|
resolve(JSON.parse(Buffer.concat(body)))
|
|
239
238
|
} else if (type === 'arrayBuffer') {
|
|
@@ -275,8 +274,10 @@ function consumeFinish (consume, err) {
|
|
|
275
274
|
consume.resolve()
|
|
276
275
|
}
|
|
277
276
|
|
|
278
|
-
consume.
|
|
277
|
+
consume.type = null
|
|
278
|
+
consume.stream = null
|
|
279
279
|
consume.resolve = null
|
|
280
|
-
consume.
|
|
280
|
+
consume.reject = null
|
|
281
|
+
consume.length = 0
|
|
281
282
|
consume.body = null
|
|
282
283
|
}
|
package/lib/balanced-pool.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const {
|
|
4
|
-
BalancedPoolMissingUpstreamError
|
|
4
|
+
BalancedPoolMissingUpstreamError
|
|
5
5
|
} = require('./core/errors')
|
|
6
6
|
const {
|
|
7
7
|
PoolBase,
|
|
@@ -9,8 +9,8 @@ const {
|
|
|
9
9
|
kNeedDrain,
|
|
10
10
|
kAddClient,
|
|
11
11
|
kRemoveClient,
|
|
12
|
-
kDispatch
|
|
13
|
-
|
|
12
|
+
kDispatch
|
|
13
|
+
} = require('./pool-base')
|
|
14
14
|
const Pool = require('./pool')
|
|
15
15
|
const { kUrl } = require('./core/symbols')
|
|
16
16
|
|
|
@@ -73,7 +73,7 @@ class BalancedPool extends PoolBase {
|
|
|
73
73
|
throw new BalancedPoolMissingUpstreamError()
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
|
|
76
|
+
const dispatcher = this[kClients].find(dispatcher => (
|
|
77
77
|
!dispatcher[kNeedDrain] &&
|
|
78
78
|
dispatcher.closed !== true &&
|
|
79
79
|
dispatcher.destroyed !== true
|
package/lib/core/util.js
CHANGED
|
@@ -150,7 +150,7 @@ function isDestroyed (stream) {
|
|
|
150
150
|
return !stream || !!(stream.destroyed || stream[kDestroyed])
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
-
function
|
|
153
|
+
function isReadableAborted (stream) {
|
|
154
154
|
const state = stream && stream._readableState
|
|
155
155
|
return isDestroyed(stream) && state && !state.endEmitted
|
|
156
156
|
}
|
|
@@ -244,15 +244,28 @@ function validateHandler (handler, method, upgrade) {
|
|
|
244
244
|
// A body is disturbed if it has been read from and it cannot
|
|
245
245
|
// be re-used without losing state or data.
|
|
246
246
|
function isDisturbed (body) {
|
|
247
|
-
const state = body && body._readableState
|
|
248
247
|
return !!(body && (
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
248
|
+
stream.isDisturbed
|
|
249
|
+
? stream.isDisturbed(body) || body[kBodyUsed] // TODO (fix): Why is body[kBodyUsed] needed?
|
|
250
|
+
: body[kBodyUsed] ||
|
|
251
|
+
body.readableDidRead ||
|
|
252
|
+
(body._readableState && body._readableState.dataEmitted) ||
|
|
253
|
+
isReadableAborted(body)
|
|
253
254
|
))
|
|
254
255
|
}
|
|
255
256
|
|
|
257
|
+
function isErrored (body) {
|
|
258
|
+
return !!(body && (
|
|
259
|
+
stream.isErrored
|
|
260
|
+
? stream.isErrored(body)
|
|
261
|
+
: /state: 'errored'/.test(nodeUtil.inspect(body)
|
|
262
|
+
)))
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function isReadable (body) {
|
|
266
|
+
return !!(body && /state: 'readable'/.test(nodeUtil.inspect(body)))
|
|
267
|
+
}
|
|
268
|
+
|
|
256
269
|
function getSocketInfo (socket) {
|
|
257
270
|
return {
|
|
258
271
|
localAddress: socket.localAddress,
|
|
@@ -310,8 +323,10 @@ module.exports = {
|
|
|
310
323
|
kEnumerableProperty,
|
|
311
324
|
nop,
|
|
312
325
|
isDisturbed,
|
|
326
|
+
isErrored,
|
|
327
|
+
isReadable,
|
|
313
328
|
toUSVString: nodeUtil.toUSVString || ((val) => `${val}`),
|
|
314
|
-
|
|
329
|
+
isReadableAborted,
|
|
315
330
|
isBlobLike,
|
|
316
331
|
parseOrigin,
|
|
317
332
|
parseURL,
|
package/lib/fetch/body.js
CHANGED
|
@@ -9,6 +9,7 @@ const { kBodyUsed } = require('../core/symbols')
|
|
|
9
9
|
const assert = require('assert')
|
|
10
10
|
const nodeUtil = require('util')
|
|
11
11
|
const { NotSupportedError } = require('../core/errors')
|
|
12
|
+
const { isErrored } = require('../core/util')
|
|
12
13
|
|
|
13
14
|
let ReadableStream
|
|
14
15
|
|
|
@@ -169,7 +170,7 @@ function extractBody (object, keepalive = false) {
|
|
|
169
170
|
}
|
|
170
171
|
|
|
171
172
|
// 8. If action is non-null, then run these steps in in parallel:
|
|
172
|
-
if (action
|
|
173
|
+
if (action != null) {
|
|
173
174
|
// Run action.
|
|
174
175
|
let iterator
|
|
175
176
|
stream = new ReadableStream({
|
|
@@ -187,7 +188,7 @@ function extractBody (object, keepalive = false) {
|
|
|
187
188
|
// Whenever one or more bytes are available and stream is not errored,
|
|
188
189
|
// enqueue a Uint8Array wrapping an ArrayBuffer containing the available
|
|
189
190
|
// bytes into stream.
|
|
190
|
-
if (
|
|
191
|
+
if (!isErrored(stream)) {
|
|
191
192
|
controller.enqueue(new Uint8Array(value))
|
|
192
193
|
}
|
|
193
194
|
}
|
|
@@ -348,19 +349,12 @@ const properties = {
|
|
|
348
349
|
}
|
|
349
350
|
}
|
|
350
351
|
|
|
351
|
-
function cancelBody (body, reason) {
|
|
352
|
-
if (body.stream && !/state: 'errored'/.test(nodeUtil.inspect(body.stream))) {
|
|
353
|
-
body.stream.cancel(reason)
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
|
|
357
352
|
function mixinBody (prototype) {
|
|
358
353
|
Object.assign(prototype, methods)
|
|
359
354
|
Object.defineProperties(prototype, properties)
|
|
360
355
|
}
|
|
361
356
|
|
|
362
357
|
module.exports = {
|
|
363
|
-
cancelBody,
|
|
364
358
|
extractBody,
|
|
365
359
|
safelyExtractBody,
|
|
366
360
|
cloneBody,
|
package/lib/fetch/file.js
CHANGED
package/lib/fetch/headers.js
CHANGED
|
@@ -153,7 +153,7 @@ class Headers {
|
|
|
153
153
|
constructor (...args) {
|
|
154
154
|
if (
|
|
155
155
|
args[0] !== undefined &&
|
|
156
|
-
!(typeof args[0] === 'object' && args[0]
|
|
156
|
+
!(typeof args[0] === 'object' && args[0] != null) &&
|
|
157
157
|
!Array.isArray(args[0])
|
|
158
158
|
) {
|
|
159
159
|
throw new TypeError(
|
package/lib/fetch/index.js
CHANGED
|
@@ -24,7 +24,7 @@ const {
|
|
|
24
24
|
requestCurrentURL,
|
|
25
25
|
setRequestReferrerPolicyOnRedirect,
|
|
26
26
|
tryUpgradeRequestToAPotentiallyTrustworthyURL,
|
|
27
|
-
|
|
27
|
+
createOpaqueTimingInfo,
|
|
28
28
|
appendFetchMetadata,
|
|
29
29
|
corsCheck,
|
|
30
30
|
crossOriginResourcePolicyCheck,
|
|
@@ -34,7 +34,7 @@ const {
|
|
|
34
34
|
const { kState, kHeaders, kGuard, kRealm } = require('./symbols')
|
|
35
35
|
const { AbortError } = require('../core/errors')
|
|
36
36
|
const assert = require('assert')
|
|
37
|
-
const { safelyExtractBody
|
|
37
|
+
const { safelyExtractBody } = require('./body')
|
|
38
38
|
const {
|
|
39
39
|
redirectStatus,
|
|
40
40
|
nullBodyStatus,
|
|
@@ -44,14 +44,32 @@ const {
|
|
|
44
44
|
} = require('./constants')
|
|
45
45
|
const { kHeadersList } = require('../core/symbols')
|
|
46
46
|
const EE = require('events')
|
|
47
|
-
const { PassThrough, pipeline
|
|
47
|
+
const { PassThrough, pipeline } = require('stream')
|
|
48
|
+
const { isErrored, isReadable } = require('../core/util')
|
|
48
49
|
|
|
49
50
|
let ReadableStream
|
|
50
51
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
52
|
+
class Fetch extends EE {
|
|
53
|
+
constructor (dispatcher) {
|
|
54
|
+
super()
|
|
55
|
+
|
|
56
|
+
this.dispatcher = dispatcher
|
|
57
|
+
this.terminated = null
|
|
58
|
+
this.connection = null
|
|
59
|
+
this.dump = false
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
terminate ({ reason, aborted } = {}) {
|
|
63
|
+
if (this.terminated) {
|
|
64
|
+
return
|
|
65
|
+
}
|
|
66
|
+
this.terminated = { aborted, reason }
|
|
67
|
+
|
|
68
|
+
this.connection?.destroy(reason)
|
|
69
|
+
|
|
70
|
+
this.emit('terminated', reason)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
55
73
|
|
|
56
74
|
// https://fetch.spec.whatwg.org/#fetch-method
|
|
57
75
|
async function fetch (...args) {
|
|
@@ -73,26 +91,7 @@ async function fetch (...args) {
|
|
|
73
91
|
const resource = args[0]
|
|
74
92
|
const init = args.length >= 1 ? args[1] ?? {} : {}
|
|
75
93
|
|
|
76
|
-
const context =
|
|
77
|
-
dispatcher: this,
|
|
78
|
-
terminated: false,
|
|
79
|
-
connection: null,
|
|
80
|
-
dump: false,
|
|
81
|
-
terminate ({ reason, aborted } = {}) {
|
|
82
|
-
if (this.terminated) {
|
|
83
|
-
return
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
if (this.connection) {
|
|
87
|
-
this.connection.destroy()
|
|
88
|
-
this.connection = null
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
this.terminated = { aborted }
|
|
92
|
-
|
|
93
|
-
this.emit('terminated', reason)
|
|
94
|
-
}
|
|
95
|
-
})
|
|
94
|
+
const context = new Fetch(this)
|
|
96
95
|
|
|
97
96
|
// 1. Let p be a new promise.
|
|
98
97
|
const p = createDeferredPromise()
|
|
@@ -154,7 +153,7 @@ async function fetch (...args) {
|
|
|
154
153
|
const handleFetchDone = (response) =>
|
|
155
154
|
finalizeAndReportTiming(response, 'fetch')
|
|
156
155
|
|
|
157
|
-
// 12. Fetch request with
|
|
156
|
+
// 12. Fetch request with processResponseEndOfBody set to handleFetchDone,
|
|
158
157
|
// and processResponse given response being these substeps:
|
|
159
158
|
const processResponse = (response) => {
|
|
160
159
|
// 1. If locallyAborted is true, terminate these substeps.
|
|
@@ -194,7 +193,7 @@ async function fetch (...args) {
|
|
|
194
193
|
fetching
|
|
195
194
|
.call(context, {
|
|
196
195
|
request,
|
|
197
|
-
|
|
196
|
+
processResponseEndOfBody: handleFetchDone,
|
|
198
197
|
processResponse
|
|
199
198
|
})
|
|
200
199
|
.catch((err) => {
|
|
@@ -227,11 +226,9 @@ function finalizeAndReportTiming (response, initiatorType = 'other') {
|
|
|
227
226
|
|
|
228
227
|
// 6. If response’s timing allow passed flag is not set, then:
|
|
229
228
|
if (!timingInfo.timingAllowPassed) {
|
|
230
|
-
// 1. Set timingInfo to a
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
startTime: timingInfo.startTime,
|
|
234
|
-
postRedirectStartTime: timingInfo.postRedirectStartTime
|
|
229
|
+
// 1. Set timingInfo to a the result of creating an opaque timing info for timingInfo.
|
|
230
|
+
timingInfo = createOpaqueTimingInfo({
|
|
231
|
+
startTime: timingInfo.startTime
|
|
235
232
|
})
|
|
236
233
|
|
|
237
234
|
// 2. Set cacheState to the empty string.
|
|
@@ -266,8 +263,6 @@ function markResourceTiming () {
|
|
|
266
263
|
|
|
267
264
|
// https://fetch.spec.whatwg.org/#abort-fetch
|
|
268
265
|
function abortFetch (p, request, responseObject) {
|
|
269
|
-
const context = this
|
|
270
|
-
|
|
271
266
|
// 1. Let error be an "AbortError" DOMException.
|
|
272
267
|
const error = new AbortError()
|
|
273
268
|
|
|
@@ -276,8 +271,14 @@ function abortFetch (p, request, responseObject) {
|
|
|
276
271
|
|
|
277
272
|
// 3. If request’s body is not null and is readable, then cancel request’s
|
|
278
273
|
// body with error.
|
|
279
|
-
if (request.body
|
|
280
|
-
|
|
274
|
+
if (request.body != null && isReadable(request.body?.stream)) {
|
|
275
|
+
request.body.stream.cancel(error).catch((err) => {
|
|
276
|
+
if (err.code === 'ERR_INVALID_STATE') {
|
|
277
|
+
// Node bug?
|
|
278
|
+
return
|
|
279
|
+
}
|
|
280
|
+
throw err
|
|
281
|
+
})
|
|
281
282
|
}
|
|
282
283
|
|
|
283
284
|
// 4. If responseObject is null, then return.
|
|
@@ -290,13 +291,27 @@ function abortFetch (p, request, responseObject) {
|
|
|
290
291
|
|
|
291
292
|
// 6. If response’s body is not null and is readable, then error response’s
|
|
292
293
|
// body with error.
|
|
293
|
-
if (response.body != null) {
|
|
294
|
-
|
|
294
|
+
if (response.body != null && isReadable(response.body?.stream)) {
|
|
295
|
+
response.body.stream.cancel(error).catch((err) => {
|
|
296
|
+
if (err.code === 'ERR_INVALID_STATE') {
|
|
297
|
+
// Node bug?
|
|
298
|
+
return
|
|
299
|
+
}
|
|
300
|
+
throw err
|
|
301
|
+
})
|
|
295
302
|
}
|
|
296
303
|
}
|
|
297
304
|
|
|
298
305
|
// https://fetch.spec.whatwg.org/#fetching
|
|
299
|
-
function fetching ({
|
|
306
|
+
function fetching ({
|
|
307
|
+
request,
|
|
308
|
+
processRequestBodyChunkLength,
|
|
309
|
+
processRequestEndOfBody,
|
|
310
|
+
processResponse,
|
|
311
|
+
processResponseEndOfBody,
|
|
312
|
+
processResponseConsumeBody,
|
|
313
|
+
useParallelQueue = false,
|
|
314
|
+
}) {
|
|
300
315
|
// 1. Let taskDestination be null.
|
|
301
316
|
let taskDestination = null
|
|
302
317
|
|
|
@@ -304,7 +319,7 @@ function fetching ({ request, processResponse, processResponseDone }) {
|
|
|
304
319
|
let crossOriginIsolatedCapability = false
|
|
305
320
|
|
|
306
321
|
// 3. If request’s client is non-null, then:
|
|
307
|
-
if (request.client
|
|
322
|
+
if (request.client != null) {
|
|
308
323
|
// 1. Set taskDestination to request’s client’s global object.
|
|
309
324
|
taskDestination = request.client.globalObject
|
|
310
325
|
|
|
@@ -322,26 +337,28 @@ function fetching ({ request, processResponse, processResponseDone }) {
|
|
|
322
337
|
// post-redirect start time are the coarsened shared current time given
|
|
323
338
|
// crossOriginIsolatedCapability.
|
|
324
339
|
const currenTime = coarsenedSharedCurrentTime(crossOriginIsolatedCapability)
|
|
325
|
-
const timingInfo =
|
|
326
|
-
startTime: currenTime
|
|
327
|
-
postRedirectStartTime: currenTime
|
|
340
|
+
const timingInfo = createOpaqueTimingInfo({
|
|
341
|
+
startTime: currenTime
|
|
328
342
|
})
|
|
329
343
|
|
|
330
|
-
// 6. Let fetchParams be a new fetch params whose
|
|
331
|
-
//
|
|
332
|
-
//
|
|
333
|
-
//
|
|
334
|
-
//
|
|
335
|
-
//
|
|
336
|
-
// is
|
|
344
|
+
// 6. Let fetchParams be a new fetch params whose
|
|
345
|
+
// request is request,
|
|
346
|
+
// timing info is timingInfo,
|
|
347
|
+
// process request body chunk length is processRequestBodyChunkLength,
|
|
348
|
+
// process request end-of-body is processRequestEndOfBody,
|
|
349
|
+
// process response is processResponse,
|
|
350
|
+
// process response consume body is processResponseConsumeBody,
|
|
351
|
+
// process response end-of-body is processResponseEndOfBody,
|
|
352
|
+
// task destination is taskDestination,
|
|
353
|
+
// and cross-origin isolated capability is crossOriginIsolatedCapability.
|
|
337
354
|
const fetchParams = {
|
|
338
355
|
request,
|
|
339
356
|
timingInfo,
|
|
340
|
-
|
|
341
|
-
processRequestEndOfBody
|
|
357
|
+
processRequestBodyChunkLength,
|
|
358
|
+
processRequestEndOfBody,
|
|
342
359
|
processResponse,
|
|
343
|
-
|
|
344
|
-
|
|
360
|
+
processResponseConsumeBody,
|
|
361
|
+
processResponseEndOfBody,
|
|
345
362
|
taskDestination,
|
|
346
363
|
crossOriginIsolatedCapability
|
|
347
364
|
}
|
|
@@ -374,7 +391,7 @@ function fetching ({ request, processResponse, processResponseDone }) {
|
|
|
374
391
|
if (request.policyContainer === 'client') {
|
|
375
392
|
// 1. If request’s client is non-null, then set request’s policy
|
|
376
393
|
// container to a clone of request’s client’s policy container. [HTML]
|
|
377
|
-
if (request.client
|
|
394
|
+
if (request.client != null) {
|
|
378
395
|
request.policyContainer = clonePolicyContainer(
|
|
379
396
|
request.client.policyContainer
|
|
380
397
|
)
|
|
@@ -649,7 +666,7 @@ async function mainFetch (fetchParams, recursive = false) {
|
|
|
649
666
|
nullBodyStatus.includes(internalResponse.status))
|
|
650
667
|
) {
|
|
651
668
|
internalResponse.body = null
|
|
652
|
-
context.
|
|
669
|
+
context.dump = true
|
|
653
670
|
}
|
|
654
671
|
|
|
655
672
|
// 20. If request’s integrity metadata is not the empty string, then:
|
|
@@ -659,10 +676,9 @@ async function mainFetch (fetchParams, recursive = false) {
|
|
|
659
676
|
const processBodyError = (reason) =>
|
|
660
677
|
fetchFinale(fetchParams, makeNetworkError(reason))
|
|
661
678
|
|
|
662
|
-
// 2. If request’s response tainting is "opaque", response is
|
|
663
|
-
//
|
|
664
|
-
|
|
665
|
-
if (request.responseTainting === 'opaque' && response.status === 0) {
|
|
679
|
+
// 2. If request’s response tainting is "opaque", or response’s body is null,
|
|
680
|
+
// then run processBodyError and abort these steps.
|
|
681
|
+
if (request.responseTainting === 'opaque' || response.body == null) {
|
|
666
682
|
processBodyError(response.error)
|
|
667
683
|
return
|
|
668
684
|
}
|
|
@@ -704,7 +720,7 @@ function finalizeResponse (fetchParams, response) {
|
|
|
704
720
|
// 2, If fetchParams’s process response done is not null, then queue a fetch
|
|
705
721
|
// task to run fetchParams’s process response done given response, with
|
|
706
722
|
// fetchParams’s task destination.
|
|
707
|
-
if (fetchParams.processResponseDone
|
|
723
|
+
if (fetchParams.processResponseDone != null) {
|
|
708
724
|
fetchParams.processResponseDone(response)
|
|
709
725
|
}
|
|
710
726
|
}
|
|
@@ -716,17 +732,17 @@ function fetchFinale (fetchParams, response) {
|
|
|
716
732
|
// 1. If fetchParams’s process response is non-null,
|
|
717
733
|
// then queue a fetch task to run fetchParams’s process response
|
|
718
734
|
// given response, with fetchParams’s task destination.
|
|
719
|
-
if (fetchParams.processResponse
|
|
735
|
+
if (fetchParams.processResponse != null) {
|
|
720
736
|
fetchParams.processResponse(response)
|
|
721
737
|
}
|
|
722
738
|
|
|
723
|
-
// 2. If fetchParams’s process response
|
|
739
|
+
// 2. If fetchParams’s process response consume is non-null, then:.
|
|
724
740
|
// TODO
|
|
725
741
|
// 1. Let processBody given nullOrBytes be this step: run fetchParams’s
|
|
726
|
-
// process response
|
|
742
|
+
// process response consume given response and nullOrBytes.on.
|
|
727
743
|
// TODO
|
|
728
744
|
// 2. Let processBodyError be this step: run fetchParams’s process
|
|
729
|
-
// response
|
|
745
|
+
// response consume given response and failure.on.
|
|
730
746
|
// TODO
|
|
731
747
|
// 3. If response’s body is null, then queue a fetch task to run
|
|
732
748
|
// processBody given null, with fetchParams’s task destination.on.
|
|
@@ -915,7 +931,7 @@ async function httpRedirectFetch (fetchParams, response) {
|
|
|
915
931
|
// and request’s body’s source is null, then return a network error.
|
|
916
932
|
if (
|
|
917
933
|
actualResponse.status !== 303 &&
|
|
918
|
-
request.body
|
|
934
|
+
request.body != null &&
|
|
919
935
|
request.body.source == null
|
|
920
936
|
) {
|
|
921
937
|
return makeNetworkError()
|
|
@@ -953,7 +969,7 @@ async function httpRedirectFetch (fetchParams, response) {
|
|
|
953
969
|
|
|
954
970
|
// 14. If request’s body is non-null, then set request’s body to the first return
|
|
955
971
|
// value of safely extracting request’s body’s source.
|
|
956
|
-
if (request.body
|
|
972
|
+
if (request.body != null) {
|
|
957
973
|
assert(request.body.source)
|
|
958
974
|
request.body = safelyExtractBody(request.body.source)[0]
|
|
959
975
|
}
|
|
@@ -1058,7 +1074,7 @@ async function httpNetworkOrCacheFetch (
|
|
|
1058
1074
|
|
|
1059
1075
|
// 7. If contentLength is non-null, then set contentLengthHeaderValue to
|
|
1060
1076
|
// contentLength, serialized and isomorphic encoded.
|
|
1061
|
-
if (contentLength
|
|
1077
|
+
if (contentLength != null) {
|
|
1062
1078
|
// TODO: isomorphic encoded
|
|
1063
1079
|
contentLengthHeaderValue = String(contentLength)
|
|
1064
1080
|
}
|
|
@@ -1066,13 +1082,13 @@ async function httpNetworkOrCacheFetch (
|
|
|
1066
1082
|
// 8. If contentLengthHeaderValue is non-null, then append
|
|
1067
1083
|
// `Content-Length`/contentLengthHeaderValue to httpRequest’s header
|
|
1068
1084
|
// list.
|
|
1069
|
-
if (contentLengthHeaderValue
|
|
1085
|
+
if (contentLengthHeaderValue != null) {
|
|
1070
1086
|
httpRequest.headersList.append('content-length', contentLengthHeaderValue)
|
|
1071
1087
|
}
|
|
1072
1088
|
|
|
1073
1089
|
// 9. If contentLength is non-null and httpRequest’s keepalive is true,
|
|
1074
1090
|
// then:
|
|
1075
|
-
if (contentLength
|
|
1091
|
+
if (contentLength != null && httpRequest.keepalive) {
|
|
1076
1092
|
// NOTE: keepalive is a noop outside of browser context.
|
|
1077
1093
|
}
|
|
1078
1094
|
|
|
@@ -1259,11 +1275,15 @@ async function httpNetworkOrCacheFetch (
|
|
|
1259
1275
|
// 3. If the ongoing fetch is terminated, then:
|
|
1260
1276
|
if (context.terminated) {
|
|
1261
1277
|
// 1. Let aborted be the termination’s aborted flag.
|
|
1278
|
+
const aborted = context.terminated.aborted
|
|
1279
|
+
|
|
1262
1280
|
// 2. If aborted is set, then return an aborted network error.
|
|
1281
|
+
if (aborted) {
|
|
1282
|
+
return makeNetworkError(new AbortError())
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1263
1285
|
// 3. Return a network error.
|
|
1264
|
-
return makeNetworkError(
|
|
1265
|
-
context.terminated.aborted ? new AbortError() : null
|
|
1266
|
-
)
|
|
1286
|
+
return makeNetworkError(context.terminated.reason)
|
|
1267
1287
|
}
|
|
1268
1288
|
|
|
1269
1289
|
// 4. Prompt the end user as appropriate in request’s window and store
|
|
@@ -1283,7 +1303,7 @@ async function httpNetworkOrCacheFetch (
|
|
|
1283
1303
|
// isNewConnectionFetch is false
|
|
1284
1304
|
!isNewConnectionFetch &&
|
|
1285
1305
|
// request’s body is null, or request’s body is non-null and request’s body’s source is non-null
|
|
1286
|
-
(request.body == null || request.body.source
|
|
1306
|
+
(request.body == null || request.body.source != null)
|
|
1287
1307
|
) {
|
|
1288
1308
|
// then:
|
|
1289
1309
|
|
|
@@ -1293,10 +1313,12 @@ async function httpNetworkOrCacheFetch (
|
|
|
1293
1313
|
const aborted = context.terminated.aborted
|
|
1294
1314
|
|
|
1295
1315
|
// 2. If aborted is set, then return an aborted network error.
|
|
1296
|
-
|
|
1316
|
+
if (aborted) {
|
|
1317
|
+
return makeNetworkError(new AbortError())
|
|
1318
|
+
}
|
|
1297
1319
|
|
|
1298
1320
|
// 3. Return a network error.
|
|
1299
|
-
return makeNetworkError(reason)
|
|
1321
|
+
return makeNetworkError(context.terminated.reason)
|
|
1300
1322
|
}
|
|
1301
1323
|
|
|
1302
1324
|
// 2. Set response to the result of running HTTP-network-or-cache
|
|
@@ -1335,41 +1357,16 @@ function httpNetworkFetch (
|
|
|
1335
1357
|
return new Promise((resolve) => {
|
|
1336
1358
|
assert(!context.connection || context.connection.destroyed)
|
|
1337
1359
|
|
|
1338
|
-
|
|
1360
|
+
context.connection = {
|
|
1339
1361
|
abort: null,
|
|
1340
|
-
controller: null,
|
|
1341
1362
|
destroyed: false,
|
|
1342
|
-
errored: false,
|
|
1343
|
-
dump: false,
|
|
1344
1363
|
destroy (err) {
|
|
1345
|
-
if (this.destroyed) {
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
this.destroyed = true
|
|
1350
|
-
|
|
1351
|
-
if (this.abort) {
|
|
1352
|
-
this.abort()
|
|
1353
|
-
this.abort = null
|
|
1354
|
-
}
|
|
1355
|
-
|
|
1356
|
-
if (err) {
|
|
1357
|
-
this.errored = err
|
|
1358
|
-
}
|
|
1359
|
-
|
|
1360
|
-
if (this.controller) {
|
|
1361
|
-
try {
|
|
1362
|
-
this.controller.error(err ?? new AbortError())
|
|
1363
|
-
this.controller = null
|
|
1364
|
-
} catch (err) {
|
|
1365
|
-
// Will throw TypeError if body is not readable.
|
|
1366
|
-
if (err.name !== 'TypeError') {
|
|
1367
|
-
throw err
|
|
1368
|
-
}
|
|
1369
|
-
}
|
|
1364
|
+
if (!this.destroyed) {
|
|
1365
|
+
this.destroyed = true
|
|
1366
|
+
this.abort?.(err ?? new AbortError())
|
|
1370
1367
|
}
|
|
1371
1368
|
}
|
|
1372
|
-
}
|
|
1369
|
+
}
|
|
1373
1370
|
|
|
1374
1371
|
// 1. Let request be fetchParams’s request.
|
|
1375
1372
|
const request = fetchParams.request
|
|
@@ -1505,16 +1502,18 @@ function httpNetworkFetch (
|
|
|
1505
1502
|
// 9. If aborted, then:
|
|
1506
1503
|
function onRequestAborted () {
|
|
1507
1504
|
// 1. Let aborted be the termination’s aborted flag.
|
|
1508
|
-
const aborted =
|
|
1505
|
+
const aborted = this.terminated.aborted
|
|
1509
1506
|
|
|
1510
1507
|
// 2. If connection uses HTTP/2, then transmit an RST_STREAM frame.
|
|
1511
|
-
connection.destroy()
|
|
1508
|
+
this.connection.destroy()
|
|
1512
1509
|
|
|
1513
1510
|
// 3. If aborted is set, then return an aborted network error.
|
|
1514
|
-
|
|
1511
|
+
if (aborted) {
|
|
1512
|
+
return resolve(makeNetworkError(new AbortError()))
|
|
1513
|
+
}
|
|
1515
1514
|
|
|
1516
1515
|
// 4. Return a network error.
|
|
1517
|
-
resolve(makeNetworkError(reason))
|
|
1516
|
+
return resolve(makeNetworkError(this.terminated.reason))
|
|
1518
1517
|
}
|
|
1519
1518
|
|
|
1520
1519
|
// 10. Let pullAlgorithm be an action that resumes the ongoing fetch
|
|
@@ -1529,7 +1528,7 @@ function httpNetworkFetch (
|
|
|
1529
1528
|
|
|
1530
1529
|
// 12. Let highWaterMark be a non-negative, non-NaN number, chosen by
|
|
1531
1530
|
// the user agent.
|
|
1532
|
-
const highWaterMark =
|
|
1531
|
+
const highWaterMark = 64 * 1024 // Same as nodejs fs streams.
|
|
1533
1532
|
|
|
1534
1533
|
// 13. Let sizeAlgorithm be an algorithm that accepts a chunk object
|
|
1535
1534
|
// and returns a non-negative, non-NaN, non-infinite number, chosen by the user agent.
|
|
@@ -1543,20 +1542,23 @@ function httpNetworkFetch (
|
|
|
1543
1542
|
ReadableStream = require('stream/web').ReadableStream
|
|
1544
1543
|
}
|
|
1545
1544
|
|
|
1545
|
+
let pullResolve
|
|
1546
|
+
|
|
1546
1547
|
const stream = new ReadableStream(
|
|
1547
1548
|
{
|
|
1548
1549
|
async start (controller) {
|
|
1549
|
-
|
|
1550
|
+
context.controller = controller
|
|
1550
1551
|
},
|
|
1551
|
-
async pull () {
|
|
1552
|
-
if (pullAlgorithm) {
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1552
|
+
async pull (controller) {
|
|
1553
|
+
if (!pullAlgorithm) {
|
|
1554
|
+
await new Promise((resolve) => {
|
|
1555
|
+
pullResolve = resolve
|
|
1556
|
+
})
|
|
1556
1557
|
}
|
|
1558
|
+
await pullAlgorithm(controller)
|
|
1557
1559
|
},
|
|
1558
1560
|
async cancel (reason) {
|
|
1559
|
-
cancelAlgorithm()
|
|
1561
|
+
await cancelAlgorithm(reason)
|
|
1560
1562
|
}
|
|
1561
1563
|
},
|
|
1562
1564
|
{ highWaterMark }
|
|
@@ -1586,7 +1588,7 @@ function httpNetworkFetch (
|
|
|
1586
1588
|
finalizeResponse(fetchParams, response)
|
|
1587
1589
|
|
|
1588
1590
|
// 2. Let aborted be the termination’s aborted flag.
|
|
1589
|
-
const aborted =
|
|
1591
|
+
const aborted = this.terminated.aborted
|
|
1590
1592
|
|
|
1591
1593
|
// 3. If aborted is set, then:
|
|
1592
1594
|
if (aborted) {
|
|
@@ -1594,15 +1596,19 @@ function httpNetworkFetch (
|
|
|
1594
1596
|
response.aborted = true
|
|
1595
1597
|
|
|
1596
1598
|
// 2. If stream is readable, error stream with an "AbortError" DOMException.
|
|
1597
|
-
|
|
1599
|
+
if (isReadable(stream)) {
|
|
1600
|
+
this.controller.error(new AbortError())
|
|
1601
|
+
}
|
|
1598
1602
|
} else {
|
|
1599
1603
|
// 4. Otherwise, if stream is readable, error stream with a TypeError.
|
|
1600
|
-
|
|
1604
|
+
if (isReadable(stream)) {
|
|
1605
|
+
this.controller.error(new TypeError('terminated'))
|
|
1606
|
+
}
|
|
1601
1607
|
}
|
|
1602
1608
|
|
|
1603
1609
|
// 5. If connection uses HTTP/2, then transmit an RST_STREAM frame.
|
|
1604
1610
|
// 6. Otherwise, the user agent should close connection unless it would be bad for performance to do so.
|
|
1605
|
-
connection.destroy()
|
|
1611
|
+
this.connection.destroy()
|
|
1606
1612
|
}
|
|
1607
1613
|
|
|
1608
1614
|
// 19. Return response.
|
|
@@ -1621,12 +1627,17 @@ function httpNetworkFetch (
|
|
|
1621
1627
|
},
|
|
1622
1628
|
{
|
|
1623
1629
|
decoder: null,
|
|
1630
|
+
abort: null,
|
|
1631
|
+
context,
|
|
1624
1632
|
|
|
1625
1633
|
onConnect (abort) {
|
|
1634
|
+
// TODO (fix): Do we need connection here?
|
|
1635
|
+
const { connection } = this.context
|
|
1636
|
+
|
|
1626
1637
|
if (connection.destroyed) {
|
|
1627
1638
|
abort(new AbortError())
|
|
1628
1639
|
} else {
|
|
1629
|
-
connection.abort = abort
|
|
1640
|
+
this.abort = connection.abort = abort
|
|
1630
1641
|
}
|
|
1631
1642
|
},
|
|
1632
1643
|
|
|
@@ -1643,19 +1654,14 @@ function httpNetworkFetch (
|
|
|
1643
1654
|
)
|
|
1644
1655
|
}
|
|
1645
1656
|
|
|
1646
|
-
const hasPulled = pullAlgorithm !== undefined
|
|
1647
|
-
|
|
1648
|
-
const body = { stream }
|
|
1649
|
-
registry.register(body, connection.abort)
|
|
1650
|
-
|
|
1651
1657
|
response = makeResponse({
|
|
1652
1658
|
status,
|
|
1653
1659
|
statusText,
|
|
1654
1660
|
headersList: headers[kHeadersList],
|
|
1655
|
-
body
|
|
1661
|
+
body: { stream }
|
|
1656
1662
|
})
|
|
1657
1663
|
|
|
1658
|
-
context.on('terminated', onResponseAborted)
|
|
1664
|
+
this.context.on('terminated', onResponseAborted)
|
|
1659
1665
|
|
|
1660
1666
|
const codings =
|
|
1661
1667
|
headers
|
|
@@ -1680,31 +1686,18 @@ function httpNetworkFetch (
|
|
|
1680
1686
|
}
|
|
1681
1687
|
}
|
|
1682
1688
|
|
|
1683
|
-
let iterator
|
|
1684
|
-
|
|
1685
1689
|
if (decoders.length > 1) {
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
this.decoder = new PassThrough()
|
|
1691
|
-
iterator = pipeline(this.decoder, ...decoders, () => {})[
|
|
1692
|
-
Symbol.asyncIterator
|
|
1693
|
-
]()
|
|
1694
|
-
}
|
|
1695
|
-
} else if (decoders.length === 1) {
|
|
1696
|
-
this.decoder = decoders[0]
|
|
1697
|
-
iterator = this.decoder[Symbol.asyncIterator]()
|
|
1698
|
-
} else {
|
|
1699
|
-
this.decoder = new PassThrough()
|
|
1700
|
-
iterator = this.decoder[Symbol.asyncIterator]()
|
|
1690
|
+
pipeline(...decoders, () => {})
|
|
1691
|
+
} else if (decoders.length === 0) {
|
|
1692
|
+
// TODO (perf): Avoid intermediate.
|
|
1693
|
+
decoders.push(new PassThrough())
|
|
1701
1694
|
}
|
|
1702
1695
|
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1696
|
+
this.decoder = decoders[0].on('drain', resume)
|
|
1697
|
+
|
|
1698
|
+
const iterator = decoders[decoders.length - 1][Symbol.asyncIterator]()
|
|
1706
1699
|
|
|
1707
|
-
pullAlgorithm = async () => {
|
|
1700
|
+
pullAlgorithm = async (controller) => {
|
|
1708
1701
|
// 4. Set bytes to the result of handling content codings given
|
|
1709
1702
|
// codings and bytes.
|
|
1710
1703
|
let bytes
|
|
@@ -1720,10 +1713,6 @@ function httpNetworkFetch (
|
|
|
1720
1713
|
}
|
|
1721
1714
|
}
|
|
1722
1715
|
|
|
1723
|
-
if (!connection.controller) {
|
|
1724
|
-
return
|
|
1725
|
-
}
|
|
1726
|
-
|
|
1727
1716
|
if (bytes === undefined) {
|
|
1728
1717
|
// 2. Otherwise, if the bytes transmission for response’s message
|
|
1729
1718
|
// body is done normally and stream is readable, then close
|
|
@@ -1731,13 +1720,7 @@ function httpNetworkFetch (
|
|
|
1731
1720
|
// abort these in-parallel steps.
|
|
1732
1721
|
finalizeResponse(fetchParams, response)
|
|
1733
1722
|
|
|
1734
|
-
|
|
1735
|
-
context.off('terminated', onRequestAborted)
|
|
1736
|
-
|
|
1737
|
-
connection.controller.close()
|
|
1738
|
-
connection.controller = null
|
|
1739
|
-
|
|
1740
|
-
connection.destroy()
|
|
1723
|
+
controller.close()
|
|
1741
1724
|
|
|
1742
1725
|
return
|
|
1743
1726
|
}
|
|
@@ -1747,27 +1730,28 @@ function httpNetworkFetch (
|
|
|
1747
1730
|
|
|
1748
1731
|
// 6. If bytes is failure, then terminate the ongoing fetch.
|
|
1749
1732
|
if (bytes instanceof Error) {
|
|
1750
|
-
context.terminate({ reason: bytes })
|
|
1733
|
+
this.context.terminate({ reason: bytes })
|
|
1751
1734
|
return
|
|
1752
1735
|
}
|
|
1753
1736
|
|
|
1754
1737
|
// 7. Enqueue a Uint8Array wrapping an ArrayBuffer containing bytes
|
|
1755
|
-
//
|
|
1756
|
-
|
|
1738
|
+
// into stream.
|
|
1739
|
+
controller.enqueue(new Uint8Array(bytes))
|
|
1757
1740
|
|
|
1758
1741
|
// 8. If stream is errored, then terminate the ongoing fetch.
|
|
1759
|
-
if (
|
|
1760
|
-
context.terminate(
|
|
1742
|
+
if (isErrored(stream)) {
|
|
1743
|
+
this.context.terminate()
|
|
1761
1744
|
return
|
|
1762
1745
|
}
|
|
1763
1746
|
|
|
1764
1747
|
// 9. If stream doesn’t need more data ask the user agent to suspend
|
|
1765
|
-
//
|
|
1766
|
-
return
|
|
1748
|
+
// the ongoing fetch.
|
|
1749
|
+
return controller.desiredSize > 0
|
|
1767
1750
|
}
|
|
1768
1751
|
|
|
1769
|
-
if (
|
|
1770
|
-
|
|
1752
|
+
if (pullResolve) {
|
|
1753
|
+
pullResolve()
|
|
1754
|
+
pullResolve = null
|
|
1771
1755
|
}
|
|
1772
1756
|
|
|
1773
1757
|
resolve(response)
|
|
@@ -1776,7 +1760,7 @@ function httpNetworkFetch (
|
|
|
1776
1760
|
},
|
|
1777
1761
|
|
|
1778
1762
|
onData (chunk) {
|
|
1779
|
-
if (
|
|
1763
|
+
if (this.context.dump) {
|
|
1780
1764
|
return
|
|
1781
1765
|
}
|
|
1782
1766
|
|
|
@@ -1798,17 +1782,14 @@ function httpNetworkFetch (
|
|
|
1798
1782
|
return this.decoder.write(bytes)
|
|
1799
1783
|
},
|
|
1800
1784
|
|
|
1801
|
-
|
|
1785
|
+
onComplete () {
|
|
1802
1786
|
this.decoder.end()
|
|
1803
1787
|
},
|
|
1804
1788
|
|
|
1805
1789
|
onError (error) {
|
|
1806
|
-
|
|
1807
|
-
context.off('terminated', onRequestAborted)
|
|
1808
|
-
|
|
1809
|
-
connection.destroy(error)
|
|
1790
|
+
this.decoder?.destroy(error)
|
|
1810
1791
|
|
|
1811
|
-
context.terminate({ reason: error })
|
|
1792
|
+
this.context.terminate({ reason: error })
|
|
1812
1793
|
|
|
1813
1794
|
if (!response) {
|
|
1814
1795
|
resolve(makeNetworkError(error))
|
package/lib/fetch/request.js
CHANGED
|
@@ -28,6 +28,10 @@ let TransformStream
|
|
|
28
28
|
|
|
29
29
|
const kInit = Symbol('init')
|
|
30
30
|
|
|
31
|
+
const requestFinalizer = new FinalizationRegistry(({ signal, abort }) => {
|
|
32
|
+
signal.removeEventListener('abort', abort)
|
|
33
|
+
})
|
|
34
|
+
|
|
31
35
|
// https://fetch.spec.whatwg.org/#request-class
|
|
32
36
|
class Request {
|
|
33
37
|
// https://fetch.spec.whatwg.org/#dom-request
|
|
@@ -123,7 +127,7 @@ class Request {
|
|
|
123
127
|
}
|
|
124
128
|
|
|
125
129
|
// 10. If init["window"] exists and is non-null, then throw a TypeError.
|
|
126
|
-
if ('window' in init && window
|
|
130
|
+
if ('window' in init && window != null) {
|
|
127
131
|
throw new TypeError(`'window' option '${window}' must be null`)
|
|
128
132
|
}
|
|
129
133
|
|
|
@@ -134,8 +138,48 @@ class Request {
|
|
|
134
138
|
|
|
135
139
|
// 12. Set request to a new request with the following properties:
|
|
136
140
|
request = makeRequest({
|
|
137
|
-
|
|
138
|
-
|
|
141
|
+
// URL request’s URL.
|
|
142
|
+
// undici implementation note: this is set as the first item in request's urlList in makeRequest
|
|
143
|
+
// method request’s method.
|
|
144
|
+
method: request.method,
|
|
145
|
+
// header list A copy of request’s header list.
|
|
146
|
+
// undici implementation note: headersList is cloned in makeRequest
|
|
147
|
+
headersList: request.headersList,
|
|
148
|
+
// unsafe-request flag Set.
|
|
149
|
+
unsafeRequest: request.unsafeRequest,
|
|
150
|
+
// client This’s relevant settings object.
|
|
151
|
+
client: request.client,
|
|
152
|
+
// window window.
|
|
153
|
+
window,
|
|
154
|
+
// priority request’s priority.
|
|
155
|
+
priority: request.priority,
|
|
156
|
+
// origin request’s origin. The propagation of the origin is only significant for navigation requests
|
|
157
|
+
// being handled by a service worker. In this scenario a request can have an origin that is different
|
|
158
|
+
// from the current client.
|
|
159
|
+
origin: request.origin,
|
|
160
|
+
// referrer request’s referrer.
|
|
161
|
+
referrer: request.referrer,
|
|
162
|
+
// referrer policy request’s referrer policy.
|
|
163
|
+
referrerPolicy: request.referrerPolicy,
|
|
164
|
+
// mode request’s mode.
|
|
165
|
+
mode: request.mode,
|
|
166
|
+
// credentials mode request’s credentials mode.
|
|
167
|
+
credentials: request.credentials,
|
|
168
|
+
// cache mode request’s cache mode.
|
|
169
|
+
cache: request.cache,
|
|
170
|
+
// redirect mode request’s redirect mode.
|
|
171
|
+
redirect: request.redirect,
|
|
172
|
+
// integrity metadata request’s integrity metadata.
|
|
173
|
+
integrity: request.integrity,
|
|
174
|
+
// keepalive request’s keepalive.
|
|
175
|
+
keepalive: request.keepalive,
|
|
176
|
+
// reload-navigation flag request’s reload-navigation flag.
|
|
177
|
+
reloadNavigation: request.reloadNavigation,
|
|
178
|
+
// history-navigation flag request’s history-navigation flag.
|
|
179
|
+
historyNavigation: request.historyNavigation,
|
|
180
|
+
// URL list A clone of request’s URL list.
|
|
181
|
+
// undici implementation note: urlList is cloned in makeRequest
|
|
182
|
+
urlList: request.urlList
|
|
139
183
|
})
|
|
140
184
|
|
|
141
185
|
// 13. If init is not empty, then:
|
|
@@ -151,11 +195,20 @@ class Request {
|
|
|
151
195
|
// 3. Unset request’s history-navigation flag.
|
|
152
196
|
request.historyNavigation = false
|
|
153
197
|
|
|
154
|
-
// 4. Set request’s
|
|
198
|
+
// 4. Set request’s origin to "client".
|
|
199
|
+
request.origin = 'client'
|
|
200
|
+
|
|
201
|
+
// 5. Set request’s referrer to "client"
|
|
155
202
|
request.referrer = 'client'
|
|
156
203
|
|
|
157
|
-
//
|
|
204
|
+
// 6. Set request’s referrer policy to the empty string.
|
|
158
205
|
request.referrerPolicy = ''
|
|
206
|
+
|
|
207
|
+
// 7. Set request’s URL to request’s current URL.
|
|
208
|
+
request.url = request.urlList[request.urlList.length - 1]
|
|
209
|
+
|
|
210
|
+
// 8. Set request’s URL list to « request’s URL ».
|
|
211
|
+
request.urlList = [request.url]
|
|
159
212
|
}
|
|
160
213
|
|
|
161
214
|
// 14. If init["referrer"] exists, then:
|
|
@@ -164,7 +217,7 @@ class Request {
|
|
|
164
217
|
const referrer = init.referrer
|
|
165
218
|
|
|
166
219
|
// 2. If referrer is the empty string, then set request’s referrer to "no-referrer".
|
|
167
|
-
if (
|
|
220
|
+
if (referrer === '') {
|
|
168
221
|
request.referrer = 'no-referrer'
|
|
169
222
|
} else {
|
|
170
223
|
// 1. Let parsedReferrer be the result of parsing referrer with
|
|
@@ -223,7 +276,7 @@ class Request {
|
|
|
223
276
|
}
|
|
224
277
|
|
|
225
278
|
// 18. If mode is non-null, set request’s mode to mode.
|
|
226
|
-
if (mode
|
|
279
|
+
if (mode != null) {
|
|
227
280
|
request.mode = mode
|
|
228
281
|
}
|
|
229
282
|
|
|
@@ -328,14 +381,9 @@ class Request {
|
|
|
328
381
|
if (signal.aborted) {
|
|
329
382
|
ac.abort()
|
|
330
383
|
} else {
|
|
331
|
-
|
|
332
|
-
signal.addEventListener(
|
|
333
|
-
|
|
334
|
-
function () {
|
|
335
|
-
ac.abort()
|
|
336
|
-
},
|
|
337
|
-
{ once: true }
|
|
338
|
-
)
|
|
384
|
+
const abort = () => ac.abort()
|
|
385
|
+
signal.addEventListener('abort', abort, { once: true })
|
|
386
|
+
requestFinalizer.register(this, { signal, abort })
|
|
339
387
|
}
|
|
340
388
|
}
|
|
341
389
|
|
|
@@ -769,7 +817,7 @@ function cloneRequest (request) {
|
|
|
769
817
|
|
|
770
818
|
// 2. If request’s body is non-null, set newRequest’s body to the
|
|
771
819
|
// result of cloning request’s body.
|
|
772
|
-
if (request.body
|
|
820
|
+
if (request.body != null) {
|
|
773
821
|
newRequest.body = cloneBody(request.body)
|
|
774
822
|
}
|
|
775
823
|
|
package/lib/fetch/response.js
CHANGED
|
@@ -327,7 +327,7 @@ function cloneResponse (response) {
|
|
|
327
327
|
|
|
328
328
|
// 3. If response’s body is non-null, then set newResponse’s body to the
|
|
329
329
|
// result of cloning response’s body.
|
|
330
|
-
if (response.body
|
|
330
|
+
if (response.body != null) {
|
|
331
331
|
newResponse.body = cloneBody(response.body)
|
|
332
332
|
}
|
|
333
333
|
|
package/lib/fetch/util.js
CHANGED
|
@@ -253,20 +253,20 @@ function coarsenedSharedCurrentTime (crossOriginIsolatedCapability) {
|
|
|
253
253
|
return performance.now()
|
|
254
254
|
}
|
|
255
255
|
|
|
256
|
-
|
|
256
|
+
// https://fetch.spec.whatwg.org/#create-an-opaque-timing-info
|
|
257
|
+
function createOpaqueTimingInfo (timingInfo) {
|
|
257
258
|
return {
|
|
258
|
-
startTime: 0,
|
|
259
|
+
startTime: timingInfo.startTime ?? 0,
|
|
259
260
|
redirectStartTime: 0,
|
|
260
261
|
redirectEndTime: 0,
|
|
261
|
-
postRedirectStartTime: 0,
|
|
262
|
+
postRedirectStartTime: timingInfo.startTime ?? 0,
|
|
262
263
|
finalServiceWorkerStartTime: 0,
|
|
263
264
|
finalNetworkResponseStartTime: 0,
|
|
264
265
|
finalNetworkRequestStartTime: 0,
|
|
265
266
|
endTime: 0,
|
|
266
267
|
encodedBodySize: 0,
|
|
267
268
|
decodedBodySize: 0,
|
|
268
|
-
finalConnectionTimingInfo: null
|
|
269
|
-
...init
|
|
269
|
+
finalConnectionTimingInfo: null
|
|
270
270
|
}
|
|
271
271
|
}
|
|
272
272
|
|
|
@@ -318,8 +318,7 @@ module.exports = {
|
|
|
318
318
|
TAOCheck,
|
|
319
319
|
corsCheck,
|
|
320
320
|
crossOriginResourcePolicyCheck,
|
|
321
|
-
|
|
322
|
-
makeTimingInfo,
|
|
321
|
+
createOpaqueTimingInfo,
|
|
323
322
|
setRequestReferrerPolicyOnRedirect,
|
|
324
323
|
isValidHTTPToken,
|
|
325
324
|
requestBadPort,
|
package/lib/pool-base.js
CHANGED
|
@@ -7,7 +7,7 @@ const {
|
|
|
7
7
|
InvalidArgumentError
|
|
8
8
|
} = require('./core/errors')
|
|
9
9
|
const FixedQueue = require('./node/fixed-queue')
|
|
10
|
-
const { kSize, kRunning, kPending, kBusy } = require('./core/symbols')
|
|
10
|
+
const { kSize, kRunning, kPending, kBusy, kUrl } = require('./core/symbols')
|
|
11
11
|
|
|
12
12
|
const kClients = Symbol('clients')
|
|
13
13
|
const kNeedDrain = Symbol('needDrain')
|
|
@@ -257,5 +257,5 @@ module.exports = {
|
|
|
257
257
|
kNeedDrain,
|
|
258
258
|
kAddClient,
|
|
259
259
|
kRemoveClient,
|
|
260
|
-
kDispatch
|
|
260
|
+
kDispatch
|
|
261
261
|
}
|
package/lib/pool.js
CHANGED
|
@@ -5,11 +5,11 @@ const {
|
|
|
5
5
|
kClients,
|
|
6
6
|
kNeedDrain,
|
|
7
7
|
kAddClient,
|
|
8
|
-
kDispatch
|
|
9
|
-
|
|
8
|
+
kDispatch
|
|
9
|
+
} = require('./pool-base')
|
|
10
10
|
const Client = require('./client')
|
|
11
11
|
const {
|
|
12
|
-
InvalidArgumentError
|
|
12
|
+
InvalidArgumentError
|
|
13
13
|
} = require('./core/errors')
|
|
14
14
|
const util = require('./core/util')
|
|
15
15
|
const { kUrl } = require('./core/symbols')
|