diff options
Diffstat (limited to 'ext/fetch/23_request.js')
-rw-r--r-- | ext/fetch/23_request.js | 1161 |
1 files changed, 584 insertions, 577 deletions
diff --git a/ext/fetch/23_request.js b/ext/fetch/23_request.js index e266a7e44..1e8d5c1ec 100644 --- a/ext/fetch/23_request.js +++ b/ext/fetch/23_request.js @@ -8,657 +8,664 @@ /// <reference path="../web/06_streams_types.d.ts" /> /// <reference path="./lib.deno_fetch.d.ts" /> /// <reference lib="esnext" /> -"use strict"; - -((window) => { - const webidl = window.__bootstrap.webidl; - const consoleInternal = window.__bootstrap.console; - const { HTTP_TOKEN_CODE_POINT_RE, byteUpperCase } = window.__bootstrap.infra; - const { URL } = window.__bootstrap.url; - const { guardFromHeaders } = window.__bootstrap.headers; - const { mixinBody, extractBody, InnerBody } = window.__bootstrap.fetchBody; - const { getLocationHref } = window.__bootstrap.location; - const { extractMimeType } = window.__bootstrap.mimesniff; - const { blobFromObjectUrl } = window.__bootstrap.file; - const { - headersFromHeaderList, - headerListFromHeaders, - fillHeaders, - getDecodeSplitHeader, - } = window.__bootstrap.headers; - const { HttpClientPrototype } = window.__bootstrap.fetch; - const abortSignal = window.__bootstrap.abortSignal; - const { - ArrayPrototypeMap, - ArrayPrototypeSlice, - ArrayPrototypeSplice, - ObjectKeys, - ObjectPrototypeIsPrototypeOf, - RegExpPrototypeTest, - Symbol, - SymbolFor, - TypeError, - } = window.__bootstrap.primordials; - - const _request = Symbol("request"); - const _headers = Symbol("headers"); - const _getHeaders = Symbol("get headers"); - const _headersCache = Symbol("headers cache"); - const _signal = Symbol("signal"); - const _mimeType = Symbol("mime type"); - const _body = Symbol("body"); - const _flash = Symbol("flash"); - const _url = Symbol("url"); - const _method = Symbol("method"); - /** - * @param {(() => string)[]} urlList - * @param {string[]} urlListProcessed - */ - function processUrlList(urlList, urlListProcessed) { - for (let i = 0; i < urlList.length; i++) { - if (urlListProcessed[i] === undefined) { - urlListProcessed[i] = urlList[i](); - } +import * as webidl from "internal:ext/webidl/00_webidl.js"; +import { createFilteredInspectProxy } from "internal:ext/console/02_console.js"; +import { + byteUpperCase, + HTTP_TOKEN_CODE_POINT_RE, +} from "internal:ext/web/00_infra.js"; +import { URL } from "internal:ext/url/00_url.js"; +import { + extractBody, + InnerBody, + mixinBody, +} from "internal:ext/fetch/22_body.js"; +import { getLocationHref } from "internal:ext/web/12_location.js"; +import { extractMimeType } from "internal:ext/web/01_mimesniff.js"; +import { blobFromObjectUrl } from "internal:ext/web/09_file.js"; +import { + fillHeaders, + getDecodeSplitHeader, + guardFromHeaders, + headerListFromHeaders, + headersFromHeaderList, +} from "internal:ext/fetch/20_headers.js"; +import { HttpClientPrototype } from "internal:ext/fetch/22_http_client.js"; +import * as abortSignal from "internal:ext/web/03_abort_signal.js"; +const primordials = globalThis.__bootstrap.primordials; +const { + ArrayPrototypeMap, + ArrayPrototypeSlice, + ArrayPrototypeSplice, + ObjectKeys, + ObjectPrototypeIsPrototypeOf, + RegExpPrototypeTest, + Symbol, + SymbolFor, + TypeError, +} = primordials; + +const _request = Symbol("request"); +const _headers = Symbol("headers"); +const _getHeaders = Symbol("get headers"); +const _headersCache = Symbol("headers cache"); +const _signal = Symbol("signal"); +const _mimeType = Symbol("mime type"); +const _body = Symbol("body"); +const _flash = Symbol("flash"); +const _url = Symbol("url"); +const _method = Symbol("method"); + +/** + * @param {(() => string)[]} urlList + * @param {string[]} urlListProcessed + */ +function processUrlList(urlList, urlListProcessed) { + for (let i = 0; i < urlList.length; i++) { + if (urlListProcessed[i] === undefined) { + urlListProcessed[i] = urlList[i](); } - return urlListProcessed; } + return urlListProcessed; +} + +/** + * @typedef InnerRequest + * @property {() => string} method + * @property {() => string} url + * @property {() => string} currentUrl + * @property {() => [string, string][]} headerList + * @property {null | typeof __window.bootstrap.fetchBody.InnerBody} body + * @property {"follow" | "error" | "manual"} redirectMode + * @property {number} redirectCount + * @property {(() => string)[]} urlList + * @property {string[]} urlListProcessed + * @property {number | null} clientRid NOTE: non standard extension for `Deno.HttpClient`. + * @property {Blob | null} blobUrlEntry + */ + +/** + * @param {() => string} method + * @param {string | () => string} url + * @param {() => [string, string][]} headerList + * @param {typeof __window.bootstrap.fetchBody.InnerBody} body + * @param {boolean} maybeBlob + * @returns {InnerRequest} + */ +function newInnerRequest(method, url, headerList, body, maybeBlob) { + let blobUrlEntry = null; + if (maybeBlob && typeof url === "string" && url.startsWith("blob:")) { + blobUrlEntry = blobFromObjectUrl(url); + } + return { + methodInner: null, + get method() { + if (this.methodInner === null) { + try { + this.methodInner = method(); + } catch { + throw new TypeError("cannot read method: request closed"); + } + } + return this.methodInner; + }, + set method(value) { + this.methodInner = value; + }, + headerListInner: null, + get headerList() { + if (this.headerListInner === null) { + try { + this.headerListInner = headerList(); + } catch { + throw new TypeError("cannot read headers: request closed"); + } + } + return this.headerListInner; + }, + set headerList(value) { + this.headerListInner = value; + }, + body, + redirectMode: "follow", + redirectCount: 0, + urlList: [typeof url === "string" ? () => url : url], + urlListProcessed: [], + clientRid: null, + blobUrlEntry, + url() { + if (this.urlListProcessed[0] === undefined) { + try { + this.urlListProcessed[0] = this.urlList[0](); + } catch { + throw new TypeError("cannot read url: request closed"); + } + } + return this.urlListProcessed[0]; + }, + currentUrl() { + const currentIndex = this.urlList.length - 1; + if (this.urlListProcessed[currentIndex] === undefined) { + try { + this.urlListProcessed[currentIndex] = this.urlList[currentIndex](); + } catch { + throw new TypeError("cannot read url: request closed"); + } + } + return this.urlListProcessed[currentIndex]; + }, + }; +} + +/** + * https://fetch.spec.whatwg.org/#concept-request-clone + * @param {InnerRequest} request + * @param {boolean} skipBody + * @param {boolean} flash + * @returns {InnerRequest} + */ +function cloneInnerRequest(request, skipBody = false, flash = false) { + const headerList = ArrayPrototypeMap( + request.headerList, + (x) => [x[0], x[1]], + ); - /** - * @typedef InnerRequest - * @property {() => string} method - * @property {() => string} url - * @property {() => string} currentUrl - * @property {() => [string, string][]} headerList - * @property {null | typeof __window.bootstrap.fetchBody.InnerBody} body - * @property {"follow" | "error" | "manual"} redirectMode - * @property {number} redirectCount - * @property {(() => string)[]} urlList - * @property {string[]} urlListProcessed - * @property {number | null} clientRid NOTE: non standard extension for `Deno.HttpClient`. - * @property {Blob | null} blobUrlEntry - */ + let body = null; + if (request.body !== null && !skipBody) { + body = request.body.clone(); + } - /** - * @param {() => string} method - * @param {string | () => string} url - * @param {() => [string, string][]} headerList - * @param {typeof __window.bootstrap.fetchBody.InnerBody} body - * @param {boolean} maybeBlob - * @returns {InnerRequest} - */ - function newInnerRequest(method, url, headerList, body, maybeBlob) { - let blobUrlEntry = null; - if (maybeBlob && typeof url === "string" && url.startsWith("blob:")) { - blobUrlEntry = blobFromObjectUrl(url); - } + if (flash) { return { - methodInner: null, - get method() { - if (this.methodInner === null) { - try { - this.methodInner = method(); - } catch { - throw new TypeError("cannot read method: request closed"); - } - } - return this.methodInner; - }, - set method(value) { - this.methodInner = value; - }, - headerListInner: null, - get headerList() { - if (this.headerListInner === null) { - try { - this.headerListInner = headerList(); - } catch { - throw new TypeError("cannot read headers: request closed"); - } - } - return this.headerListInner; - }, - set headerList(value) { - this.headerListInner = value; - }, body, + methodCb: request.methodCb, + urlCb: request.urlCb, + headerList: request.headerList, + streamRid: request.streamRid, + serverId: request.serverId, redirectMode: "follow", redirectCount: 0, - urlList: [typeof url === "string" ? () => url : url], - urlListProcessed: [], - clientRid: null, - blobUrlEntry, - url() { - if (this.urlListProcessed[0] === undefined) { - try { - this.urlListProcessed[0] = this.urlList[0](); - } catch { - throw new TypeError("cannot read url: request closed"); - } - } - return this.urlListProcessed[0]; - }, - currentUrl() { - const currentIndex = this.urlList.length - 1; - if (this.urlListProcessed[currentIndex] === undefined) { - try { - this.urlListProcessed[currentIndex] = this.urlList[currentIndex](); - } catch { - throw new TypeError("cannot read url: request closed"); - } - } - return this.urlListProcessed[currentIndex]; - }, }; } - /** - * https://fetch.spec.whatwg.org/#concept-request-clone - * @param {InnerRequest} request - * @param {boolean} skipBody - * @param {boolean} flash - * @returns {InnerRequest} - */ - function cloneInnerRequest(request, skipBody = false, flash = false) { - const headerList = ArrayPrototypeMap( - request.headerList, - (x) => [x[0], x[1]], - ); - - let body = null; - if (request.body !== null && !skipBody) { - body = request.body.clone(); - } - - if (flash) { - return { - body, - methodCb: request.methodCb, - urlCb: request.urlCb, - headerList: request.headerList, - streamRid: request.streamRid, - serverId: request.serverId, - redirectMode: "follow", - redirectCount: 0, - }; - } - - return { - method: request.method, - headerList, - body, - redirectMode: request.redirectMode, - redirectCount: request.redirectCount, - urlList: request.urlList, - urlListProcessed: request.urlListProcessed, - clientRid: request.clientRid, - blobUrlEntry: request.blobUrlEntry, - url() { - if (this.urlListProcessed[0] === undefined) { - try { - this.urlListProcessed[0] = this.urlList[0](); - } catch { - throw new TypeError("cannot read url: request closed"); - } + return { + method: request.method, + headerList, + body, + redirectMode: request.redirectMode, + redirectCount: request.redirectCount, + urlList: request.urlList, + urlListProcessed: request.urlListProcessed, + clientRid: request.clientRid, + blobUrlEntry: request.blobUrlEntry, + url() { + if (this.urlListProcessed[0] === undefined) { + try { + this.urlListProcessed[0] = this.urlList[0](); + } catch { + throw new TypeError("cannot read url: request closed"); } - return this.urlListProcessed[0]; - }, - currentUrl() { - const currentIndex = this.urlList.length - 1; - if (this.urlListProcessed[currentIndex] === undefined) { - try { - this.urlListProcessed[currentIndex] = this.urlList[currentIndex](); - } catch { - throw new TypeError("cannot read url: request closed"); - } + } + return this.urlListProcessed[0]; + }, + currentUrl() { + const currentIndex = this.urlList.length - 1; + if (this.urlListProcessed[currentIndex] === undefined) { + try { + this.urlListProcessed[currentIndex] = this.urlList[currentIndex](); + } catch { + throw new TypeError("cannot read url: request closed"); } - return this.urlListProcessed[currentIndex]; - }, - }; + } + return this.urlListProcessed[currentIndex]; + }, + }; +} + +/** + * @param {string} m + * @returns {boolean} + */ +function isKnownMethod(m) { + return ( + m === "DELETE" || + m === "GET" || + m === "HEAD" || + m === "OPTIONS" || + m === "POST" || + m === "PUT" + ); +} +/** + * @param {string} m + * @returns {string} + */ +function validateAndNormalizeMethod(m) { + // Fast path for well-known methods + if (isKnownMethod(m)) { + return m; } - /** - * @param {string} m - * @returns {boolean} - */ - function isKnownMethod(m) { - return ( - m === "DELETE" || - m === "GET" || - m === "HEAD" || - m === "OPTIONS" || - m === "POST" || - m === "PUT" - ); + // Regular path + if (!RegExpPrototypeTest(HTTP_TOKEN_CODE_POINT_RE, m)) { + throw new TypeError("Method is not valid."); } - /** - * @param {string} m - * @returns {string} - */ - function validateAndNormalizeMethod(m) { - // Fast path for well-known methods - if (isKnownMethod(m)) { - return m; + const upperCase = byteUpperCase(m); + if ( + upperCase === "CONNECT" || upperCase === "TRACE" || upperCase === "TRACK" + ) { + throw new TypeError("Method is forbidden."); + } + return upperCase; +} + +class Request { + /** @type {InnerRequest} */ + [_request]; + /** @type {Headers} */ + [_headersCache]; + [_getHeaders]; + + /** @type {Headers} */ + get [_headers]() { + if (this[_headersCache] === undefined) { + this[_headersCache] = this[_getHeaders](); } + return this[_headersCache]; + } - // Regular path - if (!RegExpPrototypeTest(HTTP_TOKEN_CODE_POINT_RE, m)) { - throw new TypeError("Method is not valid."); - } - const upperCase = byteUpperCase(m); - if ( - upperCase === "CONNECT" || upperCase === "TRACE" || upperCase === "TRACK" - ) { - throw new TypeError("Method is forbidden."); + set [_headers](value) { + this[_headersCache] = value; + } + + /** @type {AbortSignal} */ + [_signal]; + get [_mimeType]() { + const values = getDecodeSplitHeader( + headerListFromHeaders(this[_headers]), + "Content-Type", + ); + return extractMimeType(values); + } + get [_body]() { + if (this[_flash]) { + return this[_flash].body; + } else { + return this[_request].body; } - return upperCase; } - class Request { + /** + * https://fetch.spec.whatwg.org/#dom-request + * @param {RequestInfo} input + * @param {RequestInit} init + */ + constructor(input, init = {}) { + const prefix = "Failed to construct 'Request'"; + webidl.requiredArguments(arguments.length, 1, { prefix }); + input = webidl.converters["RequestInfo_DOMString"](input, { + prefix, + context: "Argument 1", + }); + init = webidl.converters["RequestInit"](init, { + prefix, + context: "Argument 2", + }); + + this[webidl.brand] = webidl.brand; + /** @type {InnerRequest} */ - [_request]; - /** @type {Headers} */ - [_headersCache]; - [_getHeaders]; - - /** @type {Headers} */ - get [_headers]() { - if (this[_headersCache] === undefined) { - this[_headersCache] = this[_getHeaders](); + let request; + const baseURL = getLocationHref(); + + // 4. + let signal = null; + + // 5. + if (typeof input === "string") { + const parsedURL = new URL(input, baseURL); + request = newInnerRequest( + () => "GET", + parsedURL.href, + () => [], + null, + true, + ); + } else { // 6. + if (!ObjectPrototypeIsPrototypeOf(RequestPrototype, input)) { + throw new TypeError("Unreachable"); } - return this[_headersCache]; + const originalReq = input[_request]; + // fold in of step 12 from below + request = cloneInnerRequest(originalReq, true); + request.redirectCount = 0; // reset to 0 - cloneInnerRequest copies the value + signal = input[_signal]; } - set [_headers](value) { - this[_headersCache] = value; + // 12. is folded into the else statement of step 6 above. + + // 22. + if (init.redirect !== undefined) { + request.redirectMode = init.redirect; } - /** @type {AbortSignal} */ - [_signal]; - get [_mimeType]() { - const values = getDecodeSplitHeader( - headerListFromHeaders(this[_headers]), - "Content-Type", - ); - return extractMimeType(values); + // 25. + if (init.method !== undefined) { + let method = init.method; + method = validateAndNormalizeMethod(method); + request.method = method; } - get [_body]() { - if (this[_flash]) { - return this[_flash].body; - } else { - return this[_request].body; - } + + // 26. + if (init.signal !== undefined) { + signal = init.signal; } - /** - * https://fetch.spec.whatwg.org/#dom-request - * @param {RequestInfo} input - * @param {RequestInit} init - */ - constructor(input, init = {}) { - const prefix = "Failed to construct 'Request'"; - webidl.requiredArguments(arguments.length, 1, { prefix }); - input = webidl.converters["RequestInfo_DOMString"](input, { - prefix, - context: "Argument 1", - }); - init = webidl.converters["RequestInit"](init, { - prefix, - context: "Argument 2", - }); - - this[webidl.brand] = webidl.brand; - - /** @type {InnerRequest} */ - let request; - const baseURL = getLocationHref(); - - // 4. - let signal = null; - - // 5. - if (typeof input === "string") { - const parsedURL = new URL(input, baseURL); - request = newInnerRequest( - () => "GET", - parsedURL.href, - () => [], - null, - true, + // NOTE: non standard extension. This handles Deno.HttpClient parameter + if (init.client !== undefined) { + if ( + init.client !== null && + !ObjectPrototypeIsPrototypeOf(HttpClientPrototype, init.client) + ) { + throw webidl.makeException( + TypeError, + "`client` must be a Deno.HttpClient", + { prefix, context: "Argument 2" }, ); - } else { // 6. - if (!ObjectPrototypeIsPrototypeOf(RequestPrototype, input)) { - throw new TypeError("Unreachable"); - } - const originalReq = input[_request]; - // fold in of step 12 from below - request = cloneInnerRequest(originalReq, true); - request.redirectCount = 0; // reset to 0 - cloneInnerRequest copies the value - signal = input[_signal]; } + request.clientRid = init.client?.rid ?? null; + } - // 12. is folded into the else statement of step 6 above. + // 27. + this[_request] = request; - // 22. - if (init.redirect !== undefined) { - request.redirectMode = init.redirect; - } + // 28. + this[_signal] = abortSignal.newSignal(); - // 25. - if (init.method !== undefined) { - let method = init.method; - method = validateAndNormalizeMethod(method); - request.method = method; - } + // 29. + if (signal !== null) { + abortSignal.follow(this[_signal], signal); + } - // 26. - if (init.signal !== undefined) { - signal = init.signal; - } + // 30. + this[_headers] = headersFromHeaderList(request.headerList, "request"); - // NOTE: non standard extension. This handles Deno.HttpClient parameter - if (init.client !== undefined) { - if ( - init.client !== null && - !ObjectPrototypeIsPrototypeOf(HttpClientPrototype, init.client) - ) { - throw webidl.makeException( - TypeError, - "`client` must be a Deno.HttpClient", - { prefix, context: "Argument 2" }, - ); - } - request.clientRid = init.client?.rid ?? null; + // 32. + if (ObjectKeys(init).length > 0) { + let headers = ArrayPrototypeSlice( + headerListFromHeaders(this[_headers]), + 0, + headerListFromHeaders(this[_headers]).length, + ); + if (init.headers !== undefined) { + headers = init.headers; } + ArrayPrototypeSplice( + headerListFromHeaders(this[_headers]), + 0, + headerListFromHeaders(this[_headers]).length, + ); + fillHeaders(this[_headers], headers); + } - // 27. - this[_request] = request; - - // 28. - this[_signal] = abortSignal.newSignal(); - - // 29. - if (signal !== null) { - abortSignal.follow(this[_signal], signal); - } + // 33. + let inputBody = null; + if (ObjectPrototypeIsPrototypeOf(RequestPrototype, input)) { + inputBody = input[_body]; + } - // 30. - this[_headers] = headersFromHeaderList(request.headerList, "request"); + // 34. + if ( + (request.method === "GET" || request.method === "HEAD") && + ((init.body !== undefined && init.body !== null) || + inputBody !== null) + ) { + throw new TypeError("Request with GET/HEAD method cannot have body."); + } - // 32. - if (ObjectKeys(init).length > 0) { - let headers = ArrayPrototypeSlice( - headerListFromHeaders(this[_headers]), - 0, - headerListFromHeaders(this[_headers]).length, - ); - if (init.headers !== undefined) { - headers = init.headers; - } - ArrayPrototypeSplice( - headerListFromHeaders(this[_headers]), - 0, - headerListFromHeaders(this[_headers]).length, - ); - fillHeaders(this[_headers], headers); - } + // 35. + let initBody = null; - // 33. - let inputBody = null; - if (ObjectPrototypeIsPrototypeOf(RequestPrototype, input)) { - inputBody = input[_body]; + // 36. + if (init.body !== undefined && init.body !== null) { + const res = extractBody(init.body); + initBody = res.body; + if (res.contentType !== null && !this[_headers].has("content-type")) { + this[_headers].append("Content-Type", res.contentType); } + } - // 34. - if ( - (request.method === "GET" || request.method === "HEAD") && - ((init.body !== undefined && init.body !== null) || - inputBody !== null) - ) { - throw new TypeError("Request with GET/HEAD method cannot have body."); - } + // 37. + const inputOrInitBody = initBody ?? inputBody; - // 35. - let initBody = null; + // 39. + let finalBody = inputOrInitBody; - // 36. - if (init.body !== undefined && init.body !== null) { - const res = extractBody(init.body); - initBody = res.body; - if (res.contentType !== null && !this[_headers].has("content-type")) { - this[_headers].append("Content-Type", res.contentType); - } + // 40. + if (initBody === null && inputBody !== null) { + if (input[_body] && input[_body].unusable()) { + throw new TypeError("Input request's body is unusable."); } + finalBody = inputBody.createProxy(); + } - // 37. - const inputOrInitBody = initBody ?? inputBody; - - // 39. - let finalBody = inputOrInitBody; - - // 40. - if (initBody === null && inputBody !== null) { - if (input[_body] && input[_body].unusable()) { - throw new TypeError("Input request's body is unusable."); - } - finalBody = inputBody.createProxy(); - } + // 41. + request.body = finalBody; + } - // 41. - request.body = finalBody; + get method() { + webidl.assertBranded(this, RequestPrototype); + if (this[_method]) { + return this[_method]; } - - get method() { - webidl.assertBranded(this, RequestPrototype); - if (this[_method]) { - return this[_method]; - } - if (this[_flash]) { - this[_method] = this[_flash].methodCb(); - return this[_method]; - } else { - this[_method] = this[_request].method; - return this[_method]; - } + if (this[_flash]) { + this[_method] = this[_flash].methodCb(); + return this[_method]; + } else { + this[_method] = this[_request].method; + return this[_method]; } + } - get url() { - webidl.assertBranded(this, RequestPrototype); - if (this[_url]) { - return this[_url]; - } - - if (this[_flash]) { - this[_url] = this[_flash].urlCb(); - return this[_url]; - } else { - this[_url] = this[_request].url(); - return this[_url]; - } + get url() { + webidl.assertBranded(this, RequestPrototype); + if (this[_url]) { + return this[_url]; } - get headers() { - webidl.assertBranded(this, RequestPrototype); - return this[_headers]; + if (this[_flash]) { + this[_url] = this[_flash].urlCb(); + return this[_url]; + } else { + this[_url] = this[_request].url(); + return this[_url]; } + } - get redirect() { - webidl.assertBranded(this, RequestPrototype); - if (this[_flash]) { - return this[_flash].redirectMode; - } - return this[_request].redirectMode; - } + get headers() { + webidl.assertBranded(this, RequestPrototype); + return this[_headers]; + } - get signal() { - webidl.assertBranded(this, RequestPrototype); - return this[_signal]; + get redirect() { + webidl.assertBranded(this, RequestPrototype); + if (this[_flash]) { + return this[_flash].redirectMode; } + return this[_request].redirectMode; + } - clone() { - webidl.assertBranded(this, RequestPrototype); - if (this[_body] && this[_body].unusable()) { - throw new TypeError("Body is unusable."); - } - let newReq; - if (this[_flash]) { - newReq = cloneInnerRequest(this[_flash], false, true); - } else { - newReq = cloneInnerRequest(this[_request]); - } - const newSignal = abortSignal.newSignal(); + get signal() { + webidl.assertBranded(this, RequestPrototype); + return this[_signal]; + } - if (this[_signal]) { - abortSignal.follow(newSignal, this[_signal]); - } + clone() { + webidl.assertBranded(this, RequestPrototype); + if (this[_body] && this[_body].unusable()) { + throw new TypeError("Body is unusable."); + } + let newReq; + if (this[_flash]) { + newReq = cloneInnerRequest(this[_flash], false, true); + } else { + newReq = cloneInnerRequest(this[_request]); + } + const newSignal = abortSignal.newSignal(); - if (this[_flash]) { - return fromInnerRequest( - newReq, - newSignal, - guardFromHeaders(this[_headers]), - true, - ); - } + if (this[_signal]) { + abortSignal.follow(newSignal, this[_signal]); + } + if (this[_flash]) { return fromInnerRequest( newReq, newSignal, guardFromHeaders(this[_headers]), - false, + true, ); } - [SymbolFor("Deno.customInspect")](inspect) { - return inspect(consoleInternal.createFilteredInspectProxy({ - object: this, - evaluate: ObjectPrototypeIsPrototypeOf(RequestPrototype, this), - keys: [ - "bodyUsed", - "headers", - "method", - "redirect", - "url", - ], - })); - } + return fromInnerRequest( + newReq, + newSignal, + guardFromHeaders(this[_headers]), + false, + ); } - webidl.configurePrototype(Request); - const RequestPrototype = Request.prototype; - mixinBody(RequestPrototype, _body, _mimeType); - - webidl.converters["Request"] = webidl.createInterfaceConverter( - "Request", - RequestPrototype, - ); - webidl.converters["RequestInfo_DOMString"] = (V, opts) => { - // Union for (Request or USVString) - if (typeof V == "object") { - if (ObjectPrototypeIsPrototypeOf(RequestPrototype, V)) { - return webidl.converters["Request"](V, opts); - } - } - // Passed to new URL(...) which implicitly converts DOMString -> USVString - return webidl.converters["DOMString"](V, opts); - }; - webidl.converters["RequestRedirect"] = webidl.createEnumConverter( - "RequestRedirect", - [ - "follow", - "error", - "manual", - ], - ); - webidl.converters["RequestInit"] = webidl.createDictionaryConverter( - "RequestInit", - [ - { key: "method", converter: webidl.converters["ByteString"] }, - { key: "headers", converter: webidl.converters["HeadersInit"] }, - { - key: "body", - converter: webidl.createNullableConverter( - webidl.converters["BodyInit_DOMString"], - ), - }, - { key: "redirect", converter: webidl.converters["RequestRedirect"] }, - { - key: "signal", - converter: webidl.createNullableConverter( - webidl.converters["AbortSignal"], - ), - }, - { key: "client", converter: webidl.converters.any }, - ], - ); - - /** - * @param {Request} request - * @returns {InnerRequest} - */ - function toInnerRequest(request) { - return request[_request]; + [SymbolFor("Deno.customInspect")](inspect) { + return inspect(createFilteredInspectProxy({ + object: this, + evaluate: ObjectPrototypeIsPrototypeOf(RequestPrototype, this), + keys: [ + "bodyUsed", + "headers", + "method", + "redirect", + "url", + ], + })); } - - /** - * @param {InnerRequest} inner - * @param {AbortSignal} signal - * @param {"request" | "immutable" | "request-no-cors" | "response" | "none"} guard - * @param {boolean} flash - * @returns {Request} - */ - function fromInnerRequest(inner, signal, guard, flash) { - const request = webidl.createBranded(Request); - if (flash) { - request[_flash] = inner; - } else { - request[_request] = inner; +} + +webidl.configurePrototype(Request); +const RequestPrototype = Request.prototype; +mixinBody(RequestPrototype, _body, _mimeType); + +webidl.converters["Request"] = webidl.createInterfaceConverter( + "Request", + RequestPrototype, +); +webidl.converters["RequestInfo_DOMString"] = (V, opts) => { + // Union for (Request or USVString) + if (typeof V == "object") { + if (ObjectPrototypeIsPrototypeOf(RequestPrototype, V)) { + return webidl.converters["Request"](V, opts); } - request[_signal] = signal; - request[_getHeaders] = flash - ? () => headersFromHeaderList(inner.headerList(), guard) - : () => headersFromHeaderList(inner.headerList, guard); - return request; } - - /** - * @param {number} serverId - * @param {number} streamRid - * @param {ReadableStream} body - * @param {() => string} methodCb - * @param {() => string} urlCb - * @param {() => [string, string][]} headersCb - * @returns {Request} - */ - function fromFlashRequest( - serverId, - streamRid, - body, + // Passed to new URL(...) which implicitly converts DOMString -> USVString + return webidl.converters["DOMString"](V, opts); +}; +webidl.converters["RequestRedirect"] = webidl.createEnumConverter( + "RequestRedirect", + [ + "follow", + "error", + "manual", + ], +); +webidl.converters["RequestInit"] = webidl.createDictionaryConverter( + "RequestInit", + [ + { key: "method", converter: webidl.converters["ByteString"] }, + { key: "headers", converter: webidl.converters["HeadersInit"] }, + { + key: "body", + converter: webidl.createNullableConverter( + webidl.converters["BodyInit_DOMString"], + ), + }, + { key: "redirect", converter: webidl.converters["RequestRedirect"] }, + { + key: "signal", + converter: webidl.createNullableConverter( + webidl.converters["AbortSignal"], + ), + }, + { key: "client", converter: webidl.converters.any }, + ], +); + +/** + * @param {Request} request + * @returns {InnerRequest} + */ +function toInnerRequest(request) { + return request[_request]; +} + +/** + * @param {InnerRequest} inner + * @param {AbortSignal} signal + * @param {"request" | "immutable" | "request-no-cors" | "response" | "none"} guard + * @param {boolean} flash + * @returns {Request} + */ +function fromInnerRequest(inner, signal, guard, flash) { + const request = webidl.createBranded(Request); + if (flash) { + request[_flash] = inner; + } else { + request[_request] = inner; + } + request[_signal] = signal; + request[_getHeaders] = flash + ? () => headersFromHeaderList(inner.headerList(), guard) + : () => headersFromHeaderList(inner.headerList, guard); + return request; +} + +/** + * @param {number} serverId + * @param {number} streamRid + * @param {ReadableStream} body + * @param {() => string} methodCb + * @param {() => string} urlCb + * @param {() => [string, string][]} headersCb + * @returns {Request} + */ +function fromFlashRequest( + serverId, + streamRid, + body, + methodCb, + urlCb, + headersCb, +) { + const request = webidl.createBranded(Request); + request[_flash] = { + body: body !== null ? new InnerBody(body) : null, methodCb, urlCb, - headersCb, - ) { - const request = webidl.createBranded(Request); - request[_flash] = { - body: body !== null ? new InnerBody(body) : null, - methodCb, - urlCb, - headerList: headersCb, - streamRid, - serverId, - redirectMode: "follow", - redirectCount: 0, - }; - request[_getHeaders] = () => headersFromHeaderList(headersCb(), "request"); - return request; - } - - window.__bootstrap.fetch ??= {}; - window.__bootstrap.fetch.Request = Request; - window.__bootstrap.fetch.toInnerRequest = toInnerRequest; - window.__bootstrap.fetch.fromFlashRequest = fromFlashRequest; - window.__bootstrap.fetch.fromInnerRequest = fromInnerRequest; - window.__bootstrap.fetch.newInnerRequest = newInnerRequest; - window.__bootstrap.fetch.processUrlList = processUrlList; - window.__bootstrap.fetch._flash = _flash; -})(globalThis); + headerList: headersCb, + streamRid, + serverId, + redirectMode: "follow", + redirectCount: 0, + }; + request[_getHeaders] = () => headersFromHeaderList(headersCb(), "request"); + return request; +} + +export { + _flash, + fromFlashRequest, + fromInnerRequest, + newInnerRequest, + processUrlList, + Request, + RequestPrototype, + toInnerRequest, +}; |