2022-01-07 22:09:52 -05:00
|
|
|
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
|
2021-04-20 08:47:22 -04:00
|
|
|
|
|
|
|
// @ts-check
|
|
|
|
/// <reference path="../webidl/internal.d.ts" />
|
|
|
|
/// <reference path="../web/internal.d.ts" />
|
2021-06-10 09:26:10 -04:00
|
|
|
/// <reference path="../web/lib.deno_web.d.ts" />
|
2021-04-20 08:47:22 -04:00
|
|
|
/// <reference path="./internal.d.ts" />
|
2021-06-14 07:51:02 -04:00
|
|
|
/// <reference path="../web/06_streams_types.d.ts" />
|
2021-04-20 08:47:22 -04:00
|
|
|
/// <reference path="./lib.deno_fetch.d.ts" />
|
|
|
|
/// <reference lib="esnext" />
|
|
|
|
"use strict";
|
|
|
|
|
|
|
|
((window) => {
|
|
|
|
const webidl = window.__bootstrap.webidl;
|
2021-07-08 09:43:36 -04:00
|
|
|
const consoleInternal = window.__bootstrap.console;
|
2021-04-20 08:47:22 -04:00
|
|
|
const { HTTP_TOKEN_CODE_POINT_RE, byteUpperCase } = window.__bootstrap.infra;
|
|
|
|
const { URL } = window.__bootstrap.url;
|
|
|
|
const { guardFromHeaders } = window.__bootstrap.headers;
|
2022-08-18 08:05:02 -04:00
|
|
|
const { mixinBody, extractBody, InnerBody } = window.__bootstrap.fetchBody;
|
2021-04-20 08:47:22 -04:00
|
|
|
const { getLocationHref } = window.__bootstrap.location;
|
2022-03-20 09:31:12 -04:00
|
|
|
const { extractMimeType } = window.__bootstrap.mimesniff;
|
fix: a `Request` whose URL is a revoked blob URL should still fetch (#11947)
In the spec, a URL record has an associated "blob URL entry", which for
`blob:` URLs is populated during parsing to contain a reference to the
`Blob` object that backs that object URL. It is this blob URL entry that
the `fetch` API uses to resolve an object URL.
Therefore, since the `Request` constructor parses URL inputs, it will
have an associated blob URL entry which will be used when fetching, even
if the object URL has been revoked since the construction of the
`Request` object. (The `Request` constructor takes the URL as a string
and parses it, so the object URL must be live at the time it is called.)
This PR adds a new `blobFromObjectUrl` JS function (backed by a new
`op_blob_from_object_url` op) that, if the URL is a valid object URL,
returns a new `Blob` object whose parts are references to the same Rust
`BlobPart`s used by the original `Blob` object. It uses this function to
add a new `blobUrlEntry` field to inner requests, which will be `null`
or such a `Blob`, and then uses `Blob.prototype.stream()` as the
response's body. As a result of this, the `blob:` URL resolution from
`op_fetch` is now useless, and has been removed.
2021-09-08 05:29:21 -04:00
|
|
|
const { blobFromObjectUrl } = window.__bootstrap.file;
|
2021-04-20 08:47:22 -04:00
|
|
|
const {
|
|
|
|
headersFromHeaderList,
|
|
|
|
headerListFromHeaders,
|
|
|
|
fillHeaders,
|
|
|
|
getDecodeSplitHeader,
|
|
|
|
} = window.__bootstrap.headers;
|
2022-02-01 12:06:11 -05:00
|
|
|
const { HttpClientPrototype } = window.__bootstrap.fetch;
|
2021-06-06 09:37:17 -04:00
|
|
|
const abortSignal = window.__bootstrap.abortSignal;
|
2021-07-06 05:32:59 -04:00
|
|
|
const {
|
|
|
|
ArrayPrototypeMap,
|
|
|
|
ArrayPrototypeSlice,
|
|
|
|
ArrayPrototypeSplice,
|
|
|
|
ObjectKeys,
|
2022-02-01 12:06:11 -05:00
|
|
|
ObjectPrototypeIsPrototypeOf,
|
2021-07-06 05:32:59 -04:00
|
|
|
RegExpPrototypeTest,
|
2022-02-07 07:54:32 -05:00
|
|
|
SafeArrayIterator,
|
2021-07-06 05:32:59 -04:00
|
|
|
Symbol,
|
|
|
|
SymbolFor,
|
|
|
|
TypeError,
|
|
|
|
} = window.__bootstrap.primordials;
|
2021-04-20 08:47:22 -04:00
|
|
|
|
|
|
|
const _request = Symbol("request");
|
|
|
|
const _headers = Symbol("headers");
|
2022-07-03 22:11:52 -04:00
|
|
|
const _getHeaders = Symbol("get headers");
|
|
|
|
const _headersCache = Symbol("headers cache");
|
2021-06-06 09:37:17 -04:00
|
|
|
const _signal = Symbol("signal");
|
2021-04-20 08:47:22 -04:00
|
|
|
const _mimeType = Symbol("mime type");
|
|
|
|
const _body = Symbol("body");
|
2022-08-18 08:05:02 -04:00
|
|
|
const _flash = Symbol("flash");
|
|
|
|
const _url = Symbol("url");
|
|
|
|
const _method = Symbol("method");
|
2021-04-20 08:47:22 -04:00
|
|
|
|
2022-08-17 10:29:26 -04:00
|
|
|
/**
|
|
|
|
* @param {(() => string)[]} urlList
|
|
|
|
* @param {string[]} urlListProcessed
|
|
|
|
*/
|
|
|
|
function processUrlList(urlList, urlListProcessed) {
|
|
|
|
for (let i = 0; i < urlList.length; i++) {
|
|
|
|
if (urlListProcessed[i] === undefined) {
|
|
|
|
urlListProcessed[i] = urlList[i]();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return urlListProcessed;
|
|
|
|
}
|
|
|
|
|
2021-04-20 08:47:22 -04:00
|
|
|
/**
|
|
|
|
* @typedef InnerRequest
|
2022-08-17 10:29:26 -04:00
|
|
|
* @property {() => string} method
|
2021-04-20 08:47:22 -04:00
|
|
|
* @property {() => string} url
|
|
|
|
* @property {() => string} currentUrl
|
2022-07-03 22:11:52 -04:00
|
|
|
* @property {() => [string, string][]} headerList
|
2021-05-18 11:24:01 -04:00
|
|
|
* @property {null | typeof __window.bootstrap.fetchBody.InnerBody} body
|
2021-04-20 08:47:22 -04:00
|
|
|
* @property {"follow" | "error" | "manual"} redirectMode
|
|
|
|
* @property {number} redirectCount
|
2022-08-17 10:29:26 -04:00
|
|
|
* @property {(() => string)[]} urlList
|
|
|
|
* @property {string[]} urlListProcessed
|
2021-04-20 08:47:22 -04:00
|
|
|
* @property {number | null} clientRid NOTE: non standard extension for `Deno.HttpClient`.
|
fix: a `Request` whose URL is a revoked blob URL should still fetch (#11947)
In the spec, a URL record has an associated "blob URL entry", which for
`blob:` URLs is populated during parsing to contain a reference to the
`Blob` object that backs that object URL. It is this blob URL entry that
the `fetch` API uses to resolve an object URL.
Therefore, since the `Request` constructor parses URL inputs, it will
have an associated blob URL entry which will be used when fetching, even
if the object URL has been revoked since the construction of the
`Request` object. (The `Request` constructor takes the URL as a string
and parses it, so the object URL must be live at the time it is called.)
This PR adds a new `blobFromObjectUrl` JS function (backed by a new
`op_blob_from_object_url` op) that, if the URL is a valid object URL,
returns a new `Blob` object whose parts are references to the same Rust
`BlobPart`s used by the original `Blob` object. It uses this function to
add a new `blobUrlEntry` field to inner requests, which will be `null`
or such a `Blob`, and then uses `Blob.prototype.stream()` as the
response's body. As a result of this, the `blob:` URL resolution from
`op_fetch` is now useless, and has been removed.
2021-09-08 05:29:21 -04:00
|
|
|
* @property {Blob | null} blobUrlEntry
|
2021-04-20 08:47:22 -04:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
2022-08-17 10:29:26 -04:00
|
|
|
* @param {() => string} method
|
|
|
|
* @param {string | () => string} url
|
2022-07-03 22:11:52 -04:00
|
|
|
* @param {() => [string, string][]} headerList
|
2021-05-18 11:24:01 -04:00
|
|
|
* @param {typeof __window.bootstrap.fetchBody.InnerBody} body
|
2021-09-27 07:19:24 -04:00
|
|
|
* @param {boolean} maybeBlob
|
2022-08-17 10:29:26 -04:00
|
|
|
* @returns {InnerRequest}
|
2021-04-20 08:47:22 -04:00
|
|
|
*/
|
2021-09-27 07:19:24 -04:00
|
|
|
function newInnerRequest(method, url, headerList, body, maybeBlob) {
|
fix: a `Request` whose URL is a revoked blob URL should still fetch (#11947)
In the spec, a URL record has an associated "blob URL entry", which for
`blob:` URLs is populated during parsing to contain a reference to the
`Blob` object that backs that object URL. It is this blob URL entry that
the `fetch` API uses to resolve an object URL.
Therefore, since the `Request` constructor parses URL inputs, it will
have an associated blob URL entry which will be used when fetching, even
if the object URL has been revoked since the construction of the
`Request` object. (The `Request` constructor takes the URL as a string
and parses it, so the object URL must be live at the time it is called.)
This PR adds a new `blobFromObjectUrl` JS function (backed by a new
`op_blob_from_object_url` op) that, if the URL is a valid object URL,
returns a new `Blob` object whose parts are references to the same Rust
`BlobPart`s used by the original `Blob` object. It uses this function to
add a new `blobUrlEntry` field to inner requests, which will be `null`
or such a `Blob`, and then uses `Blob.prototype.stream()` as the
response's body. As a result of this, the `blob:` URL resolution from
`op_fetch` is now useless, and has been removed.
2021-09-08 05:29:21 -04:00
|
|
|
let blobUrlEntry = null;
|
2022-08-17 10:29:26 -04:00
|
|
|
if (maybeBlob && typeof url === "string" && url.startsWith("blob:")) {
|
fix: a `Request` whose URL is a revoked blob URL should still fetch (#11947)
In the spec, a URL record has an associated "blob URL entry", which for
`blob:` URLs is populated during parsing to contain a reference to the
`Blob` object that backs that object URL. It is this blob URL entry that
the `fetch` API uses to resolve an object URL.
Therefore, since the `Request` constructor parses URL inputs, it will
have an associated blob URL entry which will be used when fetching, even
if the object URL has been revoked since the construction of the
`Request` object. (The `Request` constructor takes the URL as a string
and parses it, so the object URL must be live at the time it is called.)
This PR adds a new `blobFromObjectUrl` JS function (backed by a new
`op_blob_from_object_url` op) that, if the URL is a valid object URL,
returns a new `Blob` object whose parts are references to the same Rust
`BlobPart`s used by the original `Blob` object. It uses this function to
add a new `blobUrlEntry` field to inner requests, which will be `null`
or such a `Blob`, and then uses `Blob.prototype.stream()` as the
response's body. As a result of this, the `blob:` URL resolution from
`op_fetch` is now useless, and has been removed.
2021-09-08 05:29:21 -04:00
|
|
|
blobUrlEntry = blobFromObjectUrl(url);
|
|
|
|
}
|
2021-04-20 08:47:22 -04:00
|
|
|
return {
|
2022-08-17 10:29:26 -04:00
|
|
|
methodInner: null,
|
|
|
|
get method() {
|
|
|
|
if (this.methodInner === null) {
|
|
|
|
try {
|
|
|
|
this.methodInner = method();
|
|
|
|
} catch {
|
|
|
|
throw new TypeError("cannot read method: request closed");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return this.methodInner;
|
|
|
|
},
|
|
|
|
set method(value) {
|
|
|
|
this.methodInner = value;
|
|
|
|
},
|
2022-07-03 22:11:52 -04:00
|
|
|
headerListInner: null,
|
|
|
|
get headerList() {
|
|
|
|
if (this.headerListInner === null) {
|
2022-07-12 13:31:37 -04:00
|
|
|
try {
|
|
|
|
this.headerListInner = headerList();
|
|
|
|
} catch {
|
|
|
|
throw new TypeError("cannot read headers: request closed");
|
|
|
|
}
|
2022-07-03 22:11:52 -04:00
|
|
|
}
|
|
|
|
return this.headerListInner;
|
|
|
|
},
|
|
|
|
set headerList(value) {
|
|
|
|
this.headerListInner = value;
|
|
|
|
},
|
2021-04-20 08:47:22 -04:00
|
|
|
body,
|
2021-09-27 05:13:27 -04:00
|
|
|
redirectMode: "follow",
|
|
|
|
redirectCount: 0,
|
2022-08-17 10:29:26 -04:00
|
|
|
urlList: [typeof url === "string" ? () => url : url],
|
|
|
|
urlListProcessed: [],
|
2021-09-27 05:13:27 -04:00
|
|
|
clientRid: null,
|
fix: a `Request` whose URL is a revoked blob URL should still fetch (#11947)
In the spec, a URL record has an associated "blob URL entry", which for
`blob:` URLs is populated during parsing to contain a reference to the
`Blob` object that backs that object URL. It is this blob URL entry that
the `fetch` API uses to resolve an object URL.
Therefore, since the `Request` constructor parses URL inputs, it will
have an associated blob URL entry which will be used when fetching, even
if the object URL has been revoked since the construction of the
`Request` object. (The `Request` constructor takes the URL as a string
and parses it, so the object URL must be live at the time it is called.)
This PR adds a new `blobFromObjectUrl` JS function (backed by a new
`op_blob_from_object_url` op) that, if the URL is a valid object URL,
returns a new `Blob` object whose parts are references to the same Rust
`BlobPart`s used by the original `Blob` object. It uses this function to
add a new `blobUrlEntry` field to inner requests, which will be `null`
or such a `Blob`, and then uses `Blob.prototype.stream()` as the
response's body. As a result of this, the `blob:` URL resolution from
`op_fetch` is now useless, and has been removed.
2021-09-08 05:29:21 -04:00
|
|
|
blobUrlEntry,
|
2021-09-27 05:13:27 -04:00
|
|
|
url() {
|
2022-08-17 10:29:26 -04:00
|
|
|
if (this.urlListProcessed[0] === undefined) {
|
|
|
|
try {
|
|
|
|
this.urlListProcessed[0] = this.urlList[0]();
|
|
|
|
} catch {
|
|
|
|
throw new TypeError("cannot read url: request closed");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return this.urlListProcessed[0];
|
2021-09-27 05:13:27 -04:00
|
|
|
},
|
|
|
|
currentUrl() {
|
2022-08-17 10:29:26 -04:00
|
|
|
const currentIndex = this.urlList.length - 1;
|
|
|
|
if (this.urlListProcessed[currentIndex] === undefined) {
|
|
|
|
try {
|
|
|
|
this.urlListProcessed[currentIndex] = this.urlList[currentIndex]();
|
|
|
|
} catch {
|
|
|
|
throw new TypeError("cannot read url: request closed");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return this.urlListProcessed[currentIndex];
|
2021-09-27 05:13:27 -04:00
|
|
|
},
|
2021-04-20 08:47:22 -04:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* https://fetch.spec.whatwg.org/#concept-request-clone
|
2021-04-28 10:08:51 -04:00
|
|
|
* @param {InnerRequest} request
|
2021-04-20 08:47:22 -04:00
|
|
|
* @returns {InnerRequest}
|
|
|
|
*/
|
|
|
|
function cloneInnerRequest(request) {
|
2021-07-06 05:32:59 -04:00
|
|
|
const headerList = [
|
2022-02-07 07:54:32 -05:00
|
|
|
...new SafeArrayIterator(
|
|
|
|
ArrayPrototypeMap(request.headerList, (x) => [x[0], x[1]]),
|
|
|
|
),
|
2021-07-06 05:32:59 -04:00
|
|
|
];
|
2021-04-20 08:47:22 -04:00
|
|
|
let body = null;
|
|
|
|
if (request.body !== null) {
|
|
|
|
body = request.body.clone();
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
method: request.method,
|
|
|
|
headerList,
|
|
|
|
body,
|
|
|
|
redirectMode: request.redirectMode,
|
|
|
|
redirectCount: request.redirectCount,
|
|
|
|
urlList: request.urlList,
|
2022-08-17 10:29:26 -04:00
|
|
|
urlListProcessed: request.urlListProcessed,
|
2021-04-20 08:47:22 -04:00
|
|
|
clientRid: request.clientRid,
|
fix: a `Request` whose URL is a revoked blob URL should still fetch (#11947)
In the spec, a URL record has an associated "blob URL entry", which for
`blob:` URLs is populated during parsing to contain a reference to the
`Blob` object that backs that object URL. It is this blob URL entry that
the `fetch` API uses to resolve an object URL.
Therefore, since the `Request` constructor parses URL inputs, it will
have an associated blob URL entry which will be used when fetching, even
if the object URL has been revoked since the construction of the
`Request` object. (The `Request` constructor takes the URL as a string
and parses it, so the object URL must be live at the time it is called.)
This PR adds a new `blobFromObjectUrl` JS function (backed by a new
`op_blob_from_object_url` op) that, if the URL is a valid object URL,
returns a new `Blob` object whose parts are references to the same Rust
`BlobPart`s used by the original `Blob` object. It uses this function to
add a new `blobUrlEntry` field to inner requests, which will be `null`
or such a `Blob`, and then uses `Blob.prototype.stream()` as the
response's body. As a result of this, the `blob:` URL resolution from
`op_fetch` is now useless, and has been removed.
2021-09-08 05:29:21 -04:00
|
|
|
blobUrlEntry: request.blobUrlEntry,
|
2021-09-27 05:13:27 -04:00
|
|
|
url() {
|
2022-08-17 10:29:26 -04:00
|
|
|
if (this.urlListProcessed[0] === undefined) {
|
|
|
|
try {
|
|
|
|
this.urlListProcessed[0] = this.urlList[0]();
|
|
|
|
} catch {
|
|
|
|
throw new TypeError("cannot read url: request closed");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return this.urlListProcessed[0];
|
2021-09-27 05:13:27 -04:00
|
|
|
},
|
|
|
|
currentUrl() {
|
2022-08-17 10:29:26 -04:00
|
|
|
const currentIndex = this.urlList.length - 1;
|
|
|
|
if (this.urlListProcessed[currentIndex] === undefined) {
|
|
|
|
try {
|
|
|
|
this.urlListProcessed[currentIndex] = this.urlList[currentIndex]();
|
|
|
|
} catch {
|
|
|
|
throw new TypeError("cannot read url: request closed");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return this.urlListProcessed[currentIndex];
|
2021-09-27 05:13:27 -04:00
|
|
|
},
|
2021-04-20 08:47:22 -04:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @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
|
2021-07-06 05:32:59 -04:00
|
|
|
if (!RegExpPrototypeTest(HTTP_TOKEN_CODE_POINT_RE, m)) {
|
2021-04-20 08:47:22 -04:00
|
|
|
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} */
|
2022-07-03 22:11:52 -04:00
|
|
|
[_headersCache];
|
|
|
|
[_getHeaders];
|
|
|
|
|
|
|
|
/** @type {Headers} */
|
|
|
|
get [_headers]() {
|
|
|
|
if (this[_headersCache] === undefined) {
|
|
|
|
this[_headersCache] = this[_getHeaders]();
|
|
|
|
}
|
|
|
|
return this[_headersCache];
|
|
|
|
}
|
|
|
|
|
|
|
|
set [_headers](value) {
|
|
|
|
this[_headersCache] = value;
|
|
|
|
}
|
|
|
|
|
2021-06-06 09:37:17 -04:00
|
|
|
/** @type {AbortSignal} */
|
|
|
|
[_signal];
|
2021-04-20 08:47:22 -04:00
|
|
|
get [_mimeType]() {
|
2021-11-22 19:23:11 -05:00
|
|
|
const values = getDecodeSplitHeader(
|
|
|
|
headerListFromHeaders(this[_headers]),
|
|
|
|
"Content-Type",
|
|
|
|
);
|
2022-03-20 09:31:12 -04:00
|
|
|
return extractMimeType(values);
|
2021-04-20 08:47:22 -04:00
|
|
|
}
|
|
|
|
get [_body]() {
|
2022-08-18 08:05:02 -04:00
|
|
|
if (this[_flash]) {
|
|
|
|
return this[_flash].body;
|
|
|
|
} else {
|
|
|
|
return this[_request].body;
|
|
|
|
}
|
2021-04-20 08:47:22 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* https://fetch.spec.whatwg.org/#dom-request
|
2021-04-28 10:08:51 -04:00
|
|
|
* @param {RequestInfo} input
|
|
|
|
* @param {RequestInit} init
|
2021-04-20 08:47:22 -04:00
|
|
|
*/
|
|
|
|
constructor(input, init = {}) {
|
|
|
|
const prefix = "Failed to construct 'Request'";
|
|
|
|
webidl.requiredArguments(arguments.length, 1, { prefix });
|
2021-09-23 05:40:58 -04:00
|
|
|
input = webidl.converters["RequestInfo_DOMString"](input, {
|
2021-04-20 08:47:22 -04:00
|
|
|
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();
|
|
|
|
|
2021-06-06 09:37:17 -04:00
|
|
|
// 4.
|
|
|
|
let signal = null;
|
|
|
|
|
2021-04-20 08:47:22 -04:00
|
|
|
// 5.
|
|
|
|
if (typeof input === "string") {
|
|
|
|
const parsedURL = new URL(input, baseURL);
|
2022-08-17 10:29:26 -04:00
|
|
|
request = newInnerRequest(
|
|
|
|
() => "GET",
|
|
|
|
parsedURL.href,
|
|
|
|
() => [],
|
|
|
|
null,
|
|
|
|
true,
|
|
|
|
);
|
2021-04-20 08:47:22 -04:00
|
|
|
} else { // 6.
|
2022-02-01 12:06:11 -05:00
|
|
|
if (!ObjectPrototypeIsPrototypeOf(RequestPrototype, input)) {
|
|
|
|
throw new TypeError("Unreachable");
|
|
|
|
}
|
2021-04-20 08:47:22 -04:00
|
|
|
request = input[_request];
|
2021-06-06 09:37:17 -04:00
|
|
|
signal = input[_signal];
|
2021-04-20 08:47:22 -04:00
|
|
|
}
|
|
|
|
|
2021-06-06 09:37:17 -04:00
|
|
|
// 12.
|
|
|
|
// TODO(lucacasonato): create a copy of `request`
|
|
|
|
|
2021-04-20 08:47:22 -04:00
|
|
|
// 22.
|
|
|
|
if (init.redirect !== undefined) {
|
|
|
|
request.redirectMode = init.redirect;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 25.
|
|
|
|
if (init.method !== undefined) {
|
|
|
|
let method = init.method;
|
|
|
|
method = validateAndNormalizeMethod(method);
|
|
|
|
request.method = method;
|
|
|
|
}
|
|
|
|
|
2021-06-06 09:37:17 -04:00
|
|
|
// 26.
|
|
|
|
if (init.signal !== undefined) {
|
|
|
|
signal = init.signal;
|
|
|
|
}
|
|
|
|
|
2021-04-20 08:47:22 -04:00
|
|
|
// NOTE: non standard extension. This handles Deno.HttpClient parameter
|
|
|
|
if (init.client !== undefined) {
|
2022-02-01 12:06:11 -05:00
|
|
|
if (
|
|
|
|
init.client !== null &&
|
|
|
|
!ObjectPrototypeIsPrototypeOf(HttpClientPrototype, init.client)
|
|
|
|
) {
|
2021-04-20 08:47:22 -04:00
|
|
|
throw webidl.makeException(
|
|
|
|
TypeError,
|
|
|
|
"`client` must be a Deno.HttpClient",
|
|
|
|
{ prefix, context: "Argument 2" },
|
|
|
|
);
|
|
|
|
}
|
|
|
|
request.clientRid = init.client?.rid ?? null;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 27.
|
|
|
|
this[_request] = request;
|
|
|
|
|
2021-06-06 09:37:17 -04:00
|
|
|
// 28.
|
|
|
|
this[_signal] = abortSignal.newSignal();
|
2021-06-23 10:00:23 -04:00
|
|
|
|
|
|
|
// 29.
|
2021-06-06 09:37:17 -04:00
|
|
|
if (signal !== null) {
|
|
|
|
abortSignal.follow(this[_signal], signal);
|
|
|
|
}
|
|
|
|
|
2021-06-23 10:00:23 -04:00
|
|
|
// 30.
|
2021-04-20 08:47:22 -04:00
|
|
|
this[_headers] = headersFromHeaderList(request.headerList, "request");
|
|
|
|
|
2021-06-23 10:00:23 -04:00
|
|
|
// 32.
|
2021-07-06 05:32:59 -04:00
|
|
|
if (ObjectKeys(init).length > 0) {
|
|
|
|
let headers = ArrayPrototypeSlice(
|
|
|
|
headerListFromHeaders(this[_headers]),
|
2021-04-29 13:56:59 -04:00
|
|
|
0,
|
|
|
|
headerListFromHeaders(this[_headers]).length,
|
|
|
|
);
|
2021-04-20 08:47:22 -04:00
|
|
|
if (init.headers !== undefined) {
|
|
|
|
headers = init.headers;
|
|
|
|
}
|
2021-07-06 05:32:59 -04:00
|
|
|
ArrayPrototypeSplice(
|
|
|
|
headerListFromHeaders(this[_headers]),
|
2021-04-20 08:47:22 -04:00
|
|
|
0,
|
|
|
|
headerListFromHeaders(this[_headers]).length,
|
|
|
|
);
|
|
|
|
fillHeaders(this[_headers], headers);
|
|
|
|
}
|
|
|
|
|
2021-06-23 10:00:23 -04:00
|
|
|
// 33.
|
2021-04-20 08:47:22 -04:00
|
|
|
let inputBody = null;
|
2022-02-01 12:06:11 -05:00
|
|
|
if (ObjectPrototypeIsPrototypeOf(RequestPrototype, input)) {
|
2021-04-20 08:47:22 -04:00
|
|
|
inputBody = input[_body];
|
|
|
|
}
|
|
|
|
|
2021-06-23 10:00:23 -04:00
|
|
|
// 34.
|
2021-04-20 08:47:22 -04:00
|
|
|
if (
|
|
|
|
(request.method === "GET" || request.method === "HEAD") &&
|
|
|
|
((init.body !== undefined && init.body !== null) ||
|
|
|
|
inputBody !== null)
|
|
|
|
) {
|
2021-05-27 06:25:30 -04:00
|
|
|
throw new TypeError("Request with GET/HEAD method cannot have body.");
|
2021-04-20 08:47:22 -04:00
|
|
|
}
|
|
|
|
|
2021-06-23 10:00:23 -04:00
|
|
|
// 35.
|
2021-04-20 08:47:22 -04:00
|
|
|
let initBody = null;
|
|
|
|
|
2021-06-23 10:00:23 -04:00
|
|
|
// 36.
|
2021-04-20 08:47:22 -04:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-23 10:00:23 -04:00
|
|
|
// 37.
|
2021-04-20 08:47:22 -04:00
|
|
|
const inputOrInitBody = initBody ?? inputBody;
|
|
|
|
|
|
|
|
// 39.
|
2021-06-23 10:00:23 -04:00
|
|
|
let finalBody = inputOrInitBody;
|
2021-04-20 08:47:22 -04:00
|
|
|
|
|
|
|
// 40.
|
2021-06-23 10:00:23 -04:00
|
|
|
if (initBody === null && inputBody !== null) {
|
|
|
|
if (input[_body] && input[_body].unusable()) {
|
|
|
|
throw new TypeError("Input request's body is unusable.");
|
|
|
|
}
|
|
|
|
finalBody = inputBody.createProxy();
|
|
|
|
}
|
2021-06-06 09:37:17 -04:00
|
|
|
|
|
|
|
// 41.
|
2021-06-23 10:00:23 -04:00
|
|
|
request.body = finalBody;
|
2021-04-20 08:47:22 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
get method() {
|
2022-02-01 12:06:11 -05:00
|
|
|
webidl.assertBranded(this, RequestPrototype);
|
2022-08-18 08:05:02 -04:00
|
|
|
if (this[_method]) {
|
|
|
|
return this[_method];
|
|
|
|
}
|
|
|
|
if (this[_flash]) {
|
|
|
|
this[_method] = this[_flash].methodCb();
|
|
|
|
return this[_method];
|
|
|
|
} else {
|
|
|
|
this[_method] = this[_request].method;
|
|
|
|
return this[_method];
|
|
|
|
}
|
2021-04-20 08:47:22 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
get url() {
|
2022-02-01 12:06:11 -05:00
|
|
|
webidl.assertBranded(this, RequestPrototype);
|
2022-08-18 08:05:02 -04:00
|
|
|
if (this[_url]) {
|
|
|
|
return this[_url];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this[_flash]) {
|
|
|
|
this[_url] = this[_flash].urlCb();
|
|
|
|
return this[_url];
|
|
|
|
} else {
|
|
|
|
this[_url] = this[_request].url();
|
|
|
|
return this[_url];
|
|
|
|
}
|
2021-04-20 08:47:22 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
get headers() {
|
2022-02-01 12:06:11 -05:00
|
|
|
webidl.assertBranded(this, RequestPrototype);
|
2021-04-20 08:47:22 -04:00
|
|
|
return this[_headers];
|
|
|
|
}
|
|
|
|
|
|
|
|
get redirect() {
|
2022-02-01 12:06:11 -05:00
|
|
|
webidl.assertBranded(this, RequestPrototype);
|
2022-08-18 08:05:02 -04:00
|
|
|
if (this[_flash]) {
|
|
|
|
return this[_flash].redirectMode;
|
|
|
|
}
|
2021-04-20 08:47:22 -04:00
|
|
|
return this[_request].redirectMode;
|
|
|
|
}
|
|
|
|
|
2021-06-06 09:37:17 -04:00
|
|
|
get signal() {
|
2022-02-01 12:06:11 -05:00
|
|
|
webidl.assertBranded(this, RequestPrototype);
|
2021-06-06 09:37:17 -04:00
|
|
|
return this[_signal];
|
|
|
|
}
|
|
|
|
|
2021-04-20 08:47:22 -04:00
|
|
|
clone() {
|
2022-02-01 12:06:11 -05:00
|
|
|
webidl.assertBranded(this, RequestPrototype);
|
2021-04-20 08:47:22 -04:00
|
|
|
if (this[_body] && this[_body].unusable()) {
|
|
|
|
throw new TypeError("Body is unusable.");
|
|
|
|
}
|
2022-08-18 08:05:02 -04:00
|
|
|
let newReq;
|
|
|
|
if (this[_flash]) {
|
|
|
|
newReq = cloneInnerRequest(this[_flash]);
|
|
|
|
} else {
|
|
|
|
newReq = cloneInnerRequest(this[_request]);
|
|
|
|
}
|
2021-06-06 09:37:17 -04:00
|
|
|
const newSignal = abortSignal.newSignal();
|
|
|
|
abortSignal.follow(newSignal, this[_signal]);
|
|
|
|
return fromInnerRequest(
|
|
|
|
newReq,
|
|
|
|
newSignal,
|
|
|
|
guardFromHeaders(this[_headers]),
|
|
|
|
);
|
2021-04-20 08:47:22 -04:00
|
|
|
}
|
|
|
|
|
2021-07-06 05:32:59 -04:00
|
|
|
[SymbolFor("Deno.customInspect")](inspect) {
|
2021-07-08 09:43:36 -04:00
|
|
|
return inspect(consoleInternal.createFilteredInspectProxy({
|
|
|
|
object: this,
|
2022-02-01 12:06:11 -05:00
|
|
|
evaluate: ObjectPrototypeIsPrototypeOf(RequestPrototype, this),
|
2021-07-08 09:43:36 -04:00
|
|
|
keys: [
|
|
|
|
"bodyUsed",
|
|
|
|
"headers",
|
|
|
|
"method",
|
|
|
|
"redirect",
|
|
|
|
"url",
|
|
|
|
],
|
|
|
|
}));
|
2021-04-20 08:47:22 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-07 04:04:10 -04:00
|
|
|
webidl.configurePrototype(Request);
|
2022-02-01 12:06:11 -05:00
|
|
|
const RequestPrototype = Request.prototype;
|
|
|
|
mixinBody(RequestPrototype, _body, _mimeType);
|
2021-05-26 17:44:42 -04:00
|
|
|
|
2021-04-20 08:47:22 -04:00
|
|
|
webidl.converters["Request"] = webidl.createInterfaceConverter(
|
|
|
|
"Request",
|
2022-02-01 12:06:11 -05:00
|
|
|
RequestPrototype,
|
2021-04-20 08:47:22 -04:00
|
|
|
);
|
2021-09-23 05:40:58 -04:00
|
|
|
webidl.converters["RequestInfo_DOMString"] = (V, opts) => {
|
2021-04-20 08:47:22 -04:00
|
|
|
// Union for (Request or USVString)
|
|
|
|
if (typeof V == "object") {
|
2022-02-01 12:06:11 -05:00
|
|
|
if (ObjectPrototypeIsPrototypeOf(RequestPrototype, V)) {
|
2021-04-20 08:47:22 -04:00
|
|
|
return webidl.converters["Request"](V, opts);
|
|
|
|
}
|
|
|
|
}
|
2022-03-24 19:36:30 -04:00
|
|
|
// Passed to new URL(...) which implicitly converts DOMString -> USVString
|
2021-09-23 05:40:58 -04:00
|
|
|
return webidl.converters["DOMString"](V, opts);
|
2021-04-20 08:47:22 -04:00
|
|
|
};
|
|
|
|
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(
|
2021-09-25 09:30:31 -04:00
|
|
|
webidl.converters["BodyInit_DOMString"],
|
2021-04-20 08:47:22 -04:00
|
|
|
),
|
|
|
|
},
|
|
|
|
{ key: "redirect", converter: webidl.converters["RequestRedirect"] },
|
2021-06-06 09:37:17 -04:00
|
|
|
{
|
|
|
|
key: "signal",
|
|
|
|
converter: webidl.createNullableConverter(
|
|
|
|
webidl.converters["AbortSignal"],
|
|
|
|
),
|
|
|
|
},
|
2021-04-20 08:47:22 -04:00
|
|
|
{ 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}
|
|
|
|
*/
|
2021-06-06 09:37:17 -04:00
|
|
|
function fromInnerRequest(inner, signal, guard) {
|
2021-04-20 08:47:22 -04:00
|
|
|
const request = webidl.createBranded(Request);
|
|
|
|
request[_request] = inner;
|
2021-06-06 09:37:17 -04:00
|
|
|
request[_signal] = signal;
|
2022-07-03 22:11:52 -04:00
|
|
|
request[_getHeaders] = () => headersFromHeaderList(inner.headerList, guard);
|
2021-04-20 08:47:22 -04:00
|
|
|
return request;
|
|
|
|
}
|
|
|
|
|
2022-08-18 08:05:02 -04:00
|
|
|
/**
|
|
|
|
* @param {number} serverId
|
|
|
|
* @param {number} streamRid
|
|
|
|
* @param {ReadableStream} body
|
|
|
|
* @param {() => string} methodCb
|
|
|
|
* @param {() => string} urlCb
|
|
|
|
* @param {() => [string, string][]} headersCb
|
|
|
|
* @returns {Request}
|
|
|
|
*/
|
|
|
|
function fromFlashRequest(
|
|
|
|
serverId,
|
|
|
|
streamRid,
|
|
|
|
body,
|
|
|
|
methodCb,
|
|
|
|
urlCb,
|
|
|
|
headersCb,
|
|
|
|
) {
|
|
|
|
const request = webidl.createBranded(Request);
|
|
|
|
request[_flash] = {
|
|
|
|
body: body !== null ? new InnerBody(body) : null,
|
|
|
|
methodCb,
|
|
|
|
urlCb,
|
|
|
|
streamRid,
|
|
|
|
serverId,
|
|
|
|
redirectMode: "follow",
|
|
|
|
redirectCount: 0,
|
|
|
|
};
|
|
|
|
request[_getHeaders] = () => headersFromHeaderList(headersCb(), "request");
|
|
|
|
return request;
|
|
|
|
}
|
|
|
|
|
2021-04-20 08:47:22 -04:00
|
|
|
window.__bootstrap.fetch ??= {};
|
|
|
|
window.__bootstrap.fetch.Request = Request;
|
|
|
|
window.__bootstrap.fetch.toInnerRequest = toInnerRequest;
|
2022-08-18 08:05:02 -04:00
|
|
|
window.__bootstrap.fetch.fromFlashRequest = fromFlashRequest;
|
2021-04-20 08:47:22 -04:00
|
|
|
window.__bootstrap.fetch.fromInnerRequest = fromInnerRequest;
|
|
|
|
window.__bootstrap.fetch.newInnerRequest = newInnerRequest;
|
2022-08-17 10:29:26 -04:00
|
|
|
window.__bootstrap.fetch.processUrlList = processUrlList;
|
2022-08-18 08:05:02 -04:00
|
|
|
window.__bootstrap.fetch._flash = _flash;
|
2021-04-20 08:47:22 -04:00
|
|
|
})(globalThis);
|