summaryrefslogtreecommitdiff
path: root/extensions/fetch
diff options
context:
space:
mode:
Diffstat (limited to 'extensions/fetch')
-rw-r--r--extensions/fetch/01_fetch_util.js22
-rw-r--r--extensions/fetch/20_headers.js479
-rw-r--r--extensions/fetch/21_formdata.js507
-rw-r--r--extensions/fetch/22_body.js403
-rw-r--r--extensions/fetch/22_http_client.js40
-rw-r--r--extensions/fetch/23_request.js484
-rw-r--r--extensions/fetch/23_response.js451
-rw-r--r--extensions/fetch/26_fetch.js542
-rw-r--r--extensions/fetch/Cargo.toml28
-rw-r--r--extensions/fetch/README.md5
-rw-r--r--extensions/fetch/internal.d.ts108
-rw-r--r--extensions/fetch/lib.deno_fetch.d.ts437
-rw-r--r--extensions/fetch/lib.rs567
13 files changed, 0 insertions, 4073 deletions
diff --git a/extensions/fetch/01_fetch_util.js b/extensions/fetch/01_fetch_util.js
deleted file mode 100644
index 9cf19588b..000000000
--- a/extensions/fetch/01_fetch_util.js
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2018-2021 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/extensions/fetch/20_headers.js b/extensions/fetch/20_headers.js
deleted file mode 100644
index 91154d958..000000000
--- a/extensions/fetch/20_headers.js
+++ /dev/null
@@ -1,479 +0,0 @@
-// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
-
-// @ts-check
-/// <reference path="../webidl/internal.d.ts" />
-/// <reference path="../web/internal.d.ts" />
-/// <reference path="../web/lib.deno_web.d.ts" />
-/// <reference path="./internal.d.ts" />
-/// <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_WHITESPACE_PREFIX_RE,
- HTTP_WHITESPACE_SUFFIX_RE,
- HTTP_TOKEN_CODE_POINT_RE,
- byteLowerCase,
- collectSequenceOfCodepoints,
- collectHttpQuotedString,
- } = window.__bootstrap.infra;
- const {
- ArrayIsArray,
- ArrayPrototypeMap,
- ArrayPrototypePush,
- ArrayPrototypeSort,
- ArrayPrototypeJoin,
- ArrayPrototypeSplice,
- ArrayPrototypeFilter,
- ObjectKeys,
- ObjectEntries,
- RegExpPrototypeTest,
- Symbol,
- SymbolFor,
- SymbolIterator,
- SymbolToStringTag,
- StringPrototypeReplaceAll,
- StringPrototypeIncludes,
- 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[]}
- */
-
- /**
- * @param {string} potentialValue
- * @returns {string}
- */
- function normalizeHeaderValue(potentialValue) {
- potentialValue = StringPrototypeReplaceAll(
- potentialValue,
- HTTP_WHITESPACE_PREFIX_RE,
- "",
- );
- potentialValue = StringPrototypeReplaceAll(
- potentialValue,
- HTTP_WHITESPACE_SUFFIX_RE,
- "",
- );
- return potentialValue;
- }
-
- /**
- * @param {Headers} headers
- * @param {HeadersInit} object
- */
- function fillHeaders(headers, object) {
- if (ArrayIsArray(object)) {
- for (const header of object) {
- 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 of ObjectKeys(object)) {
- appendHeader(headers, key, object[key]);
- }
- }
- }
-
- /**
- * 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 (
- StringPrototypeIncludes(value, "\x00") ||
- StringPrototypeIncludes(value, "\x0A") ||
- StringPrototypeIncludes(value, "\x0D")
- ) {
- throw new TypeError("Header value is not valid.");
- }
-
- // 3.
- if (headers[_guard] == "immutable") {
- throw new TypeError("Headers are immutable.");
- }
-
- // 7.
- const list = headers[_headerList];
- name = byteLowerCase(name);
- 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) => 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;
- }
- } 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, "");
-
- 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 (const entry of list) {
- const name = 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 concatentated,
- // 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;
- }
- }
-
- return ArrayPrototypeSort(
- [...ObjectEntries(headers), ...cookies],
- (a, b) => {
- const akey = a[0];
- const bkey = b[0];
- if (akey > bkey) return 1;
- if (akey < bkey) return -1;
- return 0;
- },
- );
- }
-
- /** @param {HeadersInit} [init] */
- constructor(init = undefined) {
- const prefix = "Failed to construct 'Event'";
- 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);
- }
- }
-
- /**
- * @param {string} name
- * @param {string} value
- */
- append(name, value) {
- webidl.assertBranded(this, Headers);
- 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",
- });
-
- 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];
- name = byteLowerCase(name);
- for (let i = 0; i < list.length; i++) {
- if (list[i][0] === name) {
- ArrayPrototypeSplice(list, i, 1);
- i--;
- }
- }
- }
-
- /**
- * @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);
- }
-
- /**
- * @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];
- name = byteLowerCase(name);
- for (let i = 0; i < list.length; i++) {
- if (list[i][0] === name) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * @param {string} name
- * @param {string} value
- */
- set(name, value) {
- webidl.assertBranded(this, Headers);
- 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 (
- StringPrototypeIncludes(value, "\x00") ||
- StringPrototypeIncludes(value, "\x0A") ||
- StringPrototypeIncludes(value, "\x0D")
- ) {
- throw new TypeError("Header value is not valid.");
- }
-
- if (this[_guard] == "immutable") {
- throw new TypeError("Headers are immutable.");
- }
-
- const list = this[_headerList];
- name = byteLowerCase(name);
- let added = false;
- for (let i = 0; i < list.length; i++) {
- if (list[i][0] === name) {
- if (!added) {
- list[i][1] = value;
- added = true;
- } else {
- ArrayPrototypeSplice(list, i, 1);
- i--;
- }
- }
- }
- if (!added) {
- ArrayPrototypePush(list, [name, value]);
- }
- }
-
- [SymbolFor("Deno.privateCustomInspect")](inspect) {
- const headers = {};
- for (const header of this) {
- headers[header[0]] = header[1];
- }
- return `Headers ${inspect(headers)}`;
- }
-
- get [SymbolToStringTag]() {
- return "Headers";
- }
- }
-
- webidl.mixinPairIterable("Headers", Headers, _iterableHeaders, 0, 1);
-
- webidl.configurePrototype(Headers);
-
- 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);
- }
- 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,
- );
-
- /**
- * @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}
- * @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 = {
- Headers,
- headersFromHeaderList,
- headerListFromHeaders,
- fillHeaders,
- getDecodeSplitHeader,
- guardFromHeaders,
- };
-})(this);
diff --git a/extensions/fetch/21_formdata.js b/extensions/fetch/21_formdata.js
deleted file mode 100644
index 25ed32c2d..000000000
--- a/extensions/fetch/21_formdata.js
+++ /dev/null
@@ -1,507 +0,0 @@
-// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
-
-// @ts-check
-/// <reference path="../webidl/internal.d.ts" />
-/// <reference path="../web/internal.d.ts" />
-/// <reference path="../web/lib.deno_web.d.ts" />
-/// <reference path="./internal.d.ts" />
-/// <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, File } = globalThis.__bootstrap.file;
- const {
- ArrayPrototypeMap,
- ArrayPrototypePush,
- ArrayPrototypeSlice,
- ArrayPrototypeSplice,
- ArrayPrototypeFilter,
- ArrayPrototypeForEach,
- Map,
- MapPrototypeGet,
- MapPrototypeSet,
- MathRandom,
- Symbol,
- SymbolToStringTag,
- 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 (value instanceof Blob && !(value instanceof File)) {
- value = new File([value], "blob", { type: value.type });
- }
- if (value instanceof File && 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 {
- get [SymbolToStringTag]() {
- return "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, FormData);
- const prefix = "Failed to execute 'append' on 'FormData'";
- webidl.requiredArguments(arguments.length, 2, { prefix });
-
- name = webidl.converters["USVString"](name, {
- prefix,
- context: "Argument 1",
- });
- if (valueOrBlobValue instanceof Blob) {
- 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, {
- prefix,
- context: "Argument 2",
- });
- }
-
- const entry = createEntry(name, valueOrBlobValue, filename);
-
- ArrayPrototypePush(this[entryList], entry);
- }
-
- /**
- * @param {string} name
- * @returns {void}
- */
- delete(name) {
- webidl.assertBranded(this, FormData);
- 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--;
- }
- }
- }
-
- /**
- * @param {string} name
- * @returns {FormDataEntryValue | null}
- */
- get(name) {
- webidl.assertBranded(this, FormData);
- const prefix = "Failed to execute 'get' on 'FormData'";
- webidl.requiredArguments(arguments.length, 1, { prefix });
-
- name = webidl.converters["USVString"](name, {
- prefix,
- context: "Argument 1",
- });
-
- for (const entry of this[entryList]) {
- if (entry.name === name) return entry.value;
- }
- return null;
- }
-
- /**
- * @param {string} name
- * @returns {FormDataEntryValue[]}
- */
- getAll(name) {
- webidl.assertBranded(this, FormData);
- const prefix = "Failed to execute 'getAll' on 'FormData'";
- webidl.requiredArguments(arguments.length, 1, { prefix });
-
- name = webidl.converters["USVString"](name, {
- prefix,
- context: "Argument 1",
- });
-
- const returnList = [];
- for (const entry of this[entryList]) {
- if (entry.name === name) ArrayPrototypePush(returnList, entry.value);
- }
- return returnList;
- }
-
- /**
- * @param {string} name
- * @returns {boolean}
- */
- has(name) {
- webidl.assertBranded(this, FormData);
- const prefix = "Failed to execute 'has' on 'FormData'";
- webidl.requiredArguments(arguments.length, 1, { prefix });
-
- name = webidl.converters["USVString"](name, {
- prefix,
- context: "Argument 1",
- });
-
- for (const entry of this[entryList]) {
- 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, FormData);
- const prefix = "Failed to execute 'set' on 'FormData'";
- webidl.requiredArguments(arguments.length, 2, { prefix });
-
- name = webidl.converters["USVString"](name, {
- prefix,
- context: "Argument 1",
- });
- if (valueOrBlobValue instanceof Blob) {
- 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, {
- prefix,
- context: "Argument 2",
- });
- }
-
- 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--;
- }
- }
- }
- if (!added) {
- ArrayPrototypePush(list, entry);
- }
- }
- }
-
- webidl.mixinPairIterable("FormData", FormData, entryList, "name", "value");
-
- webidl.configurePrototype(FormData);
-
- const escape = (str, isFilename) =>
- StringPrototypeReplace(
- StringPrototypeReplace(
- StringPrototypeReplace(
- (isFilename ? str : StringPrototypeReplace(str, /\r?\n|\r/g, "\r\n")),
- /\n/g,
- "%0A",
- ),
- /\r/g,
- "%0D",
- ),
- /"/g,
- "%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="`;
-
- for (const [name, 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,
- });
- }
-
- /**
- * @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
- ArrayPrototypeForEach(
- ArrayPrototypeMap(
- ArrayPrototypeFilter(
- ArrayPrototypeMap(
- ArrayPrototypeSlice(StringPrototypeSplit(value, ";"), 1),
- (s) => StringPrototypeSplit(StringPrototypeTrim(s), "="),
- ),
- (arr) => arr.length > 1,
- ),
- ([k, v]) => [k, StringPrototypeReplace(v, /^"([^"]*)"$/, "$1")],
- ),
- ([k, v]) => MapPrototypeSet(params, k, v),
- );
-
- return params;
- }
-
- 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");
- }
-
- this.boundary = `--${boundary}`;
- this.body = body;
- this.boundaryChars = core.encode(this.boundary);
- }
-
- /**
- * @param {string} headersText
- * @returns {{ headers: Headers, disposition: Map<string, string> }}
- */
- #parseHeaders(headersText) {
- const headers = new Headers();
- const rawHeaders = StringPrototypeSplit(headersText, "\r\n");
- for (const rawHeader of rawHeaders) {
- 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") ?? "",
- );
-
- return { headers, disposition };
- }
-
- /**
- * @returns {FormData}
- */
- parse() {
- // Body must be at least 2 boundaries + \r\n + -- on the last boundary.
- if (this.body.length < (this.boundary.length * 2) + 4) {
- throw new TypeError("Form data too short to be valid.");
- }
-
- 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;
-
- 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) {
- 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 (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;
- }
- }
-
- 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;
- }
-
- webidl.converters["FormData"] = webidl
- .createInterfaceConverter("FormData", FormData);
-
- globalThis.__bootstrap.formData = {
- FormData,
- formDataToBlob,
- parseFormData,
- formDataFromEntries,
- };
-})(globalThis);
diff --git a/extensions/fetch/22_body.js b/extensions/fetch/22_body.js
deleted file mode 100644
index 49da149c2..000000000
--- a/extensions/fetch/22_body.js
+++ /dev/null
@@ -1,403 +0,0 @@
-// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
-
-// @ts-check
-/// <reference path="../webidl/internal.d.ts" />
-/// <reference path="../url/internal.d.ts" />
-/// <reference path="../url/lib.deno_url.d.ts" />
-/// <reference path="../web/internal.d.ts" />
-/// <reference path="../web/lib.deno_web.d.ts" />
-/// <reference path="./internal.d.ts" />
-/// <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 { parseFormData, formDataFromEntries, formDataToBlob } =
- globalThis.__bootstrap.formData;
- const mimesniff = globalThis.__bootstrap.mimesniff;
- const { isReadableStreamDisturbed, errorReadableStream, createProxy } =
- globalThis.__bootstrap.streams;
- const {
- ArrayBuffer,
- ArrayBufferIsView,
- ArrayPrototypePush,
- ArrayPrototypeMap,
- JSONParse,
- ObjectDefineProperties,
- PromiseResolve,
- TypedArrayPrototypeSet,
- TypedArrayPrototypeSlice,
- TypeError,
- Uint8Array,
- } = window.__bootstrap.primordials;
-
- class InnerBody {
- /** @type {ReadableStream<Uint8Array> | { body: Uint8Array, consumed: boolean }} */
- streamOrStatic;
- /** @type {null | Uint8Array | Blob | FormData} */
- source = null;
- /** @type {null | number} */
- length = null;
-
- /**
- * @param {ReadableStream<Uint8Array> | { body: Uint8Array, consumed: boolean }} stream
- */
- constructor(stream) {
- this.streamOrStatic = stream ??
- { body: new Uint8Array(), consumed: false };
- }
-
- get stream() {
- if (!(this.streamOrStatic instanceof ReadableStream)) {
- const { body, consumed } = this.streamOrStatic;
- if (consumed) {
- this.streamOrStatic = new ReadableStream();
- this.streamOrStatic.getReader();
- } else {
- this.streamOrStatic = new ReadableStream({
- start(controller) {
- controller.enqueue(body);
- controller.close();
- },
- });
- }
- }
- return this.streamOrStatic;
- }
-
- /**
- * https://fetch.spec.whatwg.org/#body-unusable
- * @returns {boolean}
- */
- unusable() {
- if (this.streamOrStatic instanceof ReadableStream) {
- return this.streamOrStatic.locked ||
- isReadableStreamDisturbed(this.streamOrStatic);
- }
- return this.streamOrStatic.consumed;
- }
-
- /**
- * @returns {boolean}
- */
- consumed() {
- if (this.streamOrStatic instanceof ReadableStream) {
- return isReadableStreamDisturbed(this.streamOrStatic);
- }
- return this.streamOrStatic.consumed;
- }
-
- /**
- * https://fetch.spec.whatwg.org/#concept-body-consume-body
- * @returns {Promise<Uint8Array>}
- */
- async consume() {
- if (this.unusable()) throw new TypeError("Body already consumed.");
- if (this.streamOrStatic instanceof ReadableStream) {
- const reader = this.stream.getReader();
- /** @type {Uint8Array[]} */
- const chunks = [];
- let totalLength = 0;
- while (true) {
- const { value: chunk, done } = await reader.read();
- if (done) break;
- ArrayPrototypePush(chunks, chunk);
- totalLength += chunk.byteLength;
- }
- const finalBuffer = new Uint8Array(totalLength);
- let i = 0;
- for (const chunk of chunks) {
- TypedArrayPrototypeSet(finalBuffer, chunk, i);
- i += chunk.byteLength;
- }
- return finalBuffer;
- } else {
- this.streamOrStatic.consumed = true;
- return this.streamOrStatic.body;
- }
- }
-
- cancel(error) {
- if (this.streamOrStatic instanceof ReadableStream) {
- this.streamOrStatic.cancel(error);
- } else {
- this.streamOrStatic.consumed = true;
- }
- }
-
- error(error) {
- if (this.streamOrStatic instanceof ReadableStream) {
- errorReadableStream(this.streamOrStatic, error);
- } else {
- this.streamOrStatic.consumed = true;
- }
- }
-
- /**
- * @returns {InnerBody}
- */
- clone() {
- const [out1, 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;
- }
-
- /**
- * @returns {InnerBody}
- */
- createProxy() {
- let proxyStreamOrStatic;
- if (this.streamOrStatic instanceof ReadableStream) {
- 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;
- }
- }
-
- /**
- * @param {any} prototype
- * @param {symbol} bodySymbol
- * @param {symbol} mimeTypeSymbol
- * @returns {void}
- */
- function mixinBody(prototype, bodySymbol, mimeTypeSymbol) {
- function consumeBody(object) {
- if (object[bodySymbol] !== null) {
- return object[bodySymbol].consume();
- }
- return PromiseResolve(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,
- },
- bodyUsed: {
- /**
- * @returns {boolean}
- */
- get() {
- webidl.assertBranded(this, prototype);
- if (this[bodySymbol] !== null) {
- return this[bodySymbol].consumed();
- }
- return false;
- },
- configurable: true,
- enumerable: true,
- },
- arrayBuffer: {
- /** @returns {Promise<ArrayBuffer>} */
- value: async function arrayBuffer() {
- webidl.assertBranded(this, prototype);
- const body = await consumeBody(this);
- return packageData(body, "ArrayBuffer");
- },
- writable: true,
- configurable: true,
- enumerable: true,
- },
- blob: {
- /** @returns {Promise<Blob>} */
- value: async function blob() {
- webidl.assertBranded(this, prototype);
- const body = await consumeBody(this);
- return packageData(body, "Blob", this[mimeTypeSymbol]);
- },
- writable: true,
- configurable: true,
- enumerable: true,
- },
- formData: {
- /** @returns {Promise<FormData>} */
- value: async function formData() {
- webidl.assertBranded(this, prototype);
- const body = await consumeBody(this);
- return packageData(body, "FormData", this[mimeTypeSymbol]);
- },
- writable: true,
- configurable: true,
- enumerable: true,
- },
- json: {
- /** @returns {Promise<any>} */
- value: async function json() {
- webidl.assertBranded(this, prototype);
- const body = await consumeBody(this);
- return packageData(body, "JSON");
- },
- writable: true,
- configurable: true,
- enumerable: true,
- },
- text: {
- /** @returns {Promise<string>} */
- value: async function text() {
- webidl.assertBranded(this, prototype);
- const body = await consumeBody(this);
- return packageData(body, "text");
- },
- writable: true,
- configurable: true,
- enumerable: true,
- },
- };
- return ObjectDefineProperties(prototype.prototype, mixin);
- }
-
- /**
- * https://fetch.spec.whatwg.org/#concept-body-package-data
- * @param {Uint8Array} bytes
- * @param {"ArrayBuffer" | "Blob" | "FormData" | "JSON" | "text"} type
- * @param {MimeType | null} [mimeType]
- */
- function packageData(bytes, type, mimeType) {
- switch (type) {
- case "ArrayBuffer":
- return 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(bytes, boundary);
- } else if (essence === "application/x-www-form-urlencoded") {
- const entries = parseUrlEncoded(bytes);
- return formDataFromEntries(
- ArrayPrototypeMap(
- entries,
- (x) => ({ name: x[0], value: x[1] }),
- ),
- );
- }
- throw new TypeError("Body can not be decoded as form data");
- }
- throw new TypeError("Missing content type");
- }
- case "JSON":
- return JSONParse(core.decode(bytes));
- case "text":
- return core.decode(bytes);
- }
- }
-
- /**
- * @param {BodyInit} object
- * @returns {{body: InnerBody, contentType: string | null}}
- */
- function extractBody(object) {
- /** @type {ReadableStream<Uint8Array> | { body: Uint8Array, consumed: boolean }} */
- let stream;
- let source = null;
- let length = null;
- let contentType = null;
- if (object instanceof Blob) {
- stream = object.stream();
- source = object;
- length = object.size;
- if (object.type.length !== 0) {
- contentType = object.type;
- }
- } else if (ArrayBufferIsView(object) || object instanceof ArrayBuffer) {
- 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 (object instanceof FormData) {
- const res = formDataToBlob(object);
- stream = res.stream();
- source = res;
- length = res.size;
- contentType = res.type;
- } else if (object instanceof URLSearchParams) {
- // TODO(@satyarohith): not sure what primordial here.
- source = core.encode(object.toString());
- contentType = "application/x-www-form-urlencoded;charset=UTF-8";
- } else if (typeof object === "string") {
- source = core.encode(object);
- contentType = "text/plain;charset=UTF-8";
- } else if (object instanceof ReadableStream) {
- stream = object;
- if (object.locked || isReadableStreamDisturbed(object)) {
- throw new TypeError("ReadableStream is locked or disturbed");
- }
- }
- if (source instanceof Uint8Array) {
- 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"] = (V, opts) => {
- // Union for (ReadableStream or Blob or ArrayBufferView or ArrayBuffer or FormData or URLSearchParams or USVString)
- if (V instanceof ReadableStream) {
- // TODO(lucacasonato): ReadableStream is not branded
- return V;
- } else if (V instanceof Blob) {
- return webidl.converters["Blob"](V, opts);
- } else if (V instanceof FormData) {
- return webidl.converters["FormData"](V, opts);
- } else if (V instanceof URLSearchParams) {
- // TODO(lucacasonato): URLSearchParams is not branded
- return V;
- }
- if (typeof V === "object") {
- if (V instanceof ArrayBuffer || V instanceof SharedArrayBuffer) {
- return webidl.converters["ArrayBuffer"](V, opts);
- }
- if (ArrayBufferIsView(V)) {
- return webidl.converters["ArrayBufferView"](V, opts);
- }
- }
- return webidl.converters["USVString"](V, opts);
- };
- webidl.converters["BodyInit?"] = webidl.createNullableConverter(
- webidl.converters["BodyInit"],
- );
-
- window.__bootstrap.fetchBody = { mixinBody, InnerBody, extractBody };
-})(globalThis);
diff --git a/extensions/fetch/22_http_client.js b/extensions/fetch/22_http_client.js
deleted file mode 100644
index 60b069aa7..000000000
--- a/extensions/fetch/22_http_client.js
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
-
-// @ts-check
-/// <reference path="../webidl/internal.d.ts" />
-/// <reference path="../web/internal.d.ts" />
-/// <reference path="../url/internal.d.ts" />
-/// <reference path="../web/lib.deno_web.d.ts" />
-/// <reference path="./internal.d.ts" />
-/// <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;
-
- /**
- * @param {Deno.CreateHttpClientOptions} options
- * @returns {HttpClient}
- */
- function createHttpClient(options) {
- return new HttpClient(core.opSync("op_create_http_client", options));
- }
-
- class HttpClient {
- /**
- * @param {number} rid
- */
- constructor(rid) {
- this.rid = rid;
- }
- close() {
- core.close(this.rid);
- }
- }
-
- window.__bootstrap.fetch ??= {};
- window.__bootstrap.fetch.createHttpClient = createHttpClient;
- window.__bootstrap.fetch.HttpClient = HttpClient;
-})(globalThis);
diff --git a/extensions/fetch/23_request.js b/extensions/fetch/23_request.js
deleted file mode 100644
index 1372125c1..000000000
--- a/extensions/fetch/23_request.js
+++ /dev/null
@@ -1,484 +0,0 @@
-// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
-
-// @ts-check
-/// <reference path="../webidl/internal.d.ts" />
-/// <reference path="../web/internal.d.ts" />
-/// <reference path="../web/lib.deno_web.d.ts" />
-/// <reference path="./internal.d.ts" />
-/// <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 } = window.__bootstrap.fetchBody;
- const { getLocationHref } = window.__bootstrap.location;
- const mimesniff = window.__bootstrap.mimesniff;
- const {
- headersFromHeaderList,
- headerListFromHeaders,
- fillHeaders,
- getDecodeSplitHeader,
- } = window.__bootstrap.headers;
- const { HttpClient } = window.__bootstrap.fetch;
- const abortSignal = window.__bootstrap.abortSignal;
- const {
- ArrayPrototypeMap,
- ArrayPrototypeSlice,
- ArrayPrototypeSplice,
- MapPrototypeHas,
- MapPrototypeGet,
- MapPrototypeSet,
- ObjectKeys,
- RegExpPrototypeTest,
- Symbol,
- SymbolFor,
- SymbolToStringTag,
- TypeError,
- } = window.__bootstrap.primordials;
-
- const _request = Symbol("request");
- const _headers = Symbol("headers");
- const _signal = Symbol("signal");
- const _mimeType = Symbol("mime type");
- const _body = Symbol("body");
-
- /**
- * @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 {number | null} clientRid NOTE: non standard extension for `Deno.HttpClient`.
- */
-
- const defaultInnerRequest = {
- url() {
- return this.urlList[0];
- },
- currentUrl() {
- return this.urlList[this.urlList.length - 1];
- },
- redirectMode: "follow",
- redirectCount: 0,
- clientRid: null,
- };
-
- /**
- * @param {string} method
- * @param {string} url
- * @param {[string, string][]} headerList
- * @param {typeof __window.bootstrap.fetchBody.InnerBody} body
- * @returns
- */
- function newInnerRequest(method, url, headerList = [], body = null) {
- return {
- method: method,
- headerList,
- body,
- urlList: [url],
- ...defaultInnerRequest,
- };
- }
-
- /**
- * https://fetch.spec.whatwg.org/#concept-request-clone
- * @param {InnerRequest} request
- * @returns {InnerRequest}
- */
- function cloneInnerRequest(request) {
- const headerList = [
- ...ArrayPrototypeMap(request.headerList, (x) => [x[0], x[1]]),
- ];
- let body = null;
- if (request.body !== null) {
- body = request.body.clone();
- }
-
- return {
- method: request.method,
- url() {
- return this.urlList[0];
- },
- currentUrl() {
- return this.urlList[this.urlList.length - 1];
- },
- headerList,
- body,
- redirectMode: request.redirectMode,
- redirectCount: request.redirectCount,
- urlList: request.urlList,
- clientRid: request.clientRid,
- };
- }
-
- /**
- * @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;
- }
-
- // 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.");
- }
- return upperCase;
- }
-
- class Request {
- /** @type {InnerRequest} */
- [_request];
- /** @type {Headers} */
- [_headers];
- /** @type {AbortSignal} */
- [_signal];
- get [_mimeType]() {
- let charset = null;
- let essence = null;
- let mimeType = null;
- const headerList = headerListFromHeaders(this[_headers]);
- const values = getDecodeSplitHeader(headerList, "content-type");
- if (values === null) return null;
- for (const value of values) {
- const temporaryMimeType = mimesniff.parseMimeType(value);
- if (
- temporaryMimeType === null ||
- mimesniff.essence(temporaryMimeType) == "*/*"
- ) {
- continue;
- }
- mimeType = temporaryMimeType;
- if (mimesniff.essence(mimeType) !== essence) {
- charset = null;
- const newCharset = MapPrototypeGet(mimeType.parameters, "charset");
- if (newCharset !== undefined) {
- charset = newCharset;
- }
- essence = mimesniff.essence(mimeType);
- } else {
- if (
- MapPrototypeHas(mimeType.parameters, "charset") === null &&
- charset !== null
- ) {
- MapPrototypeSet(mimeType.parameters, "charset", charset);
- }
- }
- }
- if (mimeType === null) return null;
- return mimeType;
- }
- get [_body]() {
- return this[_request].body;
- }
-
- /**
- * 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"](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);
- } else { // 6.
- if (!(input instanceof Request)) throw new TypeError("Unreachable");
- request = input[_request];
- signal = input[_signal];
- }
-
- // 12.
- // TODO(lucacasonato): create a copy of `request`
-
- // 22.
- if (init.redirect !== undefined) {
- request.redirectMode = init.redirect;
- }
-
- // 25.
- if (init.method !== undefined) {
- let method = init.method;
- method = validateAndNormalizeMethod(method);
- request.method = method;
- }
-
- // 26.
- if (init.signal !== undefined) {
- signal = init.signal;
- }
-
- // NOTE: non standard extension. This handles Deno.HttpClient parameter
- if (init.client !== undefined) {
- if (init.client !== null && !(init.client instanceof HttpClient)) {
- throw webidl.makeException(
- TypeError,
- "`client` must be a Deno.HttpClient",
- { prefix, context: "Argument 2" },
- );
- }
- request.clientRid = init.client?.rid ?? null;
- }
-
- // 27.
- this[_request] = request;
-
- // 28.
- this[_signal] = abortSignal.newSignal();
-
- // 29.
- if (signal !== null) {
- abortSignal.follow(this[_signal], signal);
- }
-
- // 30.
- this[_headers] = headersFromHeaderList(request.headerList, "request");
-
- // 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);
- }
-
- // 33.
- let inputBody = null;
- if (input instanceof Request) {
- inputBody = input[_body];
- }
-
- // 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.");
- }
-
- // 35.
- let initBody = null;
-
- // 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);
- }
- }
-
- // 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;
- }
-
- get method() {
- webidl.assertBranded(this, Request);
- return this[_request].method;
- }
-
- get url() {
- webidl.assertBranded(this, Request);
- return this[_request].url();
- }
-
- get headers() {
- webidl.assertBranded(this, Request);
- return this[_headers];
- }
-
- get redirect() {
- webidl.assertBranded(this, Request);
- return this[_request].redirectMode;
- }
-
- get signal() {
- webidl.assertBranded(this, Request);
- return this[_signal];
- }
-
- clone() {
- webidl.assertBranded(this, Request);
- if (this[_body] && this[_body].unusable()) {
- throw new TypeError("Body is unusable.");
- }
- const newReq = cloneInnerRequest(this[_request]);
- const newSignal = abortSignal.newSignal();
- abortSignal.follow(newSignal, this[_signal]);
- return fromInnerRequest(
- newReq,
- newSignal,
- guardFromHeaders(this[_headers]),
- );
- }
-
- get [SymbolToStringTag]() {
- return "Request";
- }
-
- [SymbolFor("Deno.customInspect")](inspect) {
- return inspect(consoleInternal.createFilteredInspectProxy({
- object: this,
- evaluate: this instanceof Request,
- keys: [
- "bodyUsed",
- "headers",
- "method",
- "redirect",
- "url",
- ],
- }));
- }
- }
-
- mixinBody(Request, _body, _mimeType);
-
- webidl.configurePrototype(Request);
-
- webidl.converters["Request"] = webidl.createInterfaceConverter(
- "Request",
- Request,
- );
- webidl.converters["RequestInfo"] = (V, opts) => {
- // Union for (Request or USVString)
- if (typeof V == "object") {
- if (V instanceof Request) {
- return webidl.converters["Request"](V, opts);
- }
- }
- return webidl.converters["USVString"](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"],
- ),
- },
- { 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 {"request" | "immutable" | "request-no-cors" | "response" | "none"} guard
- * @returns {Request}
- */
- function fromInnerRequest(inner, signal, guard) {
- const request = webidl.createBranded(Request);
- request[_request] = inner;
- request[_signal] = signal;
- request[_headers] = headersFromHeaderList(inner.headerList, guard);
- return request;
- }
-
- window.__bootstrap.fetch ??= {};
- window.__bootstrap.fetch.Request = Request;
- window.__bootstrap.fetch.toInnerRequest = toInnerRequest;
- window.__bootstrap.fetch.fromInnerRequest = fromInnerRequest;
- window.__bootstrap.fetch.newInnerRequest = newInnerRequest;
-})(globalThis);
diff --git a/extensions/fetch/23_response.js b/extensions/fetch/23_response.js
deleted file mode 100644
index 0db20e90e..000000000
--- a/extensions/fetch/23_response.js
+++ /dev/null
@@ -1,451 +0,0 @@
-// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
-
-// @ts-check
-/// <reference path="../webidl/internal.d.ts" />
-/// <reference path="../web/internal.d.ts" />
-/// <reference path="../url/internal.d.ts" />
-/// <reference path="../web/lib.deno_web.d.ts" />
-/// <reference path="./internal.d.ts" />
-/// <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_TAB_OR_SPACE, regexMatcher } = window.__bootstrap.infra;
- const { extractBody, mixinBody } = window.__bootstrap.fetchBody;
- const { getLocationHref } = window.__bootstrap.location;
- const mimesniff = window.__bootstrap.mimesniff;
- const { URL } = window.__bootstrap.url;
- const {
- getDecodeSplitHeader,
- headerListFromHeaders,
- headersFromHeaderList,
- guardFromHeaders,
- fillHeaders,
- } = window.__bootstrap.headers;
- const {
- ArrayPrototypeMap,
- ArrayPrototypePush,
- MapPrototypeHas,
- MapPrototypeGet,
- MapPrototypeSet,
- RangeError,
- RegExp,
- RegExpPrototypeTest,
- Symbol,
- SymbolFor,
- SymbolToStringTag,
- TypeError,
- } = window.__bootstrap.primordials;
-
- const VCHAR = ["\x21-\x7E"];
- const OBS_TEXT = ["\x80-\xFF"];
-
- const REASON_PHRASE = [...HTTP_TAB_OR_SPACE, ...VCHAR, ...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 = [...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,
- url() {
- if (this.urlList.length == 0) return null;
- return this.urlList[this.urlList.length - 1];
- },
- urlList,
- status: response.status,
- statusMessage: response.statusMessage,
- aborted: response.aborted,
- };
- }
-
- const defaultInnerResponse = {
- type: "default",
- body: null,
- aborted: false,
- url() {
- if (this.urlList.length == 0) return null;
- return this.urlList[this.urlList.length - 1];
- },
- };
-
- /**
- * @returns {InnerResponse}
- */
- function newInnerResponse(status = 200, statusMessage = "") {
- return {
- headerList: [],
- urlList: [],
- status,
- statusMessage,
- ...defaultInnerResponse,
- };
- }
-
- /**
- * @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;
- }
-
- class Response {
- /** @type {InnerResponse} */
- [_response];
- /** @type {Headers} */
- [_headers];
- get [_mimeType]() {
- let charset = null;
- let essence = null;
- let mimeType = null;
- const headerList = headerListFromHeaders(this[_headers]);
- const values = getDecodeSplitHeader(headerList, "content-type");
- if (values === null) return null;
- for (const value of values) {
- const temporaryMimeType = mimesniff.parseMimeType(value);
- if (
- temporaryMimeType === null ||
- mimesniff.essence(temporaryMimeType) == "*/*"
- ) {
- continue;
- }
- mimeType = temporaryMimeType;
- if (mimesniff.essence(mimeType) !== essence) {
- charset = null;
- const newCharset = MapPrototypeGet(mimeType.parameters, "charset");
- if (newCharset !== undefined) {
- charset = newCharset;
- }
- essence = mimesniff.essence(mimeType);
- } else {
- if (
- MapPrototypeHas(mimeType.parameters, "charset") === null &&
- charset !== null
- ) {
- MapPrototypeSet(mimeType.parameters, "charset", charset);
- }
- }
- }
- if (mimeType === null) return null;
- return mimeType;
- }
- 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 {BodyInit | null} body
- * @param {ResponseInit} init
- */
- constructor(body = null, init = {}) {
- const prefix = "Failed to construct 'Response'";
- body = webidl.converters["BodyInit?"](body, {
- prefix,
- context: "Argument 1",
- });
- init = webidl.converters["ResponseInit"](init, {
- prefix,
- context: "Argument 2",
- });
-
- if (init.status < 200 || init.status > 599) {
- throw new RangeError(
- `The status provided (${init.status}) is outside the range [200, 599].`,
- );
- }
-
- if (!RegExpPrototypeTest(REASON_PHRASE_RE, init.statusText)) {
- throw new TypeError("Status text is not valid.");
- }
-
- this[webidl.brand] = webidl.brand;
- const response = newInnerResponse(init.status, init.statusText);
- this[_response] = response;
- this[_headers] = headersFromHeaderList(response.headerList, "response");
- if (init.headers !== undefined) {
- fillHeaders(this[_headers], init.headers);
- }
- if (body !== null) {
- if (nullBodyStatus(response.status)) {
- throw new TypeError(
- "Response with null body status cannot have body",
- );
- }
- const res = extractBody(body);
- response.body = res.body;
- if (res.contentType !== null && !this[_headers].has("content-type")) {
- this[_headers].append("Content-Type", res.contentType);
- }
- }
- }
-
- /**
- * @returns {"basic" | "cors" | "default" | "error" | "opaque" | "opaqueredirect"}
- */
- get type() {
- webidl.assertBranded(this, Response);
- return this[_response].type;
- }
-
- /**
- * @returns {string}
- */
- get url() {
- webidl.assertBranded(this, Response);
- 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, Response);
- return this[_response].urlList.length > 1;
- }
-
- /**
- * @returns {number}
- */
- get status() {
- webidl.assertBranded(this, Response);
- return this[_response].status;
- }
-
- /**
- * @returns {boolean}
- */
- get ok() {
- webidl.assertBranded(this, Response);
- const status = this[_response].status;
- return status >= 200 && status <= 299;
- }
-
- /**
- * @returns {string}
- */
- get statusText() {
- webidl.assertBranded(this, Response);
- return this[_response].statusMessage;
- }
-
- /**
- * @returns {Headers}
- */
- get headers() {
- webidl.assertBranded(this, Response);
- return this[_headers];
- }
-
- /**
- * @returns {Response}
- */
- clone() {
- webidl.assertBranded(this, Response);
- 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;
- }
-
- get [SymbolToStringTag]() {
- return "Response";
- }
-
- [SymbolFor("Deno.customInspect")](inspect) {
- return inspect(consoleInternal.createFilteredInspectProxy({
- object: this,
- evaluate: this instanceof Response,
- keys: [
- "body",
- "bodyUsed",
- "headers",
- "ok",
- "redirected",
- "status",
- "statusText",
- "url",
- ],
- }));
- }
- }
-
- mixinBody(Response, _body, _mimeType);
-
- webidl.configurePrototype(Response);
-
- webidl.converters["Response"] = webidl.createInterfaceConverter(
- "Response",
- Response,
- );
- 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"],
- }],
- );
-
- /**
- * @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;
- }
-
- window.__bootstrap.fetch ??= {};
- window.__bootstrap.fetch.Response = Response;
- 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);
diff --git a/extensions/fetch/26_fetch.js b/extensions/fetch/26_fetch.js
deleted file mode 100644
index f7166001e..000000000
--- a/extensions/fetch/26_fetch.js
+++ /dev/null
@@ -1,542 +0,0 @@
-// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
-
-// @ts-check
-/// <reference path="../../core/lib.deno_core.d.ts" />
-/// <reference path="../web/internal.d.ts" />
-/// <reference path="../url/internal.d.ts" />
-/// <reference path="../web/lib.deno_web.d.ts" />
-/// <reference path="../web/06_streams_types.d.ts" />
-/// <reference path="./internal.d.ts" />
-/// <reference path="./lib.deno_fetch.d.ts" />
-/// <reference lib="esnext" />
-"use strict";
-
-((window) => {
- const core = window.Deno.core;
- const webidl = window.__bootstrap.webidl;
- const { errorReadableStream } = window.__bootstrap.streams;
- const { InnerBody, extractBody } = window.__bootstrap.fetchBody;
- const {
- toInnerRequest,
- toInnerResponse,
- fromInnerResponse,
- redirectStatus,
- nullBodyStatus,
- networkError,
- abortedNetworkError,
- } = window.__bootstrap.fetch;
- const abortSignal = window.__bootstrap.abortSignal;
- const { DOMException } = window.__bootstrap.domException;
- const {
- ArrayPrototypePush,
- ArrayPrototypeSplice,
- ArrayPrototypeFilter,
- ArrayPrototypeIncludes,
- Promise,
- PromisePrototypeThen,
- PromisePrototypeCatch,
- StringPrototypeToLowerCase,
- TypedArrayPrototypeSubarray,
- TypeError,
- Uint8Array,
- } = window.__bootstrap.primordials;
-
- const REQUEST_BODY_HEADER_NAMES = [
- "content-encoding",
- "content-language",
- "content-location",
- "content-type",
- ];
-
- /**
- * @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(args, body) {
- return core.opSync("op_fetch", args, 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} rid
- * @param {Uint8Array} body
- * @returns {Promise<void>}
- */
- function opFetchRequestWrite(rid, body) {
- return core.opAsync("op_fetch_request_write", rid, body);
- }
-
- /**
- * @param {number} rid
- * @param {Uint8Array} body
- * @returns {Promise<number>}
- */
- function opFetchResponseRead(rid, body) {
- return core.opAsync("op_fetch_response_read", rid, body);
- }
-
- // A finalization registry to clean up underlying fetch resources that are GC'ed.
- const RESOURCE_REGISTRY = new FinalizationRegistry((rid) => {
- try {
- core.close(rid);
- } catch {
- // might have already been closed
- }
- });
-
- /**
- * @param {number} responseBodyRid
- * @param {AbortSignal} [terminator]
- * @returns {ReadableStream<Uint8Array>}
- */
- function createResponseBodyStream(responseBodyRid, terminator) {
- function onAbort() {
- if (readable) {
- errorReadableStream(
- readable,
- new DOMException("Ongoing fetch was aborted.", "AbortError"),
- );
- }
- try {
- core.close(responseBodyRid);
- } catch (_) {
- // might have already been closed
- }
- }
- // TODO(lucacasonato): clean up registration
- terminator[abortSignal.add](onAbort);
- const readable = new ReadableStream({
- type: "bytes",
- async pull(controller) {
- try {
- // This is the largest possible size for a single packet on a TLS
- // stream.
- const chunk = new Uint8Array(16 * 1024 + 256);
- const read = await opFetchResponseRead(
- responseBodyRid,
- chunk,
- );
- if (read > 0) {
- // We read some data. Enqueue it onto the stream.
- controller.enqueue(TypedArrayPrototypeSubarray(chunk, 0, read));
- } else {
- RESOURCE_REGISTRY.unregister(readable);
- // We have reached the end of the body, so we close the stream.
- controller.close();
- try {
- core.close(responseBodyRid);
- } catch (_) {
- // might have already been closed
- }
- }
- } catch (err) {
- RESOURCE_REGISTRY.unregister(readable);
- if (terminator.aborted) {
- controller.error(
- new DOMException("Ongoing fetch was aborted.", "AbortError"),
- );
- } else {
- // There was an error while reading a chunk of the body, so we
- // error.
- controller.error(err);
- }
- try {
- core.close(responseBodyRid);
- } catch (_) {
- // might have already been closed
- }
- }
- },
- cancel() {
- if (!terminator.aborted) {
- terminator[abortSignal.signalAbort]();
- }
- },
- });
- RESOURCE_REGISTRY.register(readable, responseBodyRid, readable);
- return readable;
- }
-
- /**
- * @param {InnerRequest} req
- * @param {boolean} recursive
- * @param {AbortSignal} terminator
- * @returns {Promise<InnerResponse>}
- */
- async function mainFetch(req, recursive, terminator) {
- /** @type {ReadableStream<Uint8Array> | Uint8Array | null} */
- let reqBody = null;
-
- if (req.body !== null) {
- if (req.body.streamOrStatic instanceof ReadableStream) {
- if (req.body.length === null || req.body.source instanceof Blob) {
- reqBody = req.body.stream;
- } else {
- const reader = req.body.stream.getReader();
- 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");
- }
- }
- } else {
- req.body.streamOrStatic.consumed = true;
- reqBody = req.body.streamOrStatic.body;
- }
- }
-
- const { requestRid, requestBodyRid, cancelHandleRid } = opFetch({
- method: req.method,
- url: req.currentUrl(),
- headers: req.headerList,
- clientRid: req.clientRid,
- hasBody: reqBody !== null,
- bodyLength: req.body?.length,
- }, reqBody instanceof Uint8Array ? reqBody : null);
-
- function onAbort() {
- try {
- core.close(cancelHandleRid);
- } catch (_) {
- // might have already been closed
- }
- try {
- core.close(requestBodyRid);
- } catch (_) {
- // might have already been closed
- }
- }
- terminator[abortSignal.add](onAbort);
-
- if (requestBodyRid !== null) {
- if (reqBody === null || !(reqBody instanceof ReadableStream)) {
- throw new TypeError("Unreachable");
- }
- const reader = reqBody.getReader();
- (async () => {
- while (true) {
- const { value, done } = await PromisePrototypeCatch(
- reader.read(),
- (err) => {
- if (terminator.aborted) return { done: true, value: undefined };
- throw err;
- },
- );
- if (done) break;
- if (!(value instanceof Uint8Array)) {
- await reader.cancel("value not a Uint8Array");
- break;
- }
- try {
- await PromisePrototypeCatch(
- opFetchRequestWrite(requestBodyRid, value),
- (err) => {
- if (terminator.aborted) return;
- throw err;
- },
- );
- if (terminator.aborted) break;
- } catch (err) {
- await reader.cancel(err);
- break;
- }
- }
- try {
- core.close(requestBodyRid);
- } catch (_) {
- // might have already been closed
- }
- })();
- }
-
- let resp;
- try {
- resp = await PromisePrototypeCatch(opFetchSend(requestRid), (err) => {
- if (terminator.aborted) return;
- throw err;
- });
- } finally {
- try {
- core.close(cancelHandleRid);
- } catch (_) {
- // might have already been closed
- }
- }
- if (terminator.aborted) return abortedNetworkError();
-
- /** @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.urlList,
- };
- 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)) {
- 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),
- );
- }
- }
-
- if (recursive) return response;
-
- if (response.urlList.length === 0) {
- response.urlList = [...req.urlList];
- }
-
- return response;
- }
-
- /**
- * @param {InnerRequest} request
- * @param {InnerResponse} response
- * @returns {Promise<InnerResponse>}
- */
- function httpRedirectFetch(request, response, terminator) {
- const locationHeaders = ArrayPrototypeFilter(
- response.headerList,
- (entry) => entry[0] === "location",
- );
- if (locationHeaders.length === 0) {
- return response;
- }
- 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 (
- ((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,
- 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);
- }
-
- /**
- * @param {RequestInfo} input
- * @param {RequestInit} init
- */
- function fetch(input, init = {}) {
- // 1.
- const p = new Promise((resolve, reject) => {
- const prefix = "Failed to call 'fetch'";
- webidl.requiredArguments(arguments.length, 1, { prefix });
- input = webidl.converters["RequestInfo"](input, {
- prefix,
- context: "Argument 1",
- });
- init = webidl.converters["RequestInit"](init, {
- prefix,
- context: "Argument 2",
- });
-
- // 2.
- const requestObject = new Request(input, init);
- // 3.
- const request = toInnerRequest(requestObject);
- // 4.
- if (requestObject.signal.aborted) {
- reject(abortFetch(request, null));
- return;
- }
-
- // 7.
- let responseObject = null;
- // 9.
- let locallyAborted = false;
- // 10.
- function onabort() {
- locallyAborted = true;
- reject(abortFetch(request, responseObject));
- }
- requestObject.signal[abortSignal.add](onabort);
-
- if (!requestObject.headers.has("accept")) {
- ArrayPrototypePush(request.headerList, ["accept", "*/*"]);
- }
-
- // 12.
- PromisePrototypeCatch(
- PromisePrototypeThen(
- mainFetch(request, false, requestObject.signal),
- (response) => {
- // 12.1.
- if (locallyAborted) return;
- // 12.2.
- if (response.aborted) {
- reject(request, responseObject);
- 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);
- requestObject.signal[abortSignal.remove](onabort);
- },
- ),
- (err) => {
- reject(err);
- requestObject.signal[abortSignal.remove](onabort);
- },
- );
- });
- return p;
- }
-
- function abortFetch(request, responseObject) {
- const error = new DOMException("Ongoing fetch was aborted.", "AbortError");
- if (request.body !== null) request.body.cancel(error);
- if (responseObject !== null) {
- const response = toInnerResponse(responseObject);
- if (response.body !== null) response.body.error(error);
- }
- return error;
- }
-
- /**
- * Handle the Promise<Response> argument to the WebAssembly streaming
- * APIs. This function should be registered through
- * `Deno.core.setWasmStreamingCallback`.
- *
- * @param {any} source The source parameter that the WebAssembly
- * streaming API was called with.
- * @param {number} rid An rid that can be used with
- * `Deno.core.wasmStreamingFeed`.
- */
- function handleWasmStreaming(source, rid) {
- // This implements part of
- // https://webassembly.github.io/spec/web-api/#compile-a-potential-webassembly-response
- (async () => {
- try {
- const res = webidl.converters["Response"](await 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.
- if (
- StringPrototypeToLowerCase(res.headers.get("Content-Type")) !==
- "application/wasm"
- ) {
- throw new TypeError("Invalid WebAssembly content type.");
- }
-
- // 2.5.
- if (!res.ok) {
- throw new TypeError(`HTTP status code ${res.status}`);
- }
-
- // 2.6.
- // Rather than consuming the body as an ArrayBuffer, this passes each
- // chunk to the feed as soon as it's available.
- if (res.body !== null) {
- const reader = res.body.getReader();
- while (true) {
- const { value: chunk, done } = await reader.read();
- if (done) break;
- Deno.core.wasmStreamingFeed(rid, "bytes", chunk);
- }
- }
-
- // 2.7.
- Deno.core.wasmStreamingFeed(rid, "finish");
- } catch (err) {
- // 2.8 and 3
- Deno.core.wasmStreamingFeed(rid, "abort", err);
- }
- })();
- }
-
- window.__bootstrap.fetch ??= {};
- window.__bootstrap.fetch.fetch = fetch;
- window.__bootstrap.fetch.handleWasmStreaming = handleWasmStreaming;
-})(this);
diff --git a/extensions/fetch/Cargo.toml b/extensions/fetch/Cargo.toml
deleted file mode 100644
index 80d0cb2e1..000000000
--- a/extensions/fetch/Cargo.toml
+++ /dev/null
@@ -1,28 +0,0 @@
-# Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
-
-[package]
-name = "deno_fetch"
-version = "0.37.0"
-authors = ["the Deno authors"]
-edition = "2018"
-license = "MIT"
-readme = "README.md"
-repository = "https://github.com/denoland/deno"
-description = "Fetch API implementation for Deno"
-
-[lib]
-path = "lib.rs"
-
-[dependencies]
-bytes = "1.0.1"
-data-url = "0.1.0"
-deno_core = { version = "0.96.0", path = "../../core" }
-deno_tls = { version = "0.1.0", path = "../tls" }
-deno_web = { version = "0.45.0", path = "../web" }
-http = "0.2.4"
-lazy_static = "1.4.0"
-reqwest = { version = "0.11.4", default-features = false, features = ["rustls-tls", "stream", "gzip", "brotli"] }
-serde = { version = "1.0.126", features = ["derive"] }
-tokio = { version = "1.8.1", features = ["full"] }
-tokio-stream = "0.1.7"
-tokio-util = "0.6.7"
diff --git a/extensions/fetch/README.md b/extensions/fetch/README.md
deleted file mode 100644
index 2c946197e..000000000
--- a/extensions/fetch/README.md
+++ /dev/null
@@ -1,5 +0,0 @@
-# deno_fetch
-
-This crate implements the Fetch API.
-
-Spec: https://fetch.spec.whatwg.org/
diff --git a/extensions/fetch/internal.d.ts b/extensions/fetch/internal.d.ts
deleted file mode 100644
index a84e0bcce..000000000
--- a/extensions/fetch/internal.d.ts
+++ /dev/null
@@ -1,108 +0,0 @@
-// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
-
-// deno-lint-ignore-file no-explicit-any
-
-/// <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 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 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 namespace fetch {
- function toInnerRequest(request: Request): InnerRequest;
- function fromInnerRequest(
- inner: InnerRequest,
- signal: AbortSignal | null,
- guard:
- | "request"
- | "immutable"
- | "request-no-cors"
- | "response"
- | "none",
- ): 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;
- }
- }
-}
diff --git a/extensions/fetch/lib.deno_fetch.d.ts b/extensions/fetch/lib.deno_fetch.d.ts
deleted file mode 100644
index 7fe7d9453..000000000
--- a/extensions/fetch/lib.deno_fetch.d.ts
+++ /dev/null
@@ -1,437 +0,0 @@
-// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
-
-// deno-lint-ignore-file no-explicit-any
-
-/// <reference no-default-lib="true" />
-/// <reference lib="esnext" />
-
-interface DomIterable<K, V> {
- keys(): IterableIterator<K>;
- values(): IterableIterator<V>;
- entries(): IterableIterator<[K, V]>;
- [Symbol.iterator](): IterableIterator<[K, V]>;
- forEach(
- callback: (value: V, key: K, parent: this) => void,
- thisArg?: any,
- ): void;
-}
-
-type FormDataEntryValue = File | string;
-
-/** Provides a way to easily construct a set of key/value pairs representing
- * form fields and their values, which can then be easily sent using the
- * XMLHttpRequest.send() method. It uses the same format a form would use if the
- * encoding type were set to "multipart/form-data". */
-declare class FormData implements DomIterable<string, FormDataEntryValue> {
- // TODO(ry) FormData constructor is non-standard.
- // new(form?: HTMLFormElement): FormData;
- constructor();
-
- append(name: string, value: string | Blob, fileName?: string): void;
- delete(name: string): void;
- get(name: string): FormDataEntryValue | null;
- getAll(name: string): FormDataEntryValue[];
- has(name: string): boolean;
- set(name: string, value: string | Blob, fileName?: string): void;
- keys(): IterableIterator<string>;
- values(): IterableIterator<string>;
- entries(): IterableIterator<[string, FormDataEntryValue]>;
- [Symbol.iterator](): IterableIterator<[string, FormDataEntryValue]>;
- forEach(
- callback: (value: FormDataEntryValue, key: string, parent: this) => void,
- thisArg?: any,
- ): void;
-}
-
-interface Body {
- /** A simple getter used to expose a `ReadableStream` of the body contents. */
- readonly body: ReadableStream<Uint8Array> | null;
- /** Stores a `Boolean` that declares whether the body has been used in a
- * response yet.
- */
- readonly bodyUsed: boolean;
- /** Takes a `Response` stream and reads it to completion. It returns a promise
- * that resolves with an `ArrayBuffer`.
- */
- arrayBuffer(): Promise<ArrayBuffer>;
- /** Takes a `Response` stream and reads it to completion. It returns a promise
- * that resolves with a `Blob`.
- */
- blob(): Promise<Blob>;
- /** Takes a `Response` stream and reads it to completion. It returns a promise
- * that resolves with a `FormData` object.
- */
- formData(): Promise<FormData>;
- /** Takes a `Response` stream and reads it to completion. It returns a promise
- * that resolves with the result of parsing the body text as JSON.
- */
- json(): Promise<any>;
- /** Takes a `Response` stream and reads it to completion. It returns a promise
- * that resolves with a `USVString` (text).
- */
- text(): Promise<string>;
-}
-
-type HeadersInit = Headers | string[][] | Record<string, string>;
-
-/** This Fetch API interface allows you to perform various actions on HTTP
- * request and response headers. These actions include retrieving, setting,
- * adding to, and removing. A Headers object has an associated header list,
- * which is initially empty and consists of zero or more name and value pairs.
- * You can add to this using methods like append() (see Examples). In all
- * methods of this interface, header names are matched by case-insensitive byte
- * sequence. */
-interface Headers {
- append(name: string, value: string): void;
- delete(name: string): void;
- get(name: string): string | null;
- has(name: string): boolean;
- set(name: string, value: string): void;
- forEach(
- callbackfn: (value: string, key: string, parent: Headers) => void,
- thisArg?: any,
- ): void;
-}
-
-declare class Headers implements DomIterable<string, string> {
- constructor(init?: HeadersInit);
-
- /** Appends a new value onto an existing header inside a `Headers` object, or
- * adds the header if it does not already exist.
- */
- append(name: string, value: string): void;
- /** Deletes a header from a `Headers` object. */
- delete(name: string): void;
- /** Returns an iterator allowing to go through all key/value pairs
- * contained in this Headers object. The both the key and value of each pairs
- * are ByteString objects.
- */
- entries(): IterableIterator<[string, string]>;
- /** Returns a `ByteString` sequence of all the values of a header within a
- * `Headers` object with a given name.
- */
- get(name: string): string | null;
- /** Returns a boolean stating whether a `Headers` object contains a certain
- * header.
- */
- has(name: string): boolean;
- /** Returns an iterator allowing to go through all keys contained in
- * this Headers object. The keys are ByteString objects.
- */
- keys(): IterableIterator<string>;
- /** Sets a new value for an existing header inside a Headers object, or adds
- * the header if it does not already exist.
- */
- set(name: string, value: string): void;
- /** Returns an iterator allowing to go through all values contained in
- * this Headers object. The values are ByteString objects.
- */
- values(): IterableIterator<string>;
- forEach(
- callbackfn: (value: string, key: string, parent: this) => void,
- thisArg?: any,
- ): void;
- /** The Symbol.iterator well-known symbol specifies the default
- * iterator for this Headers object
- */
- [Symbol.iterator](): IterableIterator<[string, string]>;
-}
-
-type RequestInfo = Request | string;
-type RequestCache =
- | "default"
- | "force-cache"
- | "no-cache"
- | "no-store"
- | "only-if-cached"
- | "reload";
-type RequestCredentials = "include" | "omit" | "same-origin";
-type RequestMode = "cors" | "navigate" | "no-cors" | "same-origin";
-type RequestRedirect = "error" | "follow" | "manual";
-type ReferrerPolicy =
- | ""
- | "no-referrer"
- | "no-referrer-when-downgrade"
- | "origin"
- | "origin-when-cross-origin"
- | "same-origin"
- | "strict-origin"
- | "strict-origin-when-cross-origin"
- | "unsafe-url";
-type BodyInit =
- | Blob
- | BufferSource
- | FormData
- | URLSearchParams
- | ReadableStream<Uint8Array>
- | string;
-type RequestDestination =
- | ""
- | "audio"
- | "audioworklet"
- | "document"
- | "embed"
- | "font"
- | "image"
- | "manifest"
- | "object"
- | "paintworklet"
- | "report"
- | "script"
- | "sharedworker"
- | "style"
- | "track"
- | "video"
- | "worker"
- | "xslt";
-
-interface RequestInit {
- /**
- * A BodyInit object or null to set request's body.
- */
- body?: BodyInit | null;
- /**
- * A string indicating how the request will interact with the browser's cache
- * to set request's cache.
- */
- cache?: RequestCache;
- /**
- * A string indicating whether credentials will be sent with the request
- * always, never, or only when sent to a same-origin URL. Sets request's
- * credentials.
- */
- credentials?: RequestCredentials;
- /**
- * A Headers object, an object literal, or an array of two-item arrays to set
- * request's headers.
- */
- headers?: HeadersInit;
- /**
- * A cryptographic hash of the resource to be fetched by request. Sets
- * request's integrity.
- */
- integrity?: string;
- /**
- * A boolean to set request's keepalive.
- */
- keepalive?: boolean;
- /**
- * A string to set request's method.
- */
- method?: string;
- /**
- * A string to indicate whether the request will use CORS, or will be
- * restricted to same-origin URLs. Sets request's mode.
- */
- mode?: RequestMode;
- /**
- * A string indicating whether request follows redirects, results in an error
- * upon encountering a redirect, or returns the redirect (in an opaque
- * fashion). Sets request's redirect.
- */
- redirect?: RequestRedirect;
- /**
- * A string whose value is a same-origin URL, "about:client", or the empty
- * string, to set request's referrer.
- */
- referrer?: string;
- /**
- * A referrer policy to set request's referrerPolicy.
- */
- referrerPolicy?: ReferrerPolicy;
- /**
- * An AbortSignal to set request's signal.
- */
- signal?: AbortSignal | null;
- /**
- * Can only be null. Used to disassociate request from any Window.
- */
- window?: any;
-}
-
-/** This Fetch API interface represents a resource request. */
-declare class Request implements Body {
- constructor(input: RequestInfo, init?: RequestInit);
-
- /**
- * Returns the cache mode associated with request, which is a string
- * indicating how the request will interact with the browser's cache when
- * fetching.
- */
- readonly cache: RequestCache;
- /**
- * Returns the credentials mode associated with request, which is a string
- * indicating whether credentials will be sent with the request always, never,
- * or only when sent to a same-origin URL.
- */
- readonly credentials: RequestCredentials;
- /**
- * Returns the kind of resource requested by request, e.g., "document" or "script".
- */
- readonly destination: RequestDestination;
- /**
- * Returns a Headers object consisting of the headers associated with request.
- * Note that headers added in the network layer by the user agent will not be
- * accounted for in this object, e.g., the "Host" header.
- */
- readonly headers: Headers;
- /**
- * Returns request's subresource integrity metadata, which is a cryptographic
- * hash of the resource being fetched. Its value consists of multiple hashes
- * separated by whitespace. [SRI]
- */
- readonly integrity: string;
- /**
- * Returns a boolean indicating whether or not request is for a history
- * navigation (a.k.a. back-forward navigation).
- */
- readonly isHistoryNavigation: boolean;
- /**
- * Returns a boolean indicating whether or not request is for a reload
- * navigation.
- */
- readonly isReloadNavigation: boolean;
- /**
- * Returns a boolean indicating whether or not request can outlive the global
- * in which it was created.
- */
- readonly keepalive: boolean;
- /**
- * Returns request's HTTP method, which is "GET" by default.
- */
- readonly method: string;
- /**
- * Returns the mode associated with request, which is a string indicating
- * whether the request will use CORS, or will be restricted to same-origin
- * URLs.
- */
- readonly mode: RequestMode;
- /**
- * Returns the redirect mode associated with request, which is a string
- * indicating how redirects for the request will be handled during fetching. A
- * request will follow redirects by default.
- */
- readonly redirect: RequestRedirect;
- /**
- * Returns the referrer of request. Its value can be a same-origin URL if
- * explicitly set in init, the empty string to indicate no referrer, and
- * "about:client" when defaulting to the global's default. This is used during
- * fetching to determine the value of the `Referer` header of the request
- * being made.
- */
- readonly referrer: string;
- /**
- * Returns the referrer policy associated with request. This is used during
- * fetching to compute the value of the request's referrer.
- */
- readonly referrerPolicy: ReferrerPolicy;
- /**
- * Returns the signal associated with request, which is an AbortSignal object
- * indicating whether or not request has been aborted, and its abort event
- * handler.
- */
- readonly signal: AbortSignal;
- /**
- * Returns the URL of request as a string.
- */
- readonly url: string;
- clone(): Request;
-
- /** A simple getter used to expose a `ReadableStream` of the body contents. */
- readonly body: ReadableStream<Uint8Array> | null;
- /** Stores a `Boolean` that declares whether the body has been used in a
- * response yet.
- */
- readonly bodyUsed: boolean;
- /** Takes a `Response` stream and reads it to completion. It returns a promise
- * that resolves with an `ArrayBuffer`.
- */
- arrayBuffer(): Promise<ArrayBuffer>;
- /** Takes a `Response` stream and reads it to completion. It returns a promise
- * that resolves with a `Blob`.
- */
- blob(): Promise<Blob>;
- /** Takes a `Response` stream and reads it to completion. It returns a promise
- * that resolves with a `FormData` object.
- */
- formData(): Promise<FormData>;
- /** Takes a `Response` stream and reads it to completion. It returns a promise
- * that resolves with the result of parsing the body text as JSON.
- */
- json(): Promise<any>;
- /** Takes a `Response` stream and reads it to completion. It returns a promise
- * that resolves with a `USVString` (text).
- */
- text(): Promise<string>;
-}
-
-interface ResponseInit {
- headers?: HeadersInit;
- status?: number;
- statusText?: string;
-}
-
-type ResponseType =
- | "basic"
- | "cors"
- | "default"
- | "error"
- | "opaque"
- | "opaqueredirect";
-
-/** This Fetch API interface represents the response to a request. */
-declare class Response implements Body {
- constructor(body?: BodyInit | null, init?: ResponseInit);
- static error(): Response;
- static redirect(url: string, status?: number): Response;
-
- readonly headers: Headers;
- readonly ok: boolean;
- readonly redirected: boolean;
- readonly status: number;
- readonly statusText: string;
- readonly trailer: Promise<Headers>;
- readonly type: ResponseType;
- readonly url: string;
- clone(): Response;
-
- /** A simple getter used to expose a `ReadableStream` of the body contents. */
- readonly body: ReadableStream<Uint8Array> | null;
- /** Stores a `Boolean` that declares whether the body has been used in a
- * response yet.
- */
- readonly bodyUsed: boolean;
- /** Takes a `Response` stream and reads it to completion. It returns a promise
- * that resolves with an `ArrayBuffer`.
- */
- arrayBuffer(): Promise<ArrayBuffer>;
- /** Takes a `Response` stream and reads it to completion. It returns a promise
- * that resolves with a `Blob`.
- */
- blob(): Promise<Blob>;
- /** Takes a `Response` stream and reads it to completion. It returns a promise
- * that resolves with a `FormData` object.
- */
- formData(): Promise<FormData>;
- /** Takes a `Response` stream and reads it to completion. It returns a promise
- * that resolves with the result of parsing the body text as JSON.
- */
- json(): Promise<any>;
- /** Takes a `Response` stream and reads it to completion. It returns a promise
- * that resolves with a `USVString` (text).
- */
- text(): Promise<string>;
-}
-
-/** Fetch a resource from the network. It returns a Promise that resolves to the
- * Response to that request, whether it is successful or not.
- *
- * const response = await fetch("http://my.json.host/data.json");
- * console.log(response.status); // e.g. 200
- * console.log(response.statusText); // e.g. "OK"
- * const jsonData = await response.json();
- */
-declare function fetch(
- input: Request | URL | string,
- init?: RequestInit,
-): Promise<Response>;
diff --git a/extensions/fetch/lib.rs b/extensions/fetch/lib.rs
deleted file mode 100644
index d9e016b0d..000000000
--- a/extensions/fetch/lib.rs
+++ /dev/null
@@ -1,567 +0,0 @@
-// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
-
-use data_url::DataUrl;
-use deno_core::error::bad_resource_id;
-use deno_core::error::null_opbuf;
-use deno_core::error::type_error;
-use deno_core::error::AnyError;
-use deno_core::futures::Future;
-use deno_core::futures::Stream;
-use deno_core::futures::StreamExt;
-use deno_core::include_js_files;
-use deno_core::op_async;
-use deno_core::op_sync;
-use deno_core::url::Url;
-use deno_core::AsyncRefCell;
-use deno_core::ByteString;
-use deno_core::CancelFuture;
-use deno_core::CancelHandle;
-use deno_core::CancelTryFuture;
-use deno_core::Canceled;
-use deno_core::Extension;
-use deno_core::OpState;
-use deno_core::RcRef;
-use deno_core::Resource;
-use deno_core::ResourceId;
-use deno_core::ZeroCopyBuf;
-use deno_tls::create_http_client;
-use deno_tls::rustls::RootCertStore;
-use deno_tls::Proxy;
-use deno_web::BlobStore;
-use http::header::CONTENT_LENGTH;
-use reqwest::header::HeaderName;
-use reqwest::header::HeaderValue;
-use reqwest::header::HOST;
-use reqwest::Body;
-use reqwest::Client;
-use reqwest::Method;
-use reqwest::RequestBuilder;
-use reqwest::Response;
-use serde::Deserialize;
-use serde::Serialize;
-use std::borrow::Cow;
-use std::cell::RefCell;
-use std::convert::From;
-use std::fs::File;
-use std::io::Read;
-use std::path::Path;
-use std::path::PathBuf;
-use std::pin::Pin;
-use std::rc::Rc;
-use tokio::io::AsyncReadExt;
-use tokio::sync::mpsc;
-use tokio_stream::wrappers::ReceiverStream;
-use tokio_util::io::StreamReader;
-
-pub use reqwest; // Re-export reqwest
-
-pub fn init<P: FetchPermissions + 'static>(
- user_agent: String,
- root_cert_store: Option<RootCertStore>,
- proxy: Option<Proxy>,
- request_builder_hook: Option<fn(RequestBuilder) -> RequestBuilder>,
- unsafely_ignore_certificate_errors: Option<Vec<String>>,
-) -> Extension {
- Extension::builder()
- .js(include_js_files!(
- prefix "deno:extensions/fetch",
- "01_fetch_util.js",
- "20_headers.js",
- "21_formdata.js",
- "22_body.js",
- "22_http_client.js",
- "23_request.js",
- "23_response.js",
- "26_fetch.js",
- ))
- .ops(vec![
- ("op_fetch", op_sync(op_fetch::<P>)),
- ("op_fetch_send", op_async(op_fetch_send)),
- ("op_fetch_request_write", op_async(op_fetch_request_write)),
- ("op_fetch_response_read", op_async(op_fetch_response_read)),
- ("op_create_http_client", op_sync(op_create_http_client::<P>)),
- ])
- .state(move |state| {
- state.put::<reqwest::Client>({
- create_http_client(
- user_agent.clone(),
- root_cert_store.clone(),
- None,
- proxy.clone(),
- unsafely_ignore_certificate_errors.clone(),
- )
- .unwrap()
- });
- state.put::<HttpClientDefaults>(HttpClientDefaults {
- user_agent: user_agent.clone(),
- root_cert_store: root_cert_store.clone(),
- proxy: proxy.clone(),
- request_builder_hook,
- unsafely_ignore_certificate_errors: unsafely_ignore_certificate_errors
- .clone(),
- });
- Ok(())
- })
- .build()
-}
-
-pub struct HttpClientDefaults {
- pub user_agent: String,
- pub root_cert_store: Option<RootCertStore>,
- pub proxy: Option<Proxy>,
- pub request_builder_hook: Option<fn(RequestBuilder) -> RequestBuilder>,
- pub unsafely_ignore_certificate_errors: Option<Vec<String>>,
-}
-
-pub trait FetchPermissions {
- fn check_net_url(&mut self, _url: &Url) -> Result<(), AnyError>;
- fn check_read(&mut self, _p: &Path) -> Result<(), AnyError>;
-}
-
-/// For use with `op_fetch` when the user does not want permissions.
-pub struct NoFetchPermissions;
-
-impl FetchPermissions for NoFetchPermissions {
- fn check_net_url(&mut self, _url: &Url) -> Result<(), AnyError> {
- Ok(())
- }
-
- fn check_read(&mut self, _p: &Path) -> Result<(), AnyError> {
- Ok(())
- }
-}
-
-pub fn get_declaration() -> PathBuf {
- PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("lib.deno_fetch.d.ts")
-}
-
-#[derive(Deserialize)]
-#[serde(rename_all = "camelCase")]
-pub struct FetchArgs {
- method: ByteString,
- url: String,
- headers: Vec<(ByteString, ByteString)>,
- client_rid: Option<u32>,
- has_body: bool,
- body_length: Option<u64>,
-}
-
-#[derive(Serialize)]
-#[serde(rename_all = "camelCase")]
-pub struct FetchReturn {
- request_rid: ResourceId,
- request_body_rid: Option<ResourceId>,
- cancel_handle_rid: Option<ResourceId>,
-}
-
-pub fn op_fetch<FP>(
- state: &mut OpState,
- args: FetchArgs,
- data: Option<ZeroCopyBuf>,
-) -> Result<FetchReturn, AnyError>
-where
- FP: FetchPermissions + 'static,
-{
- let client = if let Some(rid) = args.client_rid {
- let r = state
- .resource_table
- .get::<HttpClientResource>(rid)
- .ok_or_else(bad_resource_id)?;
- r.client.clone()
- } else {
- let client = state.borrow::<reqwest::Client>();
- client.clone()
- };
-
- let method = Method::from_bytes(&args.method)?;
- let url = Url::parse(&args.url)?;
-
- // Check scheme before asking for net permission
- let scheme = url.scheme();
- let (request_rid, request_body_rid, cancel_handle_rid) = match scheme {
- "http" | "https" => {
- let permissions = state.borrow_mut::<FP>();
- permissions.check_net_url(&url)?;
-
- let mut request = client.request(method, url);
-
- let request_body_rid = if args.has_body {
- match data {
- None => {
- // If no body is passed, we return a writer for streaming the body.
- let (tx, rx) = mpsc::channel::<std::io::Result<Vec<u8>>>(1);
-
- // If the size of the body is known, we include a content-length
- // header explicitly.
- if let Some(body_size) = args.body_length {
- request =
- request.header(CONTENT_LENGTH, HeaderValue::from(body_size))
- }
-
- request = request.body(Body::wrap_stream(ReceiverStream::new(rx)));
-
- let request_body_rid =
- state.resource_table.add(FetchRequestBodyResource {
- body: AsyncRefCell::new(tx),
- cancel: CancelHandle::default(),
- });
-
- Some(request_body_rid)
- }
- Some(data) => {
- // If a body is passed, we use it, and don't return a body for streaming.
- request = request.body(Vec::from(&*data));
- None
- }
- }
- } else {
- None
- };
-
- for (key, value) in args.headers {
- let name = HeaderName::from_bytes(&key).unwrap();
- let v = HeaderValue::from_bytes(&value).unwrap();
- if name != HOST {
- request = request.header(name, v);
- }
- }
-
- let defaults = state.borrow::<HttpClientDefaults>();
- if let Some(request_builder_hook) = defaults.request_builder_hook {
- request = request_builder_hook(request);
- }
-
- let cancel_handle = CancelHandle::new_rc();
- let cancel_handle_ = cancel_handle.clone();
-
- let fut = async move {
- request
- .send()
- .or_cancel(cancel_handle_)
- .await
- .map(|res| res.map_err(|err| type_error(err.to_string())))
- };
-
- let request_rid = state
- .resource_table
- .add(FetchRequestResource(Box::pin(fut)));
-
- let cancel_handle_rid =
- state.resource_table.add(FetchCancelHandle(cancel_handle));
-
- (request_rid, request_body_rid, Some(cancel_handle_rid))
- }
- "data" => {
- let data_url = DataUrl::process(url.as_str())
- .map_err(|e| type_error(format!("{:?}", e)))?;
-
- let (body, _) = data_url
- .decode_to_vec()
- .map_err(|e| type_error(format!("{:?}", e)))?;
-
- let response = http::Response::builder()
- .status(http::StatusCode::OK)
- .header(http::header::CONTENT_TYPE, data_url.mime_type().to_string())
- .body(reqwest::Body::from(body))?;
-
- let fut = async move { Ok(Ok(Response::from(response))) };
-
- let request_rid = state
- .resource_table
- .add(FetchRequestResource(Box::pin(fut)));
-
- (request_rid, None, None)
- }
- "blob" => {
- let blob_store = state.try_borrow::<BlobStore>().ok_or_else(|| {
- type_error("Blob URLs are not supported in this context.")
- })?;
-
- let blob = blob_store
- .get_object_url(url)?
- .ok_or_else(|| type_error("Blob for the given URL not found."))?;
-
- if method != "GET" {
- return Err(type_error("Blob URL fetch only supports GET method."));
- }
-
- let cancel_handle = CancelHandle::new_rc();
- let cancel_handle_ = cancel_handle.clone();
-
- let fut = async move {
- // TODO(lucacsonato): this should be a stream!
- let chunk = match blob.read_all().or_cancel(cancel_handle_).await? {
- Ok(chunk) => chunk,
- Err(err) => return Ok(Err(err)),
- };
-
- let res = http::Response::builder()
- .status(http::StatusCode::OK)
- .header(http::header::CONTENT_LENGTH, chunk.len())
- .header(http::header::CONTENT_TYPE, blob.media_type.clone())
- .body(reqwest::Body::from(chunk))
- .map_err(|err| type_error(err.to_string()));
-
- match res {
- Ok(response) => Ok(Ok(Response::from(response))),
- Err(err) => Ok(Err(err)),
- }
- };
-
- let request_rid = state
- .resource_table
- .add(FetchRequestResource(Box::pin(fut)));
-
- let cancel_handle_rid =
- state.resource_table.add(FetchCancelHandle(cancel_handle));
-
- (request_rid, None, Some(cancel_handle_rid))
- }
- _ => return Err(type_error(format!("scheme '{}' not supported", scheme))),
- };
-
- Ok(FetchReturn {
- request_rid,
- request_body_rid,
- cancel_handle_rid,
- })
-}
-
-#[derive(Serialize)]
-#[serde(rename_all = "camelCase")]
-pub struct FetchResponse {
- status: u16,
- status_text: String,
- headers: Vec<(ByteString, ByteString)>,
- url: String,
- response_rid: ResourceId,
-}
-
-pub async fn op_fetch_send(
- state: Rc<RefCell<OpState>>,
- rid: ResourceId,
- _: (),
-) -> Result<FetchResponse, AnyError> {
- let request = state
- .borrow_mut()
- .resource_table
- .take::<FetchRequestResource>(rid)
- .ok_or_else(bad_resource_id)?;
-
- let request = Rc::try_unwrap(request)
- .ok()
- .expect("multiple op_fetch_send ongoing");
-
- let res = match request.0.await {
- Ok(Ok(res)) => res,
- Ok(Err(err)) => return Err(type_error(err.to_string())),
- Err(_) => return Err(type_error("request was cancelled")),
- };
-
- //debug!("Fetch response {}", url);
- let status = res.status();
- let url = res.url().to_string();
- let mut res_headers = Vec::new();
- for (key, val) in res.headers().iter() {
- let key_bytes: &[u8] = key.as_ref();
- res_headers.push((
- ByteString(key_bytes.to_owned()),
- ByteString(val.as_bytes().to_owned()),
- ));
- }
-
- let stream: BytesStream = Box::pin(res.bytes_stream().map(|r| {
- r.map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err))
- }));
- let stream_reader = StreamReader::new(stream);
- let rid = state
- .borrow_mut()
- .resource_table
- .add(FetchResponseBodyResource {
- reader: AsyncRefCell::new(stream_reader),
- cancel: CancelHandle::default(),
- });
-
- Ok(FetchResponse {
- status: status.as_u16(),
- status_text: status.canonical_reason().unwrap_or("").to_string(),
- headers: res_headers,
- url,
- response_rid: rid,
- })
-}
-
-pub async fn op_fetch_request_write(
- state: Rc<RefCell<OpState>>,
- rid: ResourceId,
- data: Option<ZeroCopyBuf>,
-) -> Result<(), AnyError> {
- let data = data.ok_or_else(null_opbuf)?;
- let buf = Vec::from(&*data);
-
- let resource = state
- .borrow()
- .resource_table
- .get::<FetchRequestBodyResource>(rid)
- .ok_or_else(bad_resource_id)?;
- let body = RcRef::map(&resource, |r| &r.body).borrow_mut().await;
- let cancel = RcRef::map(resource, |r| &r.cancel);
- body.send(Ok(buf)).or_cancel(cancel).await?.map_err(|_| {
- type_error("request body receiver not connected (request closed)")
- })?;
-
- Ok(())
-}
-
-pub async fn op_fetch_response_read(
- state: Rc<RefCell<OpState>>,
- rid: ResourceId,
- data: Option<ZeroCopyBuf>,
-) -> Result<usize, AnyError> {
- let data = data.ok_or_else(null_opbuf)?;
-
- let resource = state
- .borrow()
- .resource_table
- .get::<FetchResponseBodyResource>(rid)
- .ok_or_else(bad_resource_id)?;
- let mut reader = RcRef::map(&resource, |r| &r.reader).borrow_mut().await;
- let cancel = RcRef::map(resource, |r| &r.cancel);
- let mut buf = data.clone();
- let read = reader.read(&mut buf).try_or_cancel(cancel).await?;
- Ok(read)
-}
-
-type CancelableResponseResult = Result<Result<Response, AnyError>, Canceled>;
-
-struct FetchRequestResource(
- Pin<Box<dyn Future<Output = CancelableResponseResult>>>,
-);
-
-impl Resource for FetchRequestResource {
- fn name(&self) -> Cow<str> {
- "fetchRequest".into()
- }
-}
-
-struct FetchCancelHandle(Rc<CancelHandle>);
-
-impl Resource for FetchCancelHandle {
- fn name(&self) -> Cow<str> {
- "fetchCancelHandle".into()
- }
-
- fn close(self: Rc<Self>) {
- self.0.cancel()
- }
-}
-
-struct FetchRequestBodyResource {
- body: AsyncRefCell<mpsc::Sender<std::io::Result<Vec<u8>>>>,
- cancel: CancelHandle,
-}
-
-impl Resource for FetchRequestBodyResource {
- fn name(&self) -> Cow<str> {
- "fetchRequestBody".into()
- }
-
- fn close(self: Rc<Self>) {
- self.cancel.cancel()
- }
-}
-
-type BytesStream =
- Pin<Box<dyn Stream<Item = Result<bytes::Bytes, std::io::Error>> + Unpin>>;
-
-struct FetchResponseBodyResource {
- reader: AsyncRefCell<StreamReader<BytesStream, bytes::Bytes>>,
- cancel: CancelHandle,
-}
-
-impl Resource for FetchResponseBodyResource {
- fn name(&self) -> Cow<str> {
- "fetchResponseBody".into()
- }
-
- fn close(self: Rc<Self>) {
- self.cancel.cancel()
- }
-}
-
-struct HttpClientResource {
- client: Client,
-}
-
-impl Resource for HttpClientResource {
- fn name(&self) -> Cow<str> {
- "httpClient".into()
- }
-}
-
-impl HttpClientResource {
- fn new(client: Client) -> Self {
- Self { client }
- }
-}
-
-#[derive(Deserialize, Default, Debug)]
-#[serde(rename_all = "camelCase")]
-#[serde(default)]
-pub struct CreateHttpClientOptions {
- ca_stores: Option<Vec<String>>,
- ca_file: Option<String>,
- ca_data: Option<ByteString>,
- proxy: Option<Proxy>,
-}
-
-pub fn op_create_http_client<FP>(
- state: &mut OpState,
- args: CreateHttpClientOptions,
- _: (),
-) -> Result<ResourceId, AnyError>
-where
- FP: FetchPermissions + 'static,
-{
- if let Some(ca_file) = args.ca_file.clone() {
- let permissions = state.borrow_mut::<FP>();
- permissions.check_read(&PathBuf::from(ca_file))?;
- }
-
- if let Some(proxy) = args.proxy.clone() {
- let permissions = state.borrow_mut::<FP>();
- let url = Url::parse(&proxy.url)?;
- permissions.check_net_url(&url)?;
- }
-
- let defaults = state.borrow::<HttpClientDefaults>();
- let cert_data =
- get_cert_data(args.ca_file.as_deref(), args.ca_data.as_deref())?;
-
- let client = create_http_client(
- defaults.user_agent.clone(),
- defaults.root_cert_store.clone(),
- cert_data,
- args.proxy,
- defaults.unsafely_ignore_certificate_errors.clone(),
- )
- .unwrap();
-
- let rid = state.resource_table.add(HttpClientResource::new(client));
- Ok(rid)
-}
-
-fn get_cert_data(
- ca_file: Option<&str>,
- ca_data: Option<&[u8]>,
-) -> Result<Option<Vec<u8>>, AnyError> {
- if let Some(ca_data) = ca_data {
- Ok(Some(ca_data.to_vec()))
- } else if let Some(ca_file) = ca_file {
- let mut buf = Vec::new();
- File::open(ca_file)?.read_to_end(&mut buf)?;
- Ok(Some(buf))
- } else {
- Ok(None)
- }
-}