summaryrefslogtreecommitdiff
path: root/ext/fetch
diff options
context:
space:
mode:
Diffstat (limited to 'ext/fetch')
-rw-r--r--ext/fetch/01_fetch_util.js22
-rw-r--r--ext/fetch/20_headers.js808
-rw-r--r--ext/fetch/21_formdata.js888
-rw-r--r--ext/fetch/22_body.js820
-rw-r--r--ext/fetch/22_http_client.js54
-rw-r--r--ext/fetch/23_request.js1161
-rw-r--r--ext/fetch/23_response.js918
-rw-r--r--ext/fetch/26_fetch.js1033
-rw-r--r--ext/fetch/internal.d.ts188
-rw-r--r--ext/fetch/lib.rs3
10 files changed, 2932 insertions, 2963 deletions
diff --git a/ext/fetch/01_fetch_util.js b/ext/fetch/01_fetch_util.js
deleted file mode 100644
index 3ed554ecb..000000000
--- a/ext/fetch/01_fetch_util.js
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
-"use strict";
-
-((window) => {
- const { TypeError } = window.__bootstrap.primordials;
- function requiredArguments(
- name,
- length,
- required,
- ) {
- if (length < required) {
- const errMsg = `${name} requires at least ${required} argument${
- required === 1 ? "" : "s"
- }, but only ${length} present`;
- throw new TypeError(errMsg);
- }
- }
-
- window.__bootstrap.fetchUtil = {
- requiredArguments,
- };
-})(this);
diff --git a/ext/fetch/20_headers.js b/ext/fetch/20_headers.js
index 54e635522..9790bb69f 100644
--- a/ext/fetch/20_headers.js
+++ b/ext/fetch/20_headers.js
@@ -8,472 +8,470 @@
/// <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 {
- HTTP_TAB_OR_SPACE_PREFIX_RE,
- HTTP_TAB_OR_SPACE_SUFFIX_RE,
- HTTP_TOKEN_CODE_POINT_RE,
- byteLowerCase,
- collectSequenceOfCodepoints,
- collectHttpQuotedString,
- httpTrim,
- } = window.__bootstrap.infra;
- const {
- ArrayIsArray,
- ArrayPrototypeMap,
- ArrayPrototypePush,
- ArrayPrototypeSort,
- ArrayPrototypeJoin,
- ArrayPrototypeSplice,
- ArrayPrototypeFilter,
- ObjectPrototypeHasOwnProperty,
- ObjectEntries,
- RegExpPrototypeTest,
- SafeArrayIterator,
- Symbol,
- SymbolFor,
- SymbolIterator,
- StringPrototypeReplaceAll,
- TypeError,
- } = window.__bootstrap.primordials;
-
- const _headerList = Symbol("header list");
- const _iterableHeaders = Symbol("iterable headers");
- const _guard = Symbol("guard");
-
- /**
- * @typedef Header
- * @type {[string, string]}
- */
- /**
- * @typedef HeaderList
- * @type {Header[]}
- */
+import * as webidl from "internal:ext/webidl/00_webidl.js";
+import {
+ byteLowerCase,
+ collectHttpQuotedString,
+ collectSequenceOfCodepoints,
+ HTTP_TAB_OR_SPACE_PREFIX_RE,
+ HTTP_TAB_OR_SPACE_SUFFIX_RE,
+ HTTP_TOKEN_CODE_POINT_RE,
+ httpTrim,
+} from "internal:ext/web/00_infra.js";
+const primordials = globalThis.__bootstrap.primordials;
+const {
+ ArrayIsArray,
+ ArrayPrototypeMap,
+ ArrayPrototypePush,
+ ArrayPrototypeSort,
+ ArrayPrototypeJoin,
+ ArrayPrototypeSplice,
+ ArrayPrototypeFilter,
+ ObjectPrototypeHasOwnProperty,
+ ObjectEntries,
+ RegExpPrototypeTest,
+ SafeArrayIterator,
+ Symbol,
+ SymbolFor,
+ SymbolIterator,
+ StringPrototypeReplaceAll,
+ TypeError,
+} = primordials;
+
+const _headerList = Symbol("header list");
+const _iterableHeaders = Symbol("iterable headers");
+const _guard = Symbol("guard");
+
+/**
+ * @typedef Header
+ * @type {[string, string]}
+ */
+
+/**
+ * @typedef HeaderList
+ * @type {Header[]}
+ */
+
+/**
+ * @param {string} potentialValue
+ * @returns {string}
+ */
+function normalizeHeaderValue(potentialValue) {
+ return httpTrim(potentialValue);
+}
+
+/**
+ * @param {Headers} headers
+ * @param {HeadersInit} object
+ */
+function fillHeaders(headers, object) {
+ if (ArrayIsArray(object)) {
+ for (let i = 0; i < object.length; ++i) {
+ const header = object[i];
+ if (header.length !== 2) {
+ throw new TypeError(
+ `Invalid header. Length must be 2, but is ${header.length}`,
+ );
+ }
+ appendHeader(headers, header[0], header[1]);
+ }
+ } else {
+ for (const key in object) {
+ if (!ObjectPrototypeHasOwnProperty(object, key)) {
+ continue;
+ }
+ appendHeader(headers, key, object[key]);
+ }
+ }
+}
+
+// Regex matching illegal chars in a header value
+// deno-lint-ignore no-control-regex
+const ILLEGAL_VALUE_CHARS = /[\x00\x0A\x0D]/;
+
+/**
+ * https://fetch.spec.whatwg.org/#concept-headers-append
+ * @param {Headers} headers
+ * @param {string} name
+ * @param {string} value
+ */
+function appendHeader(headers, name, value) {
+ // 1.
+ value = normalizeHeaderValue(value);
+
+ // 2.
+ if (!RegExpPrototypeTest(HTTP_TOKEN_CODE_POINT_RE, name)) {
+ throw new TypeError("Header name is not valid.");
+ }
+ if (RegExpPrototypeTest(ILLEGAL_VALUE_CHARS, value)) {
+ throw new TypeError("Header value is not valid.");
+ }
- /**
- * @param {string} potentialValue
- * @returns {string}
- */
- function normalizeHeaderValue(potentialValue) {
- return httpTrim(potentialValue);
+ // 3.
+ if (headers[_guard] == "immutable") {
+ throw new TypeError("Headers are immutable.");
}
- /**
- * @param {Headers} headers
- * @param {HeadersInit} object
- */
- function fillHeaders(headers, object) {
- if (ArrayIsArray(object)) {
- for (let i = 0; i < object.length; ++i) {
- const header = object[i];
- if (header.length !== 2) {
- throw new TypeError(
- `Invalid header. Length must be 2, but is ${header.length}`,
- );
+ // 7.
+ const list = headers[_headerList];
+ const lowercaseName = byteLowerCase(name);
+ for (let i = 0; i < list.length; i++) {
+ if (byteLowerCase(list[i][0]) === lowercaseName) {
+ name = list[i][0];
+ break;
+ }
+ }
+ ArrayPrototypePush(list, [name, value]);
+}
+
+/**
+ * https://fetch.spec.whatwg.org/#concept-header-list-get
+ * @param {HeaderList} list
+ * @param {string} name
+ */
+function getHeader(list, name) {
+ const lowercaseName = byteLowerCase(name);
+ const entries = ArrayPrototypeMap(
+ ArrayPrototypeFilter(
+ list,
+ (entry) => byteLowerCase(entry[0]) === lowercaseName,
+ ),
+ (entry) => entry[1],
+ );
+ if (entries.length === 0) {
+ return null;
+ } else {
+ return ArrayPrototypeJoin(entries, "\x2C\x20");
+ }
+}
+
+/**
+ * https://fetch.spec.whatwg.org/#concept-header-list-get-decode-split
+ * @param {HeaderList} list
+ * @param {string} name
+ * @returns {string[] | null}
+ */
+function getDecodeSplitHeader(list, name) {
+ const initialValue = getHeader(list, name);
+ if (initialValue === null) return null;
+ const input = initialValue;
+ let position = 0;
+ const values = [];
+ let value = "";
+ while (position < initialValue.length) {
+ // 7.1. collect up to " or ,
+ const res = collectSequenceOfCodepoints(
+ initialValue,
+ position,
+ (c) => c !== "\u0022" && c !== "\u002C",
+ );
+ value += res.result;
+ position = res.position;
+
+ if (position < initialValue.length) {
+ if (input[position] === "\u0022") {
+ const res = collectHttpQuotedString(input, position, false);
+ value += res.result;
+ position = res.position;
+ if (position < initialValue.length) {
+ continue;
}
- appendHeader(headers, header[0], header[1]);
+ } else {
+ if (input[position] !== "\u002C") throw new TypeError("Unreachable");
+ position += 1;
}
- } else {
- for (const key in object) {
- if (!ObjectPrototypeHasOwnProperty(object, key)) {
- continue;
+ }
+
+ value = StringPrototypeReplaceAll(value, HTTP_TAB_OR_SPACE_PREFIX_RE, "");
+ value = StringPrototypeReplaceAll(value, HTTP_TAB_OR_SPACE_SUFFIX_RE, "");
+
+ ArrayPrototypePush(values, value);
+ value = "";
+ }
+ return values;
+}
+
+class Headers {
+ /** @type {HeaderList} */
+ [_headerList] = [];
+ /** @type {"immutable" | "request" | "request-no-cors" | "response" | "none"} */
+ [_guard];
+
+ get [_iterableHeaders]() {
+ const list = this[_headerList];
+
+ // The order of steps are not similar to the ones suggested by the
+ // spec but produce the same result.
+ const headers = {};
+ const cookies = [];
+ for (let i = 0; i < list.length; ++i) {
+ const entry = list[i];
+ const name = byteLowerCase(entry[0]);
+ const value = entry[1];
+ if (value === null) throw new TypeError("Unreachable");
+ // The following if statement is not spec compliant.
+ // `set-cookie` is the only header that can not be concatenated,
+ // so must be given to the user as multiple headers.
+ // The else block of the if statement is spec compliant again.
+ if (name === "set-cookie") {
+ ArrayPrototypePush(cookies, [name, value]);
+ } else {
+ // The following code has the same behaviour as getHeader()
+ // at the end of loop. But it avoids looping through the entire
+ // list to combine multiple values with same header name. It
+ // instead gradually combines them as they are found.
+ let header = headers[name];
+ if (header && header.length > 0) {
+ header += "\x2C\x20" + value;
+ } else {
+ header = value;
}
- appendHeader(headers, key, object[key]);
+ headers[name] = header;
}
}
+
+ return ArrayPrototypeSort(
+ [
+ ...new SafeArrayIterator(ObjectEntries(headers)),
+ ...new SafeArrayIterator(cookies),
+ ],
+ (a, b) => {
+ const akey = a[0];
+ const bkey = b[0];
+ if (akey > bkey) return 1;
+ if (akey < bkey) return -1;
+ return 0;
+ },
+ );
}
- // Regex matching illegal chars in a header value
- // deno-lint-ignore no-control-regex
- const ILLEGAL_VALUE_CHARS = /[\x00\x0A\x0D]/;
+ /** @param {HeadersInit} [init] */
+ constructor(init = undefined) {
+ const prefix = "Failed to construct 'Headers'";
+ if (init !== undefined) {
+ init = webidl.converters["HeadersInit"](init, {
+ prefix,
+ context: "Argument 1",
+ });
+ }
+
+ this[webidl.brand] = webidl.brand;
+ this[_guard] = "none";
+ if (init !== undefined) {
+ fillHeaders(this, init);
+ }
+ }
/**
- * https://fetch.spec.whatwg.org/#concept-headers-append
- * @param {Headers} headers
* @param {string} name
* @param {string} value
*/
- function appendHeader(headers, name, value) {
- // 1.
- value = normalizeHeaderValue(value);
+ append(name, value) {
+ webidl.assertBranded(this, HeadersPrototype);
+ const prefix = "Failed to execute 'append' on 'Headers'";
+ webidl.requiredArguments(arguments.length, 2, { prefix });
+ name = webidl.converters["ByteString"](name, {
+ prefix,
+ context: "Argument 1",
+ });
+ value = webidl.converters["ByteString"](value, {
+ prefix,
+ context: "Argument 2",
+ });
+ appendHeader(this, name, value);
+ }
+
+ /**
+ * @param {string} name
+ */
+ delete(name) {
+ const prefix = "Failed to execute 'delete' on 'Headers'";
+ webidl.requiredArguments(arguments.length, 1, { prefix });
+ name = webidl.converters["ByteString"](name, {
+ prefix,
+ context: "Argument 1",
+ });
- // 2.
if (!RegExpPrototypeTest(HTTP_TOKEN_CODE_POINT_RE, name)) {
throw new TypeError("Header name is not valid.");
}
- if (RegExpPrototypeTest(ILLEGAL_VALUE_CHARS, value)) {
- throw new TypeError("Header value is not valid.");
- }
-
- // 3.
- if (headers[_guard] == "immutable") {
+ if (this[_guard] == "immutable") {
throw new TypeError("Headers are immutable.");
}
- // 7.
- const list = headers[_headerList];
+ const list = this[_headerList];
const lowercaseName = byteLowerCase(name);
for (let i = 0; i < list.length; i++) {
if (byteLowerCase(list[i][0]) === lowercaseName) {
- name = list[i][0];
- break;
+ ArrayPrototypeSplice(list, i, 1);
+ i--;
}
}
- ArrayPrototypePush(list, [name, value]);
}
/**
- * https://fetch.spec.whatwg.org/#concept-header-list-get
- * @param {HeaderList} list
* @param {string} name
*/
- function getHeader(list, name) {
- const lowercaseName = byteLowerCase(name);
- const entries = ArrayPrototypeMap(
- ArrayPrototypeFilter(
- list,
- (entry) => byteLowerCase(entry[0]) === lowercaseName,
- ),
- (entry) => entry[1],
- );
- if (entries.length === 0) {
- return null;
- } else {
- return ArrayPrototypeJoin(entries, "\x2C\x20");
+ get(name) {
+ const prefix = "Failed to execute 'get' on 'Headers'";
+ webidl.requiredArguments(arguments.length, 1, { prefix });
+ name = webidl.converters["ByteString"](name, {
+ prefix,
+ context: "Argument 1",
+ });
+
+ if (!RegExpPrototypeTest(HTTP_TOKEN_CODE_POINT_RE, name)) {
+ throw new TypeError("Header name is not valid.");
}
+
+ const list = this[_headerList];
+ return getHeader(list, name);
}
/**
- * https://fetch.spec.whatwg.org/#concept-header-list-get-decode-split
- * @param {HeaderList} list
* @param {string} name
- * @returns {string[] | null}
*/
- function getDecodeSplitHeader(list, name) {
- const initialValue = getHeader(list, name);
- if (initialValue === null) return null;
- const input = initialValue;
- let position = 0;
- const values = [];
- let value = "";
- while (position < initialValue.length) {
- // 7.1. collect up to " or ,
- const res = collectSequenceOfCodepoints(
- initialValue,
- position,
- (c) => c !== "\u0022" && c !== "\u002C",
- );
- value += res.result;
- position = res.position;
-
- if (position < initialValue.length) {
- if (input[position] === "\u0022") {
- const res = collectHttpQuotedString(input, position, false);
- value += res.result;
- position = res.position;
- if (position < initialValue.length) {
- continue;
- }
- } else {
- if (input[position] !== "\u002C") throw new TypeError("Unreachable");
- position += 1;
- }
- }
-
- value = StringPrototypeReplaceAll(value, HTTP_TAB_OR_SPACE_PREFIX_RE, "");
- value = StringPrototypeReplaceAll(value, HTTP_TAB_OR_SPACE_SUFFIX_RE, "");
+ has(name) {
+ const prefix = "Failed to execute 'has' on 'Headers'";
+ webidl.requiredArguments(arguments.length, 1, { prefix });
+ name = webidl.converters["ByteString"](name, {
+ prefix,
+ context: "Argument 1",
+ });
- ArrayPrototypePush(values, value);
- value = "";
+ if (!RegExpPrototypeTest(HTTP_TOKEN_CODE_POINT_RE, name)) {
+ throw new TypeError("Header name is not valid.");
}
- return values;
- }
- class Headers {
- /** @type {HeaderList} */
- [_headerList] = [];
- /** @type {"immutable" | "request" | "request-no-cors" | "response" | "none"} */
- [_guard];
-
- get [_iterableHeaders]() {
- const list = this[_headerList];
-
- // The order of steps are not similar to the ones suggested by the
- // spec but produce the same result.
- const headers = {};
- const cookies = [];
- for (let i = 0; i < list.length; ++i) {
- const entry = list[i];
- const name = byteLowerCase(entry[0]);
- const value = entry[1];
- if (value === null) throw new TypeError("Unreachable");
- // The following if statement is not spec compliant.
- // `set-cookie` is the only header that can not be concatenated,
- // so must be given to the user as multiple headers.
- // The else block of the if statement is spec compliant again.
- if (name === "set-cookie") {
- ArrayPrototypePush(cookies, [name, value]);
- } else {
- // The following code has the same behaviour as getHeader()
- // at the end of loop. But it avoids looping through the entire
- // list to combine multiple values with same header name. It
- // instead gradually combines them as they are found.
- let header = headers[name];
- if (header && header.length > 0) {
- header += "\x2C\x20" + value;
- } else {
- header = value;
- }
- headers[name] = header;
- }
+ const list = this[_headerList];
+ const lowercaseName = byteLowerCase(name);
+ for (let i = 0; i < list.length; i++) {
+ if (byteLowerCase(list[i][0]) === lowercaseName) {
+ return true;
}
-
- return ArrayPrototypeSort(
- [
- ...new SafeArrayIterator(ObjectEntries(headers)),
- ...new SafeArrayIterator(cookies),
- ],
- (a, b) => {
- const akey = a[0];
- const bkey = b[0];
- if (akey > bkey) return 1;
- if (akey < bkey) return -1;
- return 0;
- },
- );
}
+ return false;
+ }
- /** @param {HeadersInit} [init] */
- constructor(init = undefined) {
- const prefix = "Failed to construct 'Headers'";
- if (init !== undefined) {
- init = webidl.converters["HeadersInit"](init, {
- prefix,
- context: "Argument 1",
- });
- }
+ /**
+ * @param {string} name
+ * @param {string} value
+ */
+ set(name, value) {
+ webidl.assertBranded(this, HeadersPrototype);
+ const prefix = "Failed to execute 'set' on 'Headers'";
+ webidl.requiredArguments(arguments.length, 2, { prefix });
+ name = webidl.converters["ByteString"](name, {
+ prefix,
+ context: "Argument 1",
+ });
+ value = webidl.converters["ByteString"](value, {
+ prefix,
+ context: "Argument 2",
+ });
- this[webidl.brand] = webidl.brand;
- this[_guard] = "none";
- if (init !== undefined) {
- fillHeaders(this, init);
- }
- }
+ value = normalizeHeaderValue(value);
- /**
- * @param {string} name
- * @param {string} value
- */
- append(name, value) {
- webidl.assertBranded(this, HeadersPrototype);
- const prefix = "Failed to execute 'append' on 'Headers'";
- webidl.requiredArguments(arguments.length, 2, { prefix });
- name = webidl.converters["ByteString"](name, {
- prefix,
- context: "Argument 1",
- });
- value = webidl.converters["ByteString"](value, {
- prefix,
- context: "Argument 2",
- });
- appendHeader(this, name, value);
+ // 2.
+ if (!RegExpPrototypeTest(HTTP_TOKEN_CODE_POINT_RE, name)) {
+ throw new TypeError("Header name is not valid.");
}
-
- /**
- * @param {string} name
- */
- delete(name) {
- const prefix = "Failed to execute 'delete' on 'Headers'";
- webidl.requiredArguments(arguments.length, 1, { prefix });
- name = webidl.converters["ByteString"](name, {
- prefix,
- context: "Argument 1",
- });
-
- if (!RegExpPrototypeTest(HTTP_TOKEN_CODE_POINT_RE, name)) {
- throw new TypeError("Header name is not valid.");
- }
- if (this[_guard] == "immutable") {
- throw new TypeError("Headers are immutable.");
- }
-
- const list = this[_headerList];
- const lowercaseName = byteLowerCase(name);
- for (let i = 0; i < list.length; i++) {
- if (byteLowerCase(list[i][0]) === lowercaseName) {
- ArrayPrototypeSplice(list, i, 1);
- i--;
- }
- }
+ if (RegExpPrototypeTest(ILLEGAL_VALUE_CHARS, value)) {
+ throw new TypeError("Header value is not valid.");
}
- /**
- * @param {string} name
- */
- get(name) {
- const prefix = "Failed to execute 'get' on 'Headers'";
- webidl.requiredArguments(arguments.length, 1, { prefix });
- name = webidl.converters["ByteString"](name, {
- prefix,
- context: "Argument 1",
- });
-
- if (!RegExpPrototypeTest(HTTP_TOKEN_CODE_POINT_RE, name)) {
- throw new TypeError("Header name is not valid.");
- }
-
- const list = this[_headerList];
- return getHeader(list, name);
+ if (this[_guard] == "immutable") {
+ throw new TypeError("Headers are immutable.");
}
- /**
- * @param {string} name
- */
- has(name) {
- const prefix = "Failed to execute 'has' on 'Headers'";
- webidl.requiredArguments(arguments.length, 1, { prefix });
- name = webidl.converters["ByteString"](name, {
- prefix,
- context: "Argument 1",
- });
-
- if (!RegExpPrototypeTest(HTTP_TOKEN_CODE_POINT_RE, name)) {
- throw new TypeError("Header name is not valid.");
- }
-
- const list = this[_headerList];
- const lowercaseName = byteLowerCase(name);
- for (let i = 0; i < list.length; i++) {
- if (byteLowerCase(list[i][0]) === lowercaseName) {
- return true;
+ const list = this[_headerList];
+ const lowercaseName = byteLowerCase(name);
+ let added = false;
+ for (let i = 0; i < list.length; i++) {
+ if (byteLowerCase(list[i][0]) === lowercaseName) {
+ if (!added) {
+ list[i][1] = value;
+ added = true;
+ } else {
+ ArrayPrototypeSplice(list, i, 1);
+ i--;
}
}
- return false;
}
-
- /**
- * @param {string} name
- * @param {string} value
- */
- set(name, value) {
- webidl.assertBranded(this, HeadersPrototype);
- const prefix = "Failed to execute 'set' on 'Headers'";
- webidl.requiredArguments(arguments.length, 2, { prefix });
- name = webidl.converters["ByteString"](name, {
- prefix,
- context: "Argument 1",
- });
- value = webidl.converters["ByteString"](value, {
- prefix,
- context: "Argument 2",
- });
-
- value = normalizeHeaderValue(value);
-
- // 2.
- if (!RegExpPrototypeTest(HTTP_TOKEN_CODE_POINT_RE, name)) {
- throw new TypeError("Header name is not valid.");
- }
- if (RegExpPrototypeTest(ILLEGAL_VALUE_CHARS, value)) {
- throw new TypeError("Header value is not valid.");
- }
-
- if (this[_guard] == "immutable") {
- throw new TypeError("Headers are immutable.");
- }
-
- const list = this[_headerList];
- const lowercaseName = byteLowerCase(name);
- let added = false;
- for (let i = 0; i < list.length; i++) {
- if (byteLowerCase(list[i][0]) === lowercaseName) {
- if (!added) {
- list[i][1] = value;
- added = true;
- } else {
- ArrayPrototypeSplice(list, i, 1);
- i--;
- }
- }
- }
- if (!added) {
- ArrayPrototypePush(list, [name, value]);
- }
+ if (!added) {
+ ArrayPrototypePush(list, [name, value]);
}
+ }
- [SymbolFor("Deno.privateCustomInspect")](inspect) {
- const headers = {};
- // deno-lint-ignore prefer-primordials
- for (const header of this) {
- headers[header[0]] = header[1];
- }
- return `Headers ${inspect(headers)}`;
+ [SymbolFor("Deno.privateCustomInspect")](inspect) {
+ const headers = {};
+ // deno-lint-ignore prefer-primordials
+ for (const header of this) {
+ headers[header[0]] = header[1];
}
+ return `Headers ${inspect(headers)}`;
}
+}
- webidl.mixinPairIterable("Headers", Headers, _iterableHeaders, 0, 1);
+webidl.mixinPairIterable("Headers", Headers, _iterableHeaders, 0, 1);
- webidl.configurePrototype(Headers);
- const HeadersPrototype = Headers.prototype;
+webidl.configurePrototype(Headers);
+const HeadersPrototype = Headers.prototype;
- webidl.converters["HeadersInit"] = (V, opts) => {
- // Union for (sequence<sequence<ByteString>> or record<ByteString, ByteString>)
- if (webidl.type(V) === "Object" && V !== null) {
- if (V[SymbolIterator] !== undefined) {
- return webidl.converters["sequence<sequence<ByteString>>"](V, opts);
- }
- return webidl.converters["record<ByteString, ByteString>"](V, opts);
+webidl.converters["HeadersInit"] = (V, opts) => {
+ // Union for (sequence<sequence<ByteString>> or record<ByteString, ByteString>)
+ if (webidl.type(V) === "Object" && V !== null) {
+ if (V[SymbolIterator] !== undefined) {
+ return webidl.converters["sequence<sequence<ByteString>>"](V, opts);
}
- throw webidl.makeException(
- TypeError,
- "The provided value is not of type '(sequence<sequence<ByteString>> or record<ByteString, ByteString>)'",
- opts,
- );
- };
- webidl.converters["Headers"] = webidl.createInterfaceConverter(
- "Headers",
- Headers.prototype,
- );
-
- /**
- * @param {HeaderList} list
- * @param {"immutable" | "request" | "request-no-cors" | "response" | "none"} guard
- * @returns {Headers}
- */
- function headersFromHeaderList(list, guard) {
- const headers = webidl.createBranded(Headers);
- headers[_headerList] = list;
- headers[_guard] = guard;
- return headers;
+ return webidl.converters["record<ByteString, ByteString>"](V, opts);
}
-
- /**
- * @param {Headers}
- * @returns {HeaderList}
- */
- function headerListFromHeaders(headers) {
- return headers[_headerList];
- }
-
- /**
- * @param {Headers}
- * @returns {"immutable" | "request" | "request-no-cors" | "response" | "none"}
- */
- function guardFromHeaders(headers) {
- return headers[_guard];
- }
-
- window.__bootstrap.headers = {
- headersFromHeaderList,
- headerListFromHeaders,
- getDecodeSplitHeader,
- guardFromHeaders,
- fillHeaders,
- getHeader,
- Headers,
- };
-})(this);
+ throw webidl.makeException(
+ TypeError,
+ "The provided value is not of type '(sequence<sequence<ByteString>> or record<ByteString, ByteString>)'",
+ opts,
+ );
+};
+webidl.converters["Headers"] = webidl.createInterfaceConverter(
+ "Headers",
+ Headers.prototype,
+);
+
+/**
+ * @param {HeaderList} list
+ * @param {"immutable" | "request" | "request-no-cors" | "response" | "none"} guard
+ * @returns {Headers}
+ */
+function headersFromHeaderList(list, guard) {
+ const headers = webidl.createBranded(Headers);
+ headers[_headerList] = list;
+ headers[_guard] = guard;
+ return headers;
+}
+
+/**
+ * @param {Headers} headers
+ * @returns {HeaderList}
+ */
+function headerListFromHeaders(headers) {
+ return headers[_headerList];
+}
+
+/**
+ * @param {Headers} headers
+ * @returns {"immutable" | "request" | "request-no-cors" | "response" | "none"}
+ */
+function guardFromHeaders(headers) {
+ return headers[_guard];
+}
+
+export {
+ fillHeaders,
+ getDecodeSplitHeader,
+ getHeader,
+ guardFromHeaders,
+ headerListFromHeaders,
+ Headers,
+ headersFromHeaderList,
+};
diff --git a/ext/fetch/21_formdata.js b/ext/fetch/21_formdata.js
index d253976ef..1639646e0 100644
--- a/ext/fetch/21_formdata.js
+++ b/ext/fetch/21_formdata.js
@@ -8,516 +8,518 @@
/// <reference path="../web/06_streams_types.d.ts" />
/// <reference path="./lib.deno_fetch.d.ts" />
/// <reference lib="esnext" />
-"use strict";
-
-((window) => {
- const core = window.Deno.core;
- const webidl = globalThis.__bootstrap.webidl;
- const { Blob, BlobPrototype, File, FilePrototype } =
- globalThis.__bootstrap.file;
- const {
- ArrayPrototypePush,
- ArrayPrototypeSlice,
- ArrayPrototypeSplice,
- Map,
- MapPrototypeGet,
- MapPrototypeSet,
- MathRandom,
- ObjectPrototypeIsPrototypeOf,
- Symbol,
- StringFromCharCode,
- StringPrototypeTrim,
- StringPrototypeSlice,
- StringPrototypeSplit,
- StringPrototypeReplace,
- StringPrototypeIndexOf,
- StringPrototypePadStart,
- StringPrototypeCodePointAt,
- StringPrototypeReplaceAll,
- TypeError,
- TypedArrayPrototypeSubarray,
- } = window.__bootstrap.primordials;
-
- const entryList = Symbol("entry list");
- /**
- * @param {string} name
- * @param {string | Blob} value
- * @param {string | undefined} filename
- * @returns {FormDataEntry}
- */
- function createEntry(name, value, filename) {
- if (
- ObjectPrototypeIsPrototypeOf(BlobPrototype, value) &&
- !ObjectPrototypeIsPrototypeOf(FilePrototype, value)
- ) {
- value = new File([value], "blob", { type: value.type });
- }
- if (
- ObjectPrototypeIsPrototypeOf(FilePrototype, value) &&
- filename !== undefined
- ) {
- value = new File([value], filename, {
- type: value.type,
- lastModified: value.lastModified,
- });
+const core = globalThis.Deno.core;
+import * as webidl from "internal:ext/webidl/00_webidl.js";
+import {
+ Blob,
+ BlobPrototype,
+ File,
+ FilePrototype,
+} from "internal:ext/web/09_file.js";
+const primordials = globalThis.__bootstrap.primordials;
+const {
+ ArrayPrototypePush,
+ ArrayPrototypeSlice,
+ ArrayPrototypeSplice,
+ Map,
+ MapPrototypeGet,
+ MapPrototypeSet,
+ MathRandom,
+ ObjectPrototypeIsPrototypeOf,
+ Symbol,
+ StringFromCharCode,
+ StringPrototypeTrim,
+ StringPrototypeSlice,
+ StringPrototypeSplit,
+ StringPrototypeReplace,
+ StringPrototypeIndexOf,
+ StringPrototypePadStart,
+ StringPrototypeCodePointAt,
+ StringPrototypeReplaceAll,
+ TypeError,
+ TypedArrayPrototypeSubarray,
+} = primordials;
+
+const entryList = Symbol("entry list");
+
+/**
+ * @param {string} name
+ * @param {string | Blob} value
+ * @param {string | undefined} filename
+ * @returns {FormDataEntry}
+ */
+function createEntry(name, value, filename) {
+ if (
+ ObjectPrototypeIsPrototypeOf(BlobPrototype, value) &&
+ !ObjectPrototypeIsPrototypeOf(FilePrototype, value)
+ ) {
+ value = new File([value], "blob", { type: value.type });
+ }
+ if (
+ ObjectPrototypeIsPrototypeOf(FilePrototype, value) &&
+ filename !== undefined
+ ) {
+ value = new File([value], filename, {
+ type: value.type,
+ lastModified: value.lastModified,
+ });
+ }
+ return {
+ name,
+ // @ts-expect-error because TS is not smart enough
+ value,
+ };
+}
+
+/**
+ * @typedef FormDataEntry
+ * @property {string} name
+ * @property {FormDataEntryValue} value
+ */
+
+class FormData {
+ /** @type {FormDataEntry[]} */
+ [entryList] = [];
+
+ /** @param {void} form */
+ constructor(form) {
+ if (form !== undefined) {
+ webidl.illegalConstructor();
}
- return {
- name,
- // @ts-expect-error because TS is not smart enough
- value,
- };
+ this[webidl.brand] = webidl.brand;
}
/**
- * @typedef FormDataEntry
- * @property {string} name
- * @property {FormDataEntryValue} value
+ * @param {string} name
+ * @param {string | Blob} valueOrBlobValue
+ * @param {string} [filename]
+ * @returns {void}
*/
-
- class FormData {
- /** @type {FormDataEntry[]} */
- [entryList] = [];
-
- /** @param {void} form */
- constructor(form) {
- if (form !== undefined) {
- webidl.illegalConstructor();
- }
- this[webidl.brand] = webidl.brand;
- }
-
- /**
- * @param {string} name
- * @param {string | Blob} valueOrBlobValue
- * @param {string} [filename]
- * @returns {void}
- */
- append(name, valueOrBlobValue, filename) {
- webidl.assertBranded(this, FormDataPrototype);
- const prefix = "Failed to execute 'append' on 'FormData'";
- webidl.requiredArguments(arguments.length, 2, { prefix });
-
- name = webidl.converters["USVString"](name, {
+ append(name, valueOrBlobValue, filename) {
+ webidl.assertBranded(this, FormDataPrototype);
+ const prefix = "Failed to execute 'append' on 'FormData'";
+ webidl.requiredArguments(arguments.length, 2, { prefix });
+
+ name = webidl.converters["USVString"](name, {
+ prefix,
+ context: "Argument 1",
+ });
+ if (ObjectPrototypeIsPrototypeOf(BlobPrototype, valueOrBlobValue)) {
+ valueOrBlobValue = webidl.converters["Blob"](valueOrBlobValue, {
prefix,
- context: "Argument 1",
+ context: "Argument 2",
});
- if (ObjectPrototypeIsPrototypeOf(BlobPrototype, valueOrBlobValue)) {
- valueOrBlobValue = webidl.converters["Blob"](valueOrBlobValue, {
- prefix,
- context: "Argument 2",
- });
- if (filename !== undefined) {
- filename = webidl.converters["USVString"](filename, {
- prefix,
- context: "Argument 3",
- });
- }
- } else {
- valueOrBlobValue = webidl.converters["USVString"](valueOrBlobValue, {
+ if (filename !== undefined) {
+ filename = webidl.converters["USVString"](filename, {
prefix,
- context: "Argument 2",
+ context: "Argument 3",
});
}
-
- const entry = createEntry(name, valueOrBlobValue, filename);
-
- ArrayPrototypePush(this[entryList], entry);
- }
-
- /**
- * @param {string} name
- * @returns {void}
- */
- delete(name) {
- webidl.assertBranded(this, FormDataPrototype);
- const prefix = "Failed to execute 'name' on 'FormData'";
- webidl.requiredArguments(arguments.length, 1, { prefix });
-
- name = webidl.converters["USVString"](name, {
+ } else {
+ valueOrBlobValue = webidl.converters["USVString"](valueOrBlobValue, {
prefix,
- context: "Argument 1",
+ context: "Argument 2",
});
-
- const list = this[entryList];
- for (let i = 0; i < list.length; i++) {
- if (list[i].name === name) {
- ArrayPrototypeSplice(list, i, 1);
- i--;
- }
- }
}
- /**
- * @param {string} name
- * @returns {FormDataEntryValue | null}
- */
- get(name) {
- webidl.assertBranded(this, FormDataPrototype);
- const prefix = "Failed to execute 'get' on 'FormData'";
- webidl.requiredArguments(arguments.length, 1, { prefix });
+ const entry = createEntry(name, valueOrBlobValue, filename);
- name = webidl.converters["USVString"](name, {
- prefix,
- context: "Argument 1",
- });
+ ArrayPrototypePush(this[entryList], entry);
+ }
- const entries = this[entryList];
- for (let i = 0; i < entries.length; ++i) {
- const entry = entries[i];
- if (entry.name === name) return entry.value;
+ /**
+ * @param {string} name
+ * @returns {void}
+ */
+ delete(name) {
+ webidl.assertBranded(this, FormDataPrototype);
+ const prefix = "Failed to execute 'name' on 'FormData'";
+ webidl.requiredArguments(arguments.length, 1, { prefix });
+
+ name = webidl.converters["USVString"](name, {
+ prefix,
+ context: "Argument 1",
+ });
+
+ const list = this[entryList];
+ for (let i = 0; i < list.length; i++) {
+ if (list[i].name === name) {
+ ArrayPrototypeSplice(list, i, 1);
+ i--;
}
- return null;
}
+ }
- /**
- * @param {string} name
- * @returns {FormDataEntryValue[]}
- */
- getAll(name) {
- webidl.assertBranded(this, FormDataPrototype);
- const prefix = "Failed to execute 'getAll' on 'FormData'";
- webidl.requiredArguments(arguments.length, 1, { prefix });
-
- name = webidl.converters["USVString"](name, {
- prefix,
- context: "Argument 1",
- });
+ /**
+ * @param {string} name
+ * @returns {FormDataEntryValue | null}
+ */
+ get(name) {
+ webidl.assertBranded(this, FormDataPrototype);
+ const prefix = "Failed to execute 'get' on 'FormData'";
+ webidl.requiredArguments(arguments.length, 1, { prefix });
+
+ name = webidl.converters["USVString"](name, {
+ prefix,
+ context: "Argument 1",
+ });
- const returnList = [];
- const entries = this[entryList];
- for (let i = 0; i < entries.length; ++i) {
- const entry = entries[i];
- if (entry.name === name) ArrayPrototypePush(returnList, entry.value);
- }
- return returnList;
+ const entries = this[entryList];
+ for (let i = 0; i < entries.length; ++i) {
+ const entry = entries[i];
+ if (entry.name === name) return entry.value;
}
+ return null;
+ }
- /**
- * @param {string} name
- * @returns {boolean}
- */
- has(name) {
- webidl.assertBranded(this, FormDataPrototype);
- const prefix = "Failed to execute 'has' on 'FormData'";
- webidl.requiredArguments(arguments.length, 1, { prefix });
+ /**
+ * @param {string} name
+ * @returns {FormDataEntryValue[]}
+ */
+ getAll(name) {
+ webidl.assertBranded(this, FormDataPrototype);
+ const prefix = "Failed to execute 'getAll' on 'FormData'";
+ webidl.requiredArguments(arguments.length, 1, { prefix });
+
+ name = webidl.converters["USVString"](name, {
+ prefix,
+ context: "Argument 1",
+ });
- name = webidl.converters["USVString"](name, {
- prefix,
- context: "Argument 1",
- });
+ const returnList = [];
+ const entries = this[entryList];
+ for (let i = 0; i < entries.length; ++i) {
+ const entry = entries[i];
+ if (entry.name === name) ArrayPrototypePush(returnList, entry.value);
+ }
+ return returnList;
+ }
- const entries = this[entryList];
- for (let i = 0; i < entries.length; ++i) {
- const entry = entries[i];
- if (entry.name === name) return true;
- }
- return false;
+ /**
+ * @param {string} name
+ * @returns {boolean}
+ */
+ has(name) {
+ webidl.assertBranded(this, FormDataPrototype);
+ const prefix = "Failed to execute 'has' on 'FormData'";
+ webidl.requiredArguments(arguments.length, 1, { prefix });
+
+ name = webidl.converters["USVString"](name, {
+ prefix,
+ context: "Argument 1",
+ });
+
+ const entries = this[entryList];
+ for (let i = 0; i < entries.length; ++i) {
+ const entry = entries[i];
+ if (entry.name === name) return true;
}
+ return false;
+ }
- /**
- * @param {string} name
- * @param {string | Blob} valueOrBlobValue
- * @param {string} [filename]
- * @returns {void}
- */
- set(name, valueOrBlobValue, filename) {
- webidl.assertBranded(this, FormDataPrototype);
- const prefix = "Failed to execute 'set' on 'FormData'";
- webidl.requiredArguments(arguments.length, 2, { prefix });
-
- name = webidl.converters["USVString"](name, {
+ /**
+ * @param {string} name
+ * @param {string | Blob} valueOrBlobValue
+ * @param {string} [filename]
+ * @returns {void}
+ */
+ set(name, valueOrBlobValue, filename) {
+ webidl.assertBranded(this, FormDataPrototype);
+ const prefix = "Failed to execute 'set' on 'FormData'";
+ webidl.requiredArguments(arguments.length, 2, { prefix });
+
+ name = webidl.converters["USVString"](name, {
+ prefix,
+ context: "Argument 1",
+ });
+ if (ObjectPrototypeIsPrototypeOf(BlobPrototype, valueOrBlobValue)) {
+ valueOrBlobValue = webidl.converters["Blob"](valueOrBlobValue, {
prefix,
- context: "Argument 1",
+ context: "Argument 2",
});
- if (ObjectPrototypeIsPrototypeOf(BlobPrototype, valueOrBlobValue)) {
- valueOrBlobValue = webidl.converters["Blob"](valueOrBlobValue, {
- prefix,
- context: "Argument 2",
- });
- if (filename !== undefined) {
- filename = webidl.converters["USVString"](filename, {
- prefix,
- context: "Argument 3",
- });
- }
- } else {
- valueOrBlobValue = webidl.converters["USVString"](valueOrBlobValue, {
+ if (filename !== undefined) {
+ filename = webidl.converters["USVString"](filename, {
prefix,
- context: "Argument 2",
+ context: "Argument 3",
});
}
+ } else {
+ valueOrBlobValue = webidl.converters["USVString"](valueOrBlobValue, {
+ prefix,
+ context: "Argument 2",
+ });
+ }
- const entry = createEntry(name, valueOrBlobValue, filename);
+ const entry = createEntry(name, valueOrBlobValue, filename);
- const list = this[entryList];
- let added = false;
- for (let i = 0; i < list.length; i++) {
- if (list[i].name === name) {
- if (!added) {
- list[i] = entry;
- added = true;
- } else {
- ArrayPrototypeSplice(list, i, 1);
- i--;
- }
+ const list = this[entryList];
+ let added = false;
+ for (let i = 0; i < list.length; i++) {
+ if (list[i].name === name) {
+ if (!added) {
+ list[i] = entry;
+ added = true;
+ } else {
+ ArrayPrototypeSplice(list, i, 1);
+ i--;
}
}
- if (!added) {
- ArrayPrototypePush(list, entry);
- }
+ }
+ if (!added) {
+ ArrayPrototypePush(list, entry);
}
}
+}
- webidl.mixinPairIterable("FormData", FormData, entryList, "name", "value");
+webidl.mixinPairIterable("FormData", FormData, entryList, "name", "value");
- webidl.configurePrototype(FormData);
- const FormDataPrototype = FormData.prototype;
+webidl.configurePrototype(FormData);
+const FormDataPrototype = FormData.prototype;
- const escape = (str, isFilename) => {
- const escapeMap = {
- "\n": "%0A",
- "\r": "%0D",
- '"': "%22",
- };
-
- return StringPrototypeReplace(
- isFilename ? str : StringPrototypeReplace(str, /\r?\n|\r/g, "\r\n"),
- /([\n\r"])/g,
- (c) => escapeMap[c],
- );
+const escape = (str, isFilename) => {
+ const escapeMap = {
+ "\n": "%0A",
+ "\r": "%0D",
+ '"': "%22",
};
- /**
- * convert FormData to a Blob synchronous without reading all of the files
- * @param {globalThis.FormData} formData
- */
- function formDataToBlob(formData) {
- const boundary = StringPrototypePadStart(
- StringPrototypeSlice(
- StringPrototypeReplaceAll(`${MathRandom()}${MathRandom()}`, ".", ""),
- -28,
- ),
- 32,
- "-",
- );
- const chunks = [];
- const prefix = `--${boundary}\r\nContent-Disposition: form-data; name="`;
-
- // deno-lint-ignore prefer-primordials
- for (const { 0: name, 1: value } of formData) {
- if (typeof value === "string") {
- ArrayPrototypePush(
- chunks,
- prefix + escape(name) + '"' + CRLF + CRLF +
- StringPrototypeReplace(value, /\r(?!\n)|(?<!\r)\n/g, CRLF) + CRLF,
- );
- } else {
- ArrayPrototypePush(
- chunks,
- prefix + escape(name) + `"; filename="${escape(value.name, true)}"` +
- CRLF +
- `Content-Type: ${value.type || "application/octet-stream"}\r\n\r\n`,
- value,
- CRLF,
- );
- }
+ return StringPrototypeReplace(
+ isFilename ? str : StringPrototypeReplace(str, /\r?\n|\r/g, "\r\n"),
+ /([\n\r"])/g,
+ (c) => escapeMap[c],
+ );
+};
+
+/**
+ * convert FormData to a Blob synchronous without reading all of the files
+ * @param {globalThis.FormData} formData
+ */
+function formDataToBlob(formData) {
+ const boundary = StringPrototypePadStart(
+ StringPrototypeSlice(
+ StringPrototypeReplaceAll(`${MathRandom()}${MathRandom()}`, ".", ""),
+ -28,
+ ),
+ 32,
+ "-",
+ );
+ const chunks = [];
+ const prefix = `--${boundary}\r\nContent-Disposition: form-data; name="`;
+
+ // deno-lint-ignore prefer-primordials
+ for (const { 0: name, 1: value } of formData) {
+ if (typeof value === "string") {
+ ArrayPrototypePush(
+ chunks,
+ prefix + escape(name) + '"' + CRLF + CRLF +
+ StringPrototypeReplace(value, /\r(?!\n)|(?<!\r)\n/g, CRLF) + CRLF,
+ );
+ } else {
+ ArrayPrototypePush(
+ chunks,
+ prefix + escape(name) + `"; filename="${escape(value.name, true)}"` +
+ CRLF +
+ `Content-Type: ${value.type || "application/octet-stream"}\r\n\r\n`,
+ value,
+ CRLF,
+ );
}
+ }
- ArrayPrototypePush(chunks, `--${boundary}--`);
-
- return new Blob(chunks, {
- type: "multipart/form-data; boundary=" + boundary,
- });
+ ArrayPrototypePush(chunks, `--${boundary}--`);
+
+ return new Blob(chunks, {
+ type: "multipart/form-data; boundary=" + boundary,
+ });
+}
+
+/**
+ * @param {string} value
+ * @returns {Map<string, string>}
+ */
+function parseContentDisposition(value) {
+ /** @type {Map<string, string>} */
+ const params = new Map();
+ // Forced to do so for some Map constructor param mismatch
+ const values = ArrayPrototypeSlice(StringPrototypeSplit(value, ";"), 1);
+ for (let i = 0; i < values.length; i++) {
+ const entries = StringPrototypeSplit(StringPrototypeTrim(values[i]), "=");
+ if (entries.length > 1) {
+ MapPrototypeSet(
+ params,
+ entries[0],
+ StringPrototypeReplace(entries[1], /^"([^"]*)"$/, "$1"),
+ );
+ }
}
+ return params;
+}
+const CRLF = "\r\n";
+const LF = StringPrototypeCodePointAt(CRLF, 1);
+const CR = StringPrototypeCodePointAt(CRLF, 0);
+
+class MultipartParser {
/**
- * @param {string} value
- * @returns {Map<string, string>}
+ * @param {Uint8Array} body
+ * @param {string | undefined} boundary
*/
- function parseContentDisposition(value) {
- /** @type {Map<string, string>} */
- const params = new Map();
- // Forced to do so for some Map constructor param mismatch
- const values = ArrayPrototypeSlice(StringPrototypeSplit(value, ";"), 1);
- for (let i = 0; i < values.length; i++) {
- const entries = StringPrototypeSplit(StringPrototypeTrim(values[i]), "=");
- if (entries.length > 1) {
- MapPrototypeSet(
- params,
- entries[0],
- StringPrototypeReplace(entries[1], /^"([^"]*)"$/, "$1"),
- );
- }
+ constructor(body, boundary) {
+ if (!boundary) {
+ throw new TypeError("multipart/form-data must provide a boundary");
}
- return params;
+
+ this.boundary = `--${boundary}`;
+ this.body = body;
+ this.boundaryChars = core.encode(this.boundary);
}
- const CRLF = "\r\n";
- const LF = StringPrototypeCodePointAt(CRLF, 1);
- const CR = StringPrototypeCodePointAt(CRLF, 0);
-
- class MultipartParser {
- /**
- * @param {Uint8Array} body
- * @param {string | undefined} boundary
- */
- constructor(body, boundary) {
- if (!boundary) {
- throw new TypeError("multipart/form-data must provide a boundary");
+ /**
+ * @param {string} headersText
+ * @returns {{ headers: Headers, disposition: Map<string, string> }}
+ */
+ #parseHeaders(headersText) {
+ const headers = new Headers();
+ const rawHeaders = StringPrototypeSplit(headersText, "\r\n");
+ for (let i = 0; i < rawHeaders.length; ++i) {
+ const rawHeader = rawHeaders[i];
+ const sepIndex = StringPrototypeIndexOf(rawHeader, ":");
+ if (sepIndex < 0) {
+ continue; // Skip this header
}
-
- this.boundary = `--${boundary}`;
- this.body = body;
- this.boundaryChars = core.encode(this.boundary);
+ const key = StringPrototypeSlice(rawHeader, 0, sepIndex);
+ const value = StringPrototypeSlice(rawHeader, sepIndex + 1);
+ headers.set(key, value);
}
- /**
- * @param {string} headersText
- * @returns {{ headers: Headers, disposition: Map<string, string> }}
- */
- #parseHeaders(headersText) {
- const headers = new Headers();
- const rawHeaders = StringPrototypeSplit(headersText, "\r\n");
- for (let i = 0; i < rawHeaders.length; ++i) {
- const rawHeader = rawHeaders[i];
- const sepIndex = StringPrototypeIndexOf(rawHeader, ":");
- if (sepIndex < 0) {
- continue; // Skip this header
- }
- const key = StringPrototypeSlice(rawHeader, 0, sepIndex);
- const value = StringPrototypeSlice(rawHeader, sepIndex + 1);
- headers.set(key, value);
- }
-
- const disposition = parseContentDisposition(
- headers.get("Content-Disposition") ?? "",
- );
+ const disposition = parseContentDisposition(
+ headers.get("Content-Disposition") ?? "",
+ );
- return { headers, disposition };
- }
+ return { headers, disposition };
+ }
- /**
- * @returns {FormData}
- */
- parse() {
- // To have fields body must be at least 2 boundaries + \r\n + --
- // on the last boundary.
- if (this.body.length < (this.boundary.length * 2) + 4) {
- const decodedBody = core.decode(this.body);
- const lastBoundary = this.boundary + "--";
- // check if it's an empty valid form data
- if (
- decodedBody === lastBoundary ||
- decodedBody === lastBoundary + "\r\n"
- ) {
- return new FormData();
- }
- throw new TypeError("Unable to parse body as form data.");
+ /**
+ * @returns {FormData}
+ */
+ parse() {
+ // To have fields body must be at least 2 boundaries + \r\n + --
+ // on the last boundary.
+ if (this.body.length < (this.boundary.length * 2) + 4) {
+ const decodedBody = core.decode(this.body);
+ const lastBoundary = this.boundary + "--";
+ // check if it's an empty valid form data
+ if (
+ decodedBody === lastBoundary ||
+ decodedBody === lastBoundary + "\r\n"
+ ) {
+ return new FormData();
}
+ throw new TypeError("Unable to parse body as form data.");
+ }
- const formData = new FormData();
- let headerText = "";
- let boundaryIndex = 0;
- let state = 0;
- let fileStart = 0;
+ const formData = new FormData();
+ let headerText = "";
+ let boundaryIndex = 0;
+ let state = 0;
+ let fileStart = 0;
- for (let i = 0; i < this.body.length; i++) {
- const byte = this.body[i];
- const prevByte = this.body[i - 1];
- const isNewLine = byte === LF && prevByte === CR;
+ for (let i = 0; i < this.body.length; i++) {
+ const byte = this.body[i];
+ const prevByte = this.body[i - 1];
+ const isNewLine = byte === LF && prevByte === CR;
- if (state === 1 || state === 2 || state == 3) {
- headerText += StringFromCharCode(byte);
- }
- if (state === 0 && isNewLine) {
- state = 1;
- } else if (state === 1 && isNewLine) {
- state = 2;
- const headersDone = this.body[i + 1] === CR &&
- this.body[i + 2] === LF;
-
- if (headersDone) {
- state = 3;
- }
- } else if (state === 2 && isNewLine) {
+ if (state === 1 || state === 2 || state == 3) {
+ headerText += StringFromCharCode(byte);
+ }
+ if (state === 0 && isNewLine) {
+ state = 1;
+ } else if (state === 1 && isNewLine) {
+ state = 2;
+ const headersDone = this.body[i + 1] === CR &&
+ this.body[i + 2] === LF;
+
+ if (headersDone) {
state = 3;
- } else if (state === 3 && isNewLine) {
- state = 4;
- fileStart = i + 1;
- } else if (state === 4) {
- if (this.boundaryChars[boundaryIndex] !== byte) {
- boundaryIndex = 0;
- } else {
- boundaryIndex++;
+ }
+ } else if (state === 2 && isNewLine) {
+ state = 3;
+ } else if (state === 3 && isNewLine) {
+ state = 4;
+ fileStart = i + 1;
+ } else if (state === 4) {
+ if (this.boundaryChars[boundaryIndex] !== byte) {
+ boundaryIndex = 0;
+ } else {
+ boundaryIndex++;
+ }
+
+ if (boundaryIndex >= this.boundary.length) {
+ const { headers, disposition } = this.#parseHeaders(headerText);
+ const content = TypedArrayPrototypeSubarray(
+ this.body,
+ fileStart,
+ i - boundaryIndex - 1,
+ );
+ // https://fetch.spec.whatwg.org/#ref-for-dom-body-formdata
+ const filename = MapPrototypeGet(disposition, "filename");
+ const name = MapPrototypeGet(disposition, "name");
+
+ state = 5;
+ // Reset
+ boundaryIndex = 0;
+ headerText = "";
+
+ if (!name) {
+ continue; // Skip, unknown name
}
- if (boundaryIndex >= this.boundary.length) {
- const { headers, disposition } = this.#parseHeaders(headerText);
- const content = TypedArrayPrototypeSubarray(
- this.body,
- fileStart,
- i - boundaryIndex - 1,
- );
- // https://fetch.spec.whatwg.org/#ref-for-dom-body-formdata
- const filename = MapPrototypeGet(disposition, "filename");
- const name = MapPrototypeGet(disposition, "name");
-
- state = 5;
- // Reset
- boundaryIndex = 0;
- headerText = "";
-
- if (!name) {
- continue; // Skip, unknown name
- }
-
- if (filename) {
- const blob = new Blob([content], {
- type: headers.get("Content-Type") || "application/octet-stream",
- });
- formData.append(name, blob, filename);
- } else {
- formData.append(name, core.decode(content));
- }
+ if (filename) {
+ const blob = new Blob([content], {
+ type: headers.get("Content-Type") || "application/octet-stream",
+ });
+ formData.append(name, blob, filename);
+ } else {
+ formData.append(name, core.decode(content));
}
- } else if (state === 5 && isNewLine) {
- state = 1;
}
+ } else if (state === 5 && isNewLine) {
+ state = 1;
}
-
- return formData;
}
- }
-
- /**
- * @param {Uint8Array} body
- * @param {string | undefined} boundary
- * @returns {FormData}
- */
- function parseFormData(body, boundary) {
- const parser = new MultipartParser(body, boundary);
- return parser.parse();
- }
- /**
- * @param {FormDataEntry[]} entries
- * @returns {FormData}
- */
- function formDataFromEntries(entries) {
- const fd = new FormData();
- fd[entryList] = entries;
- return fd;
+ return formData;
}
-
- webidl.converters["FormData"] = webidl
- .createInterfaceConverter("FormData", FormDataPrototype);
-
- globalThis.__bootstrap.formData = {
- FormData,
- FormDataPrototype,
- formDataToBlob,
- parseFormData,
- formDataFromEntries,
- };
-})(globalThis);
+}
+
+/**
+ * @param {Uint8Array} body
+ * @param {string | undefined} boundary
+ * @returns {FormData}
+ */
+function parseFormData(body, boundary) {
+ const parser = new MultipartParser(body, boundary);
+ return parser.parse();
+}
+
+/**
+ * @param {FormDataEntry[]} entries
+ * @returns {FormData}
+ */
+function formDataFromEntries(entries) {
+ const fd = new FormData();
+ fd[entryList] = entries;
+ return fd;
+}
+
+webidl.converters["FormData"] = webidl
+ .createInterfaceConverter("FormData", FormDataPrototype);
+
+export {
+ FormData,
+ formDataFromEntries,
+ FormDataPrototype,
+ formDataToBlob,
+ parseFormData,
+};
diff --git a/ext/fetch/22_body.js b/ext/fetch/22_body.js
index bea1abce2..48819650a 100644
--- a/ext/fetch/22_body.js
+++ b/ext/fetch/22_body.js
@@ -10,462 +10,462 @@
/// <reference path="../web/06_streams_types.d.ts" />
/// <reference path="./lib.deno_fetch.d.ts" />
/// <reference lib="esnext" />
-"use strict";
-((window) => {
- const core = window.Deno.core;
- const webidl = globalThis.__bootstrap.webidl;
- const { parseUrlEncoded } = globalThis.__bootstrap.url;
- const { URLSearchParamsPrototype } = globalThis.__bootstrap.url;
- const {
- parseFormData,
- formDataFromEntries,
- formDataToBlob,
- FormDataPrototype,
- } = globalThis.__bootstrap.formData;
- const mimesniff = globalThis.__bootstrap.mimesniff;
- const { BlobPrototype } = globalThis.__bootstrap.file;
- const {
- isReadableStreamDisturbed,
- errorReadableStream,
- readableStreamClose,
- readableStreamDisturb,
- readableStreamCollectIntoUint8Array,
- readableStreamThrowIfErrored,
- createProxy,
- ReadableStreamPrototype,
- } = globalThis.__bootstrap.streams;
- const {
- ArrayBufferPrototype,
- ArrayBufferIsView,
- ArrayPrototypeMap,
- JSONParse,
- ObjectDefineProperties,
- ObjectPrototypeIsPrototypeOf,
- // TODO(lucacasonato): add SharedArrayBuffer to primordials
- // SharedArrayBufferPrototype
- TypedArrayPrototypeSlice,
- TypeError,
- Uint8Array,
- Uint8ArrayPrototype,
- } = window.__bootstrap.primordials;
+const core = globalThis.Deno.core;
+import * as webidl from "internal:ext/webidl/00_webidl.js";
+import {
+ parseUrlEncoded,
+ URLSearchParamsPrototype,
+} from "internal:ext/url/00_url.js";
+import {
+ formDataFromEntries,
+ FormDataPrototype,
+ formDataToBlob,
+ parseFormData,
+} from "internal:ext/fetch/21_formdata.js";
+import * as mimesniff from "internal:ext/web/01_mimesniff.js";
+import { BlobPrototype } from "internal:ext/web/09_file.js";
+import {
+ createProxy,
+ errorReadableStream,
+ isReadableStreamDisturbed,
+ readableStreamClose,
+ readableStreamCollectIntoUint8Array,
+ readableStreamDisturb,
+ ReadableStreamPrototype,
+ readableStreamThrowIfErrored,
+} from "internal:ext/web/06_streams.js";
+const primordials = globalThis.__bootstrap.primordials;
+const {
+ ArrayBufferPrototype,
+ ArrayBufferIsView,
+ ArrayPrototypeMap,
+ JSONParse,
+ ObjectDefineProperties,
+ ObjectPrototypeIsPrototypeOf,
+ // TODO(lucacasonato): add SharedArrayBuffer to primordials
+ // SharedArrayBufferPrototype
+ TypedArrayPrototypeSlice,
+ TypeError,
+ Uint8Array,
+ Uint8ArrayPrototype,
+} = primordials;
- /**
- * @param {Uint8Array | string} chunk
- * @returns {Uint8Array}
- */
- function chunkToU8(chunk) {
- return typeof chunk === "string" ? core.encode(chunk) : chunk;
- }
+/**
+ * @param {Uint8Array | string} chunk
+ * @returns {Uint8Array}
+ */
+function chunkToU8(chunk) {
+ return typeof chunk === "string" ? core.encode(chunk) : chunk;
+}
+/**
+ * @param {Uint8Array | string} chunk
+ * @returns {string}
+ */
+function chunkToString(chunk) {
+ return typeof chunk === "string" ? chunk : core.decode(chunk);
+}
+
+class InnerBody {
/**
- * @param {Uint8Array | string} chunk
- * @returns {string}
+ * @param {ReadableStream<Uint8Array> | { body: Uint8Array | string, consumed: boolean }} stream
*/
- function chunkToString(chunk) {
- return typeof chunk === "string" ? chunk : core.decode(chunk);
+ constructor(stream) {
+ /** @type {ReadableStream<Uint8Array> | { body: Uint8Array | string, consumed: boolean }} */
+ this.streamOrStatic = stream ??
+ { body: new Uint8Array(), consumed: false };
+ /** @type {null | Uint8Array | string | Blob | FormData} */
+ this.source = null;
+ /** @type {null | number} */
+ this.length = null;
}
- class InnerBody {
- /**
- * @param {ReadableStream<Uint8Array> | { body: Uint8Array | string, consumed: boolean }} stream
- */
- constructor(stream) {
- /** @type {ReadableStream<Uint8Array> | { body: Uint8Array | string, consumed: boolean }} */
- this.streamOrStatic = stream ??
- { body: new Uint8Array(), consumed: false };
- /** @type {null | Uint8Array | string | Blob | FormData} */
- this.source = null;
- /** @type {null | number} */
- this.length = null;
- }
-
- get stream() {
- if (
- !ObjectPrototypeIsPrototypeOf(
- ReadableStreamPrototype,
- this.streamOrStatic,
- )
- ) {
- const { body, consumed } = this.streamOrStatic;
- if (consumed) {
- this.streamOrStatic = new ReadableStream();
- this.streamOrStatic.getReader();
- readableStreamDisturb(this.streamOrStatic);
- readableStreamClose(this.streamOrStatic);
- } else {
- this.streamOrStatic = new ReadableStream({
- start(controller) {
- controller.enqueue(chunkToU8(body));
- controller.close();
- },
- });
- }
- }
- return this.streamOrStatic;
- }
-
- /**
- * https://fetch.spec.whatwg.org/#body-unusable
- * @returns {boolean}
- */
- unusable() {
- if (
- ObjectPrototypeIsPrototypeOf(
- ReadableStreamPrototype,
- this.streamOrStatic,
- )
- ) {
- return this.streamOrStatic.locked ||
- isReadableStreamDisturbed(this.streamOrStatic);
+ get stream() {
+ if (
+ !ObjectPrototypeIsPrototypeOf(
+ ReadableStreamPrototype,
+ this.streamOrStatic,
+ )
+ ) {
+ const { body, consumed } = this.streamOrStatic;
+ if (consumed) {
+ this.streamOrStatic = new ReadableStream();
+ this.streamOrStatic.getReader();
+ readableStreamDisturb(this.streamOrStatic);
+ readableStreamClose(this.streamOrStatic);
+ } else {
+ this.streamOrStatic = new ReadableStream({
+ start(controller) {
+ controller.enqueue(chunkToU8(body));
+ controller.close();
+ },
+ });
}
- return this.streamOrStatic.consumed;
}
+ return this.streamOrStatic;
+ }
- /**
- * @returns {boolean}
- */
- consumed() {
- if (
- ObjectPrototypeIsPrototypeOf(
- ReadableStreamPrototype,
- this.streamOrStatic,
- )
- ) {
- return isReadableStreamDisturbed(this.streamOrStatic);
- }
- return this.streamOrStatic.consumed;
+ /**
+ * https://fetch.spec.whatwg.org/#body-unusable
+ * @returns {boolean}
+ */
+ unusable() {
+ if (
+ ObjectPrototypeIsPrototypeOf(
+ ReadableStreamPrototype,
+ this.streamOrStatic,
+ )
+ ) {
+ return this.streamOrStatic.locked ||
+ isReadableStreamDisturbed(this.streamOrStatic);
}
+ return this.streamOrStatic.consumed;
+ }
- /**
- * https://fetch.spec.whatwg.org/#concept-body-consume-body
- * @returns {Promise<Uint8Array>}
- */
- consume() {
- if (this.unusable()) throw new TypeError("Body already consumed.");
- if (
- ObjectPrototypeIsPrototypeOf(
- ReadableStreamPrototype,
- this.streamOrStatic,
- )
- ) {
- readableStreamThrowIfErrored(this.stream);
- return readableStreamCollectIntoUint8Array(this.stream);
- } else {
- this.streamOrStatic.consumed = true;
- return this.streamOrStatic.body;
- }
+ /**
+ * @returns {boolean}
+ */
+ consumed() {
+ if (
+ ObjectPrototypeIsPrototypeOf(
+ ReadableStreamPrototype,
+ this.streamOrStatic,
+ )
+ ) {
+ return isReadableStreamDisturbed(this.streamOrStatic);
}
+ return this.streamOrStatic.consumed;
+ }
- cancel(error) {
- if (
- ObjectPrototypeIsPrototypeOf(
- ReadableStreamPrototype,
- this.streamOrStatic,
- )
- ) {
- this.streamOrStatic.cancel(error);
- } else {
- this.streamOrStatic.consumed = true;
- }
+ /**
+ * https://fetch.spec.whatwg.org/#concept-body-consume-body
+ * @returns {Promise<Uint8Array>}
+ */
+ consume() {
+ if (this.unusable()) throw new TypeError("Body already consumed.");
+ if (
+ ObjectPrototypeIsPrototypeOf(
+ ReadableStreamPrototype,
+ this.streamOrStatic,
+ )
+ ) {
+ readableStreamThrowIfErrored(this.stream);
+ return readableStreamCollectIntoUint8Array(this.stream);
+ } else {
+ this.streamOrStatic.consumed = true;
+ return this.streamOrStatic.body;
}
+ }
- error(error) {
- if (
- ObjectPrototypeIsPrototypeOf(
- ReadableStreamPrototype,
- this.streamOrStatic,
- )
- ) {
- errorReadableStream(this.streamOrStatic, error);
- } else {
- this.streamOrStatic.consumed = true;
- }
+ cancel(error) {
+ if (
+ ObjectPrototypeIsPrototypeOf(
+ ReadableStreamPrototype,
+ this.streamOrStatic,
+ )
+ ) {
+ this.streamOrStatic.cancel(error);
+ } else {
+ this.streamOrStatic.consumed = true;
}
+ }
- /**
- * @returns {InnerBody}
- */
- clone() {
- const { 0: out1, 1: out2 } = this.stream.tee();
- this.streamOrStatic = out1;
- const second = new InnerBody(out2);
- second.source = core.deserialize(core.serialize(this.source));
- second.length = this.length;
- return second;
+ error(error) {
+ if (
+ ObjectPrototypeIsPrototypeOf(
+ ReadableStreamPrototype,
+ this.streamOrStatic,
+ )
+ ) {
+ errorReadableStream(this.streamOrStatic, error);
+ } else {
+ this.streamOrStatic.consumed = true;
}
+ }
- /**
- * @returns {InnerBody}
- */
- createProxy() {
- let proxyStreamOrStatic;
- if (
- ObjectPrototypeIsPrototypeOf(
- ReadableStreamPrototype,
- this.streamOrStatic,
- )
- ) {
- proxyStreamOrStatic = createProxy(this.streamOrStatic);
- } else {
- proxyStreamOrStatic = { ...this.streamOrStatic };
- this.streamOrStatic.consumed = true;
- }
- const proxy = new InnerBody(proxyStreamOrStatic);
- proxy.source = this.source;
- proxy.length = this.length;
- return proxy;
- }
+ /**
+ * @returns {InnerBody}
+ */
+ clone() {
+ const { 0: out1, 1: out2 } = this.stream.tee();
+ this.streamOrStatic = out1;
+ const second = new InnerBody(out2);
+ second.source = core.deserialize(core.serialize(this.source));
+ second.length = this.length;
+ return second;
}
/**
- * @param {any} prototype
- * @param {symbol} bodySymbol
- * @param {symbol} mimeTypeSymbol
- * @returns {void}
+ * @returns {InnerBody}
*/
- function mixinBody(prototype, bodySymbol, mimeTypeSymbol) {
- async function consumeBody(object, type) {
- webidl.assertBranded(object, prototype);
+ createProxy() {
+ let proxyStreamOrStatic;
+ if (
+ ObjectPrototypeIsPrototypeOf(
+ ReadableStreamPrototype,
+ this.streamOrStatic,
+ )
+ ) {
+ proxyStreamOrStatic = createProxy(this.streamOrStatic);
+ } else {
+ proxyStreamOrStatic = { ...this.streamOrStatic };
+ this.streamOrStatic.consumed = true;
+ }
+ const proxy = new InnerBody(proxyStreamOrStatic);
+ proxy.source = this.source;
+ proxy.length = this.length;
+ return proxy;
+ }
+}
- const body = object[bodySymbol] !== null
- ? await object[bodySymbol].consume()
- : new Uint8Array();
+/**
+ * @param {any} prototype
+ * @param {symbol} bodySymbol
+ * @param {symbol} mimeTypeSymbol
+ * @returns {void}
+ */
+function mixinBody(prototype, bodySymbol, mimeTypeSymbol) {
+ async function consumeBody(object, type) {
+ webidl.assertBranded(object, prototype);
- const mimeType = type === "Blob" || type === "FormData"
- ? object[mimeTypeSymbol]
- : null;
- return packageData(body, type, mimeType);
- }
+ const body = object[bodySymbol] !== null
+ ? await object[bodySymbol].consume()
+ : new Uint8Array();
- /** @type {PropertyDescriptorMap} */
- const mixin = {
- body: {
- /**
- * @returns {ReadableStream<Uint8Array> | null}
- */
- get() {
- webidl.assertBranded(this, prototype);
- if (this[bodySymbol] === null) {
- return null;
- } else {
- return this[bodySymbol].stream;
- }
- },
- configurable: true,
- enumerable: true,
+ const mimeType = type === "Blob" || type === "FormData"
+ ? object[mimeTypeSymbol]
+ : null;
+ return packageData(body, type, mimeType);
+ }
+
+ /** @type {PropertyDescriptorMap} */
+ const mixin = {
+ body: {
+ /**
+ * @returns {ReadableStream<Uint8Array> | null}
+ */
+ get() {
+ webidl.assertBranded(this, prototype);
+ if (this[bodySymbol] === null) {
+ return null;
+ } else {
+ return this[bodySymbol].stream;
+ }
},
- bodyUsed: {
- /**
- * @returns {boolean}
- */
- get() {
- webidl.assertBranded(this, prototype);
- if (this[bodySymbol] !== null) {
- return this[bodySymbol].consumed();
- }
- return false;
- },
- configurable: true,
- enumerable: true,
+ configurable: true,
+ enumerable: true,
+ },
+ bodyUsed: {
+ /**
+ * @returns {boolean}
+ */
+ get() {
+ webidl.assertBranded(this, prototype);
+ if (this[bodySymbol] !== null) {
+ return this[bodySymbol].consumed();
+ }
+ return false;
},
- arrayBuffer: {
- /** @returns {Promise<ArrayBuffer>} */
- value: function arrayBuffer() {
- return consumeBody(this, "ArrayBuffer");
- },
- writable: true,
- configurable: true,
- enumerable: true,
+ configurable: true,
+ enumerable: true,
+ },
+ arrayBuffer: {
+ /** @returns {Promise<ArrayBuffer>} */
+ value: function arrayBuffer() {
+ return consumeBody(this, "ArrayBuffer");
},
- blob: {
- /** @returns {Promise<Blob>} */
- value: function blob() {
- return consumeBody(this, "Blob");
- },
- writable: true,
- configurable: true,
- enumerable: true,
+ writable: true,
+ configurable: true,
+ enumerable: true,
+ },
+ blob: {
+ /** @returns {Promise<Blob>} */
+ value: function blob() {
+ return consumeBody(this, "Blob");
},
- formData: {
- /** @returns {Promise<FormData>} */
- value: function formData() {
- return consumeBody(this, "FormData");
- },
- writable: true,
- configurable: true,
- enumerable: true,
+ writable: true,
+ configurable: true,
+ enumerable: true,
+ },
+ formData: {
+ /** @returns {Promise<FormData>} */
+ value: function formData() {
+ return consumeBody(this, "FormData");
},
- json: {
- /** @returns {Promise<any>} */
- value: function json() {
- return consumeBody(this, "JSON");
- },
- writable: true,
- configurable: true,
- enumerable: true,
+ writable: true,
+ configurable: true,
+ enumerable: true,
+ },
+ json: {
+ /** @returns {Promise<any>} */
+ value: function json() {
+ return consumeBody(this, "JSON");
},
- text: {
- /** @returns {Promise<string>} */
- value: function text() {
- return consumeBody(this, "text");
- },
- writable: true,
- configurable: true,
- enumerable: true,
+ writable: true,
+ configurable: true,
+ enumerable: true,
+ },
+ text: {
+ /** @returns {Promise<string>} */
+ value: function text() {
+ return consumeBody(this, "text");
},
- };
- return ObjectDefineProperties(prototype, mixin);
- }
+ writable: true,
+ configurable: true,
+ enumerable: true,
+ },
+ };
+ return ObjectDefineProperties(prototype, mixin);
+}
- /**
- * https://fetch.spec.whatwg.org/#concept-body-package-data
- * @param {Uint8Array | string} bytes
- * @param {"ArrayBuffer" | "Blob" | "FormData" | "JSON" | "text"} type
- * @param {MimeType | null} [mimeType]
- */
- function packageData(bytes, type, mimeType) {
- switch (type) {
- case "ArrayBuffer":
- return chunkToU8(bytes).buffer;
- case "Blob":
- return new Blob([bytes], {
- type: mimeType !== null ? mimesniff.serializeMimeType(mimeType) : "",
- });
- case "FormData": {
- if (mimeType !== null) {
- const essence = mimesniff.essence(mimeType);
- if (essence === "multipart/form-data") {
- const boundary = mimeType.parameters.get("boundary");
- if (boundary === null) {
- throw new TypeError(
- "Missing boundary parameter in mime type of multipart formdata.",
- );
- }
- return parseFormData(chunkToU8(bytes), boundary);
- } else if (essence === "application/x-www-form-urlencoded") {
- // TODO(@AaronO): pass as-is with StringOrBuffer in op-layer
- const entries = parseUrlEncoded(chunkToU8(bytes));
- return formDataFromEntries(
- ArrayPrototypeMap(
- entries,
- (x) => ({ name: x[0], value: x[1] }),
- ),
+/**
+ * https://fetch.spec.whatwg.org/#concept-body-package-data
+ * @param {Uint8Array | string} bytes
+ * @param {"ArrayBuffer" | "Blob" | "FormData" | "JSON" | "text"} type
+ * @param {MimeType | null} [mimeType]
+ */
+function packageData(bytes, type, mimeType) {
+ switch (type) {
+ case "ArrayBuffer":
+ return chunkToU8(bytes).buffer;
+ case "Blob":
+ return new Blob([bytes], {
+ type: mimeType !== null ? mimesniff.serializeMimeType(mimeType) : "",
+ });
+ case "FormData": {
+ if (mimeType !== null) {
+ const essence = mimesniff.essence(mimeType);
+ if (essence === "multipart/form-data") {
+ const boundary = mimeType.parameters.get("boundary");
+ if (boundary === null) {
+ throw new TypeError(
+ "Missing boundary parameter in mime type of multipart formdata.",
);
}
- throw new TypeError("Body can not be decoded as form data");
+ return parseFormData(chunkToU8(bytes), boundary);
+ } else if (essence === "application/x-www-form-urlencoded") {
+ // TODO(@AaronO): pass as-is with StringOrBuffer in op-layer
+ const entries = parseUrlEncoded(chunkToU8(bytes));
+ return formDataFromEntries(
+ ArrayPrototypeMap(
+ entries,
+ (x) => ({ name: x[0], value: x[1] }),
+ ),
+ );
}
- throw new TypeError("Missing content type");
+ throw new TypeError("Body can not be decoded as form data");
}
- case "JSON":
- return JSONParse(chunkToString(bytes));
- case "text":
- return chunkToString(bytes);
+ throw new TypeError("Missing content type");
}
+ case "JSON":
+ return JSONParse(chunkToString(bytes));
+ case "text":
+ return chunkToString(bytes);
}
+}
- /**
- * @param {BodyInit} object
- * @returns {{body: InnerBody, contentType: string | null}}
- */
- function extractBody(object) {
- /** @type {ReadableStream<Uint8Array> | { body: Uint8Array | string, consumed: boolean }} */
- let stream;
- let source = null;
- let length = null;
- let contentType = null;
- if (typeof object === "string") {
- source = object;
- contentType = "text/plain;charset=UTF-8";
- } else if (ObjectPrototypeIsPrototypeOf(BlobPrototype, object)) {
- stream = object.stream();
- source = object;
- length = object.size;
- if (object.type.length !== 0) {
- contentType = object.type;
- }
- } else if (ObjectPrototypeIsPrototypeOf(Uint8ArrayPrototype, object)) {
- // Fast(er) path for common case of Uint8Array
- const copy = TypedArrayPrototypeSlice(object, 0, object.byteLength);
- source = copy;
- } else if (
- ArrayBufferIsView(object) ||
- ObjectPrototypeIsPrototypeOf(ArrayBufferPrototype, object)
- ) {
- const u8 = ArrayBufferIsView(object)
- ? new Uint8Array(
- object.buffer,
- object.byteOffset,
- object.byteLength,
- )
- : new Uint8Array(object);
- const copy = TypedArrayPrototypeSlice(u8, 0, u8.byteLength);
- source = copy;
- } else if (ObjectPrototypeIsPrototypeOf(FormDataPrototype, object)) {
- const res = formDataToBlob(object);
- stream = res.stream();
- source = res;
- length = res.size;
- contentType = res.type;
- } else if (
- ObjectPrototypeIsPrototypeOf(URLSearchParamsPrototype, object)
- ) {
- // TODO(@satyarohith): not sure what primordial here.
- source = object.toString();
- contentType = "application/x-www-form-urlencoded;charset=UTF-8";
- } else if (ObjectPrototypeIsPrototypeOf(ReadableStreamPrototype, object)) {
- stream = object;
- if (object.locked || isReadableStreamDisturbed(object)) {
- throw new TypeError("ReadableStream is locked or disturbed");
- }
+/**
+ * @param {BodyInit} object
+ * @returns {{body: InnerBody, contentType: string | null}}
+ */
+function extractBody(object) {
+ /** @type {ReadableStream<Uint8Array> | { body: Uint8Array | string, consumed: boolean }} */
+ let stream;
+ let source = null;
+ let length = null;
+ let contentType = null;
+ if (typeof object === "string") {
+ source = object;
+ contentType = "text/plain;charset=UTF-8";
+ } else if (ObjectPrototypeIsPrototypeOf(BlobPrototype, object)) {
+ stream = object.stream();
+ source = object;
+ length = object.size;
+ if (object.type.length !== 0) {
+ contentType = object.type;
}
- if (typeof source === "string") {
- // WARNING: this deviates from spec (expects length to be set)
- // https://fetch.spec.whatwg.org/#bodyinit > 7.
- // no observable side-effect for users so far, but could change
- stream = { body: source, consumed: false };
- length = null; // NOTE: string length != byte length
- } else if (ObjectPrototypeIsPrototypeOf(Uint8ArrayPrototype, source)) {
- stream = { body: source, consumed: false };
- length = source.byteLength;
+ } else if (ObjectPrototypeIsPrototypeOf(Uint8ArrayPrototype, object)) {
+ // Fast(er) path for common case of Uint8Array
+ const copy = TypedArrayPrototypeSlice(object, 0, object.byteLength);
+ source = copy;
+ } else if (
+ ArrayBufferIsView(object) ||
+ ObjectPrototypeIsPrototypeOf(ArrayBufferPrototype, object)
+ ) {
+ const u8 = ArrayBufferIsView(object)
+ ? new Uint8Array(
+ object.buffer,
+ object.byteOffset,
+ object.byteLength,
+ )
+ : new Uint8Array(object);
+ const copy = TypedArrayPrototypeSlice(u8, 0, u8.byteLength);
+ source = copy;
+ } else if (ObjectPrototypeIsPrototypeOf(FormDataPrototype, object)) {
+ const res = formDataToBlob(object);
+ stream = res.stream();
+ source = res;
+ length = res.size;
+ contentType = res.type;
+ } else if (
+ ObjectPrototypeIsPrototypeOf(URLSearchParamsPrototype, object)
+ ) {
+ // TODO(@satyarohith): not sure what primordial here.
+ source = object.toString();
+ contentType = "application/x-www-form-urlencoded;charset=UTF-8";
+ } else if (ObjectPrototypeIsPrototypeOf(ReadableStreamPrototype, object)) {
+ stream = object;
+ if (object.locked || isReadableStreamDisturbed(object)) {
+ throw new TypeError("ReadableStream is locked or disturbed");
}
- const body = new InnerBody(stream);
- body.source = source;
- body.length = length;
- return { body, contentType };
}
+ if (typeof source === "string") {
+ // WARNING: this deviates from spec (expects length to be set)
+ // https://fetch.spec.whatwg.org/#bodyinit > 7.
+ // no observable side-effect for users so far, but could change
+ stream = { body: source, consumed: false };
+ length = null; // NOTE: string length != byte length
+ } else if (ObjectPrototypeIsPrototypeOf(Uint8ArrayPrototype, source)) {
+ stream = { body: source, consumed: false };
+ length = source.byteLength;
+ }
+ const body = new InnerBody(stream);
+ body.source = source;
+ body.length = length;
+ return { body, contentType };
+}
- webidl.converters["BodyInit_DOMString"] = (V, opts) => {
- // Union for (ReadableStream or Blob or ArrayBufferView or ArrayBuffer or FormData or URLSearchParams or USVString)
- if (ObjectPrototypeIsPrototypeOf(ReadableStreamPrototype, V)) {
- return webidl.converters["ReadableStream"](V, opts);
- } else if (ObjectPrototypeIsPrototypeOf(BlobPrototype, V)) {
- return webidl.converters["Blob"](V, opts);
- } else if (ObjectPrototypeIsPrototypeOf(FormDataPrototype, V)) {
- return webidl.converters["FormData"](V, opts);
- } else if (ObjectPrototypeIsPrototypeOf(URLSearchParamsPrototype, V)) {
- return webidl.converters["URLSearchParams"](V, opts);
+webidl.converters["BodyInit_DOMString"] = (V, opts) => {
+ // Union for (ReadableStream or Blob or ArrayBufferView or ArrayBuffer or FormData or URLSearchParams or USVString)
+ if (ObjectPrototypeIsPrototypeOf(ReadableStreamPrototype, V)) {
+ return webidl.converters["ReadableStream"](V, opts);
+ } else if (ObjectPrototypeIsPrototypeOf(BlobPrototype, V)) {
+ return webidl.converters["Blob"](V, opts);
+ } else if (ObjectPrototypeIsPrototypeOf(FormDataPrototype, V)) {
+ return webidl.converters["FormData"](V, opts);
+ } else if (ObjectPrototypeIsPrototypeOf(URLSearchParamsPrototype, V)) {
+ return webidl.converters["URLSearchParams"](V, opts);
+ }
+ if (typeof V === "object") {
+ if (
+ ObjectPrototypeIsPrototypeOf(ArrayBufferPrototype, V) ||
+ // deno-lint-ignore prefer-primordials
+ ObjectPrototypeIsPrototypeOf(SharedArrayBuffer.prototype, V)
+ ) {
+ return webidl.converters["ArrayBuffer"](V, opts);
}
- if (typeof V === "object") {
- if (
- ObjectPrototypeIsPrototypeOf(ArrayBufferPrototype, V) ||
- // deno-lint-ignore prefer-primordials
- ObjectPrototypeIsPrototypeOf(SharedArrayBuffer.prototype, V)
- ) {
- return webidl.converters["ArrayBuffer"](V, opts);
- }
- if (ArrayBufferIsView(V)) {
- return webidl.converters["ArrayBufferView"](V, opts);
- }
+ if (ArrayBufferIsView(V)) {
+ return webidl.converters["ArrayBufferView"](V, opts);
}
- // BodyInit conversion is passed to extractBody(), which calls core.encode().
- // core.encode() will UTF-8 encode strings with replacement, being equivalent to the USV normalization.
- // Therefore we can convert to DOMString instead of USVString and avoid a costly redundant conversion.
- return webidl.converters["DOMString"](V, opts);
- };
- webidl.converters["BodyInit_DOMString?"] = webidl.createNullableConverter(
- webidl.converters["BodyInit_DOMString"],
- );
+ }
+ // BodyInit conversion is passed to extractBody(), which calls core.encode().
+ // core.encode() will UTF-8 encode strings with replacement, being equivalent to the USV normalization.
+ // Therefore we can convert to DOMString instead of USVString and avoid a costly redundant conversion.
+ return webidl.converters["DOMString"](V, opts);
+};
+webidl.converters["BodyInit_DOMString?"] = webidl.createNullableConverter(
+ webidl.converters["BodyInit_DOMString"],
+);
- window.__bootstrap.fetchBody = { mixinBody, InnerBody, extractBody };
-})(globalThis);
+export { extractBody, InnerBody, mixinBody };
diff --git a/ext/fetch/22_http_client.js b/ext/fetch/22_http_client.js
index 7b9f5c446..9d37f1b7f 100644
--- a/ext/fetch/22_http_client.js
+++ b/ext/fetch/22_http_client.js
@@ -9,40 +9,34 @@
/// <reference path="../web/06_streams_types.d.ts" />
/// <reference path="./lib.deno_fetch.d.ts" />
/// <reference lib="esnext" />
-"use strict";
-((window) => {
- const core = window.Deno.core;
- const ops = core.ops;
+const core = globalThis.Deno.core;
+const ops = core.ops;
+/**
+ * @param {Deno.CreateHttpClientOptions} options
+ * @returns {HttpClient}
+ */
+function createHttpClient(options) {
+ options.caCerts ??= [];
+ return new HttpClient(
+ ops.op_fetch_custom_client(
+ options,
+ ),
+ );
+}
+
+class HttpClient {
/**
- * @param {Deno.CreateHttpClientOptions} options
- * @returns {HttpClient}
+ * @param {number} rid
*/
- function createHttpClient(options) {
- options.caCerts ??= [];
- return new HttpClient(
- ops.op_fetch_custom_client(
- options,
- ),
- );
+ constructor(rid) {
+ this.rid = rid;
}
-
- class HttpClient {
- /**
- * @param {number} rid
- */
- constructor(rid) {
- this.rid = rid;
- }
- close() {
- core.close(this.rid);
- }
+ close() {
+ core.close(this.rid);
}
- const HttpClientPrototype = HttpClient.prototype;
+}
+const HttpClientPrototype = HttpClient.prototype;
- window.__bootstrap.fetch ??= {};
- window.__bootstrap.fetch.createHttpClient = createHttpClient;
- window.__bootstrap.fetch.HttpClient = HttpClient;
- window.__bootstrap.fetch.HttpClientPrototype = HttpClientPrototype;
-})(globalThis);
+export { createHttpClient, HttpClient, HttpClientPrototype };
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,
+};
diff --git a/ext/fetch/23_response.js b/ext/fetch/23_response.js
index 070068d28..46912135a 100644
--- a/ext/fetch/23_response.js
+++ b/ext/fetch/23_response.js
@@ -9,510 +9,510 @@
/// <reference path="../web/06_streams_types.d.ts" />
/// <reference path="./lib.deno_fetch.d.ts" />
/// <reference lib="esnext" />
-"use strict";
-
-((window) => {
- const { isProxy } = Deno.core;
- const webidl = window.__bootstrap.webidl;
- const consoleInternal = window.__bootstrap.console;
- const {
- byteLowerCase,
- } = window.__bootstrap.infra;
- const { HTTP_TAB_OR_SPACE, regexMatcher, serializeJSValueToJSONString } =
- window.__bootstrap.infra;
- const { extractBody, mixinBody } = window.__bootstrap.fetchBody;
- const { getLocationHref } = window.__bootstrap.location;
- const { extractMimeType } = window.__bootstrap.mimesniff;
- const { URL } = window.__bootstrap.url;
- const {
- getDecodeSplitHeader,
- headerListFromHeaders,
- headersFromHeaderList,
- guardFromHeaders,
- fillHeaders,
- } = window.__bootstrap.headers;
- const {
- ArrayPrototypeMap,
- ArrayPrototypePush,
- ObjectDefineProperties,
- ObjectPrototypeIsPrototypeOf,
- RangeError,
- RegExp,
- RegExpPrototypeTest,
- SafeArrayIterator,
- Symbol,
- SymbolFor,
- TypeError,
- } = window.__bootstrap.primordials;
-
- const VCHAR = ["\x21-\x7E"];
- const OBS_TEXT = ["\x80-\xFF"];
-
- const REASON_PHRASE = [
- ...new SafeArrayIterator(HTTP_TAB_OR_SPACE),
- ...new SafeArrayIterator(VCHAR),
- ...new SafeArrayIterator(OBS_TEXT),
- ];
- const REASON_PHRASE_MATCHER = regexMatcher(REASON_PHRASE);
- const REASON_PHRASE_RE = new RegExp(`^[${REASON_PHRASE_MATCHER}]*$`);
-
- const _response = Symbol("response");
- const _headers = Symbol("headers");
- const _mimeType = Symbol("mime type");
- const _body = Symbol("body");
+
+const core = globalThis.Deno.core;
+import * as webidl from "internal:ext/webidl/00_webidl.js";
+import { createFilteredInspectProxy } from "internal:ext/console/02_console.js";
+import {
+ byteLowerCase,
+ HTTP_TAB_OR_SPACE,
+ regexMatcher,
+ serializeJSValueToJSONString,
+} from "internal:ext/web/00_infra.js";
+import { extractBody, 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 { URL } from "internal:ext/url/00_url.js";
+import {
+ fillHeaders,
+ getDecodeSplitHeader,
+ guardFromHeaders,
+ headerListFromHeaders,
+ headersFromHeaderList,
+} from "internal:ext/fetch/20_headers.js";
+const primordials = globalThis.__bootstrap.primordials;
+const {
+ ArrayPrototypeMap,
+ ArrayPrototypePush,
+ ObjectDefineProperties,
+ ObjectPrototypeIsPrototypeOf,
+ RangeError,
+ RegExp,
+ RegExpPrototypeTest,
+ SafeArrayIterator,
+ Symbol,
+ SymbolFor,
+ TypeError,
+} = primordials;
+
+const VCHAR = ["\x21-\x7E"];
+const OBS_TEXT = ["\x80-\xFF"];
+
+const REASON_PHRASE = [
+ ...new SafeArrayIterator(HTTP_TAB_OR_SPACE),
+ ...new SafeArrayIterator(VCHAR),
+ ...new SafeArrayIterator(OBS_TEXT),
+];
+const REASON_PHRASE_MATCHER = regexMatcher(REASON_PHRASE);
+const REASON_PHRASE_RE = new RegExp(`^[${REASON_PHRASE_MATCHER}]*$`);
+
+const _response = Symbol("response");
+const _headers = Symbol("headers");
+const _mimeType = Symbol("mime type");
+const _body = Symbol("body");
+
+/**
+ * @typedef InnerResponse
+ * @property {"basic" | "cors" | "default" | "error" | "opaque" | "opaqueredirect"} type
+ * @property {() => string | null} url
+ * @property {string[]} urlList
+ * @property {number} status
+ * @property {string} statusMessage
+ * @property {[string, string][]} headerList
+ * @property {null | typeof __window.bootstrap.fetchBody.InnerBody} body
+ * @property {boolean} aborted
+ * @property {string} [error]
+ */
+
+/**
+ * @param {number} status
+ * @returns {boolean}
+ */
+function nullBodyStatus(status) {
+ return status === 101 || status === 204 || status === 205 || status === 304;
+}
+
+/**
+ * @param {number} status
+ * @returns {boolean}
+ */
+function redirectStatus(status) {
+ return status === 301 || status === 302 || status === 303 ||
+ status === 307 || status === 308;
+}
+
+/**
+ * https://fetch.spec.whatwg.org/#concept-response-clone
+ * @param {InnerResponse} response
+ * @returns {InnerResponse}
+ */
+function cloneInnerResponse(response) {
+ const urlList = [...new SafeArrayIterator(response.urlList)];
+ const headerList = ArrayPrototypeMap(
+ response.headerList,
+ (x) => [x[0], x[1]],
+ );
+
+ let body = null;
+ if (response.body !== null) {
+ body = response.body.clone();
+ }
+
+ return {
+ type: response.type,
+ body,
+ headerList,
+ urlList,
+ status: response.status,
+ statusMessage: response.statusMessage,
+ aborted: response.aborted,
+ url() {
+ if (this.urlList.length == 0) return null;
+ return this.urlList[this.urlList.length - 1];
+ },
+ };
+}
+
+/**
+ * @returns {InnerResponse}
+ */
+function newInnerResponse(status = 200, statusMessage = "") {
+ return {
+ type: "default",
+ body: null,
+ headerList: [],
+ urlList: [],
+ status,
+ statusMessage,
+ aborted: false,
+ url() {
+ if (this.urlList.length == 0) return null;
+ return this.urlList[this.urlList.length - 1];
+ },
+ };
+}
+
+/**
+ * @param {string} error
+ * @returns {InnerResponse}
+ */
+function networkError(error) {
+ const resp = newInnerResponse(0);
+ resp.type = "error";
+ resp.error = error;
+ return resp;
+}
+
+/**
+ * @returns {InnerResponse}
+ */
+function abortedNetworkError() {
+ const resp = networkError("aborted");
+ resp.aborted = true;
+ return resp;
+}
+
+/**
+ * https://fetch.spec.whatwg.org#initialize-a-response
+ * @param {Response} response
+ * @param {ResponseInit} init
+ * @param {{ body: fetchBody.InnerBody, contentType: string | null } | null} bodyWithType
+ */
+function initializeAResponse(response, init, bodyWithType) {
+ // 1.
+ if ((init.status < 200 || init.status > 599) && init.status != 101) {
+ throw new RangeError(
+ `The status provided (${init.status}) is not equal to 101 and outside the range [200, 599].`,
+ );
+ }
+
+ // 2.
+ if (
+ init.statusText &&
+ !RegExpPrototypeTest(REASON_PHRASE_RE, init.statusText)
+ ) {
+ throw new TypeError("Status text is not valid.");
+ }
+
+ // 3.
+ response[_response].status = init.status;
+
+ // 4.
+ response[_response].statusMessage = init.statusText;
+ // 5.
+ /** @type {headers.Headers} */
+ const headers = response[_headers];
+ if (init.headers) {
+ fillHeaders(headers, init.headers);
+ }
+
+ // 6.
+ if (bodyWithType !== null) {
+ if (nullBodyStatus(response[_response].status)) {
+ throw new TypeError(
+ "Response with null body status cannot have body",
+ );
+ }
+
+ const { body, contentType } = bodyWithType;
+ response[_response].body = body;
+
+ if (contentType !== null) {
+ let hasContentType = false;
+ const list = headerListFromHeaders(headers);
+ for (let i = 0; i < list.length; i++) {
+ if (byteLowerCase(list[i][0]) === "content-type") {
+ hasContentType = true;
+ break;
+ }
+ }
+ if (!hasContentType) {
+ ArrayPrototypePush(list, ["Content-Type", contentType]);
+ }
+ }
+ }
+}
+
+class Response {
+ get [_mimeType]() {
+ const values = getDecodeSplitHeader(
+ headerListFromHeaders(this[_headers]),
+ "Content-Type",
+ );
+ return extractMimeType(values);
+ }
+ get [_body]() {
+ return this[_response].body;
+ }
/**
- * @typedef InnerResponse
- * @property {"basic" | "cors" | "default" | "error" | "opaque" | "opaqueredirect"} type
- * @property {() => string | null} url
- * @property {string[]} urlList
- * @property {number} status
- * @property {string} statusMessage
- * @property {[string, string][]} headerList
- * @property {null | typeof __window.bootstrap.fetchBody.InnerBody} body
- * @property {boolean} aborted
- * @property {string} [error]
+ * @returns {Response}
*/
+ static error() {
+ const inner = newInnerResponse(0);
+ inner.type = "error";
+ const response = webidl.createBranded(Response);
+ response[_response] = inner;
+ response[_headers] = headersFromHeaderList(
+ response[_response].headerList,
+ "immutable",
+ );
+ return response;
+ }
/**
+ * @param {string} url
* @param {number} status
- * @returns {boolean}
+ * @returns {Response}
*/
- function nullBodyStatus(status) {
- return status === 101 || status === 204 || status === 205 || status === 304;
+ static redirect(url, status = 302) {
+ const prefix = "Failed to call 'Response.redirect'";
+ url = webidl.converters["USVString"](url, {
+ prefix,
+ context: "Argument 1",
+ });
+ status = webidl.converters["unsigned short"](status, {
+ prefix,
+ context: "Argument 2",
+ });
+
+ const baseURL = getLocationHref();
+ const parsedURL = new URL(url, baseURL);
+ if (!redirectStatus(status)) {
+ throw new RangeError("Invalid redirect status code.");
+ }
+ const inner = newInnerResponse(status);
+ inner.type = "default";
+ ArrayPrototypePush(inner.headerList, ["Location", parsedURL.href]);
+ const response = webidl.createBranded(Response);
+ response[_response] = inner;
+ response[_headers] = headersFromHeaderList(
+ response[_response].headerList,
+ "immutable",
+ );
+ return response;
}
/**
- * @param {number} status
- * @returns {boolean}
+ * @param {any} data
+ * @param {ResponseInit} init
+ * @returns {Response}
*/
- function redirectStatus(status) {
- return status === 301 || status === 302 || status === 303 ||
- status === 307 || status === 308;
+ static json(data = undefined, init = {}) {
+ const prefix = "Failed to call 'Response.json'";
+ data = webidl.converters.any(data);
+ init = webidl.converters["ResponseInit_fast"](init, {
+ prefix,
+ context: "Argument 2",
+ });
+
+ const str = serializeJSValueToJSONString(data);
+ const res = extractBody(str);
+ res.contentType = "application/json";
+ const response = webidl.createBranded(Response);
+ response[_response] = newInnerResponse();
+ response[_headers] = headersFromHeaderList(
+ response[_response].headerList,
+ "response",
+ );
+ initializeAResponse(response, init, res);
+ return response;
}
/**
- * https://fetch.spec.whatwg.org/#concept-response-clone
- * @param {InnerResponse} response
- * @returns {InnerResponse}
+ * @param {BodyInit | null} body
+ * @param {ResponseInit} init
*/
- function cloneInnerResponse(response) {
- const urlList = [...new SafeArrayIterator(response.urlList)];
- const headerList = ArrayPrototypeMap(
- response.headerList,
- (x) => [x[0], x[1]],
+ constructor(body = null, init = undefined) {
+ const prefix = "Failed to construct 'Response'";
+ body = webidl.converters["BodyInit_DOMString?"](body, {
+ prefix,
+ context: "Argument 1",
+ });
+ init = webidl.converters["ResponseInit_fast"](init, {
+ prefix,
+ context: "Argument 2",
+ });
+
+ this[_response] = newInnerResponse();
+ this[_headers] = headersFromHeaderList(
+ this[_response].headerList,
+ "response",
);
- let body = null;
- if (response.body !== null) {
- body = response.body.clone();
+ let bodyWithType = null;
+ if (body !== null) {
+ bodyWithType = extractBody(body);
}
-
- return {
- type: response.type,
- body,
- headerList,
- urlList,
- status: response.status,
- statusMessage: response.statusMessage,
- aborted: response.aborted,
- url() {
- if (this.urlList.length == 0) return null;
- return this.urlList[this.urlList.length - 1];
- },
- };
+ initializeAResponse(this, init, bodyWithType);
+ this[webidl.brand] = webidl.brand;
}
/**
- * @returns {InnerResponse}
+ * @returns {"basic" | "cors" | "default" | "error" | "opaque" | "opaqueredirect"}
*/
- function newInnerResponse(status = 200, statusMessage = "") {
- return {
- type: "default",
- body: null,
- headerList: [],
- urlList: [],
- status,
- statusMessage,
- aborted: false,
- url() {
- if (this.urlList.length == 0) return null;
- return this.urlList[this.urlList.length - 1];
- },
- };
+ get type() {
+ webidl.assertBranded(this, ResponsePrototype);
+ return this[_response].type;
}
/**
- * @param {string} error
- * @returns {InnerResponse}
+ * @returns {string}
*/
- function networkError(error) {
- const resp = newInnerResponse(0);
- resp.type = "error";
- resp.error = error;
- return resp;
+ get url() {
+ webidl.assertBranded(this, ResponsePrototype);
+ const url = this[_response].url();
+ if (url === null) return "";
+ const newUrl = new URL(url);
+ newUrl.hash = "";
+ return newUrl.href;
}
/**
- * @returns {InnerResponse}
+ * @returns {boolean}
*/
- function abortedNetworkError() {
- const resp = networkError("aborted");
- resp.aborted = true;
- return resp;
+ get redirected() {
+ webidl.assertBranded(this, ResponsePrototype);
+ return this[_response].urlList.length > 1;
}
/**
- * https://fetch.spec.whatwg.org#initialize-a-response
- * @param {Response} response
- * @param {ResponseInit} init
- * @param {{ body: __bootstrap.fetchBody.InnerBody, contentType: string | null } | null} bodyWithType
+ * @returns {number}
*/
- function initializeAResponse(response, init, bodyWithType) {
- // 1.
- if ((init.status < 200 || init.status > 599) && init.status != 101) {
- throw new RangeError(
- `The status provided (${init.status}) is not equal to 101 and outside the range [200, 599].`,
- );
- }
-
- // 2.
- if (
- init.statusText &&
- !RegExpPrototypeTest(REASON_PHRASE_RE, init.statusText)
- ) {
- throw new TypeError("Status text is not valid.");
- }
-
- // 3.
- response[_response].status = init.status;
-
- // 4.
- response[_response].statusMessage = init.statusText;
- // 5.
- /** @type {__bootstrap.headers.Headers} */
- const headers = response[_headers];
- if (init.headers) {
- fillHeaders(headers, init.headers);
- }
-
- // 6.
- if (bodyWithType !== null) {
- if (nullBodyStatus(response[_response].status)) {
- throw new TypeError(
- "Response with null body status cannot have body",
- );
- }
-
- const { body, contentType } = bodyWithType;
- response[_response].body = body;
-
- if (contentType !== null) {
- let hasContentType = false;
- const list = headerListFromHeaders(headers);
- for (let i = 0; i < list.length; i++) {
- if (byteLowerCase(list[i][0]) === "content-type") {
- hasContentType = true;
- break;
- }
- }
- if (!hasContentType) {
- ArrayPrototypePush(list, ["Content-Type", contentType]);
- }
- }
- }
+ get status() {
+ webidl.assertBranded(this, ResponsePrototype);
+ return this[_response].status;
}
- class Response {
- get [_mimeType]() {
- const values = getDecodeSplitHeader(
- headerListFromHeaders(this[_headers]),
- "Content-Type",
- );
- return extractMimeType(values);
- }
- get [_body]() {
- return this[_response].body;
- }
-
- /**
- * @returns {Response}
- */
- static error() {
- const inner = newInnerResponse(0);
- inner.type = "error";
- const response = webidl.createBranded(Response);
- response[_response] = inner;
- response[_headers] = headersFromHeaderList(
- response[_response].headerList,
- "immutable",
- );
- return response;
- }
-
- /**
- * @param {string} url
- * @param {number} status
- * @returns {Response}
- */
- static redirect(url, status = 302) {
- const prefix = "Failed to call 'Response.redirect'";
- url = webidl.converters["USVString"](url, {
- prefix,
- context: "Argument 1",
- });
- status = webidl.converters["unsigned short"](status, {
- prefix,
- context: "Argument 2",
- });
-
- const baseURL = getLocationHref();
- const parsedURL = new URL(url, baseURL);
- if (!redirectStatus(status)) {
- throw new RangeError("Invalid redirect status code.");
- }
- const inner = newInnerResponse(status);
- inner.type = "default";
- ArrayPrototypePush(inner.headerList, ["Location", parsedURL.href]);
- const response = webidl.createBranded(Response);
- response[_response] = inner;
- response[_headers] = headersFromHeaderList(
- response[_response].headerList,
- "immutable",
- );
- return response;
- }
-
- /**
- * @param {any} data
- * @param {ResponseInit} init
- * @returns {Response}
- */
- static json(data = undefined, init = {}) {
- const prefix = "Failed to call 'Response.json'";
- data = webidl.converters.any(data);
- init = webidl.converters["ResponseInit_fast"](init, {
- prefix,
- context: "Argument 2",
- });
-
- const str = serializeJSValueToJSONString(data);
- const res = extractBody(str);
- res.contentType = "application/json";
- const response = webidl.createBranded(Response);
- response[_response] = newInnerResponse();
- response[_headers] = headersFromHeaderList(
- response[_response].headerList,
- "response",
- );
- initializeAResponse(response, init, res);
- return response;
- }
-
- /**
- * @param {BodyInit | null} body
- * @param {ResponseInit} init
- */
- constructor(body = null, init = undefined) {
- const prefix = "Failed to construct 'Response'";
- body = webidl.converters["BodyInit_DOMString?"](body, {
- prefix,
- context: "Argument 1",
- });
- init = webidl.converters["ResponseInit_fast"](init, {
- prefix,
- context: "Argument 2",
- });
-
- this[_response] = newInnerResponse();
- this[_headers] = headersFromHeaderList(
- this[_response].headerList,
- "response",
- );
-
- let bodyWithType = null;
- if (body !== null) {
- bodyWithType = extractBody(body);
- }
- initializeAResponse(this, init, bodyWithType);
- this[webidl.brand] = webidl.brand;
- }
-
- /**
- * @returns {"basic" | "cors" | "default" | "error" | "opaque" | "opaqueredirect"}
- */
- get type() {
- webidl.assertBranded(this, ResponsePrototype);
- return this[_response].type;
- }
-
- /**
- * @returns {string}
- */
- get url() {
- webidl.assertBranded(this, ResponsePrototype);
- const url = this[_response].url();
- if (url === null) return "";
- const newUrl = new URL(url);
- newUrl.hash = "";
- return newUrl.href;
- }
-
- /**
- * @returns {boolean}
- */
- get redirected() {
- webidl.assertBranded(this, ResponsePrototype);
- return this[_response].urlList.length > 1;
- }
-
- /**
- * @returns {number}
- */
- get status() {
- webidl.assertBranded(this, ResponsePrototype);
- return this[_response].status;
- }
-
- /**
- * @returns {boolean}
- */
- get ok() {
- webidl.assertBranded(this, ResponsePrototype);
- const status = this[_response].status;
- return status >= 200 && status <= 299;
- }
-
- /**
- * @returns {string}
- */
- get statusText() {
- webidl.assertBranded(this, ResponsePrototype);
- return this[_response].statusMessage;
- }
-
- /**
- * @returns {Headers}
- */
- get headers() {
- webidl.assertBranded(this, ResponsePrototype);
- return this[_headers];
- }
-
- /**
- * @returns {Response}
- */
- clone() {
- webidl.assertBranded(this, ResponsePrototype);
- if (this[_body] && this[_body].unusable()) {
- throw new TypeError("Body is unusable.");
- }
- const second = webidl.createBranded(Response);
- const newRes = cloneInnerResponse(this[_response]);
- second[_response] = newRes;
- second[_headers] = headersFromHeaderList(
- newRes.headerList,
- guardFromHeaders(this[_headers]),
- );
- return second;
- }
-
- [SymbolFor("Deno.customInspect")](inspect) {
- return inspect(consoleInternal.createFilteredInspectProxy({
- object: this,
- evaluate: ObjectPrototypeIsPrototypeOf(ResponsePrototype, this),
- keys: [
- "body",
- "bodyUsed",
- "headers",
- "ok",
- "redirected",
- "status",
- "statusText",
- "url",
- ],
- }));
- }
+ /**
+ * @returns {boolean}
+ */
+ get ok() {
+ webidl.assertBranded(this, ResponsePrototype);
+ const status = this[_response].status;
+ return status >= 200 && status <= 299;
}
- webidl.configurePrototype(Response);
- ObjectDefineProperties(Response, {
- json: { enumerable: true },
- redirect: { enumerable: true },
- error: { enumerable: true },
- });
- const ResponsePrototype = Response.prototype;
- mixinBody(ResponsePrototype, _body, _mimeType);
-
- webidl.converters["Response"] = webidl.createInterfaceConverter(
- "Response",
- ResponsePrototype,
- );
- webidl.converters["ResponseInit"] = webidl.createDictionaryConverter(
- "ResponseInit",
- [{
- key: "status",
- defaultValue: 200,
- converter: webidl.converters["unsigned short"],
- }, {
- key: "statusText",
- defaultValue: "",
- converter: webidl.converters["ByteString"],
- }, {
- key: "headers",
- converter: webidl.converters["HeadersInit"],
- }],
- );
- webidl.converters["ResponseInit_fast"] = function (init, opts) {
- if (init === undefined || init === null) {
- return { status: 200, statusText: "", headers: undefined };
- }
- // Fast path, if not a proxy
- if (typeof init === "object" && !isProxy(init)) {
- // Not a proxy fast path
- const status = init.status !== undefined
- ? webidl.converters["unsigned short"](init.status)
- : 200;
- const statusText = init.statusText !== undefined
- ? webidl.converters["ByteString"](init.statusText)
- : "";
- const headers = init.headers !== undefined
- ? webidl.converters["HeadersInit"](init.headers)
- : undefined;
- return { status, statusText, headers };
- }
- // Slow default path
- return webidl.converters["ResponseInit"](init, opts);
- };
+ /**
+ * @returns {string}
+ */
+ get statusText() {
+ webidl.assertBranded(this, ResponsePrototype);
+ return this[_response].statusMessage;
+ }
/**
- * @param {Response} response
- * @returns {InnerResponse}
+ * @returns {Headers}
*/
- function toInnerResponse(response) {
- return response[_response];
+ get headers() {
+ webidl.assertBranded(this, ResponsePrototype);
+ return this[_headers];
}
/**
- * @param {InnerResponse} inner
- * @param {"request" | "immutable" | "request-no-cors" | "response" | "none"} guard
* @returns {Response}
*/
- function fromInnerResponse(inner, guard) {
- const response = webidl.createBranded(Response);
- response[_response] = inner;
- response[_headers] = headersFromHeaderList(inner.headerList, guard);
- return response;
+ clone() {
+ webidl.assertBranded(this, ResponsePrototype);
+ if (this[_body] && this[_body].unusable()) {
+ throw new TypeError("Body is unusable.");
+ }
+ const second = webidl.createBranded(Response);
+ const newRes = cloneInnerResponse(this[_response]);
+ second[_response] = newRes;
+ second[_headers] = headersFromHeaderList(
+ newRes.headerList,
+ guardFromHeaders(this[_headers]),
+ );
+ return second;
}
- window.__bootstrap.fetch ??= {};
- window.__bootstrap.fetch.Response = Response;
- window.__bootstrap.fetch.ResponsePrototype = ResponsePrototype;
- window.__bootstrap.fetch.newInnerResponse = newInnerResponse;
- window.__bootstrap.fetch.toInnerResponse = toInnerResponse;
- window.__bootstrap.fetch.fromInnerResponse = fromInnerResponse;
- window.__bootstrap.fetch.redirectStatus = redirectStatus;
- window.__bootstrap.fetch.nullBodyStatus = nullBodyStatus;
- window.__bootstrap.fetch.networkError = networkError;
- window.__bootstrap.fetch.abortedNetworkError = abortedNetworkError;
-})(globalThis);
+ [SymbolFor("Deno.customInspect")](inspect) {
+ return inspect(createFilteredInspectProxy({
+ object: this,
+ evaluate: ObjectPrototypeIsPrototypeOf(ResponsePrototype, this),
+ keys: [
+ "body",
+ "bodyUsed",
+ "headers",
+ "ok",
+ "redirected",
+ "status",
+ "statusText",
+ "url",
+ ],
+ }));
+ }
+}
+
+webidl.configurePrototype(Response);
+ObjectDefineProperties(Response, {
+ json: { enumerable: true },
+ redirect: { enumerable: true },
+ error: { enumerable: true },
+});
+const ResponsePrototype = Response.prototype;
+mixinBody(ResponsePrototype, _body, _mimeType);
+
+webidl.converters["Response"] = webidl.createInterfaceConverter(
+ "Response",
+ ResponsePrototype,
+);
+webidl.converters["ResponseInit"] = webidl.createDictionaryConverter(
+ "ResponseInit",
+ [{
+ key: "status",
+ defaultValue: 200,
+ converter: webidl.converters["unsigned short"],
+ }, {
+ key: "statusText",
+ defaultValue: "",
+ converter: webidl.converters["ByteString"],
+ }, {
+ key: "headers",
+ converter: webidl.converters["HeadersInit"],
+ }],
+);
+webidl.converters["ResponseInit_fast"] = function (init, opts) {
+ if (init === undefined || init === null) {
+ return { status: 200, statusText: "", headers: undefined };
+ }
+ // Fast path, if not a proxy
+ if (typeof init === "object" && !core.isProxy(init)) {
+ // Not a proxy fast path
+ const status = init.status !== undefined
+ ? webidl.converters["unsigned short"](init.status)
+ : 200;
+ const statusText = init.statusText !== undefined
+ ? webidl.converters["ByteString"](init.statusText)
+ : "";
+ const headers = init.headers !== undefined
+ ? webidl.converters["HeadersInit"](init.headers)
+ : undefined;
+ return { status, statusText, headers };
+ }
+ // Slow default path
+ return webidl.converters["ResponseInit"](init, opts);
+};
+
+/**
+ * @param {Response} response
+ * @returns {InnerResponse}
+ */
+function toInnerResponse(response) {
+ return response[_response];
+}
+
+/**
+ * @param {InnerResponse} inner
+ * @param {"request" | "immutable" | "request-no-cors" | "response" | "none"} guard
+ * @returns {Response}
+ */
+function fromInnerResponse(inner, guard) {
+ const response = webidl.createBranded(Response);
+ response[_response] = inner;
+ response[_headers] = headersFromHeaderList(inner.headerList, guard);
+ return response;
+}
+
+export {
+ abortedNetworkError,
+ fromInnerResponse,
+ networkError,
+ newInnerResponse,
+ nullBodyStatus,
+ redirectStatus,
+ Response,
+ ResponsePrototype,
+ toInnerResponse,
+};
diff --git a/ext/fetch/26_fetch.js b/ext/fetch/26_fetch.js
index ddb023a37..9c136f242 100644
--- a/ext/fetch/26_fetch.js
+++ b/ext/fetch/26_fetch.js
@@ -9,578 +9,577 @@
/// <reference path="./internal.d.ts" />
/// <reference path="./lib.deno_fetch.d.ts" />
/// <reference lib="esnext" />
-"use strict";
-
-((window) => {
- const core = window.Deno.core;
- const ops = core.ops;
- const webidl = window.__bootstrap.webidl;
- const { byteLowerCase } = window.__bootstrap.infra;
- const { BlobPrototype } = window.__bootstrap.file;
- const { errorReadableStream, ReadableStreamPrototype, readableStreamForRid } =
- window.__bootstrap.streams;
- const { InnerBody, extractBody } = window.__bootstrap.fetchBody;
- const {
- toInnerRequest,
- toInnerResponse,
- fromInnerResponse,
- redirectStatus,
- nullBodyStatus,
- networkError,
- abortedNetworkError,
- processUrlList,
- } = window.__bootstrap.fetch;
- const abortSignal = window.__bootstrap.abortSignal;
- const {
- ArrayPrototypePush,
- ArrayPrototypeSplice,
- ArrayPrototypeFilter,
- ArrayPrototypeIncludes,
- ObjectPrototypeIsPrototypeOf,
- Promise,
- PromisePrototypeThen,
- PromisePrototypeCatch,
- SafeArrayIterator,
- String,
- StringPrototypeStartsWith,
- StringPrototypeToLowerCase,
- TypeError,
- Uint8Array,
- Uint8ArrayPrototype,
- WeakMap,
- WeakMapPrototypeDelete,
- WeakMapPrototypeGet,
- WeakMapPrototypeHas,
- WeakMapPrototypeSet,
- } = window.__bootstrap.primordials;
-
- const REQUEST_BODY_HEADER_NAMES = [
- "content-encoding",
- "content-language",
- "content-location",
- "content-type",
- ];
-
- const requestBodyReaders = new WeakMap();
-
- /**
- * @param {{ method: string, url: string, headers: [string, string][], clientRid: number | null, hasBody: boolean }} args
- * @param {Uint8Array | null} body
- * @returns {{ requestRid: number, requestBodyRid: number | null }}
- */
- function opFetch(method, url, headers, clientRid, hasBody, bodyLength, body) {
- return ops.op_fetch(
- method,
- url,
- headers,
- clientRid,
- hasBody,
- bodyLength,
- body,
- );
- }
- /**
- * @param {number} rid
- * @returns {Promise<{ status: number, statusText: string, headers: [string, string][], url: string, responseRid: number }>}
- */
- function opFetchSend(rid) {
- return core.opAsync("op_fetch_send", rid);
+const core = globalThis.Deno.core;
+const ops = core.ops;
+import * as webidl from "internal:ext/webidl/00_webidl.js";
+import { byteLowerCase } from "internal:ext/web/00_infra.js";
+import { BlobPrototype } from "internal:ext/web/09_file.js";
+import {
+ errorReadableStream,
+ readableStreamForRid,
+ ReadableStreamPrototype,
+} from "internal:ext/web/06_streams.js";
+import { extractBody, InnerBody } from "internal:ext/fetch/22_body.js";
+import {
+ processUrlList,
+ toInnerRequest,
+} from "internal:ext/fetch/23_request.js";
+import {
+ abortedNetworkError,
+ fromInnerResponse,
+ networkError,
+ nullBodyStatus,
+ redirectStatus,
+ toInnerResponse,
+} from "internal:ext/fetch/23_response.js";
+import * as abortSignal from "internal:ext/web/03_abort_signal.js";
+const primordials = globalThis.__bootstrap.primordials;
+const {
+ ArrayPrototypePush,
+ ArrayPrototypeSplice,
+ ArrayPrototypeFilter,
+ ArrayPrototypeIncludes,
+ ObjectPrototypeIsPrototypeOf,
+ Promise,
+ PromisePrototypeThen,
+ PromisePrototypeCatch,
+ SafeArrayIterator,
+ String,
+ StringPrototypeStartsWith,
+ StringPrototypeToLowerCase,
+ TypeError,
+ Uint8Array,
+ Uint8ArrayPrototype,
+ WeakMap,
+ WeakMapPrototypeDelete,
+ WeakMapPrototypeGet,
+ WeakMapPrototypeHas,
+ WeakMapPrototypeSet,
+} = primordials;
+
+const REQUEST_BODY_HEADER_NAMES = [
+ "content-encoding",
+ "content-language",
+ "content-location",
+ "content-type",
+];
+
+const requestBodyReaders = new WeakMap();
+
+/**
+ * @param {{ method: string, url: string, headers: [string, string][], clientRid: number | null, hasBody: boolean }} args
+ * @param {Uint8Array | null} body
+ * @returns {{ requestRid: number, requestBodyRid: number | null }}
+ */
+function opFetch(method, url, headers, clientRid, hasBody, bodyLength, body) {
+ return ops.op_fetch(
+ method,
+ url,
+ headers,
+ clientRid,
+ hasBody,
+ bodyLength,
+ body,
+ );
+}
+
+/**
+ * @param {number} rid
+ * @returns {Promise<{ status: number, statusText: string, headers: [string, string][], url: string, responseRid: number }>}
+ */
+function opFetchSend(rid) {
+ return core.opAsync("op_fetch_send", rid);
+}
+
+/**
+ * @param {number} responseBodyRid
+ * @param {AbortSignal} [terminator]
+ * @returns {ReadableStream<Uint8Array>}
+ */
+function createResponseBodyStream(responseBodyRid, terminator) {
+ const readable = readableStreamForRid(responseBodyRid);
+
+ function onAbort() {
+ errorReadableStream(readable, terminator.reason);
+ core.tryClose(responseBodyRid);
}
- /**
- * @param {number} responseBodyRid
- * @param {AbortSignal} [terminator]
- * @returns {ReadableStream<Uint8Array>}
- */
- function createResponseBodyStream(responseBodyRid, terminator) {
- const readable = readableStreamForRid(responseBodyRid);
-
- function onAbort() {
- errorReadableStream(readable, terminator.reason);
- core.tryClose(responseBodyRid);
+ // TODO(lucacasonato): clean up registration
+ terminator[abortSignal.add](onAbort);
+
+ return readable;
+}
+
+/**
+ * @param {InnerRequest} req
+ * @param {boolean} recursive
+ * @param {AbortSignal} terminator
+ * @returns {Promise<InnerResponse>}
+ */
+async function mainFetch(req, recursive, terminator) {
+ if (req.blobUrlEntry !== null) {
+ if (req.method !== "GET") {
+ throw new TypeError("Blob URL fetch only supports GET method.");
}
- // TODO(lucacasonato): clean up registration
- terminator[abortSignal.add](onAbort);
+ const body = new InnerBody(req.blobUrlEntry.stream());
+ terminator[abortSignal.add](() => body.error(terminator.reason));
+ processUrlList(req.urlList, req.urlListProcessed);
- return readable;
+ return {
+ headerList: [
+ ["content-length", String(req.blobUrlEntry.size)],
+ ["content-type", req.blobUrlEntry.type],
+ ],
+ status: 200,
+ statusMessage: "OK",
+ body,
+ type: "basic",
+ url() {
+ if (this.urlList.length == 0) return null;
+ return this.urlList[this.urlList.length - 1];
+ },
+ urlList: recursive
+ ? []
+ : [...new SafeArrayIterator(req.urlListProcessed)],
+ };
}
- /**
- * @param {InnerRequest} req
- * @param {boolean} recursive
- * @param {AbortSignal} terminator
- * @returns {Promise<InnerResponse>}
- */
- async function mainFetch(req, recursive, terminator) {
- if (req.blobUrlEntry !== null) {
- if (req.method !== "GET") {
- throw new TypeError("Blob URL fetch only supports GET method.");
- }
-
- const body = new InnerBody(req.blobUrlEntry.stream());
- terminator[abortSignal.add](() => body.error(terminator.reason));
- processUrlList(req.urlList, req.urlListProcessed);
-
- return {
- headerList: [
- ["content-length", String(req.blobUrlEntry.size)],
- ["content-type", req.blobUrlEntry.type],
- ],
- status: 200,
- statusMessage: "OK",
- body,
- type: "basic",
- url() {
- if (this.urlList.length == 0) return null;
- return this.urlList[this.urlList.length - 1];
- },
- urlList: recursive
- ? []
- : [...new SafeArrayIterator(req.urlListProcessed)],
- };
- }
+ /** @type {ReadableStream<Uint8Array> | Uint8Array | null} */
+ let reqBody = null;
- /** @type {ReadableStream<Uint8Array> | Uint8Array | null} */
- let reqBody = null;
-
- if (req.body !== null) {
+ if (req.body !== null) {
+ if (
+ ObjectPrototypeIsPrototypeOf(
+ ReadableStreamPrototype,
+ req.body.streamOrStatic,
+ )
+ ) {
if (
- ObjectPrototypeIsPrototypeOf(
- ReadableStreamPrototype,
- req.body.streamOrStatic,
- )
+ req.body.length === null ||
+ ObjectPrototypeIsPrototypeOf(BlobPrototype, req.body.source)
) {
- if (
- req.body.length === null ||
- ObjectPrototypeIsPrototypeOf(BlobPrototype, req.body.source)
- ) {
- reqBody = req.body.stream;
+ reqBody = req.body.stream;
+ } else {
+ const reader = req.body.stream.getReader();
+ WeakMapPrototypeSet(requestBodyReaders, req, reader);
+ const r1 = await reader.read();
+ if (r1.done) {
+ reqBody = new Uint8Array(0);
} else {
- const reader = req.body.stream.getReader();
- WeakMapPrototypeSet(requestBodyReaders, req, reader);
- const r1 = await reader.read();
- if (r1.done) {
- reqBody = new Uint8Array(0);
- } else {
- reqBody = r1.value;
- const r2 = await reader.read();
- if (!r2.done) throw new TypeError("Unreachable");
- }
- WeakMapPrototypeDelete(requestBodyReaders, req);
+ reqBody = r1.value;
+ const r2 = await reader.read();
+ if (!r2.done) throw new TypeError("Unreachable");
}
- } else {
- req.body.streamOrStatic.consumed = true;
- reqBody = req.body.streamOrStatic.body;
- // TODO(@AaronO): plumb support for StringOrBuffer all the way
- reqBody = typeof reqBody === "string" ? core.encode(reqBody) : reqBody;
+ WeakMapPrototypeDelete(requestBodyReaders, req);
}
+ } else {
+ req.body.streamOrStatic.consumed = true;
+ reqBody = req.body.streamOrStatic.body;
+ // TODO(@AaronO): plumb support for StringOrBuffer all the way
+ reqBody = typeof reqBody === "string" ? core.encode(reqBody) : reqBody;
}
+ }
- const { requestRid, requestBodyRid, cancelHandleRid } = opFetch(
- req.method,
- req.currentUrl(),
- req.headerList,
- req.clientRid,
- reqBody !== null,
- req.body?.length,
- ObjectPrototypeIsPrototypeOf(Uint8ArrayPrototype, reqBody)
- ? reqBody
- : null,
- );
-
- function onAbort() {
- if (cancelHandleRid !== null) {
- core.tryClose(cancelHandleRid);
- }
- if (requestBodyRid !== null) {
- core.tryClose(requestBodyRid);
- }
+ const { requestRid, requestBodyRid, cancelHandleRid } = opFetch(
+ req.method,
+ req.currentUrl(),
+ req.headerList,
+ req.clientRid,
+ reqBody !== null,
+ req.body?.length,
+ ObjectPrototypeIsPrototypeOf(Uint8ArrayPrototype, reqBody) ? reqBody : null,
+ );
+
+ function onAbort() {
+ if (cancelHandleRid !== null) {
+ core.tryClose(cancelHandleRid);
}
- terminator[abortSignal.add](onAbort);
-
- let requestSendError;
- let requestSendErrorSet = false;
if (requestBodyRid !== null) {
- if (
- reqBody === null ||
- !ObjectPrototypeIsPrototypeOf(ReadableStreamPrototype, reqBody)
- ) {
- throw new TypeError("Unreachable");
+ core.tryClose(requestBodyRid);
+ }
+ }
+ terminator[abortSignal.add](onAbort);
+
+ let requestSendError;
+ let requestSendErrorSet = false;
+ if (requestBodyRid !== null) {
+ if (
+ reqBody === null ||
+ !ObjectPrototypeIsPrototypeOf(ReadableStreamPrototype, reqBody)
+ ) {
+ throw new TypeError("Unreachable");
+ }
+ const reader = reqBody.getReader();
+ WeakMapPrototypeSet(requestBodyReaders, req, reader);
+ (async () => {
+ let done = false;
+ while (!done) {
+ let val;
+ try {
+ const res = await reader.read();
+ done = res.done;
+ val = res.value;
+ } catch (err) {
+ if (terminator.aborted) break;
+ // TODO(lucacasonato): propagate error into response body stream
+ requestSendError = err;
+ requestSendErrorSet = true;
+ break;
+ }
+ if (done) break;
+ if (!ObjectPrototypeIsPrototypeOf(Uint8ArrayPrototype, val)) {
+ const error = new TypeError(
+ "Item in request body ReadableStream is not a Uint8Array",
+ );
+ await reader.cancel(error);
+ // TODO(lucacasonato): propagate error into response body stream
+ requestSendError = error;
+ requestSendErrorSet = true;
+ break;
+ }
+ try {
+ await core.writeAll(requestBodyRid, val);
+ } catch (err) {
+ if (terminator.aborted) break;
+ await reader.cancel(err);
+ // TODO(lucacasonato): propagate error into response body stream
+ requestSendError = err;
+ requestSendErrorSet = true;
+ break;
+ }
}
- const reader = reqBody.getReader();
- WeakMapPrototypeSet(requestBodyReaders, req, reader);
- (async () => {
- let done = false;
- while (!done) {
- let val;
- try {
- const res = await reader.read();
- done = res.done;
- val = res.value;
- } catch (err) {
- if (terminator.aborted) break;
- // TODO(lucacasonato): propagate error into response body stream
+ if (done && !terminator.aborted) {
+ try {
+ await core.shutdown(requestBodyRid);
+ } catch (err) {
+ if (!terminator.aborted) {
requestSendError = err;
requestSendErrorSet = true;
- break;
- }
- if (done) break;
- if (!ObjectPrototypeIsPrototypeOf(Uint8ArrayPrototype, val)) {
- const error = new TypeError(
- "Item in request body ReadableStream is not a Uint8Array",
- );
- await reader.cancel(error);
- // TODO(lucacasonato): propagate error into response body stream
- requestSendError = error;
- requestSendErrorSet = true;
- break;
- }
- try {
- await core.writeAll(requestBodyRid, val);
- } catch (err) {
- if (terminator.aborted) break;
- await reader.cancel(err);
- // TODO(lucacasonato): propagate error into response body stream
- requestSendError = err;
- requestSendErrorSet = true;
- break;
}
}
- if (done && !terminator.aborted) {
- try {
- await core.shutdown(requestBodyRid);
- } catch (err) {
- if (!terminator.aborted) {
- requestSendError = err;
- requestSendErrorSet = true;
- }
- }
- }
- WeakMapPrototypeDelete(requestBodyReaders, req);
- core.tryClose(requestBodyRid);
- })();
- }
- let resp;
- try {
- resp = await opFetchSend(requestRid);
- } catch (err) {
- if (terminator.aborted) return;
- if (requestSendErrorSet) {
- // if the request body stream errored, we want to propagate that error
- // instead of the original error from opFetchSend
- throw new TypeError("Failed to fetch: request body stream errored", {
- cause: requestSendError,
- });
- }
- throw err;
- } finally {
- if (cancelHandleRid !== null) {
- core.tryClose(cancelHandleRid);
}
+ WeakMapPrototypeDelete(requestBodyReaders, req);
+ core.tryClose(requestBodyRid);
+ })();
+ }
+ let resp;
+ try {
+ resp = await opFetchSend(requestRid);
+ } catch (err) {
+ if (terminator.aborted) return;
+ if (requestSendErrorSet) {
+ // if the request body stream errored, we want to propagate that error
+ // instead of the original error from opFetchSend
+ throw new TypeError("Failed to fetch: request body stream errored", {
+ cause: requestSendError,
+ });
}
- if (terminator.aborted) return abortedNetworkError();
-
- processUrlList(req.urlList, req.urlListProcessed);
-
- /** @type {InnerResponse} */
- const response = {
- headerList: resp.headers,
- status: resp.status,
- body: null,
- statusMessage: resp.statusText,
- type: "basic",
- url() {
- if (this.urlList.length == 0) return null;
- return this.urlList[this.urlList.length - 1];
- },
- urlList: req.urlListProcessed,
- };
- if (redirectStatus(resp.status)) {
- switch (req.redirectMode) {
- case "error":
- core.close(resp.responseRid);
- return networkError(
- "Encountered redirect while redirect mode is set to 'error'",
- );
- case "follow":
- core.close(resp.responseRid);
- return httpRedirectFetch(req, response, terminator);
- case "manual":
- break;
- }
+ throw err;
+ } finally {
+ if (cancelHandleRid !== null) {
+ core.tryClose(cancelHandleRid);
+ }
+ }
+ if (terminator.aborted) return abortedNetworkError();
+
+ processUrlList(req.urlList, req.urlListProcessed);
+
+ /** @type {InnerResponse} */
+ const response = {
+ headerList: resp.headers,
+ status: resp.status,
+ body: null,
+ statusMessage: resp.statusText,
+ type: "basic",
+ url() {
+ if (this.urlList.length == 0) return null;
+ return this.urlList[this.urlList.length - 1];
+ },
+ urlList: req.urlListProcessed,
+ };
+ if (redirectStatus(resp.status)) {
+ switch (req.redirectMode) {
+ case "error":
+ core.close(resp.responseRid);
+ return networkError(
+ "Encountered redirect while redirect mode is set to 'error'",
+ );
+ case "follow":
+ core.close(resp.responseRid);
+ return httpRedirectFetch(req, response, terminator);
+ case "manual":
+ break;
}
+ }
- if (nullBodyStatus(response.status)) {
+ if (nullBodyStatus(response.status)) {
+ core.close(resp.responseRid);
+ } else {
+ if (req.method === "HEAD" || req.method === "CONNECT") {
+ response.body = null;
core.close(resp.responseRid);
} else {
- if (req.method === "HEAD" || req.method === "CONNECT") {
- response.body = null;
- core.close(resp.responseRid);
- } else {
- response.body = new InnerBody(
- createResponseBodyStream(resp.responseRid, terminator),
- );
- }
+ response.body = new InnerBody(
+ createResponseBodyStream(resp.responseRid, terminator),
+ );
}
+ }
- if (recursive) return response;
+ if (recursive) return response;
- if (response.urlList.length === 0) {
- processUrlList(req.urlList, req.urlListProcessed);
- response.urlList = [...new SafeArrayIterator(req.urlListProcessed)];
- }
+ if (response.urlList.length === 0) {
+ processUrlList(req.urlList, req.urlListProcessed);
+ response.urlList = [...new SafeArrayIterator(req.urlListProcessed)];
+ }
+ return response;
+}
+
+/**
+ * @param {InnerRequest} request
+ * @param {InnerResponse} response
+ * @param {AbortSignal} terminator
+ * @returns {Promise<InnerResponse>}
+ */
+function httpRedirectFetch(request, response, terminator) {
+ const locationHeaders = ArrayPrototypeFilter(
+ response.headerList,
+ (entry) => byteLowerCase(entry[0]) === "location",
+ );
+ if (locationHeaders.length === 0) {
return response;
}
-
- /**
- * @param {InnerRequest} request
- * @param {InnerResponse} response
- * @param {AbortSignal} terminator
- * @returns {Promise<InnerResponse>}
- */
- function httpRedirectFetch(request, response, terminator) {
- const locationHeaders = ArrayPrototypeFilter(
- response.headerList,
- (entry) => byteLowerCase(entry[0]) === "location",
- );
- if (locationHeaders.length === 0) {
- return response;
- }
- const locationURL = new URL(
- locationHeaders[0][1],
- response.url() ?? undefined,
+ const locationURL = new URL(
+ locationHeaders[0][1],
+ response.url() ?? undefined,
+ );
+ if (locationURL.hash === "") {
+ locationURL.hash = request.currentUrl().hash;
+ }
+ if (locationURL.protocol !== "https:" && locationURL.protocol !== "http:") {
+ return networkError("Can not redirect to a non HTTP(s) url");
+ }
+ if (request.redirectCount === 20) {
+ return networkError("Maximum number of redirects (20) reached");
+ }
+ request.redirectCount++;
+ if (
+ response.status !== 303 &&
+ request.body !== null &&
+ request.body.source === null
+ ) {
+ return networkError(
+ "Can not redeliver a streaming request body after a redirect",
);
- if (locationURL.hash === "") {
- locationURL.hash = request.currentUrl().hash;
- }
- if (locationURL.protocol !== "https:" && locationURL.protocol !== "http:") {
- return networkError("Can not redirect to a non HTTP(s) url");
- }
- if (request.redirectCount === 20) {
- return networkError("Maximum number of redirects (20) reached");
- }
- request.redirectCount++;
- if (
- response.status !== 303 &&
- request.body !== null &&
- request.body.source === null
- ) {
- return networkError(
- "Can not redeliver a streaming request body after a redirect",
- );
- }
- if (
- ((response.status === 301 || response.status === 302) &&
- request.method === "POST") ||
- (response.status === 303 &&
- request.method !== "GET" &&
- request.method !== "HEAD")
- ) {
- request.method = "GET";
- request.body = null;
- for (let i = 0; i < request.headerList.length; i++) {
- if (
- ArrayPrototypeIncludes(
- REQUEST_BODY_HEADER_NAMES,
- byteLowerCase(request.headerList[i][0]),
- )
- ) {
- ArrayPrototypeSplice(request.headerList, i, 1);
- i--;
- }
+ }
+ if (
+ ((response.status === 301 || response.status === 302) &&
+ request.method === "POST") ||
+ (response.status === 303 &&
+ request.method !== "GET" &&
+ request.method !== "HEAD")
+ ) {
+ request.method = "GET";
+ request.body = null;
+ for (let i = 0; i < request.headerList.length; i++) {
+ if (
+ ArrayPrototypeIncludes(
+ REQUEST_BODY_HEADER_NAMES,
+ byteLowerCase(request.headerList[i][0]),
+ )
+ ) {
+ ArrayPrototypeSplice(request.headerList, i, 1);
+ i--;
}
}
- if (request.body !== null) {
- const res = extractBody(request.body.source);
- request.body = res.body;
- }
- ArrayPrototypePush(request.urlList, () => locationURL.href);
- return mainFetch(request, true, terminator);
}
+ if (request.body !== null) {
+ const res = extractBody(request.body.source);
+ request.body = res.body;
+ }
+ ArrayPrototypePush(request.urlList, () => locationURL.href);
+ return mainFetch(request, true, terminator);
+}
+
+/**
+ * @param {RequestInfo} input
+ * @param {RequestInit} init
+ */
+function fetch(input, init = {}) {
+ // There is an async dispatch later that causes a stack trace disconnect.
+ // We reconnect it by assigning the result of that dispatch to `opPromise`,
+ // awaiting `opPromise` in an inner function also named `fetch()` and
+ // returning the result from that.
+ let opPromise = undefined;
+ // 1.
+ const result = new Promise((resolve, reject) => {
+ const prefix = "Failed to call 'fetch'";
+ webidl.requiredArguments(arguments.length, 1, { prefix });
+ // 2.
+ const requestObject = new Request(input, init);
+ // 3.
+ const request = toInnerRequest(requestObject);
+ // 4.
+ if (requestObject.signal.aborted) {
+ reject(abortFetch(request, null, requestObject.signal.reason));
+ return;
+ }
- /**
- * @param {RequestInfo} input
- * @param {RequestInit} init
- */
- function fetch(input, init = {}) {
- // There is an async dispatch later that causes a stack trace disconnect.
- // We reconnect it by assigning the result of that dispatch to `opPromise`,
- // awaiting `opPromise` in an inner function also named `fetch()` and
- // returning the result from that.
- let opPromise = undefined;
- // 1.
- const result = new Promise((resolve, reject) => {
- const prefix = "Failed to call 'fetch'";
- webidl.requiredArguments(arguments.length, 1, { prefix });
- // 2.
- const requestObject = new Request(input, init);
- // 3.
- const request = toInnerRequest(requestObject);
- // 4.
- if (requestObject.signal.aborted) {
- reject(abortFetch(request, null, requestObject.signal.reason));
- return;
- }
-
- // 7.
- let responseObject = null;
- // 9.
- let locallyAborted = false;
- // 10.
- function onabort() {
- locallyAborted = true;
- reject(
- abortFetch(request, responseObject, requestObject.signal.reason),
- );
- }
- requestObject.signal[abortSignal.add](onabort);
+ // 7.
+ let responseObject = null;
+ // 9.
+ let locallyAborted = false;
+ // 10.
+ function onabort() {
+ locallyAborted = true;
+ reject(
+ abortFetch(request, responseObject, requestObject.signal.reason),
+ );
+ }
+ requestObject.signal[abortSignal.add](onabort);
- if (!requestObject.headers.has("Accept")) {
- ArrayPrototypePush(request.headerList, ["Accept", "*/*"]);
- }
+ if (!requestObject.headers.has("Accept")) {
+ ArrayPrototypePush(request.headerList, ["Accept", "*/*"]);
+ }
- if (!requestObject.headers.has("Accept-Language")) {
- ArrayPrototypePush(request.headerList, ["Accept-Language", "*"]);
- }
+ if (!requestObject.headers.has("Accept-Language")) {
+ ArrayPrototypePush(request.headerList, ["Accept-Language", "*"]);
+ }
- // 12.
- opPromise = PromisePrototypeCatch(
- PromisePrototypeThen(
- mainFetch(request, false, requestObject.signal),
- (response) => {
- // 12.1.
- if (locallyAborted) return;
- // 12.2.
- if (response.aborted) {
- reject(
- abortFetch(
- request,
- responseObject,
- requestObject.signal.reason,
- ),
- );
- requestObject.signal[abortSignal.remove](onabort);
- return;
- }
- // 12.3.
- if (response.type === "error") {
- const err = new TypeError(
- "Fetch failed: " + (response.error ?? "unknown error"),
- );
- reject(err);
- requestObject.signal[abortSignal.remove](onabort);
- return;
- }
- responseObject = fromInnerResponse(response, "immutable");
- resolve(responseObject);
+ // 12.
+ opPromise = PromisePrototypeCatch(
+ PromisePrototypeThen(
+ mainFetch(request, false, requestObject.signal),
+ (response) => {
+ // 12.1.
+ if (locallyAborted) return;
+ // 12.2.
+ if (response.aborted) {
+ reject(
+ abortFetch(
+ request,
+ responseObject,
+ requestObject.signal.reason,
+ ),
+ );
requestObject.signal[abortSignal.remove](onabort);
- },
- ),
- (err) => {
- reject(err);
+ return;
+ }
+ // 12.3.
+ if (response.type === "error") {
+ const err = new TypeError(
+ "Fetch failed: " + (response.error ?? "unknown error"),
+ );
+ reject(err);
+ requestObject.signal[abortSignal.remove](onabort);
+ return;
+ }
+ responseObject = fromInnerResponse(response, "immutable");
+ resolve(responseObject);
requestObject.signal[abortSignal.remove](onabort);
},
- );
- });
- if (opPromise) {
- PromisePrototypeCatch(result, () => {});
- return (async function fetch() {
- await opPromise;
- return result;
- })();
- }
- return result;
+ ),
+ (err) => {
+ reject(err);
+ requestObject.signal[abortSignal.remove](onabort);
+ },
+ );
+ });
+ if (opPromise) {
+ PromisePrototypeCatch(result, () => {});
+ return (async function fetch() {
+ await opPromise;
+ return result;
+ })();
}
+ return result;
+}
- function abortFetch(request, responseObject, error) {
- if (request.body !== null) {
- if (WeakMapPrototypeHas(requestBodyReaders, request)) {
- WeakMapPrototypeGet(requestBodyReaders, request).cancel(error);
- } else {
- request.body.cancel(error);
- }
- }
- if (responseObject !== null) {
- const response = toInnerResponse(responseObject);
- if (response.body !== null) response.body.error(error);
+function abortFetch(request, responseObject, error) {
+ if (request.body !== null) {
+ if (WeakMapPrototypeHas(requestBodyReaders, request)) {
+ WeakMapPrototypeGet(requestBodyReaders, request).cancel(error);
+ } else {
+ request.body.cancel(error);
}
- return error;
}
+ if (responseObject !== null) {
+ const response = toInnerResponse(responseObject);
+ if (response.body !== null) response.body.error(error);
+ }
+ return error;
+}
+
+/**
+ * Handle the Response argument to the WebAssembly streaming APIs, after
+ * resolving if it was passed as a promise. This function should be registered
+ * through `Deno.core.setWasmStreamingCallback`.
+ *
+ * @param {any} source The source parameter that the WebAssembly streaming API
+ * was called with. If it was called with a Promise, `source` is the resolved
+ * value of that promise.
+ * @param {number} rid An rid that represents the wasm streaming resource.
+ */
+function handleWasmStreaming(source, rid) {
+ // This implements part of
+ // https://webassembly.github.io/spec/web-api/#compile-a-potential-webassembly-response
+ try {
+ const res = webidl.converters["Response"](source, {
+ prefix: "Failed to call 'WebAssembly.compileStreaming'",
+ context: "Argument 1",
+ });
- /**
- * Handle the Response argument to the WebAssembly streaming APIs, after
- * resolving if it was passed as a promise. This function should be registered
- * through `Deno.core.setWasmStreamingCallback`.
- *
- * @param {any} source The source parameter that the WebAssembly streaming API
- * was called with. If it was called with a Promise, `source` is the resolved
- * value of that promise.
- * @param {number} rid An rid that represents the wasm streaming resource.
- */
- function handleWasmStreaming(source, rid) {
- // This implements part of
- // https://webassembly.github.io/spec/web-api/#compile-a-potential-webassembly-response
- try {
- const res = webidl.converters["Response"](source, {
- prefix: "Failed to call 'WebAssembly.compileStreaming'",
- context: "Argument 1",
- });
-
- // 2.3.
- // The spec is ambiguous here, see
- // https://github.com/WebAssembly/spec/issues/1138. The WPT tests expect
- // the raw value of the Content-Type attribute lowercased. We ignore this
- // for file:// because file fetches don't have a Content-Type.
- if (!StringPrototypeStartsWith(res.url, "file://")) {
- const contentType = res.headers.get("Content-Type");
- if (
- typeof contentType !== "string" ||
- StringPrototypeToLowerCase(contentType) !== "application/wasm"
- ) {
- throw new TypeError("Invalid WebAssembly content type.");
- }
+ // 2.3.
+ // The spec is ambiguous here, see
+ // https://github.com/WebAssembly/spec/issues/1138. The WPT tests expect
+ // the raw value of the Content-Type attribute lowercased. We ignore this
+ // for file:// because file fetches don't have a Content-Type.
+ if (!StringPrototypeStartsWith(res.url, "file://")) {
+ const contentType = res.headers.get("Content-Type");
+ if (
+ typeof contentType !== "string" ||
+ StringPrototypeToLowerCase(contentType) !== "application/wasm"
+ ) {
+ throw new TypeError("Invalid WebAssembly content type.");
}
+ }
- // 2.5.
- if (!res.ok) {
- throw new TypeError(`HTTP status code ${res.status}`);
- }
+ // 2.5.
+ if (!res.ok) {
+ throw new TypeError(`HTTP status code ${res.status}`);
+ }
- // Pass the resolved URL to v8.
- ops.op_wasm_streaming_set_url(rid, res.url);
-
- if (res.body !== null) {
- // 2.6.
- // Rather than consuming the body as an ArrayBuffer, this passes each
- // chunk to the feed as soon as it's available.
- PromisePrototypeThen(
- (async () => {
- const reader = res.body.getReader();
- while (true) {
- const { value: chunk, done } = await reader.read();
- if (done) break;
- ops.op_wasm_streaming_feed(rid, chunk);
- }
- })(),
- // 2.7
- () => core.close(rid),
- // 2.8
- (err) => core.abortWasmStreaming(rid, err),
- );
- } else {
+ // Pass the resolved URL to v8.
+ ops.op_wasm_streaming_set_url(rid, res.url);
+
+ if (res.body !== null) {
+ // 2.6.
+ // Rather than consuming the body as an ArrayBuffer, this passes each
+ // chunk to the feed as soon as it's available.
+ PromisePrototypeThen(
+ (async () => {
+ const reader = res.body.getReader();
+ while (true) {
+ const { value: chunk, done } = await reader.read();
+ if (done) break;
+ ops.op_wasm_streaming_feed(rid, chunk);
+ }
+ })(),
// 2.7
- core.close(rid);
- }
- } catch (err) {
- // 2.8
- core.abortWasmStreaming(rid, err);
+ () => core.close(rid),
+ // 2.8
+ (err) => core.abortWasmStreaming(rid, err),
+ );
+ } else {
+ // 2.7
+ core.close(rid);
}
+ } catch (err) {
+ // 2.8
+ core.abortWasmStreaming(rid, err);
}
+}
- window.__bootstrap.fetch ??= {};
- window.__bootstrap.fetch.fetch = fetch;
- window.__bootstrap.fetch.handleWasmStreaming = handleWasmStreaming;
-})(this);
+export { fetch, handleWasmStreaming };
diff --git a/ext/fetch/internal.d.ts b/ext/fetch/internal.d.ts
index 13a91d2d0..596e3ffcb 100644
--- a/ext/fetch/internal.d.ts
+++ b/ext/fetch/internal.d.ts
@@ -5,106 +5,98 @@
/// <reference no-default-lib="true" />
/// <reference lib="esnext" />
-declare namespace globalThis {
- declare namespace __bootstrap {
- declare var fetchUtil: {
- requiredArguments(name: string, length: number, required: number): void;
- };
+declare var domIterable: {
+ DomIterableMixin(base: any, dataSymbol: symbol): any;
+};
- declare var domIterable: {
- DomIterableMixin(base: any, dataSymbol: symbol): any;
- };
-
- declare namespace headers {
- class Headers {
- }
- type HeaderList = [string, string][];
- function headersFromHeaderList(
- list: HeaderList,
- guard:
- | "immutable"
- | "request"
- | "request-no-cors"
- | "response"
- | "none",
- ): Headers;
- function headerListFromHeaders(headers: Headers): HeaderList;
- function fillHeaders(headers: Headers, object: HeadersInit): void;
- function getDecodeSplitHeader(
- list: HeaderList,
- name: string,
- ): string[] | null;
- function guardFromHeaders(
- headers: Headers,
- ): "immutable" | "request" | "request-no-cors" | "response" | "none";
- }
-
- declare namespace formData {
- declare type FormData = typeof FormData;
- declare function formDataToBlob(
- formData: globalThis.FormData,
- ): Blob;
- declare function parseFormData(
- body: Uint8Array,
- boundary: string | undefined,
- ): FormData;
- declare function formDataFromEntries(entries: FormDataEntry[]): FormData;
- }
+declare module "internal:ext/fetch/20_headers.js" {
+ class Headers {
+ }
+ type HeaderList = [string, string][];
+ function headersFromHeaderList(
+ list: HeaderList,
+ guard:
+ | "immutable"
+ | "request"
+ | "request-no-cors"
+ | "response"
+ | "none",
+ ): Headers;
+ function headerListFromHeaders(headers: Headers): HeaderList;
+ function fillHeaders(headers: Headers, object: HeadersInit): void;
+ function getDecodeSplitHeader(
+ list: HeaderList,
+ name: string,
+ ): string[] | null;
+ function guardFromHeaders(
+ headers: Headers,
+ ): "immutable" | "request" | "request-no-cors" | "response" | "none";
+}
- declare namespace fetchBody {
- function mixinBody(
- prototype: any,
- bodySymbol: symbol,
- mimeTypeSymbol: symbol,
- ): void;
- class InnerBody {
- constructor(stream?: ReadableStream<Uint8Array>);
- stream: ReadableStream<Uint8Array>;
- source: null | Uint8Array | Blob | FormData;
- length: null | number;
- unusable(): boolean;
- consume(): Promise<Uint8Array>;
- clone(): InnerBody;
- }
- function extractBody(object: BodyInit): {
- body: InnerBody;
- contentType: string | null;
- };
- }
+declare module "internal:ext/fetch/21_formdata.js" {
+ type FormData = typeof FormData;
+ function formDataToBlob(
+ formData: FormData,
+ ): Blob;
+ function parseFormData(
+ body: Uint8Array,
+ boundary: string | undefined,
+ ): FormData;
+ function formDataFromEntries(entries: FormDataEntry[]): FormData;
+}
- declare namespace fetch {
- function toInnerRequest(request: Request): InnerRequest;
- function fromInnerRequest(
- inner: InnerRequest,
- signal: AbortSignal | null,
- guard:
- | "request"
- | "immutable"
- | "request-no-cors"
- | "response"
- | "none",
- skipBody: boolean,
- flash: boolean,
- ): Request;
- function redirectStatus(status: number): boolean;
- function nullBodyStatus(status: number): boolean;
- function newInnerRequest(
- method: string,
- url: any,
- headerList?: [string, string][],
- body?: globalThis.__bootstrap.fetchBody.InnerBody,
- ): InnerResponse;
- function toInnerResponse(response: Response): InnerResponse;
- function fromInnerResponse(
- inner: InnerResponse,
- guard:
- | "request"
- | "immutable"
- | "request-no-cors"
- | "response"
- | "none",
- ): Response;
- function networkError(error: string): InnerResponse;
- }
+declare module "internal:ext/fetch/22_body.js" {
+ function mixinBody(
+ prototype: any,
+ bodySymbol: symbol,
+ mimeTypeSymbol: symbol,
+ ): void;
+ class InnerBody {
+ constructor(stream?: ReadableStream<Uint8Array>);
+ stream: ReadableStream<Uint8Array>;
+ source: null | Uint8Array | Blob | FormData;
+ length: null | number;
+ unusable(): boolean;
+ consume(): Promise<Uint8Array>;
+ clone(): InnerBody;
}
+ function extractBody(object: BodyInit): {
+ body: InnerBody;
+ contentType: string | null;
+ };
+}
+
+declare module "internal:ext/fetch/26_fetch.js" {
+ function toInnerRequest(request: Request): InnerRequest;
+ function fromInnerRequest(
+ inner: InnerRequest,
+ signal: AbortSignal | null,
+ guard:
+ | "request"
+ | "immutable"
+ | "request-no-cors"
+ | "response"
+ | "none",
+ skipBody: boolean,
+ flash: boolean,
+ ): Request;
+ function redirectStatus(status: number): boolean;
+ function nullBodyStatus(status: number): boolean;
+ function newInnerRequest(
+ method: string,
+ url: any,
+ headerList?: [string, string][],
+ body?: fetchBody.InnerBody,
+ ): InnerResponse;
+ function toInnerResponse(response: Response): InnerResponse;
+ function fromInnerResponse(
+ inner: InnerResponse,
+ guard:
+ | "request"
+ | "immutable"
+ | "request-no-cors"
+ | "response"
+ | "none",
+ ): Response;
+ function networkError(error: string): InnerResponse;
}
diff --git a/ext/fetch/lib.rs b/ext/fetch/lib.rs
index 78a42cd84..93c624dd6 100644
--- a/ext/fetch/lib.rs
+++ b/ext/fetch/lib.rs
@@ -97,9 +97,8 @@ where
{
Extension::builder(env!("CARGO_PKG_NAME"))
.dependencies(vec!["deno_webidl", "deno_web", "deno_url", "deno_console"])
- .js(include_js_files!(
+ .esm(include_js_files!(
prefix "internal:ext/fetch",
- "01_fetch_util.js",
"20_headers.js",
"21_formdata.js",
"22_body.js",