deepv-code 1.0.182 → 1.0.183
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/bundle/dvcode.js +635 -635
- package/package.json +1 -1
- package/bundle/assets/help/README.md +0 -113
- package/bundle/assets/sounds/README.md +0 -74
- package/bundle/node_modules/undici/LICENSE +0 -21
- package/bundle/node_modules/undici/README.md +0 -472
- package/bundle/node_modules/undici/docs/docs/api/Agent.md +0 -83
- package/bundle/node_modules/undici/docs/docs/api/BalancedPool.md +0 -99
- package/bundle/node_modules/undici/docs/docs/api/CacheStorage.md +0 -30
- package/bundle/node_modules/undici/docs/docs/api/CacheStore.md +0 -151
- package/bundle/node_modules/undici/docs/docs/api/Client.md +0 -281
- package/bundle/node_modules/undici/docs/docs/api/ClientStats.md +0 -27
- package/bundle/node_modules/undici/docs/docs/api/Connector.md +0 -115
- package/bundle/node_modules/undici/docs/docs/api/ContentType.md +0 -57
- package/bundle/node_modules/undici/docs/docs/api/Cookies.md +0 -101
- package/bundle/node_modules/undici/docs/docs/api/Debug.md +0 -62
- package/bundle/node_modules/undici/docs/docs/api/DiagnosticsChannel.md +0 -204
- package/bundle/node_modules/undici/docs/docs/api/Dispatcher.md +0 -1200
- package/bundle/node_modules/undici/docs/docs/api/EnvHttpProxyAgent.md +0 -159
- package/bundle/node_modules/undici/docs/docs/api/Errors.md +0 -49
- package/bundle/node_modules/undici/docs/docs/api/EventSource.md +0 -45
- package/bundle/node_modules/undici/docs/docs/api/Fetch.md +0 -52
- package/bundle/node_modules/undici/docs/docs/api/H2CClient.md +0 -262
- package/bundle/node_modules/undici/docs/docs/api/MockAgent.md +0 -603
- package/bundle/node_modules/undici/docs/docs/api/MockCallHistory.md +0 -197
- package/bundle/node_modules/undici/docs/docs/api/MockCallHistoryLog.md +0 -43
- package/bundle/node_modules/undici/docs/docs/api/MockClient.md +0 -77
- package/bundle/node_modules/undici/docs/docs/api/MockErrors.md +0 -12
- package/bundle/node_modules/undici/docs/docs/api/MockPool.md +0 -548
- package/bundle/node_modules/undici/docs/docs/api/Pool.md +0 -84
- package/bundle/node_modules/undici/docs/docs/api/PoolStats.md +0 -35
- package/bundle/node_modules/undici/docs/docs/api/ProxyAgent.md +0 -227
- package/bundle/node_modules/undici/docs/docs/api/RedirectHandler.md +0 -96
- package/bundle/node_modules/undici/docs/docs/api/RetryAgent.md +0 -45
- package/bundle/node_modules/undici/docs/docs/api/RetryHandler.md +0 -117
- package/bundle/node_modules/undici/docs/docs/api/Util.md +0 -25
- package/bundle/node_modules/undici/docs/docs/api/WebSocket.md +0 -85
- package/bundle/node_modules/undici/docs/docs/api/api-lifecycle.md +0 -91
- package/bundle/node_modules/undici/docs/docs/best-practices/client-certificate.md +0 -64
- package/bundle/node_modules/undici/docs/docs/best-practices/mocking-request.md +0 -190
- package/bundle/node_modules/undici/docs/docs/best-practices/proxy.md +0 -127
- package/bundle/node_modules/undici/docs/docs/best-practices/writing-tests.md +0 -20
- package/bundle/node_modules/undici/index-fetch.js +0 -35
- package/bundle/node_modules/undici/index.d.ts +0 -3
- package/bundle/node_modules/undici/index.js +0 -183
- package/bundle/node_modules/undici/lib/api/abort-signal.js +0 -59
- package/bundle/node_modules/undici/lib/api/api-connect.js +0 -110
- package/bundle/node_modules/undici/lib/api/api-pipeline.js +0 -252
- package/bundle/node_modules/undici/lib/api/api-request.js +0 -199
- package/bundle/node_modules/undici/lib/api/api-stream.js +0 -209
- package/bundle/node_modules/undici/lib/api/api-upgrade.js +0 -110
- package/bundle/node_modules/undici/lib/api/index.js +0 -7
- package/bundle/node_modules/undici/lib/api/readable.js +0 -558
- package/bundle/node_modules/undici/lib/api/util.js +0 -95
- package/bundle/node_modules/undici/lib/cache/memory-cache-store.js +0 -234
- package/bundle/node_modules/undici/lib/cache/sqlite-cache-store.js +0 -461
- package/bundle/node_modules/undici/lib/core/connect.js +0 -164
- package/bundle/node_modules/undici/lib/core/constants.js +0 -143
- package/bundle/node_modules/undici/lib/core/diagnostics.js +0 -196
- package/bundle/node_modules/undici/lib/core/errors.js +0 -244
- package/bundle/node_modules/undici/lib/core/request.js +0 -397
- package/bundle/node_modules/undici/lib/core/symbols.js +0 -68
- package/bundle/node_modules/undici/lib/core/tree.js +0 -160
- package/bundle/node_modules/undici/lib/core/util.js +0 -988
- package/bundle/node_modules/undici/lib/dispatcher/agent.js +0 -135
- package/bundle/node_modules/undici/lib/dispatcher/balanced-pool.js +0 -206
- package/bundle/node_modules/undici/lib/dispatcher/client-h1.js +0 -1615
- package/bundle/node_modules/undici/lib/dispatcher/client-h2.js +0 -798
- package/bundle/node_modules/undici/lib/dispatcher/client.js +0 -614
- package/bundle/node_modules/undici/lib/dispatcher/dispatcher-base.js +0 -161
- package/bundle/node_modules/undici/lib/dispatcher/dispatcher.js +0 -48
- package/bundle/node_modules/undici/lib/dispatcher/env-http-proxy-agent.js +0 -151
- package/bundle/node_modules/undici/lib/dispatcher/fixed-queue.js +0 -159
- package/bundle/node_modules/undici/lib/dispatcher/h2c-client.js +0 -122
- package/bundle/node_modules/undici/lib/dispatcher/pool-base.js +0 -191
- package/bundle/node_modules/undici/lib/dispatcher/pool.js +0 -118
- package/bundle/node_modules/undici/lib/dispatcher/proxy-agent.js +0 -275
- package/bundle/node_modules/undici/lib/dispatcher/retry-agent.js +0 -35
- package/bundle/node_modules/undici/lib/global.js +0 -32
- package/bundle/node_modules/undici/lib/handler/cache-handler.js +0 -448
- package/bundle/node_modules/undici/lib/handler/cache-revalidation-handler.js +0 -124
- package/bundle/node_modules/undici/lib/handler/decorator-handler.js +0 -67
- package/bundle/node_modules/undici/lib/handler/redirect-handler.js +0 -227
- package/bundle/node_modules/undici/lib/handler/retry-handler.js +0 -342
- package/bundle/node_modules/undici/lib/handler/unwrap-handler.js +0 -96
- package/bundle/node_modules/undici/lib/handler/wrap-handler.js +0 -95
- package/bundle/node_modules/undici/lib/interceptor/cache.js +0 -372
- package/bundle/node_modules/undici/lib/interceptor/dns.js +0 -432
- package/bundle/node_modules/undici/lib/interceptor/dump.js +0 -111
- package/bundle/node_modules/undici/lib/interceptor/redirect.js +0 -21
- package/bundle/node_modules/undici/lib/interceptor/response-error.js +0 -95
- package/bundle/node_modules/undici/lib/interceptor/retry.js +0 -19
- package/bundle/node_modules/undici/lib/llhttp/.gitkeep +0 -0
- package/bundle/node_modules/undici/lib/llhttp/constants.d.ts +0 -97
- package/bundle/node_modules/undici/lib/llhttp/constants.js +0 -498
- package/bundle/node_modules/undici/lib/llhttp/constants.js.map +0 -1
- package/bundle/node_modules/undici/lib/llhttp/llhttp-wasm.js +0 -15
- package/bundle/node_modules/undici/lib/llhttp/llhttp_simd-wasm.js +0 -15
- package/bundle/node_modules/undici/lib/llhttp/utils.d.ts +0 -2
- package/bundle/node_modules/undici/lib/llhttp/utils.js +0 -15
- package/bundle/node_modules/undici/lib/llhttp/utils.js.map +0 -1
- package/bundle/node_modules/undici/lib/mock/mock-agent.js +0 -224
- package/bundle/node_modules/undici/lib/mock/mock-call-history.js +0 -248
- package/bundle/node_modules/undici/lib/mock/mock-client.js +0 -64
- package/bundle/node_modules/undici/lib/mock/mock-errors.js +0 -19
- package/bundle/node_modules/undici/lib/mock/mock-interceptor.js +0 -209
- package/bundle/node_modules/undici/lib/mock/mock-pool.js +0 -64
- package/bundle/node_modules/undici/lib/mock/mock-symbols.js +0 -31
- package/bundle/node_modules/undici/lib/mock/mock-utils.js +0 -433
- package/bundle/node_modules/undici/lib/mock/pending-interceptors-formatter.js +0 -43
- package/bundle/node_modules/undici/lib/util/cache.js +0 -368
- package/bundle/node_modules/undici/lib/util/date.js +0 -259
- package/bundle/node_modules/undici/lib/util/stats.js +0 -32
- package/bundle/node_modules/undici/lib/util/timers.js +0 -423
- package/bundle/node_modules/undici/lib/web/cache/cache.js +0 -862
- package/bundle/node_modules/undici/lib/web/cache/cachestorage.js +0 -152
- package/bundle/node_modules/undici/lib/web/cache/util.js +0 -45
- package/bundle/node_modules/undici/lib/web/cookies/constants.js +0 -12
- package/bundle/node_modules/undici/lib/web/cookies/index.js +0 -199
- package/bundle/node_modules/undici/lib/web/cookies/parse.js +0 -322
- package/bundle/node_modules/undici/lib/web/cookies/util.js +0 -282
- package/bundle/node_modules/undici/lib/web/eventsource/eventsource-stream.js +0 -399
- package/bundle/node_modules/undici/lib/web/eventsource/eventsource.js +0 -484
- package/bundle/node_modules/undici/lib/web/eventsource/util.js +0 -37
- package/bundle/node_modules/undici/lib/web/fetch/LICENSE +0 -21
- package/bundle/node_modules/undici/lib/web/fetch/body.js +0 -532
- package/bundle/node_modules/undici/lib/web/fetch/constants.js +0 -131
- package/bundle/node_modules/undici/lib/web/fetch/data-url.js +0 -744
- package/bundle/node_modules/undici/lib/web/fetch/dispatcher-weakref.js +0 -46
- package/bundle/node_modules/undici/lib/web/fetch/formdata-parser.js +0 -501
- package/bundle/node_modules/undici/lib/web/fetch/formdata.js +0 -263
- package/bundle/node_modules/undici/lib/web/fetch/global.js +0 -40
- package/bundle/node_modules/undici/lib/web/fetch/headers.js +0 -719
- package/bundle/node_modules/undici/lib/web/fetch/index.js +0 -2258
- package/bundle/node_modules/undici/lib/web/fetch/request.js +0 -1099
- package/bundle/node_modules/undici/lib/web/fetch/response.js +0 -636
- package/bundle/node_modules/undici/lib/web/fetch/util.js +0 -1782
- package/bundle/node_modules/undici/lib/web/fetch/webidl.js +0 -740
- package/bundle/node_modules/undici/lib/web/websocket/connection.js +0 -325
- package/bundle/node_modules/undici/lib/web/websocket/constants.js +0 -126
- package/bundle/node_modules/undici/lib/web/websocket/events.js +0 -331
- package/bundle/node_modules/undici/lib/web/websocket/frame.js +0 -138
- package/bundle/node_modules/undici/lib/web/websocket/permessage-deflate.js +0 -70
- package/bundle/node_modules/undici/lib/web/websocket/receiver.js +0 -454
- package/bundle/node_modules/undici/lib/web/websocket/sender.js +0 -109
- package/bundle/node_modules/undici/lib/web/websocket/stream/websocketerror.js +0 -83
- package/bundle/node_modules/undici/lib/web/websocket/stream/websocketstream.js +0 -485
- package/bundle/node_modules/undici/lib/web/websocket/util.js +0 -338
- package/bundle/node_modules/undici/lib/web/websocket/websocket.js +0 -686
- package/bundle/node_modules/undici/package.json +0 -149
- package/bundle/node_modules/undici/scripts/strip-comments.js +0 -10
- package/bundle/node_modules/undici/types/README.md +0 -6
- package/bundle/node_modules/undici/types/agent.d.ts +0 -35
- package/bundle/node_modules/undici/types/api.d.ts +0 -43
- package/bundle/node_modules/undici/types/balanced-pool.d.ts +0 -29
- package/bundle/node_modules/undici/types/cache-interceptor.d.ts +0 -172
- package/bundle/node_modules/undici/types/cache.d.ts +0 -36
- package/bundle/node_modules/undici/types/client-stats.d.ts +0 -15
- package/bundle/node_modules/undici/types/client.d.ts +0 -110
- package/bundle/node_modules/undici/types/connector.d.ts +0 -34
- package/bundle/node_modules/undici/types/content-type.d.ts +0 -21
- package/bundle/node_modules/undici/types/cookies.d.ts +0 -30
- package/bundle/node_modules/undici/types/diagnostics-channel.d.ts +0 -66
- package/bundle/node_modules/undici/types/dispatcher.d.ts +0 -281
- package/bundle/node_modules/undici/types/env-http-proxy-agent.d.ts +0 -21
- package/bundle/node_modules/undici/types/errors.d.ts +0 -171
- package/bundle/node_modules/undici/types/eventsource.d.ts +0 -61
- package/bundle/node_modules/undici/types/fetch.d.ts +0 -210
- package/bundle/node_modules/undici/types/formdata.d.ts +0 -108
- package/bundle/node_modules/undici/types/global-dispatcher.d.ts +0 -9
- package/bundle/node_modules/undici/types/global-origin.d.ts +0 -7
- package/bundle/node_modules/undici/types/h2c-client.d.ts +0 -75
- package/bundle/node_modules/undici/types/handlers.d.ts +0 -15
- package/bundle/node_modules/undici/types/header.d.ts +0 -160
- package/bundle/node_modules/undici/types/index.d.ts +0 -75
- package/bundle/node_modules/undici/types/interceptors.d.ts +0 -34
- package/bundle/node_modules/undici/types/mock-agent.d.ts +0 -68
- package/bundle/node_modules/undici/types/mock-call-history.d.ts +0 -111
- package/bundle/node_modules/undici/types/mock-client.d.ts +0 -25
- package/bundle/node_modules/undici/types/mock-errors.d.ts +0 -12
- package/bundle/node_modules/undici/types/mock-interceptor.d.ts +0 -93
- package/bundle/node_modules/undici/types/mock-pool.d.ts +0 -25
- package/bundle/node_modules/undici/types/patch.d.ts +0 -29
- package/bundle/node_modules/undici/types/pool-stats.d.ts +0 -19
- package/bundle/node_modules/undici/types/pool.d.ts +0 -41
- package/bundle/node_modules/undici/types/proxy-agent.d.ts +0 -29
- package/bundle/node_modules/undici/types/readable.d.ts +0 -68
- package/bundle/node_modules/undici/types/retry-agent.d.ts +0 -8
- package/bundle/node_modules/undici/types/retry-handler.d.ts +0 -116
- package/bundle/node_modules/undici/types/util.d.ts +0 -18
- package/bundle/node_modules/undici/types/utility.d.ts +0 -7
- package/bundle/node_modules/undici/types/webidl.d.ts +0 -266
- package/bundle/node_modules/undici/types/websocket.d.ts +0 -184
|
@@ -1,2258 +0,0 @@
|
|
|
1
|
-
// https://github.com/Ethan-Arrowood/undici-fetch
|
|
2
|
-
|
|
3
|
-
'use strict'
|
|
4
|
-
|
|
5
|
-
const {
|
|
6
|
-
makeNetworkError,
|
|
7
|
-
makeAppropriateNetworkError,
|
|
8
|
-
filterResponse,
|
|
9
|
-
makeResponse,
|
|
10
|
-
fromInnerResponse,
|
|
11
|
-
getResponseState
|
|
12
|
-
} = require('./response')
|
|
13
|
-
const { HeadersList } = require('./headers')
|
|
14
|
-
const { Request, cloneRequest, getRequestDispatcher, getRequestState } = require('./request')
|
|
15
|
-
const zlib = require('node:zlib')
|
|
16
|
-
const {
|
|
17
|
-
bytesMatch,
|
|
18
|
-
makePolicyContainer,
|
|
19
|
-
clonePolicyContainer,
|
|
20
|
-
requestBadPort,
|
|
21
|
-
TAOCheck,
|
|
22
|
-
appendRequestOriginHeader,
|
|
23
|
-
responseLocationURL,
|
|
24
|
-
requestCurrentURL,
|
|
25
|
-
setRequestReferrerPolicyOnRedirect,
|
|
26
|
-
tryUpgradeRequestToAPotentiallyTrustworthyURL,
|
|
27
|
-
createOpaqueTimingInfo,
|
|
28
|
-
appendFetchMetadata,
|
|
29
|
-
corsCheck,
|
|
30
|
-
crossOriginResourcePolicyCheck,
|
|
31
|
-
determineRequestsReferrer,
|
|
32
|
-
coarsenedSharedCurrentTime,
|
|
33
|
-
createDeferredPromise,
|
|
34
|
-
sameOrigin,
|
|
35
|
-
isCancelled,
|
|
36
|
-
isAborted,
|
|
37
|
-
isErrorLike,
|
|
38
|
-
fullyReadBody,
|
|
39
|
-
readableStreamClose,
|
|
40
|
-
isomorphicEncode,
|
|
41
|
-
urlIsLocal,
|
|
42
|
-
urlIsHttpHttpsScheme,
|
|
43
|
-
urlHasHttpsScheme,
|
|
44
|
-
clampAndCoarsenConnectionTimingInfo,
|
|
45
|
-
simpleRangeHeaderValue,
|
|
46
|
-
buildContentRange,
|
|
47
|
-
createInflate,
|
|
48
|
-
extractMimeType
|
|
49
|
-
} = require('./util')
|
|
50
|
-
const assert = require('node:assert')
|
|
51
|
-
const { safelyExtractBody, extractBody } = require('./body')
|
|
52
|
-
const {
|
|
53
|
-
redirectStatusSet,
|
|
54
|
-
nullBodyStatus,
|
|
55
|
-
safeMethodsSet,
|
|
56
|
-
requestBodyHeader,
|
|
57
|
-
subresourceSet
|
|
58
|
-
} = require('./constants')
|
|
59
|
-
const EE = require('node:events')
|
|
60
|
-
const { Readable, pipeline, finished, isErrored, isReadable } = require('node:stream')
|
|
61
|
-
const { addAbortListener, bufferToLowerCasedHeaderName } = require('../../core/util')
|
|
62
|
-
const { dataURLProcessor, serializeAMimeType, minimizeSupportedMimeType } = require('./data-url')
|
|
63
|
-
const { getGlobalDispatcher } = require('../../global')
|
|
64
|
-
const { webidl } = require('./webidl')
|
|
65
|
-
const { STATUS_CODES } = require('node:http')
|
|
66
|
-
const GET_OR_HEAD = ['GET', 'HEAD']
|
|
67
|
-
|
|
68
|
-
const defaultUserAgent = typeof __UNDICI_IS_NODE__ !== 'undefined' || typeof esbuildDetection !== 'undefined'
|
|
69
|
-
? 'node'
|
|
70
|
-
: 'undici'
|
|
71
|
-
|
|
72
|
-
/** @type {import('buffer').resolveObjectURL} */
|
|
73
|
-
let resolveObjectURL
|
|
74
|
-
|
|
75
|
-
class Fetch extends EE {
|
|
76
|
-
constructor (dispatcher) {
|
|
77
|
-
super()
|
|
78
|
-
|
|
79
|
-
this.dispatcher = dispatcher
|
|
80
|
-
this.connection = null
|
|
81
|
-
this.dump = false
|
|
82
|
-
this.state = 'ongoing'
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
terminate (reason) {
|
|
86
|
-
if (this.state !== 'ongoing') {
|
|
87
|
-
return
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
this.state = 'terminated'
|
|
91
|
-
this.connection?.destroy(reason)
|
|
92
|
-
this.emit('terminated', reason)
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// https://fetch.spec.whatwg.org/#fetch-controller-abort
|
|
96
|
-
abort (error) {
|
|
97
|
-
if (this.state !== 'ongoing') {
|
|
98
|
-
return
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// 1. Set controller’s state to "aborted".
|
|
102
|
-
this.state = 'aborted'
|
|
103
|
-
|
|
104
|
-
// 2. Let fallbackError be an "AbortError" DOMException.
|
|
105
|
-
// 3. Set error to fallbackError if it is not given.
|
|
106
|
-
if (!error) {
|
|
107
|
-
error = new DOMException('The operation was aborted.', 'AbortError')
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// 4. Let serializedError be StructuredSerialize(error).
|
|
111
|
-
// If that threw an exception, catch it, and let
|
|
112
|
-
// serializedError be StructuredSerialize(fallbackError).
|
|
113
|
-
|
|
114
|
-
// 5. Set controller’s serialized abort reason to serializedError.
|
|
115
|
-
this.serializedAbortReason = error
|
|
116
|
-
|
|
117
|
-
this.connection?.destroy(error)
|
|
118
|
-
this.emit('terminated', error)
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
function handleFetchDone (response) {
|
|
123
|
-
finalizeAndReportTiming(response, 'fetch')
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// https://fetch.spec.whatwg.org/#fetch-method
|
|
127
|
-
function fetch (input, init = undefined) {
|
|
128
|
-
webidl.argumentLengthCheck(arguments, 1, 'globalThis.fetch')
|
|
129
|
-
|
|
130
|
-
// 1. Let p be a new promise.
|
|
131
|
-
let p = createDeferredPromise()
|
|
132
|
-
|
|
133
|
-
// 2. Let requestObject be the result of invoking the initial value of
|
|
134
|
-
// Request as constructor with input and init as arguments. If this throws
|
|
135
|
-
// an exception, reject p with it and return p.
|
|
136
|
-
let requestObject
|
|
137
|
-
|
|
138
|
-
try {
|
|
139
|
-
requestObject = new Request(input, init)
|
|
140
|
-
} catch (e) {
|
|
141
|
-
p.reject(e)
|
|
142
|
-
return p.promise
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// 3. Let request be requestObject’s request.
|
|
146
|
-
const request = getRequestState(requestObject)
|
|
147
|
-
|
|
148
|
-
// 4. If requestObject’s signal’s aborted flag is set, then:
|
|
149
|
-
if (requestObject.signal.aborted) {
|
|
150
|
-
// 1. Abort the fetch() call with p, request, null, and
|
|
151
|
-
// requestObject’s signal’s abort reason.
|
|
152
|
-
abortFetch(p, request, null, requestObject.signal.reason)
|
|
153
|
-
|
|
154
|
-
// 2. Return p.
|
|
155
|
-
return p.promise
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// 5. Let globalObject be request’s client’s global object.
|
|
159
|
-
const globalObject = request.client.globalObject
|
|
160
|
-
|
|
161
|
-
// 6. If globalObject is a ServiceWorkerGlobalScope object, then set
|
|
162
|
-
// request’s service-workers mode to "none".
|
|
163
|
-
if (globalObject?.constructor?.name === 'ServiceWorkerGlobalScope') {
|
|
164
|
-
request.serviceWorkers = 'none'
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// 7. Let responseObject be null.
|
|
168
|
-
let responseObject = null
|
|
169
|
-
|
|
170
|
-
// 8. Let relevantRealm be this’s relevant Realm.
|
|
171
|
-
|
|
172
|
-
// 9. Let locallyAborted be false.
|
|
173
|
-
let locallyAborted = false
|
|
174
|
-
|
|
175
|
-
// 10. Let controller be null.
|
|
176
|
-
let controller = null
|
|
177
|
-
|
|
178
|
-
// 11. Add the following abort steps to requestObject’s signal:
|
|
179
|
-
addAbortListener(
|
|
180
|
-
requestObject.signal,
|
|
181
|
-
() => {
|
|
182
|
-
// 1. Set locallyAborted to true.
|
|
183
|
-
locallyAborted = true
|
|
184
|
-
|
|
185
|
-
// 2. Assert: controller is non-null.
|
|
186
|
-
assert(controller != null)
|
|
187
|
-
|
|
188
|
-
// 3. Abort controller with requestObject’s signal’s abort reason.
|
|
189
|
-
controller.abort(requestObject.signal.reason)
|
|
190
|
-
|
|
191
|
-
const realResponse = responseObject?.deref()
|
|
192
|
-
|
|
193
|
-
// 4. Abort the fetch() call with p, request, responseObject,
|
|
194
|
-
// and requestObject’s signal’s abort reason.
|
|
195
|
-
abortFetch(p, request, realResponse, requestObject.signal.reason)
|
|
196
|
-
}
|
|
197
|
-
)
|
|
198
|
-
|
|
199
|
-
// 12. Let handleFetchDone given response response be to finalize and
|
|
200
|
-
// report timing with response, globalObject, and "fetch".
|
|
201
|
-
// see function handleFetchDone
|
|
202
|
-
|
|
203
|
-
// 13. Set controller to the result of calling fetch given request,
|
|
204
|
-
// with processResponseEndOfBody set to handleFetchDone, and processResponse
|
|
205
|
-
// given response being these substeps:
|
|
206
|
-
|
|
207
|
-
const processResponse = (response) => {
|
|
208
|
-
// 1. If locallyAborted is true, terminate these substeps.
|
|
209
|
-
if (locallyAborted) {
|
|
210
|
-
return
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// 2. If response’s aborted flag is set, then:
|
|
214
|
-
if (response.aborted) {
|
|
215
|
-
// 1. Let deserializedError be the result of deserialize a serialized
|
|
216
|
-
// abort reason given controller’s serialized abort reason and
|
|
217
|
-
// relevantRealm.
|
|
218
|
-
|
|
219
|
-
// 2. Abort the fetch() call with p, request, responseObject, and
|
|
220
|
-
// deserializedError.
|
|
221
|
-
|
|
222
|
-
abortFetch(p, request, responseObject, controller.serializedAbortReason)
|
|
223
|
-
return
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
// 3. If response is a network error, then reject p with a TypeError
|
|
227
|
-
// and terminate these substeps.
|
|
228
|
-
if (response.type === 'error') {
|
|
229
|
-
p.reject(new TypeError('fetch failed', { cause: response.error }))
|
|
230
|
-
return
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
// 4. Set responseObject to the result of creating a Response object,
|
|
234
|
-
// given response, "immutable", and relevantRealm.
|
|
235
|
-
responseObject = new WeakRef(fromInnerResponse(response, 'immutable'))
|
|
236
|
-
|
|
237
|
-
// 5. Resolve p with responseObject.
|
|
238
|
-
p.resolve(responseObject.deref())
|
|
239
|
-
p = null
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
controller = fetching({
|
|
243
|
-
request,
|
|
244
|
-
processResponseEndOfBody: handleFetchDone,
|
|
245
|
-
processResponse,
|
|
246
|
-
dispatcher: getRequestDispatcher(requestObject) // undici
|
|
247
|
-
})
|
|
248
|
-
|
|
249
|
-
// 14. Return p.
|
|
250
|
-
return p.promise
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
// https://fetch.spec.whatwg.org/#finalize-and-report-timing
|
|
254
|
-
function finalizeAndReportTiming (response, initiatorType = 'other') {
|
|
255
|
-
// 1. If response is an aborted network error, then return.
|
|
256
|
-
if (response.type === 'error' && response.aborted) {
|
|
257
|
-
return
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
// 2. If response’s URL list is null or empty, then return.
|
|
261
|
-
if (!response.urlList?.length) {
|
|
262
|
-
return
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
// 3. Let originalURL be response’s URL list[0].
|
|
266
|
-
const originalURL = response.urlList[0]
|
|
267
|
-
|
|
268
|
-
// 4. Let timingInfo be response’s timing info.
|
|
269
|
-
let timingInfo = response.timingInfo
|
|
270
|
-
|
|
271
|
-
// 5. Let cacheState be response’s cache state.
|
|
272
|
-
let cacheState = response.cacheState
|
|
273
|
-
|
|
274
|
-
// 6. If originalURL’s scheme is not an HTTP(S) scheme, then return.
|
|
275
|
-
if (!urlIsHttpHttpsScheme(originalURL)) {
|
|
276
|
-
return
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
// 7. If timingInfo is null, then return.
|
|
280
|
-
if (timingInfo === null) {
|
|
281
|
-
return
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
// 8. If response’s timing allow passed flag is not set, then:
|
|
285
|
-
if (!response.timingAllowPassed) {
|
|
286
|
-
// 1. Set timingInfo to a the result of creating an opaque timing info for timingInfo.
|
|
287
|
-
timingInfo = createOpaqueTimingInfo({
|
|
288
|
-
startTime: timingInfo.startTime
|
|
289
|
-
})
|
|
290
|
-
|
|
291
|
-
// 2. Set cacheState to the empty string.
|
|
292
|
-
cacheState = ''
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
// 9. Set timingInfo’s end time to the coarsened shared current time
|
|
296
|
-
// given global’s relevant settings object’s cross-origin isolated
|
|
297
|
-
// capability.
|
|
298
|
-
// TODO: given global’s relevant settings object’s cross-origin isolated
|
|
299
|
-
// capability?
|
|
300
|
-
timingInfo.endTime = coarsenedSharedCurrentTime()
|
|
301
|
-
|
|
302
|
-
// 10. Set response’s timing info to timingInfo.
|
|
303
|
-
response.timingInfo = timingInfo
|
|
304
|
-
|
|
305
|
-
// 11. Mark resource timing for timingInfo, originalURL, initiatorType,
|
|
306
|
-
// global, and cacheState.
|
|
307
|
-
markResourceTiming(
|
|
308
|
-
timingInfo,
|
|
309
|
-
originalURL.href,
|
|
310
|
-
initiatorType,
|
|
311
|
-
globalThis,
|
|
312
|
-
cacheState,
|
|
313
|
-
'', // bodyType
|
|
314
|
-
response.status
|
|
315
|
-
)
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
// https://w3c.github.io/resource-timing/#dfn-mark-resource-timing
|
|
319
|
-
const markResourceTiming = performance.markResourceTiming
|
|
320
|
-
|
|
321
|
-
// https://fetch.spec.whatwg.org/#abort-fetch
|
|
322
|
-
function abortFetch (p, request, responseObject, error) {
|
|
323
|
-
// 1. Reject promise with error.
|
|
324
|
-
if (p) {
|
|
325
|
-
// We might have already resolved the promise at this stage
|
|
326
|
-
p.reject(error)
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
// 2. If request’s body is not null and is readable, then cancel request’s
|
|
330
|
-
// body with error.
|
|
331
|
-
if (request.body?.stream != null && isReadable(request.body.stream)) {
|
|
332
|
-
request.body.stream.cancel(error).catch((err) => {
|
|
333
|
-
if (err.code === 'ERR_INVALID_STATE') {
|
|
334
|
-
// Node bug?
|
|
335
|
-
return
|
|
336
|
-
}
|
|
337
|
-
throw err
|
|
338
|
-
})
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
// 3. If responseObject is null, then return.
|
|
342
|
-
if (responseObject == null) {
|
|
343
|
-
return
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
// 4. Let response be responseObject’s response.
|
|
347
|
-
const response = getResponseState(responseObject)
|
|
348
|
-
|
|
349
|
-
// 5. If response’s body is not null and is readable, then error response’s
|
|
350
|
-
// body with error.
|
|
351
|
-
if (response.body?.stream != null && isReadable(response.body.stream)) {
|
|
352
|
-
response.body.stream.cancel(error).catch((err) => {
|
|
353
|
-
if (err.code === 'ERR_INVALID_STATE') {
|
|
354
|
-
// Node bug?
|
|
355
|
-
return
|
|
356
|
-
}
|
|
357
|
-
throw err
|
|
358
|
-
})
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
// https://fetch.spec.whatwg.org/#fetching
|
|
363
|
-
function fetching ({
|
|
364
|
-
request,
|
|
365
|
-
processRequestBodyChunkLength,
|
|
366
|
-
processRequestEndOfBody,
|
|
367
|
-
processResponse,
|
|
368
|
-
processResponseEndOfBody,
|
|
369
|
-
processResponseConsumeBody,
|
|
370
|
-
useParallelQueue = false,
|
|
371
|
-
dispatcher = getGlobalDispatcher() // undici
|
|
372
|
-
}) {
|
|
373
|
-
// Ensure that the dispatcher is set accordingly
|
|
374
|
-
assert(dispatcher)
|
|
375
|
-
|
|
376
|
-
// 1. Let taskDestination be null.
|
|
377
|
-
let taskDestination = null
|
|
378
|
-
|
|
379
|
-
// 2. Let crossOriginIsolatedCapability be false.
|
|
380
|
-
let crossOriginIsolatedCapability = false
|
|
381
|
-
|
|
382
|
-
// 3. If request’s client is non-null, then:
|
|
383
|
-
if (request.client != null) {
|
|
384
|
-
// 1. Set taskDestination to request’s client’s global object.
|
|
385
|
-
taskDestination = request.client.globalObject
|
|
386
|
-
|
|
387
|
-
// 2. Set crossOriginIsolatedCapability to request’s client’s cross-origin
|
|
388
|
-
// isolated capability.
|
|
389
|
-
crossOriginIsolatedCapability =
|
|
390
|
-
request.client.crossOriginIsolatedCapability
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
// 4. If useParallelQueue is true, then set taskDestination to the result of
|
|
394
|
-
// starting a new parallel queue.
|
|
395
|
-
// TODO
|
|
396
|
-
|
|
397
|
-
// 5. Let timingInfo be a new fetch timing info whose start time and
|
|
398
|
-
// post-redirect start time are the coarsened shared current time given
|
|
399
|
-
// crossOriginIsolatedCapability.
|
|
400
|
-
const currentTime = coarsenedSharedCurrentTime(crossOriginIsolatedCapability)
|
|
401
|
-
const timingInfo = createOpaqueTimingInfo({
|
|
402
|
-
startTime: currentTime
|
|
403
|
-
})
|
|
404
|
-
|
|
405
|
-
// 6. Let fetchParams be a new fetch params whose
|
|
406
|
-
// request is request,
|
|
407
|
-
// timing info is timingInfo,
|
|
408
|
-
// process request body chunk length is processRequestBodyChunkLength,
|
|
409
|
-
// process request end-of-body is processRequestEndOfBody,
|
|
410
|
-
// process response is processResponse,
|
|
411
|
-
// process response consume body is processResponseConsumeBody,
|
|
412
|
-
// process response end-of-body is processResponseEndOfBody,
|
|
413
|
-
// task destination is taskDestination,
|
|
414
|
-
// and cross-origin isolated capability is crossOriginIsolatedCapability.
|
|
415
|
-
const fetchParams = {
|
|
416
|
-
controller: new Fetch(dispatcher),
|
|
417
|
-
request,
|
|
418
|
-
timingInfo,
|
|
419
|
-
processRequestBodyChunkLength,
|
|
420
|
-
processRequestEndOfBody,
|
|
421
|
-
processResponse,
|
|
422
|
-
processResponseConsumeBody,
|
|
423
|
-
processResponseEndOfBody,
|
|
424
|
-
taskDestination,
|
|
425
|
-
crossOriginIsolatedCapability
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
// 7. If request’s body is a byte sequence, then set request’s body to
|
|
429
|
-
// request’s body as a body.
|
|
430
|
-
// NOTE: Since fetching is only called from fetch, body should already be
|
|
431
|
-
// extracted.
|
|
432
|
-
assert(!request.body || request.body.stream)
|
|
433
|
-
|
|
434
|
-
// 8. If request’s window is "client", then set request’s window to request’s
|
|
435
|
-
// client, if request’s client’s global object is a Window object; otherwise
|
|
436
|
-
// "no-window".
|
|
437
|
-
if (request.window === 'client') {
|
|
438
|
-
// TODO: What if request.client is null?
|
|
439
|
-
request.window =
|
|
440
|
-
request.client?.globalObject?.constructor?.name === 'Window'
|
|
441
|
-
? request.client
|
|
442
|
-
: 'no-window'
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
// 9. If request’s origin is "client", then set request’s origin to request’s
|
|
446
|
-
// client’s origin.
|
|
447
|
-
if (request.origin === 'client') {
|
|
448
|
-
request.origin = request.client.origin
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
// 10. If all of the following conditions are true:
|
|
452
|
-
// TODO
|
|
453
|
-
|
|
454
|
-
// 11. If request’s policy container is "client", then:
|
|
455
|
-
if (request.policyContainer === 'client') {
|
|
456
|
-
// 1. If request’s client is non-null, then set request’s policy
|
|
457
|
-
// container to a clone of request’s client’s policy container. [HTML]
|
|
458
|
-
if (request.client != null) {
|
|
459
|
-
request.policyContainer = clonePolicyContainer(
|
|
460
|
-
request.client.policyContainer
|
|
461
|
-
)
|
|
462
|
-
} else {
|
|
463
|
-
// 2. Otherwise, set request’s policy container to a new policy
|
|
464
|
-
// container.
|
|
465
|
-
request.policyContainer = makePolicyContainer()
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
// 12. If request’s header list does not contain `Accept`, then:
|
|
470
|
-
if (!request.headersList.contains('accept', true)) {
|
|
471
|
-
// 1. Let value be `*/*`.
|
|
472
|
-
const value = '*/*'
|
|
473
|
-
|
|
474
|
-
// 2. A user agent should set value to the first matching statement, if
|
|
475
|
-
// any, switching on request’s destination:
|
|
476
|
-
// "document"
|
|
477
|
-
// "frame"
|
|
478
|
-
// "iframe"
|
|
479
|
-
// `text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8`
|
|
480
|
-
// "image"
|
|
481
|
-
// `image/png,image/svg+xml,image/*;q=0.8,*/*;q=0.5`
|
|
482
|
-
// "style"
|
|
483
|
-
// `text/css,*/*;q=0.1`
|
|
484
|
-
// TODO
|
|
485
|
-
|
|
486
|
-
// 3. Append `Accept`/value to request’s header list.
|
|
487
|
-
request.headersList.append('accept', value, true)
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
// 13. If request’s header list does not contain `Accept-Language`, then
|
|
491
|
-
// user agents should append `Accept-Language`/an appropriate value to
|
|
492
|
-
// request’s header list.
|
|
493
|
-
if (!request.headersList.contains('accept-language', true)) {
|
|
494
|
-
request.headersList.append('accept-language', '*', true)
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
// 14. If request’s priority is null, then use request’s initiator and
|
|
498
|
-
// destination appropriately in setting request’s priority to a
|
|
499
|
-
// user-agent-defined object.
|
|
500
|
-
if (request.priority === null) {
|
|
501
|
-
// TODO
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
// 15. If request is a subresource request, then:
|
|
505
|
-
if (subresourceSet.has(request.destination)) {
|
|
506
|
-
// TODO
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
// 16. Run main fetch given fetchParams.
|
|
510
|
-
mainFetch(fetchParams)
|
|
511
|
-
.catch(err => {
|
|
512
|
-
fetchParams.controller.terminate(err)
|
|
513
|
-
})
|
|
514
|
-
|
|
515
|
-
// 17. Return fetchParam's controller
|
|
516
|
-
return fetchParams.controller
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
// https://fetch.spec.whatwg.org/#concept-main-fetch
|
|
520
|
-
async function mainFetch (fetchParams, recursive = false) {
|
|
521
|
-
// 1. Let request be fetchParams’s request.
|
|
522
|
-
const request = fetchParams.request
|
|
523
|
-
|
|
524
|
-
// 2. Let response be null.
|
|
525
|
-
let response = null
|
|
526
|
-
|
|
527
|
-
// 3. If request’s local-URLs-only flag is set and request’s current URL is
|
|
528
|
-
// not local, then set response to a network error.
|
|
529
|
-
if (request.localURLsOnly && !urlIsLocal(requestCurrentURL(request))) {
|
|
530
|
-
response = makeNetworkError('local URLs only')
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
// 4. Run report Content Security Policy violations for request.
|
|
534
|
-
// TODO
|
|
535
|
-
|
|
536
|
-
// 5. Upgrade request to a potentially trustworthy URL, if appropriate.
|
|
537
|
-
tryUpgradeRequestToAPotentiallyTrustworthyURL(request)
|
|
538
|
-
|
|
539
|
-
// 6. If should request be blocked due to a bad port, should fetching request
|
|
540
|
-
// be blocked as mixed content, or should request be blocked by Content
|
|
541
|
-
// Security Policy returns blocked, then set response to a network error.
|
|
542
|
-
if (requestBadPort(request) === 'blocked') {
|
|
543
|
-
response = makeNetworkError('bad port')
|
|
544
|
-
}
|
|
545
|
-
// TODO: should fetching request be blocked as mixed content?
|
|
546
|
-
// TODO: should request be blocked by Content Security Policy?
|
|
547
|
-
|
|
548
|
-
// 7. If request’s referrer policy is the empty string, then set request’s
|
|
549
|
-
// referrer policy to request’s policy container’s referrer policy.
|
|
550
|
-
if (request.referrerPolicy === '') {
|
|
551
|
-
request.referrerPolicy = request.policyContainer.referrerPolicy
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
// 8. If request’s referrer is not "no-referrer", then set request’s
|
|
555
|
-
// referrer to the result of invoking determine request’s referrer.
|
|
556
|
-
if (request.referrer !== 'no-referrer') {
|
|
557
|
-
request.referrer = determineRequestsReferrer(request)
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
// 9. Set request’s current URL’s scheme to "https" if all of the following
|
|
561
|
-
// conditions are true:
|
|
562
|
-
// - request’s current URL’s scheme is "http"
|
|
563
|
-
// - request’s current URL’s host is a domain
|
|
564
|
-
// - Matching request’s current URL’s host per Known HSTS Host Domain Name
|
|
565
|
-
// Matching results in either a superdomain match with an asserted
|
|
566
|
-
// includeSubDomains directive or a congruent match (with or without an
|
|
567
|
-
// asserted includeSubDomains directive). [HSTS]
|
|
568
|
-
// TODO
|
|
569
|
-
|
|
570
|
-
// 10. If recursive is false, then run the remaining steps in parallel.
|
|
571
|
-
// TODO
|
|
572
|
-
|
|
573
|
-
// 11. If response is null, then set response to the result of running
|
|
574
|
-
// the steps corresponding to the first matching statement:
|
|
575
|
-
if (response === null) {
|
|
576
|
-
const currentURL = requestCurrentURL(request)
|
|
577
|
-
if (
|
|
578
|
-
// - request’s current URL’s origin is same origin with request’s origin,
|
|
579
|
-
// and request’s response tainting is "basic"
|
|
580
|
-
(sameOrigin(currentURL, request.url) && request.responseTainting === 'basic') ||
|
|
581
|
-
// request’s current URL’s scheme is "data"
|
|
582
|
-
(currentURL.protocol === 'data:') ||
|
|
583
|
-
// - request’s mode is "navigate" or "websocket"
|
|
584
|
-
(request.mode === 'navigate' || request.mode === 'websocket')
|
|
585
|
-
) {
|
|
586
|
-
// 1. Set request’s response tainting to "basic".
|
|
587
|
-
request.responseTainting = 'basic'
|
|
588
|
-
|
|
589
|
-
// 2. Return the result of running scheme fetch given fetchParams.
|
|
590
|
-
response = await schemeFetch(fetchParams)
|
|
591
|
-
|
|
592
|
-
// request’s mode is "same-origin"
|
|
593
|
-
} else if (request.mode === 'same-origin') {
|
|
594
|
-
// 1. Return a network error.
|
|
595
|
-
response = makeNetworkError('request mode cannot be "same-origin"')
|
|
596
|
-
|
|
597
|
-
// request’s mode is "no-cors"
|
|
598
|
-
} else if (request.mode === 'no-cors') {
|
|
599
|
-
// 1. If request’s redirect mode is not "follow", then return a network
|
|
600
|
-
// error.
|
|
601
|
-
if (request.redirect !== 'follow') {
|
|
602
|
-
response = makeNetworkError(
|
|
603
|
-
'redirect mode cannot be "follow" for "no-cors" request'
|
|
604
|
-
)
|
|
605
|
-
} else {
|
|
606
|
-
// 2. Set request’s response tainting to "opaque".
|
|
607
|
-
request.responseTainting = 'opaque'
|
|
608
|
-
|
|
609
|
-
// 3. Return the result of running scheme fetch given fetchParams.
|
|
610
|
-
response = await schemeFetch(fetchParams)
|
|
611
|
-
}
|
|
612
|
-
// request’s current URL’s scheme is not an HTTP(S) scheme
|
|
613
|
-
} else if (!urlIsHttpHttpsScheme(requestCurrentURL(request))) {
|
|
614
|
-
// Return a network error.
|
|
615
|
-
response = makeNetworkError('URL scheme must be a HTTP(S) scheme')
|
|
616
|
-
|
|
617
|
-
// - request’s use-CORS-preflight flag is set
|
|
618
|
-
// - request’s unsafe-request flag is set and either request’s method is
|
|
619
|
-
// not a CORS-safelisted method or CORS-unsafe request-header names with
|
|
620
|
-
// request’s header list is not empty
|
|
621
|
-
// 1. Set request’s response tainting to "cors".
|
|
622
|
-
// 2. Let corsWithPreflightResponse be the result of running HTTP fetch
|
|
623
|
-
// given fetchParams and true.
|
|
624
|
-
// 3. If corsWithPreflightResponse is a network error, then clear cache
|
|
625
|
-
// entries using request.
|
|
626
|
-
// 4. Return corsWithPreflightResponse.
|
|
627
|
-
// TODO
|
|
628
|
-
|
|
629
|
-
// Otherwise
|
|
630
|
-
} else {
|
|
631
|
-
// 1. Set request’s response tainting to "cors".
|
|
632
|
-
request.responseTainting = 'cors'
|
|
633
|
-
|
|
634
|
-
// 2. Return the result of running HTTP fetch given fetchParams.
|
|
635
|
-
response = await httpFetch(fetchParams)
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
// 12. If recursive is true, then return response.
|
|
640
|
-
if (recursive) {
|
|
641
|
-
return response
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
// 13. If response is not a network error and response is not a filtered
|
|
645
|
-
// response, then:
|
|
646
|
-
if (response.status !== 0 && !response.internalResponse) {
|
|
647
|
-
// If request’s response tainting is "cors", then:
|
|
648
|
-
if (request.responseTainting === 'cors') {
|
|
649
|
-
// 1. Let headerNames be the result of extracting header list values
|
|
650
|
-
// given `Access-Control-Expose-Headers` and response’s header list.
|
|
651
|
-
// TODO
|
|
652
|
-
// 2. If request’s credentials mode is not "include" and headerNames
|
|
653
|
-
// contains `*`, then set response’s CORS-exposed header-name list to
|
|
654
|
-
// all unique header names in response’s header list.
|
|
655
|
-
// TODO
|
|
656
|
-
// 3. Otherwise, if headerNames is not null or failure, then set
|
|
657
|
-
// response’s CORS-exposed header-name list to headerNames.
|
|
658
|
-
// TODO
|
|
659
|
-
}
|
|
660
|
-
|
|
661
|
-
// Set response to the following filtered response with response as its
|
|
662
|
-
// internal response, depending on request’s response tainting:
|
|
663
|
-
if (request.responseTainting === 'basic') {
|
|
664
|
-
response = filterResponse(response, 'basic')
|
|
665
|
-
} else if (request.responseTainting === 'cors') {
|
|
666
|
-
response = filterResponse(response, 'cors')
|
|
667
|
-
} else if (request.responseTainting === 'opaque') {
|
|
668
|
-
response = filterResponse(response, 'opaque')
|
|
669
|
-
} else {
|
|
670
|
-
assert(false)
|
|
671
|
-
}
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
// 14. Let internalResponse be response, if response is a network error,
|
|
675
|
-
// and response’s internal response otherwise.
|
|
676
|
-
let internalResponse =
|
|
677
|
-
response.status === 0 ? response : response.internalResponse
|
|
678
|
-
|
|
679
|
-
// 15. If internalResponse’s URL list is empty, then set it to a clone of
|
|
680
|
-
// request’s URL list.
|
|
681
|
-
if (internalResponse.urlList.length === 0) {
|
|
682
|
-
internalResponse.urlList.push(...request.urlList)
|
|
683
|
-
}
|
|
684
|
-
|
|
685
|
-
// 16. If request’s timing allow failed flag is unset, then set
|
|
686
|
-
// internalResponse’s timing allow passed flag.
|
|
687
|
-
if (!request.timingAllowFailed) {
|
|
688
|
-
response.timingAllowPassed = true
|
|
689
|
-
}
|
|
690
|
-
|
|
691
|
-
// 17. If response is not a network error and any of the following returns
|
|
692
|
-
// blocked
|
|
693
|
-
// - should internalResponse to request be blocked as mixed content
|
|
694
|
-
// - should internalResponse to request be blocked by Content Security Policy
|
|
695
|
-
// - should internalResponse to request be blocked due to its MIME type
|
|
696
|
-
// - should internalResponse to request be blocked due to nosniff
|
|
697
|
-
// TODO
|
|
698
|
-
|
|
699
|
-
// 18. If response’s type is "opaque", internalResponse’s status is 206,
|
|
700
|
-
// internalResponse’s range-requested flag is set, and request’s header
|
|
701
|
-
// list does not contain `Range`, then set response and internalResponse
|
|
702
|
-
// to a network error.
|
|
703
|
-
if (
|
|
704
|
-
response.type === 'opaque' &&
|
|
705
|
-
internalResponse.status === 206 &&
|
|
706
|
-
internalResponse.rangeRequested &&
|
|
707
|
-
!request.headers.contains('range', true)
|
|
708
|
-
) {
|
|
709
|
-
response = internalResponse = makeNetworkError()
|
|
710
|
-
}
|
|
711
|
-
|
|
712
|
-
// 19. If response is not a network error and either request’s method is
|
|
713
|
-
// `HEAD` or `CONNECT`, or internalResponse’s status is a null body status,
|
|
714
|
-
// set internalResponse’s body to null and disregard any enqueuing toward
|
|
715
|
-
// it (if any).
|
|
716
|
-
if (
|
|
717
|
-
response.status !== 0 &&
|
|
718
|
-
(request.method === 'HEAD' ||
|
|
719
|
-
request.method === 'CONNECT' ||
|
|
720
|
-
nullBodyStatus.includes(internalResponse.status))
|
|
721
|
-
) {
|
|
722
|
-
internalResponse.body = null
|
|
723
|
-
fetchParams.controller.dump = true
|
|
724
|
-
}
|
|
725
|
-
|
|
726
|
-
// 20. If request’s integrity metadata is not the empty string, then:
|
|
727
|
-
if (request.integrity) {
|
|
728
|
-
// 1. Let processBodyError be this step: run fetch finale given fetchParams
|
|
729
|
-
// and a network error.
|
|
730
|
-
const processBodyError = (reason) =>
|
|
731
|
-
fetchFinale(fetchParams, makeNetworkError(reason))
|
|
732
|
-
|
|
733
|
-
// 2. If request’s response tainting is "opaque", or response’s body is null,
|
|
734
|
-
// then run processBodyError and abort these steps.
|
|
735
|
-
if (request.responseTainting === 'opaque' || response.body == null) {
|
|
736
|
-
processBodyError(response.error)
|
|
737
|
-
return
|
|
738
|
-
}
|
|
739
|
-
|
|
740
|
-
// 3. Let processBody given bytes be these steps:
|
|
741
|
-
const processBody = (bytes) => {
|
|
742
|
-
// 1. If bytes do not match request’s integrity metadata,
|
|
743
|
-
// then run processBodyError and abort these steps. [SRI]
|
|
744
|
-
if (!bytesMatch(bytes, request.integrity)) {
|
|
745
|
-
processBodyError('integrity mismatch')
|
|
746
|
-
return
|
|
747
|
-
}
|
|
748
|
-
|
|
749
|
-
// 2. Set response’s body to bytes as a body.
|
|
750
|
-
response.body = safelyExtractBody(bytes)[0]
|
|
751
|
-
|
|
752
|
-
// 3. Run fetch finale given fetchParams and response.
|
|
753
|
-
fetchFinale(fetchParams, response)
|
|
754
|
-
}
|
|
755
|
-
|
|
756
|
-
// 4. Fully read response’s body given processBody and processBodyError.
|
|
757
|
-
await fullyReadBody(response.body, processBody, processBodyError)
|
|
758
|
-
} else {
|
|
759
|
-
// 21. Otherwise, run fetch finale given fetchParams and response.
|
|
760
|
-
fetchFinale(fetchParams, response)
|
|
761
|
-
}
|
|
762
|
-
}
|
|
763
|
-
|
|
764
|
-
// https://fetch.spec.whatwg.org/#concept-scheme-fetch
|
|
765
|
-
// given a fetch params fetchParams
|
|
766
|
-
function schemeFetch (fetchParams) {
|
|
767
|
-
// Note: since the connection is destroyed on redirect, which sets fetchParams to a
|
|
768
|
-
// cancelled state, we do not want this condition to trigger *unless* there have been
|
|
769
|
-
// no redirects. See https://github.com/nodejs/undici/issues/1776
|
|
770
|
-
// 1. If fetchParams is canceled, then return the appropriate network error for fetchParams.
|
|
771
|
-
if (isCancelled(fetchParams) && fetchParams.request.redirectCount === 0) {
|
|
772
|
-
return Promise.resolve(makeAppropriateNetworkError(fetchParams))
|
|
773
|
-
}
|
|
774
|
-
|
|
775
|
-
// 2. Let request be fetchParams’s request.
|
|
776
|
-
const { request } = fetchParams
|
|
777
|
-
|
|
778
|
-
const { protocol: scheme } = requestCurrentURL(request)
|
|
779
|
-
|
|
780
|
-
// 3. Switch on request’s current URL’s scheme and run the associated steps:
|
|
781
|
-
switch (scheme) {
|
|
782
|
-
case 'about:': {
|
|
783
|
-
// If request’s current URL’s path is the string "blank", then return a new response
|
|
784
|
-
// whose status message is `OK`, header list is « (`Content-Type`, `text/html;charset=utf-8`) »,
|
|
785
|
-
// and body is the empty byte sequence as a body.
|
|
786
|
-
|
|
787
|
-
// Otherwise, return a network error.
|
|
788
|
-
return Promise.resolve(makeNetworkError('about scheme is not supported'))
|
|
789
|
-
}
|
|
790
|
-
case 'blob:': {
|
|
791
|
-
if (!resolveObjectURL) {
|
|
792
|
-
resolveObjectURL = require('node:buffer').resolveObjectURL
|
|
793
|
-
}
|
|
794
|
-
|
|
795
|
-
// 1. Let blobURLEntry be request’s current URL’s blob URL entry.
|
|
796
|
-
const blobURLEntry = requestCurrentURL(request)
|
|
797
|
-
|
|
798
|
-
// https://github.com/web-platform-tests/wpt/blob/7b0ebaccc62b566a1965396e5be7bb2bc06f841f/FileAPI/url/resources/fetch-tests.js#L52-L56
|
|
799
|
-
// Buffer.resolveObjectURL does not ignore URL queries.
|
|
800
|
-
if (blobURLEntry.search.length !== 0) {
|
|
801
|
-
return Promise.resolve(makeNetworkError('NetworkError when attempting to fetch resource.'))
|
|
802
|
-
}
|
|
803
|
-
|
|
804
|
-
const blob = resolveObjectURL(blobURLEntry.toString())
|
|
805
|
-
|
|
806
|
-
// 2. If request’s method is not `GET`, blobURLEntry is null, or blobURLEntry’s
|
|
807
|
-
// object is not a Blob object, then return a network error.
|
|
808
|
-
if (request.method !== 'GET' || !webidl.is.Blob(blob)) {
|
|
809
|
-
return Promise.resolve(makeNetworkError('invalid method'))
|
|
810
|
-
}
|
|
811
|
-
|
|
812
|
-
// 3. Let blob be blobURLEntry’s object.
|
|
813
|
-
// Note: done above
|
|
814
|
-
|
|
815
|
-
// 4. Let response be a new response.
|
|
816
|
-
const response = makeResponse()
|
|
817
|
-
|
|
818
|
-
// 5. Let fullLength be blob’s size.
|
|
819
|
-
const fullLength = blob.size
|
|
820
|
-
|
|
821
|
-
// 6. Let serializedFullLength be fullLength, serialized and isomorphic encoded.
|
|
822
|
-
const serializedFullLength = isomorphicEncode(`${fullLength}`)
|
|
823
|
-
|
|
824
|
-
// 7. Let type be blob’s type.
|
|
825
|
-
const type = blob.type
|
|
826
|
-
|
|
827
|
-
// 8. If request’s header list does not contain `Range`:
|
|
828
|
-
// 9. Otherwise:
|
|
829
|
-
if (!request.headersList.contains('range', true)) {
|
|
830
|
-
// 1. Let bodyWithType be the result of safely extracting blob.
|
|
831
|
-
// Note: in the FileAPI a blob "object" is a Blob *or* a MediaSource.
|
|
832
|
-
// In node, this can only ever be a Blob. Therefore we can safely
|
|
833
|
-
// use extractBody directly.
|
|
834
|
-
const bodyWithType = extractBody(blob)
|
|
835
|
-
|
|
836
|
-
// 2. Set response’s status message to `OK`.
|
|
837
|
-
response.statusText = 'OK'
|
|
838
|
-
|
|
839
|
-
// 3. Set response’s body to bodyWithType’s body.
|
|
840
|
-
response.body = bodyWithType[0]
|
|
841
|
-
|
|
842
|
-
// 4. Set response’s header list to « (`Content-Length`, serializedFullLength), (`Content-Type`, type) ».
|
|
843
|
-
response.headersList.set('content-length', serializedFullLength, true)
|
|
844
|
-
response.headersList.set('content-type', type, true)
|
|
845
|
-
} else {
|
|
846
|
-
// 1. Set response’s range-requested flag.
|
|
847
|
-
response.rangeRequested = true
|
|
848
|
-
|
|
849
|
-
// 2. Let rangeHeader be the result of getting `Range` from request’s header list.
|
|
850
|
-
const rangeHeader = request.headersList.get('range', true)
|
|
851
|
-
|
|
852
|
-
// 3. Let rangeValue be the result of parsing a single range header value given rangeHeader and true.
|
|
853
|
-
const rangeValue = simpleRangeHeaderValue(rangeHeader, true)
|
|
854
|
-
|
|
855
|
-
// 4. If rangeValue is failure, then return a network error.
|
|
856
|
-
if (rangeValue === 'failure') {
|
|
857
|
-
return Promise.resolve(makeNetworkError('failed to fetch the data URL'))
|
|
858
|
-
}
|
|
859
|
-
|
|
860
|
-
// 5. Let (rangeStart, rangeEnd) be rangeValue.
|
|
861
|
-
let { rangeStartValue: rangeStart, rangeEndValue: rangeEnd } = rangeValue
|
|
862
|
-
|
|
863
|
-
// 6. If rangeStart is null:
|
|
864
|
-
// 7. Otherwise:
|
|
865
|
-
if (rangeStart === null) {
|
|
866
|
-
// 1. Set rangeStart to fullLength − rangeEnd.
|
|
867
|
-
rangeStart = fullLength - rangeEnd
|
|
868
|
-
|
|
869
|
-
// 2. Set rangeEnd to rangeStart + rangeEnd − 1.
|
|
870
|
-
rangeEnd = rangeStart + rangeEnd - 1
|
|
871
|
-
} else {
|
|
872
|
-
// 1. If rangeStart is greater than or equal to fullLength, then return a network error.
|
|
873
|
-
if (rangeStart >= fullLength) {
|
|
874
|
-
return Promise.resolve(makeNetworkError('Range start is greater than the blob\'s size.'))
|
|
875
|
-
}
|
|
876
|
-
|
|
877
|
-
// 2. If rangeEnd is null or rangeEnd is greater than or equal to fullLength, then set
|
|
878
|
-
// rangeEnd to fullLength − 1.
|
|
879
|
-
if (rangeEnd === null || rangeEnd >= fullLength) {
|
|
880
|
-
rangeEnd = fullLength - 1
|
|
881
|
-
}
|
|
882
|
-
}
|
|
883
|
-
|
|
884
|
-
// 8. Let slicedBlob be the result of invoking slice blob given blob, rangeStart,
|
|
885
|
-
// rangeEnd + 1, and type.
|
|
886
|
-
const slicedBlob = blob.slice(rangeStart, rangeEnd, type)
|
|
887
|
-
|
|
888
|
-
// 9. Let slicedBodyWithType be the result of safely extracting slicedBlob.
|
|
889
|
-
// Note: same reason as mentioned above as to why we use extractBody
|
|
890
|
-
const slicedBodyWithType = extractBody(slicedBlob)
|
|
891
|
-
|
|
892
|
-
// 10. Set response’s body to slicedBodyWithType’s body.
|
|
893
|
-
response.body = slicedBodyWithType[0]
|
|
894
|
-
|
|
895
|
-
// 11. Let serializedSlicedLength be slicedBlob’s size, serialized and isomorphic encoded.
|
|
896
|
-
const serializedSlicedLength = isomorphicEncode(`${slicedBlob.size}`)
|
|
897
|
-
|
|
898
|
-
// 12. Let contentRange be the result of invoking build a content range given rangeStart,
|
|
899
|
-
// rangeEnd, and fullLength.
|
|
900
|
-
const contentRange = buildContentRange(rangeStart, rangeEnd, fullLength)
|
|
901
|
-
|
|
902
|
-
// 13. Set response’s status to 206.
|
|
903
|
-
response.status = 206
|
|
904
|
-
|
|
905
|
-
// 14. Set response’s status message to `Partial Content`.
|
|
906
|
-
response.statusText = 'Partial Content'
|
|
907
|
-
|
|
908
|
-
// 15. Set response’s header list to « (`Content-Length`, serializedSlicedLength),
|
|
909
|
-
// (`Content-Type`, type), (`Content-Range`, contentRange) ».
|
|
910
|
-
response.headersList.set('content-length', serializedSlicedLength, true)
|
|
911
|
-
response.headersList.set('content-type', type, true)
|
|
912
|
-
response.headersList.set('content-range', contentRange, true)
|
|
913
|
-
}
|
|
914
|
-
|
|
915
|
-
// 10. Return response.
|
|
916
|
-
return Promise.resolve(response)
|
|
917
|
-
}
|
|
918
|
-
case 'data:': {
|
|
919
|
-
// 1. Let dataURLStruct be the result of running the
|
|
920
|
-
// data: URL processor on request’s current URL.
|
|
921
|
-
const currentURL = requestCurrentURL(request)
|
|
922
|
-
const dataURLStruct = dataURLProcessor(currentURL)
|
|
923
|
-
|
|
924
|
-
// 2. If dataURLStruct is failure, then return a
|
|
925
|
-
// network error.
|
|
926
|
-
if (dataURLStruct === 'failure') {
|
|
927
|
-
return Promise.resolve(makeNetworkError('failed to fetch the data URL'))
|
|
928
|
-
}
|
|
929
|
-
|
|
930
|
-
// 3. Let mimeType be dataURLStruct’s MIME type, serialized.
|
|
931
|
-
const mimeType = serializeAMimeType(dataURLStruct.mimeType)
|
|
932
|
-
|
|
933
|
-
// 4. Return a response whose status message is `OK`,
|
|
934
|
-
// header list is « (`Content-Type`, mimeType) »,
|
|
935
|
-
// and body is dataURLStruct’s body as a body.
|
|
936
|
-
return Promise.resolve(makeResponse({
|
|
937
|
-
statusText: 'OK',
|
|
938
|
-
headersList: [
|
|
939
|
-
['content-type', { name: 'Content-Type', value: mimeType }]
|
|
940
|
-
],
|
|
941
|
-
body: safelyExtractBody(dataURLStruct.body)[0]
|
|
942
|
-
}))
|
|
943
|
-
}
|
|
944
|
-
case 'file:': {
|
|
945
|
-
// For now, unfortunate as it is, file URLs are left as an exercise for the reader.
|
|
946
|
-
// When in doubt, return a network error.
|
|
947
|
-
return Promise.resolve(makeNetworkError('not implemented... yet...'))
|
|
948
|
-
}
|
|
949
|
-
case 'http:':
|
|
950
|
-
case 'https:': {
|
|
951
|
-
// Return the result of running HTTP fetch given fetchParams.
|
|
952
|
-
|
|
953
|
-
return httpFetch(fetchParams)
|
|
954
|
-
.catch((err) => makeNetworkError(err))
|
|
955
|
-
}
|
|
956
|
-
default: {
|
|
957
|
-
return Promise.resolve(makeNetworkError('unknown scheme'))
|
|
958
|
-
}
|
|
959
|
-
}
|
|
960
|
-
}
|
|
961
|
-
|
|
962
|
-
// https://fetch.spec.whatwg.org/#finalize-response
|
|
963
|
-
function finalizeResponse (fetchParams, response) {
|
|
964
|
-
// 1. Set fetchParams’s request’s done flag.
|
|
965
|
-
fetchParams.request.done = true
|
|
966
|
-
|
|
967
|
-
// 2, If fetchParams’s process response done is not null, then queue a fetch
|
|
968
|
-
// task to run fetchParams’s process response done given response, with
|
|
969
|
-
// fetchParams’s task destination.
|
|
970
|
-
if (fetchParams.processResponseDone != null) {
|
|
971
|
-
queueMicrotask(() => fetchParams.processResponseDone(response))
|
|
972
|
-
}
|
|
973
|
-
}
|
|
974
|
-
|
|
975
|
-
// https://fetch.spec.whatwg.org/#fetch-finale
|
|
976
|
-
function fetchFinale (fetchParams, response) {
|
|
977
|
-
// 1. Let timingInfo be fetchParams’s timing info.
|
|
978
|
-
let timingInfo = fetchParams.timingInfo
|
|
979
|
-
|
|
980
|
-
// 2. If response is not a network error and fetchParams’s request’s client is a secure context,
|
|
981
|
-
// then set timingInfo’s server-timing headers to the result of getting, decoding, and splitting
|
|
982
|
-
// `Server-Timing` from response’s internal response’s header list.
|
|
983
|
-
// TODO
|
|
984
|
-
|
|
985
|
-
// 3. Let processResponseEndOfBody be the following steps:
|
|
986
|
-
const processResponseEndOfBody = () => {
|
|
987
|
-
// 1. Let unsafeEndTime be the unsafe shared current time.
|
|
988
|
-
const unsafeEndTime = Date.now() // ?
|
|
989
|
-
|
|
990
|
-
// 2. If fetchParams’s request’s destination is "document", then set fetchParams’s controller’s
|
|
991
|
-
// full timing info to fetchParams’s timing info.
|
|
992
|
-
if (fetchParams.request.destination === 'document') {
|
|
993
|
-
fetchParams.controller.fullTimingInfo = timingInfo
|
|
994
|
-
}
|
|
995
|
-
|
|
996
|
-
// 3. Set fetchParams’s controller’s report timing steps to the following steps given a global object global:
|
|
997
|
-
fetchParams.controller.reportTimingSteps = () => {
|
|
998
|
-
// 1. If fetchParams’s request’s URL’s scheme is not an HTTP(S) scheme, then return.
|
|
999
|
-
if (!urlIsHttpHttpsScheme(fetchParams.request.url)) {
|
|
1000
|
-
return
|
|
1001
|
-
}
|
|
1002
|
-
|
|
1003
|
-
// 2. Set timingInfo’s end time to the relative high resolution time given unsafeEndTime and global.
|
|
1004
|
-
timingInfo.endTime = unsafeEndTime
|
|
1005
|
-
|
|
1006
|
-
// 3. Let cacheState be response’s cache state.
|
|
1007
|
-
let cacheState = response.cacheState
|
|
1008
|
-
|
|
1009
|
-
// 4. Let bodyInfo be response’s body info.
|
|
1010
|
-
const bodyInfo = response.bodyInfo
|
|
1011
|
-
|
|
1012
|
-
// 5. If response’s timing allow passed flag is not set, then set timingInfo to the result of creating an
|
|
1013
|
-
// opaque timing info for timingInfo and set cacheState to the empty string.
|
|
1014
|
-
if (!response.timingAllowPassed) {
|
|
1015
|
-
timingInfo = createOpaqueTimingInfo(timingInfo)
|
|
1016
|
-
|
|
1017
|
-
cacheState = ''
|
|
1018
|
-
}
|
|
1019
|
-
|
|
1020
|
-
// 6. Let responseStatus be 0.
|
|
1021
|
-
let responseStatus = 0
|
|
1022
|
-
|
|
1023
|
-
// 7. If fetchParams’s request’s mode is not "navigate" or response’s has-cross-origin-redirects is false:
|
|
1024
|
-
if (fetchParams.request.mode !== 'navigator' || !response.hasCrossOriginRedirects) {
|
|
1025
|
-
// 1. Set responseStatus to response’s status.
|
|
1026
|
-
responseStatus = response.status
|
|
1027
|
-
|
|
1028
|
-
// 2. Let mimeType be the result of extracting a MIME type from response’s header list.
|
|
1029
|
-
const mimeType = extractMimeType(response.headersList)
|
|
1030
|
-
|
|
1031
|
-
// 3. If mimeType is not failure, then set bodyInfo’s content type to the result of minimizing a supported MIME type given mimeType.
|
|
1032
|
-
if (mimeType !== 'failure') {
|
|
1033
|
-
bodyInfo.contentType = minimizeSupportedMimeType(mimeType)
|
|
1034
|
-
}
|
|
1035
|
-
}
|
|
1036
|
-
|
|
1037
|
-
// 8. If fetchParams’s request’s initiator type is non-null, then mark resource timing given timingInfo,
|
|
1038
|
-
// fetchParams’s request’s URL, fetchParams’s request’s initiator type, global, cacheState, bodyInfo,
|
|
1039
|
-
// and responseStatus.
|
|
1040
|
-
if (fetchParams.request.initiatorType != null) {
|
|
1041
|
-
markResourceTiming(timingInfo, fetchParams.request.url.href, fetchParams.request.initiatorType, globalThis, cacheState, bodyInfo, responseStatus)
|
|
1042
|
-
}
|
|
1043
|
-
}
|
|
1044
|
-
|
|
1045
|
-
// 4. Let processResponseEndOfBodyTask be the following steps:
|
|
1046
|
-
const processResponseEndOfBodyTask = () => {
|
|
1047
|
-
// 1. Set fetchParams’s request’s done flag.
|
|
1048
|
-
fetchParams.request.done = true
|
|
1049
|
-
|
|
1050
|
-
// 2. If fetchParams’s process response end-of-body is non-null, then run fetchParams’s process
|
|
1051
|
-
// response end-of-body given response.
|
|
1052
|
-
if (fetchParams.processResponseEndOfBody != null) {
|
|
1053
|
-
queueMicrotask(() => fetchParams.processResponseEndOfBody(response))
|
|
1054
|
-
}
|
|
1055
|
-
|
|
1056
|
-
// 3. If fetchParams’s request’s initiator type is non-null and fetchParams’s request’s client’s
|
|
1057
|
-
// global object is fetchParams’s task destination, then run fetchParams’s controller’s report
|
|
1058
|
-
// timing steps given fetchParams’s request’s client’s global object.
|
|
1059
|
-
if (fetchParams.request.initiatorType != null) {
|
|
1060
|
-
fetchParams.controller.reportTimingSteps()
|
|
1061
|
-
}
|
|
1062
|
-
}
|
|
1063
|
-
|
|
1064
|
-
// 5. Queue a fetch task to run processResponseEndOfBodyTask with fetchParams’s task destination
|
|
1065
|
-
queueMicrotask(() => processResponseEndOfBodyTask())
|
|
1066
|
-
}
|
|
1067
|
-
|
|
1068
|
-
// 4. If fetchParams’s process response is non-null, then queue a fetch task to run fetchParams’s
|
|
1069
|
-
// process response given response, with fetchParams’s task destination.
|
|
1070
|
-
if (fetchParams.processResponse != null) {
|
|
1071
|
-
queueMicrotask(() => {
|
|
1072
|
-
fetchParams.processResponse(response)
|
|
1073
|
-
fetchParams.processResponse = null
|
|
1074
|
-
})
|
|
1075
|
-
}
|
|
1076
|
-
|
|
1077
|
-
// 5. Let internalResponse be response, if response is a network error; otherwise response’s internal response.
|
|
1078
|
-
const internalResponse = response.type === 'error' ? response : (response.internalResponse ?? response)
|
|
1079
|
-
|
|
1080
|
-
// 6. If internalResponse’s body is null, then run processResponseEndOfBody.
|
|
1081
|
-
// 7. Otherwise:
|
|
1082
|
-
if (internalResponse.body == null) {
|
|
1083
|
-
processResponseEndOfBody()
|
|
1084
|
-
} else {
|
|
1085
|
-
// mcollina: all the following steps of the specs are skipped.
|
|
1086
|
-
// The internal transform stream is not needed.
|
|
1087
|
-
// See https://github.com/nodejs/undici/pull/3093#issuecomment-2050198541
|
|
1088
|
-
|
|
1089
|
-
// 1. Let transformStream be a new TransformStream.
|
|
1090
|
-
// 2. Let identityTransformAlgorithm be an algorithm which, given chunk, enqueues chunk in transformStream.
|
|
1091
|
-
// 3. Set up transformStream with transformAlgorithm set to identityTransformAlgorithm and flushAlgorithm
|
|
1092
|
-
// set to processResponseEndOfBody.
|
|
1093
|
-
// 4. Set internalResponse’s body’s stream to the result of internalResponse’s body’s stream piped through transformStream.
|
|
1094
|
-
|
|
1095
|
-
finished(internalResponse.body.stream, () => {
|
|
1096
|
-
processResponseEndOfBody()
|
|
1097
|
-
})
|
|
1098
|
-
}
|
|
1099
|
-
}
|
|
1100
|
-
|
|
1101
|
-
// https://fetch.spec.whatwg.org/#http-fetch
|
|
1102
|
-
async function httpFetch (fetchParams) {
|
|
1103
|
-
// 1. Let request be fetchParams’s request.
|
|
1104
|
-
const request = fetchParams.request
|
|
1105
|
-
|
|
1106
|
-
// 2. Let response be null.
|
|
1107
|
-
let response = null
|
|
1108
|
-
|
|
1109
|
-
// 3. Let actualResponse be null.
|
|
1110
|
-
let actualResponse = null
|
|
1111
|
-
|
|
1112
|
-
// 4. Let timingInfo be fetchParams’s timing info.
|
|
1113
|
-
const timingInfo = fetchParams.timingInfo
|
|
1114
|
-
|
|
1115
|
-
// 5. If request’s service-workers mode is "all", then:
|
|
1116
|
-
if (request.serviceWorkers === 'all') {
|
|
1117
|
-
// TODO
|
|
1118
|
-
}
|
|
1119
|
-
|
|
1120
|
-
// 6. If response is null, then:
|
|
1121
|
-
if (response === null) {
|
|
1122
|
-
// 1. If makeCORSPreflight is true and one of these conditions is true:
|
|
1123
|
-
// TODO
|
|
1124
|
-
|
|
1125
|
-
// 2. If request’s redirect mode is "follow", then set request’s
|
|
1126
|
-
// service-workers mode to "none".
|
|
1127
|
-
if (request.redirect === 'follow') {
|
|
1128
|
-
request.serviceWorkers = 'none'
|
|
1129
|
-
}
|
|
1130
|
-
|
|
1131
|
-
// 3. Set response and actualResponse to the result of running
|
|
1132
|
-
// HTTP-network-or-cache fetch given fetchParams.
|
|
1133
|
-
actualResponse = response = await httpNetworkOrCacheFetch(fetchParams)
|
|
1134
|
-
|
|
1135
|
-
// 4. If request’s response tainting is "cors" and a CORS check
|
|
1136
|
-
// for request and response returns failure, then return a network error.
|
|
1137
|
-
if (
|
|
1138
|
-
request.responseTainting === 'cors' &&
|
|
1139
|
-
corsCheck(request, response) === 'failure'
|
|
1140
|
-
) {
|
|
1141
|
-
return makeNetworkError('cors failure')
|
|
1142
|
-
}
|
|
1143
|
-
|
|
1144
|
-
// 5. If the TAO check for request and response returns failure, then set
|
|
1145
|
-
// request’s timing allow failed flag.
|
|
1146
|
-
if (TAOCheck(request, response) === 'failure') {
|
|
1147
|
-
request.timingAllowFailed = true
|
|
1148
|
-
}
|
|
1149
|
-
}
|
|
1150
|
-
|
|
1151
|
-
// 7. If either request’s response tainting or response’s type
|
|
1152
|
-
// is "opaque", and the cross-origin resource policy check with
|
|
1153
|
-
// request’s origin, request’s client, request’s destination,
|
|
1154
|
-
// and actualResponse returns blocked, then return a network error.
|
|
1155
|
-
if (
|
|
1156
|
-
(request.responseTainting === 'opaque' || response.type === 'opaque') &&
|
|
1157
|
-
crossOriginResourcePolicyCheck(
|
|
1158
|
-
request.origin,
|
|
1159
|
-
request.client,
|
|
1160
|
-
request.destination,
|
|
1161
|
-
actualResponse
|
|
1162
|
-
) === 'blocked'
|
|
1163
|
-
) {
|
|
1164
|
-
return makeNetworkError('blocked')
|
|
1165
|
-
}
|
|
1166
|
-
|
|
1167
|
-
// 8. If actualResponse’s status is a redirect status, then:
|
|
1168
|
-
if (redirectStatusSet.has(actualResponse.status)) {
|
|
1169
|
-
// 1. If actualResponse’s status is not 303, request’s body is not null,
|
|
1170
|
-
// and the connection uses HTTP/2, then user agents may, and are even
|
|
1171
|
-
// encouraged to, transmit an RST_STREAM frame.
|
|
1172
|
-
// See, https://github.com/whatwg/fetch/issues/1288
|
|
1173
|
-
if (request.redirect !== 'manual') {
|
|
1174
|
-
fetchParams.controller.connection.destroy(undefined, false)
|
|
1175
|
-
}
|
|
1176
|
-
|
|
1177
|
-
// 2. Switch on request’s redirect mode:
|
|
1178
|
-
if (request.redirect === 'error') {
|
|
1179
|
-
// Set response to a network error.
|
|
1180
|
-
response = makeNetworkError('unexpected redirect')
|
|
1181
|
-
} else if (request.redirect === 'manual') {
|
|
1182
|
-
// Set response to an opaque-redirect filtered response whose internal
|
|
1183
|
-
// response is actualResponse.
|
|
1184
|
-
// NOTE(spec): On the web this would return an `opaqueredirect` response,
|
|
1185
|
-
// but that doesn't make sense server side.
|
|
1186
|
-
// See https://github.com/nodejs/undici/issues/1193.
|
|
1187
|
-
response = actualResponse
|
|
1188
|
-
} else if (request.redirect === 'follow') {
|
|
1189
|
-
// Set response to the result of running HTTP-redirect fetch given
|
|
1190
|
-
// fetchParams and response.
|
|
1191
|
-
response = await httpRedirectFetch(fetchParams, response)
|
|
1192
|
-
} else {
|
|
1193
|
-
assert(false)
|
|
1194
|
-
}
|
|
1195
|
-
}
|
|
1196
|
-
|
|
1197
|
-
// 9. Set response’s timing info to timingInfo.
|
|
1198
|
-
response.timingInfo = timingInfo
|
|
1199
|
-
|
|
1200
|
-
// 10. Return response.
|
|
1201
|
-
return response
|
|
1202
|
-
}
|
|
1203
|
-
|
|
1204
|
-
// https://fetch.spec.whatwg.org/#http-redirect-fetch
|
|
1205
|
-
function httpRedirectFetch (fetchParams, response) {
|
|
1206
|
-
// 1. Let request be fetchParams’s request.
|
|
1207
|
-
const request = fetchParams.request
|
|
1208
|
-
|
|
1209
|
-
// 2. Let actualResponse be response, if response is not a filtered response,
|
|
1210
|
-
// and response’s internal response otherwise.
|
|
1211
|
-
const actualResponse = response.internalResponse
|
|
1212
|
-
? response.internalResponse
|
|
1213
|
-
: response
|
|
1214
|
-
|
|
1215
|
-
// 3. Let locationURL be actualResponse’s location URL given request’s current
|
|
1216
|
-
// URL’s fragment.
|
|
1217
|
-
let locationURL
|
|
1218
|
-
|
|
1219
|
-
try {
|
|
1220
|
-
locationURL = responseLocationURL(
|
|
1221
|
-
actualResponse,
|
|
1222
|
-
requestCurrentURL(request).hash
|
|
1223
|
-
)
|
|
1224
|
-
|
|
1225
|
-
// 4. If locationURL is null, then return response.
|
|
1226
|
-
if (locationURL == null) {
|
|
1227
|
-
return response
|
|
1228
|
-
}
|
|
1229
|
-
} catch (err) {
|
|
1230
|
-
// 5. If locationURL is failure, then return a network error.
|
|
1231
|
-
return Promise.resolve(makeNetworkError(err))
|
|
1232
|
-
}
|
|
1233
|
-
|
|
1234
|
-
// 6. If locationURL’s scheme is not an HTTP(S) scheme, then return a network
|
|
1235
|
-
// error.
|
|
1236
|
-
if (!urlIsHttpHttpsScheme(locationURL)) {
|
|
1237
|
-
return Promise.resolve(makeNetworkError('URL scheme must be a HTTP(S) scheme'))
|
|
1238
|
-
}
|
|
1239
|
-
|
|
1240
|
-
// 7. If request’s redirect count is 20, then return a network error.
|
|
1241
|
-
if (request.redirectCount === 20) {
|
|
1242
|
-
return Promise.resolve(makeNetworkError('redirect count exceeded'))
|
|
1243
|
-
}
|
|
1244
|
-
|
|
1245
|
-
// 8. Increase request’s redirect count by 1.
|
|
1246
|
-
request.redirectCount += 1
|
|
1247
|
-
|
|
1248
|
-
// 9. If request’s mode is "cors", locationURL includes credentials, and
|
|
1249
|
-
// request’s origin is not same origin with locationURL’s origin, then return
|
|
1250
|
-
// a network error.
|
|
1251
|
-
if (
|
|
1252
|
-
request.mode === 'cors' &&
|
|
1253
|
-
(locationURL.username || locationURL.password) &&
|
|
1254
|
-
!sameOrigin(request, locationURL)
|
|
1255
|
-
) {
|
|
1256
|
-
return Promise.resolve(makeNetworkError('cross origin not allowed for request mode "cors"'))
|
|
1257
|
-
}
|
|
1258
|
-
|
|
1259
|
-
// 10. If request’s response tainting is "cors" and locationURL includes
|
|
1260
|
-
// credentials, then return a network error.
|
|
1261
|
-
if (
|
|
1262
|
-
request.responseTainting === 'cors' &&
|
|
1263
|
-
(locationURL.username || locationURL.password)
|
|
1264
|
-
) {
|
|
1265
|
-
return Promise.resolve(makeNetworkError(
|
|
1266
|
-
'URL cannot contain credentials for request mode "cors"'
|
|
1267
|
-
))
|
|
1268
|
-
}
|
|
1269
|
-
|
|
1270
|
-
// 11. If actualResponse’s status is not 303, request’s body is non-null,
|
|
1271
|
-
// and request’s body’s source is null, then return a network error.
|
|
1272
|
-
if (
|
|
1273
|
-
actualResponse.status !== 303 &&
|
|
1274
|
-
request.body != null &&
|
|
1275
|
-
request.body.source == null
|
|
1276
|
-
) {
|
|
1277
|
-
return Promise.resolve(makeNetworkError())
|
|
1278
|
-
}
|
|
1279
|
-
|
|
1280
|
-
// 12. If one of the following is true
|
|
1281
|
-
// - actualResponse’s status is 301 or 302 and request’s method is `POST`
|
|
1282
|
-
// - actualResponse’s status is 303 and request’s method is not `GET` or `HEAD`
|
|
1283
|
-
if (
|
|
1284
|
-
([301, 302].includes(actualResponse.status) && request.method === 'POST') ||
|
|
1285
|
-
(actualResponse.status === 303 &&
|
|
1286
|
-
!GET_OR_HEAD.includes(request.method))
|
|
1287
|
-
) {
|
|
1288
|
-
// then:
|
|
1289
|
-
// 1. Set request’s method to `GET` and request’s body to null.
|
|
1290
|
-
request.method = 'GET'
|
|
1291
|
-
request.body = null
|
|
1292
|
-
|
|
1293
|
-
// 2. For each headerName of request-body-header name, delete headerName from
|
|
1294
|
-
// request’s header list.
|
|
1295
|
-
for (const headerName of requestBodyHeader) {
|
|
1296
|
-
request.headersList.delete(headerName)
|
|
1297
|
-
}
|
|
1298
|
-
}
|
|
1299
|
-
|
|
1300
|
-
// 13. If request’s current URL’s origin is not same origin with locationURL’s
|
|
1301
|
-
// origin, then for each headerName of CORS non-wildcard request-header name,
|
|
1302
|
-
// delete headerName from request’s header list.
|
|
1303
|
-
if (!sameOrigin(requestCurrentURL(request), locationURL)) {
|
|
1304
|
-
// https://fetch.spec.whatwg.org/#cors-non-wildcard-request-header-name
|
|
1305
|
-
request.headersList.delete('authorization', true)
|
|
1306
|
-
|
|
1307
|
-
// https://fetch.spec.whatwg.org/#authentication-entries
|
|
1308
|
-
request.headersList.delete('proxy-authorization', true)
|
|
1309
|
-
|
|
1310
|
-
// "Cookie" and "Host" are forbidden request-headers, which undici doesn't implement.
|
|
1311
|
-
request.headersList.delete('cookie', true)
|
|
1312
|
-
request.headersList.delete('host', true)
|
|
1313
|
-
}
|
|
1314
|
-
|
|
1315
|
-
// 14. If request’s body is non-null, then set request’s body to the first return
|
|
1316
|
-
// value of safely extracting request’s body’s source.
|
|
1317
|
-
if (request.body != null) {
|
|
1318
|
-
assert(request.body.source != null)
|
|
1319
|
-
request.body = safelyExtractBody(request.body.source)[0]
|
|
1320
|
-
}
|
|
1321
|
-
|
|
1322
|
-
// 15. Let timingInfo be fetchParams’s timing info.
|
|
1323
|
-
const timingInfo = fetchParams.timingInfo
|
|
1324
|
-
|
|
1325
|
-
// 16. Set timingInfo’s redirect end time and post-redirect start time to the
|
|
1326
|
-
// coarsened shared current time given fetchParams’s cross-origin isolated
|
|
1327
|
-
// capability.
|
|
1328
|
-
timingInfo.redirectEndTime = timingInfo.postRedirectStartTime =
|
|
1329
|
-
coarsenedSharedCurrentTime(fetchParams.crossOriginIsolatedCapability)
|
|
1330
|
-
|
|
1331
|
-
// 17. If timingInfo’s redirect start time is 0, then set timingInfo’s
|
|
1332
|
-
// redirect start time to timingInfo’s start time.
|
|
1333
|
-
if (timingInfo.redirectStartTime === 0) {
|
|
1334
|
-
timingInfo.redirectStartTime = timingInfo.startTime
|
|
1335
|
-
}
|
|
1336
|
-
|
|
1337
|
-
// 18. Append locationURL to request’s URL list.
|
|
1338
|
-
request.urlList.push(locationURL)
|
|
1339
|
-
|
|
1340
|
-
// 19. Invoke set request’s referrer policy on redirect on request and
|
|
1341
|
-
// actualResponse.
|
|
1342
|
-
setRequestReferrerPolicyOnRedirect(request, actualResponse)
|
|
1343
|
-
|
|
1344
|
-
// 20. Return the result of running main fetch given fetchParams and true.
|
|
1345
|
-
return mainFetch(fetchParams, true)
|
|
1346
|
-
}
|
|
1347
|
-
|
|
1348
|
-
// https://fetch.spec.whatwg.org/#http-network-or-cache-fetch
|
|
1349
|
-
async function httpNetworkOrCacheFetch (
|
|
1350
|
-
fetchParams,
|
|
1351
|
-
isAuthenticationFetch = false,
|
|
1352
|
-
isNewConnectionFetch = false
|
|
1353
|
-
) {
|
|
1354
|
-
// 1. Let request be fetchParams’s request.
|
|
1355
|
-
const request = fetchParams.request
|
|
1356
|
-
|
|
1357
|
-
// 2. Let httpFetchParams be null.
|
|
1358
|
-
let httpFetchParams = null
|
|
1359
|
-
|
|
1360
|
-
// 3. Let httpRequest be null.
|
|
1361
|
-
let httpRequest = null
|
|
1362
|
-
|
|
1363
|
-
// 4. Let response be null.
|
|
1364
|
-
let response = null
|
|
1365
|
-
|
|
1366
|
-
// 5. Let storedResponse be null.
|
|
1367
|
-
// TODO: cache
|
|
1368
|
-
|
|
1369
|
-
// 6. Let httpCache be null.
|
|
1370
|
-
const httpCache = null
|
|
1371
|
-
|
|
1372
|
-
// 7. Let the revalidatingFlag be unset.
|
|
1373
|
-
const revalidatingFlag = false
|
|
1374
|
-
|
|
1375
|
-
// 8. Run these steps, but abort when the ongoing fetch is terminated:
|
|
1376
|
-
|
|
1377
|
-
// 1. If request’s window is "no-window" and request’s redirect mode is
|
|
1378
|
-
// "error", then set httpFetchParams to fetchParams and httpRequest to
|
|
1379
|
-
// request.
|
|
1380
|
-
if (request.window === 'no-window' && request.redirect === 'error') {
|
|
1381
|
-
httpFetchParams = fetchParams
|
|
1382
|
-
httpRequest = request
|
|
1383
|
-
} else {
|
|
1384
|
-
// Otherwise:
|
|
1385
|
-
|
|
1386
|
-
// 1. Set httpRequest to a clone of request.
|
|
1387
|
-
httpRequest = cloneRequest(request)
|
|
1388
|
-
|
|
1389
|
-
// 2. Set httpFetchParams to a copy of fetchParams.
|
|
1390
|
-
httpFetchParams = { ...fetchParams }
|
|
1391
|
-
|
|
1392
|
-
// 3. Set httpFetchParams’s request to httpRequest.
|
|
1393
|
-
httpFetchParams.request = httpRequest
|
|
1394
|
-
}
|
|
1395
|
-
|
|
1396
|
-
// 3. Let includeCredentials be true if one of
|
|
1397
|
-
const includeCredentials =
|
|
1398
|
-
request.credentials === 'include' ||
|
|
1399
|
-
(request.credentials === 'same-origin' &&
|
|
1400
|
-
request.responseTainting === 'basic')
|
|
1401
|
-
|
|
1402
|
-
// 4. Let contentLength be httpRequest’s body’s length, if httpRequest’s
|
|
1403
|
-
// body is non-null; otherwise null.
|
|
1404
|
-
const contentLength = httpRequest.body ? httpRequest.body.length : null
|
|
1405
|
-
|
|
1406
|
-
// 5. Let contentLengthHeaderValue be null.
|
|
1407
|
-
let contentLengthHeaderValue = null
|
|
1408
|
-
|
|
1409
|
-
// 6. If httpRequest’s body is null and httpRequest’s method is `POST` or
|
|
1410
|
-
// `PUT`, then set contentLengthHeaderValue to `0`.
|
|
1411
|
-
if (
|
|
1412
|
-
httpRequest.body == null &&
|
|
1413
|
-
['POST', 'PUT'].includes(httpRequest.method)
|
|
1414
|
-
) {
|
|
1415
|
-
contentLengthHeaderValue = '0'
|
|
1416
|
-
}
|
|
1417
|
-
|
|
1418
|
-
// 7. If contentLength is non-null, then set contentLengthHeaderValue to
|
|
1419
|
-
// contentLength, serialized and isomorphic encoded.
|
|
1420
|
-
if (contentLength != null) {
|
|
1421
|
-
contentLengthHeaderValue = isomorphicEncode(`${contentLength}`)
|
|
1422
|
-
}
|
|
1423
|
-
|
|
1424
|
-
// 8. If contentLengthHeaderValue is non-null, then append
|
|
1425
|
-
// `Content-Length`/contentLengthHeaderValue to httpRequest’s header
|
|
1426
|
-
// list.
|
|
1427
|
-
if (contentLengthHeaderValue != null) {
|
|
1428
|
-
httpRequest.headersList.append('content-length', contentLengthHeaderValue, true)
|
|
1429
|
-
}
|
|
1430
|
-
|
|
1431
|
-
// 9. If contentLengthHeaderValue is non-null, then append (`Content-Length`,
|
|
1432
|
-
// contentLengthHeaderValue) to httpRequest’s header list.
|
|
1433
|
-
|
|
1434
|
-
// 10. If contentLength is non-null and httpRequest’s keepalive is true,
|
|
1435
|
-
// then:
|
|
1436
|
-
if (contentLength != null && httpRequest.keepalive) {
|
|
1437
|
-
// NOTE: keepalive is a noop outside of browser context.
|
|
1438
|
-
}
|
|
1439
|
-
|
|
1440
|
-
// 11. If httpRequest’s referrer is a URL, then append
|
|
1441
|
-
// `Referer`/httpRequest’s referrer, serialized and isomorphic encoded,
|
|
1442
|
-
// to httpRequest’s header list.
|
|
1443
|
-
if (webidl.is.URL(httpRequest.referrer)) {
|
|
1444
|
-
httpRequest.headersList.append('referer', isomorphicEncode(httpRequest.referrer.href), true)
|
|
1445
|
-
}
|
|
1446
|
-
|
|
1447
|
-
// 12. Append a request `Origin` header for httpRequest.
|
|
1448
|
-
appendRequestOriginHeader(httpRequest)
|
|
1449
|
-
|
|
1450
|
-
// 13. Append the Fetch metadata headers for httpRequest. [FETCH-METADATA]
|
|
1451
|
-
appendFetchMetadata(httpRequest)
|
|
1452
|
-
|
|
1453
|
-
// 14. If httpRequest’s header list does not contain `User-Agent`, then
|
|
1454
|
-
// user agents should append `User-Agent`/default `User-Agent` value to
|
|
1455
|
-
// httpRequest’s header list.
|
|
1456
|
-
if (!httpRequest.headersList.contains('user-agent', true)) {
|
|
1457
|
-
httpRequest.headersList.append('user-agent', defaultUserAgent, true)
|
|
1458
|
-
}
|
|
1459
|
-
|
|
1460
|
-
// 15. If httpRequest’s cache mode is "default" and httpRequest’s header
|
|
1461
|
-
// list contains `If-Modified-Since`, `If-None-Match`,
|
|
1462
|
-
// `If-Unmodified-Since`, `If-Match`, or `If-Range`, then set
|
|
1463
|
-
// httpRequest’s cache mode to "no-store".
|
|
1464
|
-
if (
|
|
1465
|
-
httpRequest.cache === 'default' &&
|
|
1466
|
-
(httpRequest.headersList.contains('if-modified-since', true) ||
|
|
1467
|
-
httpRequest.headersList.contains('if-none-match', true) ||
|
|
1468
|
-
httpRequest.headersList.contains('if-unmodified-since', true) ||
|
|
1469
|
-
httpRequest.headersList.contains('if-match', true) ||
|
|
1470
|
-
httpRequest.headersList.contains('if-range', true))
|
|
1471
|
-
) {
|
|
1472
|
-
httpRequest.cache = 'no-store'
|
|
1473
|
-
}
|
|
1474
|
-
|
|
1475
|
-
// 16. If httpRequest’s cache mode is "no-cache", httpRequest’s prevent
|
|
1476
|
-
// no-cache cache-control header modification flag is unset, and
|
|
1477
|
-
// httpRequest’s header list does not contain `Cache-Control`, then append
|
|
1478
|
-
// `Cache-Control`/`max-age=0` to httpRequest’s header list.
|
|
1479
|
-
if (
|
|
1480
|
-
httpRequest.cache === 'no-cache' &&
|
|
1481
|
-
!httpRequest.preventNoCacheCacheControlHeaderModification &&
|
|
1482
|
-
!httpRequest.headersList.contains('cache-control', true)
|
|
1483
|
-
) {
|
|
1484
|
-
httpRequest.headersList.append('cache-control', 'max-age=0', true)
|
|
1485
|
-
}
|
|
1486
|
-
|
|
1487
|
-
// 17. If httpRequest’s cache mode is "no-store" or "reload", then:
|
|
1488
|
-
if (httpRequest.cache === 'no-store' || httpRequest.cache === 'reload') {
|
|
1489
|
-
// 1. If httpRequest’s header list does not contain `Pragma`, then append
|
|
1490
|
-
// `Pragma`/`no-cache` to httpRequest’s header list.
|
|
1491
|
-
if (!httpRequest.headersList.contains('pragma', true)) {
|
|
1492
|
-
httpRequest.headersList.append('pragma', 'no-cache', true)
|
|
1493
|
-
}
|
|
1494
|
-
|
|
1495
|
-
// 2. If httpRequest’s header list does not contain `Cache-Control`,
|
|
1496
|
-
// then append `Cache-Control`/`no-cache` to httpRequest’s header list.
|
|
1497
|
-
if (!httpRequest.headersList.contains('cache-control', true)) {
|
|
1498
|
-
httpRequest.headersList.append('cache-control', 'no-cache', true)
|
|
1499
|
-
}
|
|
1500
|
-
}
|
|
1501
|
-
|
|
1502
|
-
// 18. If httpRequest’s header list contains `Range`, then append
|
|
1503
|
-
// `Accept-Encoding`/`identity` to httpRequest’s header list.
|
|
1504
|
-
if (httpRequest.headersList.contains('range', true)) {
|
|
1505
|
-
httpRequest.headersList.append('accept-encoding', 'identity', true)
|
|
1506
|
-
}
|
|
1507
|
-
|
|
1508
|
-
// 19. Modify httpRequest’s header list per HTTP. Do not append a given
|
|
1509
|
-
// header if httpRequest’s header list contains that header’s name.
|
|
1510
|
-
// TODO: https://github.com/whatwg/fetch/issues/1285#issuecomment-896560129
|
|
1511
|
-
if (!httpRequest.headersList.contains('accept-encoding', true)) {
|
|
1512
|
-
if (urlHasHttpsScheme(requestCurrentURL(httpRequest))) {
|
|
1513
|
-
httpRequest.headersList.append('accept-encoding', 'br, gzip, deflate', true)
|
|
1514
|
-
} else {
|
|
1515
|
-
httpRequest.headersList.append('accept-encoding', 'gzip, deflate', true)
|
|
1516
|
-
}
|
|
1517
|
-
}
|
|
1518
|
-
|
|
1519
|
-
httpRequest.headersList.delete('host', true)
|
|
1520
|
-
|
|
1521
|
-
// 20. If includeCredentials is true, then:
|
|
1522
|
-
if (includeCredentials) {
|
|
1523
|
-
// 1. If the user agent is not configured to block cookies for httpRequest
|
|
1524
|
-
// (see section 7 of [COOKIES]), then:
|
|
1525
|
-
// TODO: credentials
|
|
1526
|
-
// 2. If httpRequest’s header list does not contain `Authorization`, then:
|
|
1527
|
-
// TODO: credentials
|
|
1528
|
-
}
|
|
1529
|
-
|
|
1530
|
-
// 21. If there’s a proxy-authentication entry, use it as appropriate.
|
|
1531
|
-
// TODO: proxy-authentication
|
|
1532
|
-
|
|
1533
|
-
// 22. Set httpCache to the result of determining the HTTP cache
|
|
1534
|
-
// partition, given httpRequest.
|
|
1535
|
-
// TODO: cache
|
|
1536
|
-
|
|
1537
|
-
// 23. If httpCache is null, then set httpRequest’s cache mode to
|
|
1538
|
-
// "no-store".
|
|
1539
|
-
if (httpCache == null) {
|
|
1540
|
-
httpRequest.cache = 'no-store'
|
|
1541
|
-
}
|
|
1542
|
-
|
|
1543
|
-
// 24. If httpRequest’s cache mode is neither "no-store" nor "reload",
|
|
1544
|
-
// then:
|
|
1545
|
-
if (httpRequest.cache !== 'no-store' && httpRequest.cache !== 'reload') {
|
|
1546
|
-
// TODO: cache
|
|
1547
|
-
}
|
|
1548
|
-
|
|
1549
|
-
// 9. If aborted, then return the appropriate network error for fetchParams.
|
|
1550
|
-
// TODO
|
|
1551
|
-
|
|
1552
|
-
// 10. If response is null, then:
|
|
1553
|
-
if (response == null) {
|
|
1554
|
-
// 1. If httpRequest’s cache mode is "only-if-cached", then return a
|
|
1555
|
-
// network error.
|
|
1556
|
-
if (httpRequest.cache === 'only-if-cached') {
|
|
1557
|
-
return makeNetworkError('only if cached')
|
|
1558
|
-
}
|
|
1559
|
-
|
|
1560
|
-
// 2. Let forwardResponse be the result of running HTTP-network fetch
|
|
1561
|
-
// given httpFetchParams, includeCredentials, and isNewConnectionFetch.
|
|
1562
|
-
const forwardResponse = await httpNetworkFetch(
|
|
1563
|
-
httpFetchParams,
|
|
1564
|
-
includeCredentials,
|
|
1565
|
-
isNewConnectionFetch
|
|
1566
|
-
)
|
|
1567
|
-
|
|
1568
|
-
// 3. If httpRequest’s method is unsafe and forwardResponse’s status is
|
|
1569
|
-
// in the range 200 to 399, inclusive, invalidate appropriate stored
|
|
1570
|
-
// responses in httpCache, as per the "Invalidation" chapter of HTTP
|
|
1571
|
-
// Caching, and set storedResponse to null. [HTTP-CACHING]
|
|
1572
|
-
if (
|
|
1573
|
-
!safeMethodsSet.has(httpRequest.method) &&
|
|
1574
|
-
forwardResponse.status >= 200 &&
|
|
1575
|
-
forwardResponse.status <= 399
|
|
1576
|
-
) {
|
|
1577
|
-
// TODO: cache
|
|
1578
|
-
}
|
|
1579
|
-
|
|
1580
|
-
// 4. If the revalidatingFlag is set and forwardResponse’s status is 304,
|
|
1581
|
-
// then:
|
|
1582
|
-
if (revalidatingFlag && forwardResponse.status === 304) {
|
|
1583
|
-
// TODO: cache
|
|
1584
|
-
}
|
|
1585
|
-
|
|
1586
|
-
// 5. If response is null, then:
|
|
1587
|
-
if (response == null) {
|
|
1588
|
-
// 1. Set response to forwardResponse.
|
|
1589
|
-
response = forwardResponse
|
|
1590
|
-
|
|
1591
|
-
// 2. Store httpRequest and forwardResponse in httpCache, as per the
|
|
1592
|
-
// "Storing Responses in Caches" chapter of HTTP Caching. [HTTP-CACHING]
|
|
1593
|
-
// TODO: cache
|
|
1594
|
-
}
|
|
1595
|
-
}
|
|
1596
|
-
|
|
1597
|
-
// 11. Set response’s URL list to a clone of httpRequest’s URL list.
|
|
1598
|
-
response.urlList = [...httpRequest.urlList]
|
|
1599
|
-
|
|
1600
|
-
// 12. If httpRequest’s header list contains `Range`, then set response’s
|
|
1601
|
-
// range-requested flag.
|
|
1602
|
-
if (httpRequest.headersList.contains('range', true)) {
|
|
1603
|
-
response.rangeRequested = true
|
|
1604
|
-
}
|
|
1605
|
-
|
|
1606
|
-
// 13. Set response’s request-includes-credentials to includeCredentials.
|
|
1607
|
-
response.requestIncludesCredentials = includeCredentials
|
|
1608
|
-
|
|
1609
|
-
// 14. If response’s status is 401, httpRequest’s response tainting is not
|
|
1610
|
-
// "cors", includeCredentials is true, and request’s window is an environment
|
|
1611
|
-
// settings object, then:
|
|
1612
|
-
// TODO
|
|
1613
|
-
|
|
1614
|
-
// 15. If response’s status is 407, then:
|
|
1615
|
-
if (response.status === 407) {
|
|
1616
|
-
// 1. If request’s window is "no-window", then return a network error.
|
|
1617
|
-
if (request.window === 'no-window') {
|
|
1618
|
-
return makeNetworkError()
|
|
1619
|
-
}
|
|
1620
|
-
|
|
1621
|
-
// 2. ???
|
|
1622
|
-
|
|
1623
|
-
// 3. If fetchParams is canceled, then return the appropriate network error for fetchParams.
|
|
1624
|
-
if (isCancelled(fetchParams)) {
|
|
1625
|
-
return makeAppropriateNetworkError(fetchParams)
|
|
1626
|
-
}
|
|
1627
|
-
|
|
1628
|
-
// 4. Prompt the end user as appropriate in request’s window and store
|
|
1629
|
-
// the result as a proxy-authentication entry. [HTTP-AUTH]
|
|
1630
|
-
// TODO: Invoke some kind of callback?
|
|
1631
|
-
|
|
1632
|
-
// 5. Set response to the result of running HTTP-network-or-cache fetch given
|
|
1633
|
-
// fetchParams.
|
|
1634
|
-
// TODO
|
|
1635
|
-
return makeNetworkError('proxy authentication required')
|
|
1636
|
-
}
|
|
1637
|
-
|
|
1638
|
-
// 16. If all of the following are true
|
|
1639
|
-
if (
|
|
1640
|
-
// response’s status is 421
|
|
1641
|
-
response.status === 421 &&
|
|
1642
|
-
// isNewConnectionFetch is false
|
|
1643
|
-
!isNewConnectionFetch &&
|
|
1644
|
-
// request’s body is null, or request’s body is non-null and request’s body’s source is non-null
|
|
1645
|
-
(request.body == null || request.body.source != null)
|
|
1646
|
-
) {
|
|
1647
|
-
// then:
|
|
1648
|
-
|
|
1649
|
-
// 1. If fetchParams is canceled, then return the appropriate network error for fetchParams.
|
|
1650
|
-
if (isCancelled(fetchParams)) {
|
|
1651
|
-
return makeAppropriateNetworkError(fetchParams)
|
|
1652
|
-
}
|
|
1653
|
-
|
|
1654
|
-
// 2. Set response to the result of running HTTP-network-or-cache
|
|
1655
|
-
// fetch given fetchParams, isAuthenticationFetch, and true.
|
|
1656
|
-
|
|
1657
|
-
// TODO (spec): The spec doesn't specify this but we need to cancel
|
|
1658
|
-
// the active response before we can start a new one.
|
|
1659
|
-
// https://github.com/whatwg/fetch/issues/1293
|
|
1660
|
-
fetchParams.controller.connection.destroy()
|
|
1661
|
-
|
|
1662
|
-
response = await httpNetworkOrCacheFetch(
|
|
1663
|
-
fetchParams,
|
|
1664
|
-
isAuthenticationFetch,
|
|
1665
|
-
true
|
|
1666
|
-
)
|
|
1667
|
-
}
|
|
1668
|
-
|
|
1669
|
-
// 17. If isAuthenticationFetch is true, then create an authentication entry
|
|
1670
|
-
if (isAuthenticationFetch) {
|
|
1671
|
-
// TODO
|
|
1672
|
-
}
|
|
1673
|
-
|
|
1674
|
-
// 18. Return response.
|
|
1675
|
-
return response
|
|
1676
|
-
}
|
|
1677
|
-
|
|
1678
|
-
// https://fetch.spec.whatwg.org/#http-network-fetch
|
|
1679
|
-
async function httpNetworkFetch (
|
|
1680
|
-
fetchParams,
|
|
1681
|
-
includeCredentials = false,
|
|
1682
|
-
forceNewConnection = false
|
|
1683
|
-
) {
|
|
1684
|
-
assert(!fetchParams.controller.connection || fetchParams.controller.connection.destroyed)
|
|
1685
|
-
|
|
1686
|
-
fetchParams.controller.connection = {
|
|
1687
|
-
abort: null,
|
|
1688
|
-
destroyed: false,
|
|
1689
|
-
destroy (err, abort = true) {
|
|
1690
|
-
if (!this.destroyed) {
|
|
1691
|
-
this.destroyed = true
|
|
1692
|
-
if (abort) {
|
|
1693
|
-
this.abort?.(err ?? new DOMException('The operation was aborted.', 'AbortError'))
|
|
1694
|
-
}
|
|
1695
|
-
}
|
|
1696
|
-
}
|
|
1697
|
-
}
|
|
1698
|
-
|
|
1699
|
-
// 1. Let request be fetchParams’s request.
|
|
1700
|
-
const request = fetchParams.request
|
|
1701
|
-
|
|
1702
|
-
// 2. Let response be null.
|
|
1703
|
-
let response = null
|
|
1704
|
-
|
|
1705
|
-
// 3. Let timingInfo be fetchParams’s timing info.
|
|
1706
|
-
const timingInfo = fetchParams.timingInfo
|
|
1707
|
-
|
|
1708
|
-
// 4. Let httpCache be the result of determining the HTTP cache partition,
|
|
1709
|
-
// given request.
|
|
1710
|
-
// TODO: cache
|
|
1711
|
-
const httpCache = null
|
|
1712
|
-
|
|
1713
|
-
// 5. If httpCache is null, then set request’s cache mode to "no-store".
|
|
1714
|
-
if (httpCache == null) {
|
|
1715
|
-
request.cache = 'no-store'
|
|
1716
|
-
}
|
|
1717
|
-
|
|
1718
|
-
// 6. Let networkPartitionKey be the result of determining the network
|
|
1719
|
-
// partition key given request.
|
|
1720
|
-
// TODO
|
|
1721
|
-
|
|
1722
|
-
// 7. Let newConnection be "yes" if forceNewConnection is true; otherwise
|
|
1723
|
-
// "no".
|
|
1724
|
-
const newConnection = forceNewConnection ? 'yes' : 'no' // eslint-disable-line no-unused-vars
|
|
1725
|
-
|
|
1726
|
-
// 8. Switch on request’s mode:
|
|
1727
|
-
if (request.mode === 'websocket') {
|
|
1728
|
-
// Let connection be the result of obtaining a WebSocket connection,
|
|
1729
|
-
// given request’s current URL.
|
|
1730
|
-
// TODO
|
|
1731
|
-
} else {
|
|
1732
|
-
// Let connection be the result of obtaining a connection, given
|
|
1733
|
-
// networkPartitionKey, request’s current URL’s origin,
|
|
1734
|
-
// includeCredentials, and forceNewConnection.
|
|
1735
|
-
// TODO
|
|
1736
|
-
}
|
|
1737
|
-
|
|
1738
|
-
// 9. Run these steps, but abort when the ongoing fetch is terminated:
|
|
1739
|
-
|
|
1740
|
-
// 1. If connection is failure, then return a network error.
|
|
1741
|
-
|
|
1742
|
-
// 2. Set timingInfo’s final connection timing info to the result of
|
|
1743
|
-
// calling clamp and coarsen connection timing info with connection’s
|
|
1744
|
-
// timing info, timingInfo’s post-redirect start time, and fetchParams’s
|
|
1745
|
-
// cross-origin isolated capability.
|
|
1746
|
-
|
|
1747
|
-
// 3. If connection is not an HTTP/2 connection, request’s body is non-null,
|
|
1748
|
-
// and request’s body’s source is null, then append (`Transfer-Encoding`,
|
|
1749
|
-
// `chunked`) to request’s header list.
|
|
1750
|
-
|
|
1751
|
-
// 4. Set timingInfo’s final network-request start time to the coarsened
|
|
1752
|
-
// shared current time given fetchParams’s cross-origin isolated
|
|
1753
|
-
// capability.
|
|
1754
|
-
|
|
1755
|
-
// 5. Set response to the result of making an HTTP request over connection
|
|
1756
|
-
// using request with the following caveats:
|
|
1757
|
-
|
|
1758
|
-
// - Follow the relevant requirements from HTTP. [HTTP] [HTTP-SEMANTICS]
|
|
1759
|
-
// [HTTP-COND] [HTTP-CACHING] [HTTP-AUTH]
|
|
1760
|
-
|
|
1761
|
-
// - If request’s body is non-null, and request’s body’s source is null,
|
|
1762
|
-
// then the user agent may have a buffer of up to 64 kibibytes and store
|
|
1763
|
-
// a part of request’s body in that buffer. If the user agent reads from
|
|
1764
|
-
// request’s body beyond that buffer’s size and the user agent needs to
|
|
1765
|
-
// resend request, then instead return a network error.
|
|
1766
|
-
|
|
1767
|
-
// - Set timingInfo’s final network-response start time to the coarsened
|
|
1768
|
-
// shared current time given fetchParams’s cross-origin isolated capability,
|
|
1769
|
-
// immediately after the user agent’s HTTP parser receives the first byte
|
|
1770
|
-
// of the response (e.g., frame header bytes for HTTP/2 or response status
|
|
1771
|
-
// line for HTTP/1.x).
|
|
1772
|
-
|
|
1773
|
-
// - Wait until all the headers are transmitted.
|
|
1774
|
-
|
|
1775
|
-
// - Any responses whose status is in the range 100 to 199, inclusive,
|
|
1776
|
-
// and is not 101, are to be ignored, except for the purposes of setting
|
|
1777
|
-
// timingInfo’s final network-response start time above.
|
|
1778
|
-
|
|
1779
|
-
// - If request’s header list contains `Transfer-Encoding`/`chunked` and
|
|
1780
|
-
// response is transferred via HTTP/1.0 or older, then return a network
|
|
1781
|
-
// error.
|
|
1782
|
-
|
|
1783
|
-
// - If the HTTP request results in a TLS client certificate dialog, then:
|
|
1784
|
-
|
|
1785
|
-
// 1. If request’s window is an environment settings object, make the
|
|
1786
|
-
// dialog available in request’s window.
|
|
1787
|
-
|
|
1788
|
-
// 2. Otherwise, return a network error.
|
|
1789
|
-
|
|
1790
|
-
// To transmit request’s body body, run these steps:
|
|
1791
|
-
let requestBody = null
|
|
1792
|
-
// 1. If body is null and fetchParams’s process request end-of-body is
|
|
1793
|
-
// non-null, then queue a fetch task given fetchParams’s process request
|
|
1794
|
-
// end-of-body and fetchParams’s task destination.
|
|
1795
|
-
if (request.body == null && fetchParams.processRequestEndOfBody) {
|
|
1796
|
-
queueMicrotask(() => fetchParams.processRequestEndOfBody())
|
|
1797
|
-
} else if (request.body != null) {
|
|
1798
|
-
// 2. Otherwise, if body is non-null:
|
|
1799
|
-
|
|
1800
|
-
// 1. Let processBodyChunk given bytes be these steps:
|
|
1801
|
-
const processBodyChunk = async function * (bytes) {
|
|
1802
|
-
// 1. If the ongoing fetch is terminated, then abort these steps.
|
|
1803
|
-
if (isCancelled(fetchParams)) {
|
|
1804
|
-
return
|
|
1805
|
-
}
|
|
1806
|
-
|
|
1807
|
-
// 2. Run this step in parallel: transmit bytes.
|
|
1808
|
-
yield bytes
|
|
1809
|
-
|
|
1810
|
-
// 3. If fetchParams’s process request body is non-null, then run
|
|
1811
|
-
// fetchParams’s process request body given bytes’s length.
|
|
1812
|
-
fetchParams.processRequestBodyChunkLength?.(bytes.byteLength)
|
|
1813
|
-
}
|
|
1814
|
-
|
|
1815
|
-
// 2. Let processEndOfBody be these steps:
|
|
1816
|
-
const processEndOfBody = () => {
|
|
1817
|
-
// 1. If fetchParams is canceled, then abort these steps.
|
|
1818
|
-
if (isCancelled(fetchParams)) {
|
|
1819
|
-
return
|
|
1820
|
-
}
|
|
1821
|
-
|
|
1822
|
-
// 2. If fetchParams’s process request end-of-body is non-null,
|
|
1823
|
-
// then run fetchParams’s process request end-of-body.
|
|
1824
|
-
if (fetchParams.processRequestEndOfBody) {
|
|
1825
|
-
fetchParams.processRequestEndOfBody()
|
|
1826
|
-
}
|
|
1827
|
-
}
|
|
1828
|
-
|
|
1829
|
-
// 3. Let processBodyError given e be these steps:
|
|
1830
|
-
const processBodyError = (e) => {
|
|
1831
|
-
// 1. If fetchParams is canceled, then abort these steps.
|
|
1832
|
-
if (isCancelled(fetchParams)) {
|
|
1833
|
-
return
|
|
1834
|
-
}
|
|
1835
|
-
|
|
1836
|
-
// 2. If e is an "AbortError" DOMException, then abort fetchParams’s controller.
|
|
1837
|
-
if (e.name === 'AbortError') {
|
|
1838
|
-
fetchParams.controller.abort()
|
|
1839
|
-
} else {
|
|
1840
|
-
fetchParams.controller.terminate(e)
|
|
1841
|
-
}
|
|
1842
|
-
}
|
|
1843
|
-
|
|
1844
|
-
// 4. Incrementally read request’s body given processBodyChunk, processEndOfBody,
|
|
1845
|
-
// processBodyError, and fetchParams’s task destination.
|
|
1846
|
-
requestBody = (async function * () {
|
|
1847
|
-
try {
|
|
1848
|
-
for await (const bytes of request.body.stream) {
|
|
1849
|
-
yield * processBodyChunk(bytes)
|
|
1850
|
-
}
|
|
1851
|
-
processEndOfBody()
|
|
1852
|
-
} catch (err) {
|
|
1853
|
-
processBodyError(err)
|
|
1854
|
-
}
|
|
1855
|
-
})()
|
|
1856
|
-
}
|
|
1857
|
-
|
|
1858
|
-
try {
|
|
1859
|
-
// socket is only provided for websockets
|
|
1860
|
-
const { body, status, statusText, headersList, socket } = await dispatch({ body: requestBody })
|
|
1861
|
-
|
|
1862
|
-
if (socket) {
|
|
1863
|
-
response = makeResponse({ status, statusText, headersList, socket })
|
|
1864
|
-
} else {
|
|
1865
|
-
const iterator = body[Symbol.asyncIterator]()
|
|
1866
|
-
fetchParams.controller.next = () => iterator.next()
|
|
1867
|
-
|
|
1868
|
-
response = makeResponse({ status, statusText, headersList })
|
|
1869
|
-
}
|
|
1870
|
-
} catch (err) {
|
|
1871
|
-
// 10. If aborted, then:
|
|
1872
|
-
if (err.name === 'AbortError') {
|
|
1873
|
-
// 1. If connection uses HTTP/2, then transmit an RST_STREAM frame.
|
|
1874
|
-
fetchParams.controller.connection.destroy()
|
|
1875
|
-
|
|
1876
|
-
// 2. Return the appropriate network error for fetchParams.
|
|
1877
|
-
return makeAppropriateNetworkError(fetchParams, err)
|
|
1878
|
-
}
|
|
1879
|
-
|
|
1880
|
-
return makeNetworkError(err)
|
|
1881
|
-
}
|
|
1882
|
-
|
|
1883
|
-
// 11. Let pullAlgorithm be an action that resumes the ongoing fetch
|
|
1884
|
-
// if it is suspended.
|
|
1885
|
-
const pullAlgorithm = () => {
|
|
1886
|
-
return fetchParams.controller.resume()
|
|
1887
|
-
}
|
|
1888
|
-
|
|
1889
|
-
// 12. Let cancelAlgorithm be an algorithm that aborts fetchParams’s
|
|
1890
|
-
// controller with reason, given reason.
|
|
1891
|
-
const cancelAlgorithm = (reason) => {
|
|
1892
|
-
// If the aborted fetch was already terminated, then we do not
|
|
1893
|
-
// need to do anything.
|
|
1894
|
-
if (!isCancelled(fetchParams)) {
|
|
1895
|
-
fetchParams.controller.abort(reason)
|
|
1896
|
-
}
|
|
1897
|
-
}
|
|
1898
|
-
|
|
1899
|
-
// 13. Let highWaterMark be a non-negative, non-NaN number, chosen by
|
|
1900
|
-
// the user agent.
|
|
1901
|
-
// TODO
|
|
1902
|
-
|
|
1903
|
-
// 14. Let sizeAlgorithm be an algorithm that accepts a chunk object
|
|
1904
|
-
// and returns a non-negative, non-NaN, non-infinite number, chosen by the user agent.
|
|
1905
|
-
// TODO
|
|
1906
|
-
|
|
1907
|
-
// 15. Let stream be a new ReadableStream.
|
|
1908
|
-
// 16. Set up stream with byte reading support with pullAlgorithm set to pullAlgorithm,
|
|
1909
|
-
// cancelAlgorithm set to cancelAlgorithm.
|
|
1910
|
-
const stream = new ReadableStream(
|
|
1911
|
-
{
|
|
1912
|
-
async start (controller) {
|
|
1913
|
-
fetchParams.controller.controller = controller
|
|
1914
|
-
},
|
|
1915
|
-
async pull (controller) {
|
|
1916
|
-
await pullAlgorithm(controller)
|
|
1917
|
-
},
|
|
1918
|
-
async cancel (reason) {
|
|
1919
|
-
await cancelAlgorithm(reason)
|
|
1920
|
-
},
|
|
1921
|
-
type: 'bytes'
|
|
1922
|
-
}
|
|
1923
|
-
)
|
|
1924
|
-
|
|
1925
|
-
// 17. Run these steps, but abort when the ongoing fetch is terminated:
|
|
1926
|
-
|
|
1927
|
-
// 1. Set response’s body to a new body whose stream is stream.
|
|
1928
|
-
response.body = { stream, source: null, length: null }
|
|
1929
|
-
|
|
1930
|
-
// 2. If response is not a network error and request’s cache mode is
|
|
1931
|
-
// not "no-store", then update response in httpCache for request.
|
|
1932
|
-
// TODO
|
|
1933
|
-
|
|
1934
|
-
// 3. If includeCredentials is true and the user agent is not configured
|
|
1935
|
-
// to block cookies for request (see section 7 of [COOKIES]), then run the
|
|
1936
|
-
// "set-cookie-string" parsing algorithm (see section 5.2 of [COOKIES]) on
|
|
1937
|
-
// the value of each header whose name is a byte-case-insensitive match for
|
|
1938
|
-
// `Set-Cookie` in response’s header list, if any, and request’s current URL.
|
|
1939
|
-
// TODO
|
|
1940
|
-
|
|
1941
|
-
// 18. If aborted, then:
|
|
1942
|
-
// TODO
|
|
1943
|
-
|
|
1944
|
-
// 19. Run these steps in parallel:
|
|
1945
|
-
|
|
1946
|
-
// 1. Run these steps, but abort when fetchParams is canceled:
|
|
1947
|
-
if (!fetchParams.controller.resume) {
|
|
1948
|
-
fetchParams.controller.on('terminated', onAborted)
|
|
1949
|
-
}
|
|
1950
|
-
|
|
1951
|
-
fetchParams.controller.resume = async () => {
|
|
1952
|
-
// 1. While true
|
|
1953
|
-
while (true) {
|
|
1954
|
-
// 1-3. See onData...
|
|
1955
|
-
|
|
1956
|
-
// 4. Set bytes to the result of handling content codings given
|
|
1957
|
-
// codings and bytes.
|
|
1958
|
-
let bytes
|
|
1959
|
-
let isFailure
|
|
1960
|
-
try {
|
|
1961
|
-
const { done, value } = await fetchParams.controller.next()
|
|
1962
|
-
|
|
1963
|
-
if (isAborted(fetchParams)) {
|
|
1964
|
-
break
|
|
1965
|
-
}
|
|
1966
|
-
|
|
1967
|
-
bytes = done ? undefined : value
|
|
1968
|
-
} catch (err) {
|
|
1969
|
-
if (fetchParams.controller.ended && !timingInfo.encodedBodySize) {
|
|
1970
|
-
// zlib doesn't like empty streams.
|
|
1971
|
-
bytes = undefined
|
|
1972
|
-
} else {
|
|
1973
|
-
bytes = err
|
|
1974
|
-
|
|
1975
|
-
// err may be propagated from the result of calling readablestream.cancel,
|
|
1976
|
-
// which might not be an error. https://github.com/nodejs/undici/issues/2009
|
|
1977
|
-
isFailure = true
|
|
1978
|
-
}
|
|
1979
|
-
}
|
|
1980
|
-
|
|
1981
|
-
if (bytes === undefined) {
|
|
1982
|
-
// 2. Otherwise, if the bytes transmission for response’s message
|
|
1983
|
-
// body is done normally and stream is readable, then close
|
|
1984
|
-
// stream, finalize response for fetchParams and response, and
|
|
1985
|
-
// abort these in-parallel steps.
|
|
1986
|
-
readableStreamClose(fetchParams.controller.controller)
|
|
1987
|
-
|
|
1988
|
-
finalizeResponse(fetchParams, response)
|
|
1989
|
-
|
|
1990
|
-
return
|
|
1991
|
-
}
|
|
1992
|
-
|
|
1993
|
-
// 5. Increase timingInfo’s decoded body size by bytes’s length.
|
|
1994
|
-
timingInfo.decodedBodySize += bytes?.byteLength ?? 0
|
|
1995
|
-
|
|
1996
|
-
// 6. If bytes is failure, then terminate fetchParams’s controller.
|
|
1997
|
-
if (isFailure) {
|
|
1998
|
-
fetchParams.controller.terminate(bytes)
|
|
1999
|
-
return
|
|
2000
|
-
}
|
|
2001
|
-
|
|
2002
|
-
// 7. Enqueue a Uint8Array wrapping an ArrayBuffer containing bytes
|
|
2003
|
-
// into stream.
|
|
2004
|
-
const buffer = new Uint8Array(bytes)
|
|
2005
|
-
if (buffer.byteLength) {
|
|
2006
|
-
fetchParams.controller.controller.enqueue(buffer)
|
|
2007
|
-
}
|
|
2008
|
-
|
|
2009
|
-
// 8. If stream is errored, then terminate the ongoing fetch.
|
|
2010
|
-
if (isErrored(stream)) {
|
|
2011
|
-
fetchParams.controller.terminate()
|
|
2012
|
-
return
|
|
2013
|
-
}
|
|
2014
|
-
|
|
2015
|
-
// 9. If stream doesn’t need more data ask the user agent to suspend
|
|
2016
|
-
// the ongoing fetch.
|
|
2017
|
-
if (fetchParams.controller.controller.desiredSize <= 0) {
|
|
2018
|
-
return
|
|
2019
|
-
}
|
|
2020
|
-
}
|
|
2021
|
-
}
|
|
2022
|
-
|
|
2023
|
-
// 2. If aborted, then:
|
|
2024
|
-
function onAborted (reason) {
|
|
2025
|
-
// 2. If fetchParams is aborted, then:
|
|
2026
|
-
if (isAborted(fetchParams)) {
|
|
2027
|
-
// 1. Set response’s aborted flag.
|
|
2028
|
-
response.aborted = true
|
|
2029
|
-
|
|
2030
|
-
// 2. If stream is readable, then error stream with the result of
|
|
2031
|
-
// deserialize a serialized abort reason given fetchParams’s
|
|
2032
|
-
// controller’s serialized abort reason and an
|
|
2033
|
-
// implementation-defined realm.
|
|
2034
|
-
if (isReadable(stream)) {
|
|
2035
|
-
fetchParams.controller.controller.error(
|
|
2036
|
-
fetchParams.controller.serializedAbortReason
|
|
2037
|
-
)
|
|
2038
|
-
}
|
|
2039
|
-
} else {
|
|
2040
|
-
// 3. Otherwise, if stream is readable, error stream with a TypeError.
|
|
2041
|
-
if (isReadable(stream)) {
|
|
2042
|
-
fetchParams.controller.controller.error(new TypeError('terminated', {
|
|
2043
|
-
cause: isErrorLike(reason) ? reason : undefined
|
|
2044
|
-
}))
|
|
2045
|
-
}
|
|
2046
|
-
}
|
|
2047
|
-
|
|
2048
|
-
// 4. If connection uses HTTP/2, then transmit an RST_STREAM frame.
|
|
2049
|
-
// 5. Otherwise, the user agent should close connection unless it would be bad for performance to do so.
|
|
2050
|
-
fetchParams.controller.connection.destroy()
|
|
2051
|
-
}
|
|
2052
|
-
|
|
2053
|
-
// 20. Return response.
|
|
2054
|
-
return response
|
|
2055
|
-
|
|
2056
|
-
function dispatch ({ body }) {
|
|
2057
|
-
const url = requestCurrentURL(request)
|
|
2058
|
-
/** @type {import('../..').Agent} */
|
|
2059
|
-
const agent = fetchParams.controller.dispatcher
|
|
2060
|
-
|
|
2061
|
-
return new Promise((resolve, reject) => agent.dispatch(
|
|
2062
|
-
{
|
|
2063
|
-
path: url.pathname + url.search,
|
|
2064
|
-
origin: url.origin,
|
|
2065
|
-
method: request.method,
|
|
2066
|
-
body: agent.isMockActive ? request.body && (request.body.source || request.body.stream) : body,
|
|
2067
|
-
headers: request.headersList.entries,
|
|
2068
|
-
maxRedirections: 0,
|
|
2069
|
-
upgrade: request.mode === 'websocket' ? 'websocket' : undefined
|
|
2070
|
-
},
|
|
2071
|
-
{
|
|
2072
|
-
body: null,
|
|
2073
|
-
abort: null,
|
|
2074
|
-
|
|
2075
|
-
onConnect (abort) {
|
|
2076
|
-
// TODO (fix): Do we need connection here?
|
|
2077
|
-
const { connection } = fetchParams.controller
|
|
2078
|
-
|
|
2079
|
-
// Set timingInfo’s final connection timing info to the result of calling clamp and coarsen
|
|
2080
|
-
// connection timing info with connection’s timing info, timingInfo’s post-redirect start
|
|
2081
|
-
// time, and fetchParams’s cross-origin isolated capability.
|
|
2082
|
-
// TODO: implement connection timing
|
|
2083
|
-
timingInfo.finalConnectionTimingInfo = clampAndCoarsenConnectionTimingInfo(undefined, timingInfo.postRedirectStartTime, fetchParams.crossOriginIsolatedCapability)
|
|
2084
|
-
|
|
2085
|
-
if (connection.destroyed) {
|
|
2086
|
-
abort(new DOMException('The operation was aborted.', 'AbortError'))
|
|
2087
|
-
} else {
|
|
2088
|
-
fetchParams.controller.on('terminated', abort)
|
|
2089
|
-
this.abort = connection.abort = abort
|
|
2090
|
-
}
|
|
2091
|
-
|
|
2092
|
-
// Set timingInfo’s final network-request start time to the coarsened shared current time given
|
|
2093
|
-
// fetchParams’s cross-origin isolated capability.
|
|
2094
|
-
timingInfo.finalNetworkRequestStartTime = coarsenedSharedCurrentTime(fetchParams.crossOriginIsolatedCapability)
|
|
2095
|
-
},
|
|
2096
|
-
|
|
2097
|
-
onResponseStarted () {
|
|
2098
|
-
// Set timingInfo’s final network-response start time to the coarsened shared current
|
|
2099
|
-
// time given fetchParams’s cross-origin isolated capability, immediately after the
|
|
2100
|
-
// user agent’s HTTP parser receives the first byte of the response (e.g., frame header
|
|
2101
|
-
// bytes for HTTP/2 or response status line for HTTP/1.x).
|
|
2102
|
-
timingInfo.finalNetworkResponseStartTime = coarsenedSharedCurrentTime(fetchParams.crossOriginIsolatedCapability)
|
|
2103
|
-
},
|
|
2104
|
-
|
|
2105
|
-
onHeaders (status, rawHeaders, resume, statusText) {
|
|
2106
|
-
if (status < 200) {
|
|
2107
|
-
return
|
|
2108
|
-
}
|
|
2109
|
-
|
|
2110
|
-
/** @type {string[]} */
|
|
2111
|
-
let codings = []
|
|
2112
|
-
let location = ''
|
|
2113
|
-
|
|
2114
|
-
const headersList = new HeadersList()
|
|
2115
|
-
|
|
2116
|
-
for (let i = 0; i < rawHeaders.length; i += 2) {
|
|
2117
|
-
headersList.append(bufferToLowerCasedHeaderName(rawHeaders[i]), rawHeaders[i + 1].toString('latin1'), true)
|
|
2118
|
-
}
|
|
2119
|
-
const contentEncoding = headersList.get('content-encoding', true)
|
|
2120
|
-
if (contentEncoding) {
|
|
2121
|
-
// https://www.rfc-editor.org/rfc/rfc7231#section-3.1.2.1
|
|
2122
|
-
// "All content-coding values are case-insensitive..."
|
|
2123
|
-
codings = contentEncoding.toLowerCase().split(',').map((x) => x.trim())
|
|
2124
|
-
}
|
|
2125
|
-
location = headersList.get('location', true)
|
|
2126
|
-
|
|
2127
|
-
this.body = new Readable({ read: resume })
|
|
2128
|
-
|
|
2129
|
-
const decoders = []
|
|
2130
|
-
|
|
2131
|
-
const willFollow = location && request.redirect === 'follow' &&
|
|
2132
|
-
redirectStatusSet.has(status)
|
|
2133
|
-
|
|
2134
|
-
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding
|
|
2135
|
-
if (codings.length !== 0 && request.method !== 'HEAD' && request.method !== 'CONNECT' && !nullBodyStatus.includes(status) && !willFollow) {
|
|
2136
|
-
for (let i = codings.length - 1; i >= 0; --i) {
|
|
2137
|
-
const coding = codings[i]
|
|
2138
|
-
// https://www.rfc-editor.org/rfc/rfc9112.html#section-7.2
|
|
2139
|
-
if (coding === 'x-gzip' || coding === 'gzip') {
|
|
2140
|
-
decoders.push(zlib.createGunzip({
|
|
2141
|
-
// Be less strict when decoding compressed responses, since sometimes
|
|
2142
|
-
// servers send slightly invalid responses that are still accepted
|
|
2143
|
-
// by common browsers.
|
|
2144
|
-
// Always using Z_SYNC_FLUSH is what cURL does.
|
|
2145
|
-
flush: zlib.constants.Z_SYNC_FLUSH,
|
|
2146
|
-
finishFlush: zlib.constants.Z_SYNC_FLUSH
|
|
2147
|
-
}))
|
|
2148
|
-
} else if (coding === 'deflate') {
|
|
2149
|
-
decoders.push(createInflate({
|
|
2150
|
-
flush: zlib.constants.Z_SYNC_FLUSH,
|
|
2151
|
-
finishFlush: zlib.constants.Z_SYNC_FLUSH
|
|
2152
|
-
}))
|
|
2153
|
-
} else if (coding === 'br') {
|
|
2154
|
-
decoders.push(zlib.createBrotliDecompress({
|
|
2155
|
-
flush: zlib.constants.BROTLI_OPERATION_FLUSH,
|
|
2156
|
-
finishFlush: zlib.constants.BROTLI_OPERATION_FLUSH
|
|
2157
|
-
}))
|
|
2158
|
-
} else {
|
|
2159
|
-
decoders.length = 0
|
|
2160
|
-
break
|
|
2161
|
-
}
|
|
2162
|
-
}
|
|
2163
|
-
}
|
|
2164
|
-
|
|
2165
|
-
const onError = this.onError.bind(this)
|
|
2166
|
-
|
|
2167
|
-
resolve({
|
|
2168
|
-
status,
|
|
2169
|
-
statusText,
|
|
2170
|
-
headersList,
|
|
2171
|
-
body: decoders.length
|
|
2172
|
-
? pipeline(this.body, ...decoders, (err) => {
|
|
2173
|
-
if (err) {
|
|
2174
|
-
this.onError(err)
|
|
2175
|
-
}
|
|
2176
|
-
}).on('error', onError)
|
|
2177
|
-
: this.body.on('error', onError)
|
|
2178
|
-
})
|
|
2179
|
-
|
|
2180
|
-
return true
|
|
2181
|
-
},
|
|
2182
|
-
|
|
2183
|
-
onData (chunk) {
|
|
2184
|
-
if (fetchParams.controller.dump) {
|
|
2185
|
-
return
|
|
2186
|
-
}
|
|
2187
|
-
|
|
2188
|
-
// 1. If one or more bytes have been transmitted from response’s
|
|
2189
|
-
// message body, then:
|
|
2190
|
-
|
|
2191
|
-
// 1. Let bytes be the transmitted bytes.
|
|
2192
|
-
const bytes = chunk
|
|
2193
|
-
|
|
2194
|
-
// 2. Let codings be the result of extracting header list values
|
|
2195
|
-
// given `Content-Encoding` and response’s header list.
|
|
2196
|
-
// See pullAlgorithm.
|
|
2197
|
-
|
|
2198
|
-
// 3. Increase timingInfo’s encoded body size by bytes’s length.
|
|
2199
|
-
timingInfo.encodedBodySize += bytes.byteLength
|
|
2200
|
-
|
|
2201
|
-
// 4. See pullAlgorithm...
|
|
2202
|
-
|
|
2203
|
-
return this.body.push(bytes)
|
|
2204
|
-
},
|
|
2205
|
-
|
|
2206
|
-
onComplete () {
|
|
2207
|
-
if (this.abort) {
|
|
2208
|
-
fetchParams.controller.off('terminated', this.abort)
|
|
2209
|
-
}
|
|
2210
|
-
|
|
2211
|
-
fetchParams.controller.ended = true
|
|
2212
|
-
|
|
2213
|
-
this.body.push(null)
|
|
2214
|
-
},
|
|
2215
|
-
|
|
2216
|
-
onError (error) {
|
|
2217
|
-
if (this.abort) {
|
|
2218
|
-
fetchParams.controller.off('terminated', this.abort)
|
|
2219
|
-
}
|
|
2220
|
-
|
|
2221
|
-
this.body?.destroy(error)
|
|
2222
|
-
|
|
2223
|
-
fetchParams.controller.terminate(error)
|
|
2224
|
-
|
|
2225
|
-
reject(error)
|
|
2226
|
-
},
|
|
2227
|
-
|
|
2228
|
-
onUpgrade (status, rawHeaders, socket) {
|
|
2229
|
-
if (status !== 101) {
|
|
2230
|
-
return
|
|
2231
|
-
}
|
|
2232
|
-
|
|
2233
|
-
const headersList = new HeadersList()
|
|
2234
|
-
|
|
2235
|
-
for (let i = 0; i < rawHeaders.length; i += 2) {
|
|
2236
|
-
headersList.append(bufferToLowerCasedHeaderName(rawHeaders[i]), rawHeaders[i + 1].toString('latin1'), true)
|
|
2237
|
-
}
|
|
2238
|
-
|
|
2239
|
-
resolve({
|
|
2240
|
-
status,
|
|
2241
|
-
statusText: STATUS_CODES[status],
|
|
2242
|
-
headersList,
|
|
2243
|
-
socket
|
|
2244
|
-
})
|
|
2245
|
-
|
|
2246
|
-
return true
|
|
2247
|
-
}
|
|
2248
|
-
}
|
|
2249
|
-
))
|
|
2250
|
-
}
|
|
2251
|
-
}
|
|
2252
|
-
|
|
2253
|
-
module.exports = {
|
|
2254
|
-
fetch,
|
|
2255
|
-
Fetch,
|
|
2256
|
-
fetching,
|
|
2257
|
-
finalizeAndReportTiming
|
|
2258
|
-
}
|