Spaces:
Runtime error
Runtime error
/* globals AbortController */ | |
const { extractBody, mixinBody, cloneBody } = require('./body') | |
const { Headers, fill: fillHeaders, HeadersList } = require('./headers') | |
const { FinalizationRegistry } = require('../compat/dispatcher-weakref')() | |
const util = require('../core/util') | |
const { | |
isValidHTTPToken, | |
sameOrigin, | |
normalizeMethod, | |
makePolicyContainer | |
} = require('./util') | |
const { | |
forbiddenMethodsSet, | |
corsSafeListedMethodsSet, | |
referrerPolicy, | |
requestRedirect, | |
requestMode, | |
requestCredentials, | |
requestCache, | |
requestDuplex | |
} = require('./constants') | |
const { kEnumerableProperty } = util | |
const { kHeaders, kSignal, kState, kGuard, kRealm } = require('./symbols') | |
const { webidl } = require('./webidl') | |
const { getGlobalOrigin } = require('./global') | |
const { URLSerializer } = require('./dataURL') | |
const { kHeadersList } = require('../core/symbols') | |
const assert = require('assert') | |
const { getMaxListeners, setMaxListeners, getEventListeners, defaultMaxListeners } = require('events') | |
let TransformStream = globalThis.TransformStream | |
const kInit = Symbol('init') | |
const kAbortController = Symbol('abortController') | |
const requestFinalizer = new FinalizationRegistry(({ signal, abort }) => { | |
signal.removeEventListener('abort', abort) | |
}) | |
// https://fetch.spec.whatwg.org/#request-class | |
class Request { | |
// https://fetch.spec.whatwg.org/#dom-request | |
constructor (input, init = {}) { | |
if (input === kInit) { | |
return | |
} | |
webidl.argumentLengthCheck(arguments, 1, { header: 'Request constructor' }) | |
input = webidl.converters.RequestInfo(input) | |
init = webidl.converters.RequestInit(init) | |
// https://html.spec.whatwg.org/multipage/webappapis.html#environment-settings-object | |
this[kRealm] = { | |
settingsObject: { | |
baseUrl: getGlobalOrigin(), | |
get origin () { | |
return this.baseUrl?.origin | |
}, | |
policyContainer: makePolicyContainer() | |
} | |
} | |
// 1. Let request be null. | |
let request = null | |
// 2. Let fallbackMode be null. | |
let fallbackMode = null | |
// 3. Let baseURL be this’s relevant settings object’s API base URL. | |
const baseUrl = this[kRealm].settingsObject.baseUrl | |
// 4. Let signal be null. | |
let signal = null | |
// 5. If input is a string, then: | |
if (typeof input === 'string') { | |
// 1. Let parsedURL be the result of parsing input with baseURL. | |
// 2. If parsedURL is failure, then throw a TypeError. | |
let parsedURL | |
try { | |
parsedURL = new URL(input, baseUrl) | |
} catch (err) { | |
throw new TypeError('Failed to parse URL from ' + input, { cause: err }) | |
} | |
// 3. If parsedURL includes credentials, then throw a TypeError. | |
if (parsedURL.username || parsedURL.password) { | |
throw new TypeError( | |
'Request cannot be constructed from a URL that includes credentials: ' + | |
input | |
) | |
} | |
// 4. Set request to a new request whose URL is parsedURL. | |
request = makeRequest({ urlList: [parsedURL] }) | |
// 5. Set fallbackMode to "cors". | |
fallbackMode = 'cors' | |
} else { | |
// 6. Otherwise: | |
// 7. Assert: input is a Request object. | |
assert(input instanceof Request) | |
// 8. Set request to input’s request. | |
request = input[kState] | |
// 9. Set signal to input’s signal. | |
signal = input[kSignal] | |
} | |
// 7. Let origin be this’s relevant settings object’s origin. | |
const origin = this[kRealm].settingsObject.origin | |
// 8. Let window be "client". | |
let window = 'client' | |
// 9. If request’s window is an environment settings object and its origin | |
// is same origin with origin, then set window to request’s window. | |
if ( | |
request.window?.constructor?.name === 'EnvironmentSettingsObject' && | |
sameOrigin(request.window, origin) | |
) { | |
window = request.window | |
} | |
// 10. If init["window"] exists and is non-null, then throw a TypeError. | |
if (init.window != null) { | |
throw new TypeError(`'window' option '${window}' must be null`) | |
} | |
// 11. If init["window"] exists, then set window to "no-window". | |
if ('window' in init) { | |
window = 'no-window' | |
} | |
// 12. Set request to a new request with the following properties: | |
request = makeRequest({ | |
// URL request’s URL. | |
// undici implementation note: this is set as the first item in request's urlList in makeRequest | |
// method request’s method. | |
method: request.method, | |
// header list A copy of request’s header list. | |
// undici implementation note: headersList is cloned in makeRequest | |
headersList: request.headersList, | |
// unsafe-request flag Set. | |
unsafeRequest: request.unsafeRequest, | |
// client This’s relevant settings object. | |
client: this[kRealm].settingsObject, | |
// window window. | |
window, | |
// priority request’s priority. | |
priority: request.priority, | |
// origin request’s origin. The propagation of the origin is only significant for navigation requests | |
// being handled by a service worker. In this scenario a request can have an origin that is different | |
// from the current client. | |
origin: request.origin, | |
// referrer request’s referrer. | |
referrer: request.referrer, | |
// referrer policy request’s referrer policy. | |
referrerPolicy: request.referrerPolicy, | |
// mode request’s mode. | |
mode: request.mode, | |
// credentials mode request’s credentials mode. | |
credentials: request.credentials, | |
// cache mode request’s cache mode. | |
cache: request.cache, | |
// redirect mode request’s redirect mode. | |
redirect: request.redirect, | |
// integrity metadata request’s integrity metadata. | |
integrity: request.integrity, | |
// keepalive request’s keepalive. | |
keepalive: request.keepalive, | |
// reload-navigation flag request’s reload-navigation flag. | |
reloadNavigation: request.reloadNavigation, | |
// history-navigation flag request’s history-navigation flag. | |
historyNavigation: request.historyNavigation, | |
// URL list A clone of request’s URL list. | |
urlList: [...request.urlList] | |
}) | |
// 13. If init is not empty, then: | |
if (Object.keys(init).length > 0) { | |
// 1. If request’s mode is "navigate", then set it to "same-origin". | |
if (request.mode === 'navigate') { | |
request.mode = 'same-origin' | |
} | |
// 2. Unset request’s reload-navigation flag. | |
request.reloadNavigation = false | |
// 3. Unset request’s history-navigation flag. | |
request.historyNavigation = false | |
// 4. Set request’s origin to "client". | |
request.origin = 'client' | |
// 5. Set request’s referrer to "client" | |
request.referrer = 'client' | |
// 6. Set request’s referrer policy to the empty string. | |
request.referrerPolicy = '' | |
// 7. Set request’s URL to request’s current URL. | |
request.url = request.urlList[request.urlList.length - 1] | |
// 8. Set request’s URL list to « request’s URL ». | |
request.urlList = [request.url] | |
} | |
// 14. If init["referrer"] exists, then: | |
if (init.referrer !== undefined) { | |
// 1. Let referrer be init["referrer"]. | |
const referrer = init.referrer | |
// 2. If referrer is the empty string, then set request’s referrer to "no-referrer". | |
if (referrer === '') { | |
request.referrer = 'no-referrer' | |
} else { | |
// 1. Let parsedReferrer be the result of parsing referrer with | |
// baseURL. | |
// 2. If parsedReferrer is failure, then throw a TypeError. | |
let parsedReferrer | |
try { | |
parsedReferrer = new URL(referrer, baseUrl) | |
} catch (err) { | |
throw new TypeError(`Referrer "${referrer}" is not a valid URL.`, { cause: err }) | |
} | |
// 3. If one of the following is true | |
// - parsedReferrer’s scheme is "about" and path is the string "client" | |
// - parsedReferrer’s origin is not same origin with origin | |
// then set request’s referrer to "client". | |
if ( | |
(parsedReferrer.protocol === 'about:' && parsedReferrer.hostname === 'client') || | |
(origin && !sameOrigin(parsedReferrer, this[kRealm].settingsObject.baseUrl)) | |
) { | |
request.referrer = 'client' | |
} else { | |
// 4. Otherwise, set request’s referrer to parsedReferrer. | |
request.referrer = parsedReferrer | |
} | |
} | |
} | |
// 15. If init["referrerPolicy"] exists, then set request’s referrer policy | |
// to it. | |
if (init.referrerPolicy !== undefined) { | |
request.referrerPolicy = init.referrerPolicy | |
} | |
// 16. Let mode be init["mode"] if it exists, and fallbackMode otherwise. | |
let mode | |
if (init.mode !== undefined) { | |
mode = init.mode | |
} else { | |
mode = fallbackMode | |
} | |
// 17. If mode is "navigate", then throw a TypeError. | |
if (mode === 'navigate') { | |
throw webidl.errors.exception({ | |
header: 'Request constructor', | |
message: 'invalid request mode navigate.' | |
}) | |
} | |
// 18. If mode is non-null, set request’s mode to mode. | |
if (mode != null) { | |
request.mode = mode | |
} | |
// 19. If init["credentials"] exists, then set request’s credentials mode | |
// to it. | |
if (init.credentials !== undefined) { | |
request.credentials = init.credentials | |
} | |
// 18. If init["cache"] exists, then set request’s cache mode to it. | |
if (init.cache !== undefined) { | |
request.cache = init.cache | |
} | |
// 21. If request’s cache mode is "only-if-cached" and request’s mode is | |
// not "same-origin", then throw a TypeError. | |
if (request.cache === 'only-if-cached' && request.mode !== 'same-origin') { | |
throw new TypeError( | |
"'only-if-cached' can be set only with 'same-origin' mode" | |
) | |
} | |
// 22. If init["redirect"] exists, then set request’s redirect mode to it. | |
if (init.redirect !== undefined) { | |
request.redirect = init.redirect | |
} | |
// 23. If init["integrity"] exists, then set request’s integrity metadata to it. | |
if (init.integrity !== undefined && init.integrity != null) { | |
request.integrity = String(init.integrity) | |
} | |
// 24. If init["keepalive"] exists, then set request’s keepalive to it. | |
if (init.keepalive !== undefined) { | |
request.keepalive = Boolean(init.keepalive) | |
} | |
// 25. If init["method"] exists, then: | |
if (init.method !== undefined) { | |
// 1. Let method be init["method"]. | |
let method = init.method | |
// 2. If method is not a method or method is a forbidden method, then | |
// throw a TypeError. | |
if (!isValidHTTPToken(init.method)) { | |
throw TypeError(`'${init.method}' is not a valid HTTP method.`) | |
} | |
if (forbiddenMethodsSet.has(method.toUpperCase())) { | |
throw TypeError(`'${init.method}' HTTP method is unsupported.`) | |
} | |
// 3. Normalize method. | |
method = normalizeMethod(init.method) | |
// 4. Set request’s method to method. | |
request.method = method | |
} | |
// 26. If init["signal"] exists, then set signal to it. | |
if (init.signal !== undefined) { | |
signal = init.signal | |
} | |
// 27. Set this’s request to request. | |
this[kState] = request | |
// 28. Set this’s signal to a new AbortSignal object with this’s relevant | |
// Realm. | |
// TODO: could this be simplified with AbortSignal.any | |
// (https://dom.spec.whatwg.org/#dom-abortsignal-any) | |
const ac = new AbortController() | |
this[kSignal] = ac.signal | |
this[kSignal][kRealm] = this[kRealm] | |
// 29. If signal is not null, then make this’s signal follow signal. | |
if (signal != null) { | |
if ( | |
!signal || | |
typeof signal.aborted !== 'boolean' || | |
typeof signal.addEventListener !== 'function' | |
) { | |
throw new TypeError( | |
"Failed to construct 'Request': member signal is not of type AbortSignal." | |
) | |
} | |
if (signal.aborted) { | |
ac.abort(signal.reason) | |
} else { | |
// Keep a strong ref to ac while request object | |
// is alive. This is needed to prevent AbortController | |
// from being prematurely garbage collected. | |
// See, https://github.com/nodejs/undici/issues/1926. | |
this[kAbortController] = ac | |
const acRef = new WeakRef(ac) | |
const abort = function () { | |
const ac = acRef.deref() | |
if (ac !== undefined) { | |
ac.abort(this.reason) | |
} | |
} | |
// Third-party AbortControllers may not work with these. | |
// See, https://github.com/nodejs/undici/pull/1910#issuecomment-1464495619. | |
try { | |
// If the max amount of listeners is equal to the default, increase it | |
// This is only available in node >= v19.9.0 | |
if (typeof getMaxListeners === 'function' && getMaxListeners(signal) === defaultMaxListeners) { | |
setMaxListeners(100, signal) | |
} else if (getEventListeners(signal, 'abort').length >= defaultMaxListeners) { | |
setMaxListeners(100, signal) | |
} | |
} catch {} | |
util.addAbortListener(signal, abort) | |
requestFinalizer.register(ac, { signal, abort }) | |
} | |
} | |
// 30. Set this’s headers to a new Headers object with this’s relevant | |
// Realm, whose header list is request’s header list and guard is | |
// "request". | |
this[kHeaders] = new Headers() | |
this[kHeaders][kHeadersList] = request.headersList | |
this[kHeaders][kGuard] = 'request' | |
this[kHeaders][kRealm] = this[kRealm] | |
// 31. If this’s request’s mode is "no-cors", then: | |
if (mode === 'no-cors') { | |
// 1. If this’s request’s method is not a CORS-safelisted method, | |
// then throw a TypeError. | |
if (!corsSafeListedMethodsSet.has(request.method)) { | |
throw new TypeError( | |
`'${request.method} is unsupported in no-cors mode.` | |
) | |
} | |
// 2. Set this’s headers’s guard to "request-no-cors". | |
this[kHeaders][kGuard] = 'request-no-cors' | |
} | |
// 32. If init is not empty, then: | |
if (Object.keys(init).length !== 0) { | |
// 1. Let headers be a copy of this’s headers and its associated header | |
// list. | |
let headers = new Headers(this[kHeaders]) | |
// 2. If init["headers"] exists, then set headers to init["headers"]. | |
if (init.headers !== undefined) { | |
headers = init.headers | |
} | |
// 3. Empty this’s headers’s header list. | |
this[kHeaders][kHeadersList].clear() | |
// 4. If headers is a Headers object, then for each header in its header | |
// list, append header’s name/header’s value to this’s headers. | |
if (headers.constructor.name === 'Headers') { | |
for (const [key, val] of headers) { | |
this[kHeaders].append(key, val) | |
} | |
} else { | |
// 5. Otherwise, fill this’s headers with headers. | |
fillHeaders(this[kHeaders], headers) | |
} | |
} | |
// 33. Let inputBody be input’s request’s body if input is a Request | |
// object; otherwise null. | |
const inputBody = input instanceof Request ? input[kState].body : null | |
// 34. If either init["body"] exists and is non-null or inputBody is | |
// non-null, and request’s method is `GET` or `HEAD`, then throw a | |
// TypeError. | |
if ( | |
(init.body != null || inputBody != null) && | |
(request.method === 'GET' || request.method === 'HEAD') | |
) { | |
throw new TypeError('Request with GET/HEAD method cannot have body.') | |
} | |
// 35. Let initBody be null. | |
let initBody = null | |
// 36. If init["body"] exists and is non-null, then: | |
if (init.body != null) { | |
// 1. Let Content-Type be null. | |
// 2. Set initBody and Content-Type to the result of extracting | |
// init["body"], with keepalive set to request’s keepalive. | |
const [extractedBody, contentType] = extractBody( | |
init.body, | |
request.keepalive | |
) | |
initBody = extractedBody | |
// 3, If Content-Type is non-null and this’s headers’s header list does | |
// not contain `Content-Type`, then append `Content-Type`/Content-Type to | |
// this’s headers. | |
if (contentType && !this[kHeaders][kHeadersList].contains('content-type')) { | |
this[kHeaders].append('content-type', contentType) | |
} | |
} | |
// 37. Let inputOrInitBody be initBody if it is non-null; otherwise | |
// inputBody. | |
const inputOrInitBody = initBody ?? inputBody | |
// 38. If inputOrInitBody is non-null and inputOrInitBody’s source is | |
// null, then: | |
if (inputOrInitBody != null && inputOrInitBody.source == null) { | |
// 1. If initBody is non-null and init["duplex"] does not exist, | |
// then throw a TypeError. | |
if (initBody != null && init.duplex == null) { | |
throw new TypeError('RequestInit: duplex option is required when sending a body.') | |
} | |
// 2. If this’s request’s mode is neither "same-origin" nor "cors", | |
// then throw a TypeError. | |
if (request.mode !== 'same-origin' && request.mode !== 'cors') { | |
throw new TypeError( | |
'If request is made from ReadableStream, mode should be "same-origin" or "cors"' | |
) | |
} | |
// 3. Set this’s request’s use-CORS-preflight flag. | |
request.useCORSPreflightFlag = true | |
} | |
// 39. Let finalBody be inputOrInitBody. | |
let finalBody = inputOrInitBody | |
// 40. If initBody is null and inputBody is non-null, then: | |
if (initBody == null && inputBody != null) { | |
// 1. If input is unusable, then throw a TypeError. | |
if (util.isDisturbed(inputBody.stream) || inputBody.stream.locked) { | |
throw new TypeError( | |
'Cannot construct a Request with a Request object that has already been used.' | |
) | |
} | |
// 2. Set finalBody to the result of creating a proxy for inputBody. | |
if (!TransformStream) { | |
TransformStream = require('stream/web').TransformStream | |
} | |
// https://streams.spec.whatwg.org/#readablestream-create-a-proxy | |
const identityTransform = new TransformStream() | |
inputBody.stream.pipeThrough(identityTransform) | |
finalBody = { | |
source: inputBody.source, | |
length: inputBody.length, | |
stream: identityTransform.readable | |
} | |
} | |
// 41. Set this’s request’s body to finalBody. | |
this[kState].body = finalBody | |
} | |
// Returns request’s HTTP method, which is "GET" by default. | |
get method () { | |
webidl.brandCheck(this, Request) | |
// The method getter steps are to return this’s request’s method. | |
return this[kState].method | |
} | |
// Returns the URL of request as a string. | |
get url () { | |
webidl.brandCheck(this, Request) | |
// The url getter steps are to return this’s request’s URL, serialized. | |
return URLSerializer(this[kState].url) | |
} | |
// Returns a Headers object consisting of the headers associated with request. | |
// Note that headers added in the network layer by the user agent will not | |
// be accounted for in this object, e.g., the "Host" header. | |
get headers () { | |
webidl.brandCheck(this, Request) | |
// The headers getter steps are to return this’s headers. | |
return this[kHeaders] | |
} | |
// Returns the kind of resource requested by request, e.g., "document" | |
// or "script". | |
get destination () { | |
webidl.brandCheck(this, Request) | |
// The destination getter are to return this’s request’s destination. | |
return this[kState].destination | |
} | |
// Returns the referrer of request. Its value can be a same-origin URL if | |
// explicitly set in init, the empty string to indicate no referrer, and | |
// "about:client" when defaulting to the global’s default. This is used | |
// during fetching to determine the value of the `Referer` header of the | |
// request being made. | |
get referrer () { | |
webidl.brandCheck(this, Request) | |
// 1. If this’s request’s referrer is "no-referrer", then return the | |
// empty string. | |
if (this[kState].referrer === 'no-referrer') { | |
return '' | |
} | |
// 2. If this’s request’s referrer is "client", then return | |
// "about:client". | |
if (this[kState].referrer === 'client') { | |
return 'about:client' | |
} | |
// Return this’s request’s referrer, serialized. | |
return this[kState].referrer.toString() | |
} | |
// Returns the referrer policy associated with request. | |
// This is used during fetching to compute the value of the request’s | |
// referrer. | |
get referrerPolicy () { | |
webidl.brandCheck(this, Request) | |
// The referrerPolicy getter steps are to return this’s request’s referrer policy. | |
return this[kState].referrerPolicy | |
} | |
// Returns the mode associated with request, which is a string indicating | |
// whether the request will use CORS, or will be restricted to same-origin | |
// URLs. | |
get mode () { | |
webidl.brandCheck(this, Request) | |
// The mode getter steps are to return this’s request’s mode. | |
return this[kState].mode | |
} | |
// Returns the credentials mode associated with request, | |
// which is a string indicating whether credentials will be sent with the | |
// request always, never, or only when sent to a same-origin URL. | |
get credentials () { | |
// The credentials getter steps are to return this’s request’s credentials mode. | |
return this[kState].credentials | |
} | |
// Returns the cache mode associated with request, | |
// which is a string indicating how the request will | |
// interact with the browser’s cache when fetching. | |
get cache () { | |
webidl.brandCheck(this, Request) | |
// The cache getter steps are to return this’s request’s cache mode. | |
return this[kState].cache | |
} | |
// Returns the redirect mode associated with request, | |
// which is a string indicating how redirects for the | |
// request will be handled during fetching. A request | |
// will follow redirects by default. | |
get redirect () { | |
webidl.brandCheck(this, Request) | |
// The redirect getter steps are to return this’s request’s redirect mode. | |
return this[kState].redirect | |
} | |
// Returns request’s subresource integrity metadata, which is a | |
// cryptographic hash of the resource being fetched. Its value | |
// consists of multiple hashes separated by whitespace. [SRI] | |
get integrity () { | |
webidl.brandCheck(this, Request) | |
// The integrity getter steps are to return this’s request’s integrity | |
// metadata. | |
return this[kState].integrity | |
} | |
// Returns a boolean indicating whether or not request can outlive the | |
// global in which it was created. | |
get keepalive () { | |
webidl.brandCheck(this, Request) | |
// The keepalive getter steps are to return this’s request’s keepalive. | |
return this[kState].keepalive | |
} | |
// Returns a boolean indicating whether or not request is for a reload | |
// navigation. | |
get isReloadNavigation () { | |
webidl.brandCheck(this, Request) | |
// The isReloadNavigation getter steps are to return true if this’s | |
// request’s reload-navigation flag is set; otherwise false. | |
return this[kState].reloadNavigation | |
} | |
// Returns a boolean indicating whether or not request is for a history | |
// navigation (a.k.a. back-foward navigation). | |
get isHistoryNavigation () { | |
webidl.brandCheck(this, Request) | |
// The isHistoryNavigation getter steps are to return true if this’s request’s | |
// history-navigation flag is set; otherwise false. | |
return this[kState].historyNavigation | |
} | |
// Returns the signal associated with request, which is an AbortSignal | |
// object indicating whether or not request has been aborted, and its | |
// abort event handler. | |
get signal () { | |
webidl.brandCheck(this, Request) | |
// The signal getter steps are to return this’s signal. | |
return this[kSignal] | |
} | |
get body () { | |
webidl.brandCheck(this, Request) | |
return this[kState].body ? this[kState].body.stream : null | |
} | |
get bodyUsed () { | |
webidl.brandCheck(this, Request) | |
return !!this[kState].body && util.isDisturbed(this[kState].body.stream) | |
} | |
get duplex () { | |
webidl.brandCheck(this, Request) | |
return 'half' | |
} | |
// Returns a clone of request. | |
clone () { | |
webidl.brandCheck(this, Request) | |
// 1. If this is unusable, then throw a TypeError. | |
if (this.bodyUsed || this.body?.locked) { | |
throw new TypeError('unusable') | |
} | |
// 2. Let clonedRequest be the result of cloning this’s request. | |
const clonedRequest = cloneRequest(this[kState]) | |
// 3. Let clonedRequestObject be the result of creating a Request object, | |
// given clonedRequest, this’s headers’s guard, and this’s relevant Realm. | |
const clonedRequestObject = new Request(kInit) | |
clonedRequestObject[kState] = clonedRequest | |
clonedRequestObject[kRealm] = this[kRealm] | |
clonedRequestObject[kHeaders] = new Headers() | |
clonedRequestObject[kHeaders][kHeadersList] = clonedRequest.headersList | |
clonedRequestObject[kHeaders][kGuard] = this[kHeaders][kGuard] | |
clonedRequestObject[kHeaders][kRealm] = this[kHeaders][kRealm] | |
// 4. Make clonedRequestObject’s signal follow this’s signal. | |
const ac = new AbortController() | |
if (this.signal.aborted) { | |
ac.abort(this.signal.reason) | |
} else { | |
util.addAbortListener( | |
this.signal, | |
() => { | |
ac.abort(this.signal.reason) | |
} | |
) | |
} | |
clonedRequestObject[kSignal] = ac.signal | |
// 4. Return clonedRequestObject. | |
return clonedRequestObject | |
} | |
} | |
mixinBody(Request) | |
function makeRequest (init) { | |
// https://fetch.spec.whatwg.org/#requests | |
const request = { | |
method: 'GET', | |
localURLsOnly: false, | |
unsafeRequest: false, | |
body: null, | |
client: null, | |
reservedClient: null, | |
replacesClientId: '', | |
window: 'client', | |
keepalive: false, | |
serviceWorkers: 'all', | |
initiator: '', | |
destination: '', | |
priority: null, | |
origin: 'client', | |
policyContainer: 'client', | |
referrer: 'client', | |
referrerPolicy: '', | |
mode: 'no-cors', | |
useCORSPreflightFlag: false, | |
credentials: 'same-origin', | |
useCredentials: false, | |
cache: 'default', | |
redirect: 'follow', | |
integrity: '', | |
cryptoGraphicsNonceMetadata: '', | |
parserMetadata: '', | |
reloadNavigation: false, | |
historyNavigation: false, | |
userActivation: false, | |
taintedOrigin: false, | |
redirectCount: 0, | |
responseTainting: 'basic', | |
preventNoCacheCacheControlHeaderModification: false, | |
done: false, | |
timingAllowFailed: false, | |
...init, | |
headersList: init.headersList | |
? new HeadersList(init.headersList) | |
: new HeadersList() | |
} | |
request.url = request.urlList[0] | |
return request | |
} | |
// https://fetch.spec.whatwg.org/#concept-request-clone | |
function cloneRequest (request) { | |
// To clone a request request, run these steps: | |
// 1. Let newRequest be a copy of request, except for its body. | |
const newRequest = makeRequest({ ...request, body: null }) | |
// 2. If request’s body is non-null, set newRequest’s body to the | |
// result of cloning request’s body. | |
if (request.body != null) { | |
newRequest.body = cloneBody(request.body) | |
} | |
// 3. Return newRequest. | |
return newRequest | |
} | |
Object.defineProperties(Request.prototype, { | |
method: kEnumerableProperty, | |
url: kEnumerableProperty, | |
headers: kEnumerableProperty, | |
redirect: kEnumerableProperty, | |
clone: kEnumerableProperty, | |
signal: kEnumerableProperty, | |
duplex: kEnumerableProperty, | |
destination: kEnumerableProperty, | |
body: kEnumerableProperty, | |
bodyUsed: kEnumerableProperty, | |
isHistoryNavigation: kEnumerableProperty, | |
isReloadNavigation: kEnumerableProperty, | |
keepalive: kEnumerableProperty, | |
integrity: kEnumerableProperty, | |
cache: kEnumerableProperty, | |
credentials: kEnumerableProperty, | |
attribute: kEnumerableProperty, | |
referrerPolicy: kEnumerableProperty, | |
referrer: kEnumerableProperty, | |
mode: kEnumerableProperty, | |
[Symbol.toStringTag]: { | |
value: 'Request', | |
configurable: true | |
} | |
}) | |
webidl.converters.Request = webidl.interfaceConverter( | |
Request | |
) | |
// https://fetch.spec.whatwg.org/#requestinfo | |
webidl.converters.RequestInfo = function (V) { | |
if (typeof V === 'string') { | |
return webidl.converters.USVString(V) | |
} | |
if (V instanceof Request) { | |
return webidl.converters.Request(V) | |
} | |
return webidl.converters.USVString(V) | |
} | |
webidl.converters.AbortSignal = webidl.interfaceConverter( | |
AbortSignal | |
) | |
// https://fetch.spec.whatwg.org/#requestinit | |
webidl.converters.RequestInit = webidl.dictionaryConverter([ | |
{ | |
key: 'method', | |
converter: webidl.converters.ByteString | |
}, | |
{ | |
key: 'headers', | |
converter: webidl.converters.HeadersInit | |
}, | |
{ | |
key: 'body', | |
converter: webidl.nullableConverter( | |
webidl.converters.BodyInit | |
) | |
}, | |
{ | |
key: 'referrer', | |
converter: webidl.converters.USVString | |
}, | |
{ | |
key: 'referrerPolicy', | |
converter: webidl.converters.DOMString, | |
// https://w3c.github.io/webappsec-referrer-policy/#referrer-policy | |
allowedValues: referrerPolicy | |
}, | |
{ | |
key: 'mode', | |
converter: webidl.converters.DOMString, | |
// https://fetch.spec.whatwg.org/#concept-request-mode | |
allowedValues: requestMode | |
}, | |
{ | |
key: 'credentials', | |
converter: webidl.converters.DOMString, | |
// https://fetch.spec.whatwg.org/#requestcredentials | |
allowedValues: requestCredentials | |
}, | |
{ | |
key: 'cache', | |
converter: webidl.converters.DOMString, | |
// https://fetch.spec.whatwg.org/#requestcache | |
allowedValues: requestCache | |
}, | |
{ | |
key: 'redirect', | |
converter: webidl.converters.DOMString, | |
// https://fetch.spec.whatwg.org/#requestredirect | |
allowedValues: requestRedirect | |
}, | |
{ | |
key: 'integrity', | |
converter: webidl.converters.DOMString | |
}, | |
{ | |
key: 'keepalive', | |
converter: webidl.converters.boolean | |
}, | |
{ | |
key: 'signal', | |
converter: webidl.nullableConverter( | |
(signal) => webidl.converters.AbortSignal( | |
signal, | |
{ strict: false } | |
) | |
) | |
}, | |
{ | |
key: 'window', | |
converter: webidl.converters.any | |
}, | |
{ | |
key: 'duplex', | |
converter: webidl.converters.DOMString, | |
allowedValues: requestDuplex | |
} | |
]) | |
module.exports = { Request, makeRequest } | |