mirror of
https://github.com/denoland/deno.git
synced 2025-03-03 09:31:22 -05:00
refactor: use primordials in extensions/fetch (#11266)
This commit is contained in:
parent
e8258e0210
commit
ab6b0cefd3
7 changed files with 307 additions and 134 deletions
|
@ -2,6 +2,7 @@
|
|||
"use strict";
|
||||
|
||||
((window) => {
|
||||
const { TypeError } = window.__bootstrap.primordials;
|
||||
function requiredArguments(
|
||||
name,
|
||||
length,
|
||||
|
|
|
@ -22,6 +22,25 @@
|
|||
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");
|
||||
|
@ -42,8 +61,16 @@
|
|||
* @returns {string}
|
||||
*/
|
||||
function normalizeHeaderValue(potentialValue) {
|
||||
potentialValue = potentialValue.replaceAll(HTTP_WHITESPACE_PREFIX_RE, "");
|
||||
potentialValue = potentialValue.replaceAll(HTTP_WHITESPACE_SUFFIX_RE, "");
|
||||
potentialValue = StringPrototypeReplaceAll(
|
||||
potentialValue,
|
||||
HTTP_WHITESPACE_PREFIX_RE,
|
||||
"",
|
||||
);
|
||||
potentialValue = StringPrototypeReplaceAll(
|
||||
potentialValue,
|
||||
HTTP_WHITESPACE_SUFFIX_RE,
|
||||
"",
|
||||
);
|
||||
return potentialValue;
|
||||
}
|
||||
|
||||
|
@ -52,7 +79,7 @@
|
|||
* @param {HeadersInit} object
|
||||
*/
|
||||
function fillHeaders(headers, object) {
|
||||
if (Array.isArray(object)) {
|
||||
if (ArrayIsArray(object)) {
|
||||
for (const header of object) {
|
||||
if (header.length !== 2) {
|
||||
throw new TypeError(
|
||||
|
@ -62,7 +89,7 @@
|
|||
appendHeader(headers, header[0], header[1]);
|
||||
}
|
||||
} else {
|
||||
for (const key of Object.keys(object)) {
|
||||
for (const key of ObjectKeys(object)) {
|
||||
appendHeader(headers, key, object[key]);
|
||||
}
|
||||
}
|
||||
|
@ -79,11 +106,13 @@
|
|||
value = normalizeHeaderValue(value);
|
||||
|
||||
// 2.
|
||||
if (!HTTP_TOKEN_CODE_POINT_RE.test(name)) {
|
||||
if (!RegExpPrototypeTest(HTTP_TOKEN_CODE_POINT_RE, name)) {
|
||||
throw new TypeError("Header name is not valid.");
|
||||
}
|
||||
if (
|
||||
value.includes("\x00") || value.includes("\x0A") || value.includes("\x0D")
|
||||
StringPrototypeIncludes(value, "\x00") ||
|
||||
StringPrototypeIncludes(value, "\x0A") ||
|
||||
StringPrototypeIncludes(value, "\x0D")
|
||||
) {
|
||||
throw new TypeError("Header value is not valid.");
|
||||
}
|
||||
|
@ -96,7 +125,7 @@
|
|||
// 7.
|
||||
const list = headers[_headerList];
|
||||
name = byteLowerCase(name);
|
||||
list.push([name, value]);
|
||||
ArrayPrototypePush(list, [name, value]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -106,13 +135,14 @@
|
|||
*/
|
||||
function getHeader(list, name) {
|
||||
const lowercaseName = byteLowerCase(name);
|
||||
const entries = list
|
||||
.filter((entry) => entry[0] === lowercaseName)
|
||||
.map((entry) => entry[1]);
|
||||
const entries = ArrayPrototypeMap(
|
||||
ArrayPrototypeFilter(list, (entry) => entry[0] === lowercaseName),
|
||||
(entry) => entry[1],
|
||||
);
|
||||
if (entries.length === 0) {
|
||||
return null;
|
||||
} else {
|
||||
return entries.join("\x2C\x20");
|
||||
return ArrayPrototypeJoin(entries, "\x2C\x20");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -153,10 +183,10 @@
|
|||
}
|
||||
}
|
||||
|
||||
value = value.replaceAll(HTTP_TAB_OR_SPACE_PREFIX_RE, "");
|
||||
value = value.replaceAll(HTTP_TAB_OR_SPACE_SUFFIX_RE, "");
|
||||
value = StringPrototypeReplaceAll(value, HTTP_TAB_OR_SPACE_PREFIX_RE, "");
|
||||
value = StringPrototypeReplaceAll(value, HTTP_TAB_OR_SPACE_SUFFIX_RE, "");
|
||||
|
||||
values.push(value);
|
||||
ArrayPrototypePush(values, value);
|
||||
value = "";
|
||||
}
|
||||
return values;
|
||||
|
@ -184,7 +214,7 @@
|
|||
// 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") {
|
||||
cookies.push([name, value]);
|
||||
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
|
||||
|
@ -200,13 +230,16 @@
|
|||
}
|
||||
}
|
||||
|
||||
return [...Object.entries(headers), ...cookies].sort((a, b) => {
|
||||
const akey = a[0];
|
||||
const bkey = b[0];
|
||||
if (akey > bkey) return 1;
|
||||
if (akey < bkey) return -1;
|
||||
return 0;
|
||||
});
|
||||
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] */
|
||||
|
@ -256,7 +289,7 @@
|
|||
context: "Argument 1",
|
||||
});
|
||||
|
||||
if (!HTTP_TOKEN_CODE_POINT_RE.test(name)) {
|
||||
if (!RegExpPrototypeTest(HTTP_TOKEN_CODE_POINT_RE, name)) {
|
||||
throw new TypeError("Header name is not valid.");
|
||||
}
|
||||
if (this[_guard] == "immutable") {
|
||||
|
@ -267,7 +300,7 @@
|
|||
name = byteLowerCase(name);
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
if (list[i][0] === name) {
|
||||
list.splice(i, 1);
|
||||
ArrayPrototypeSplice(list, i, 1);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
@ -284,7 +317,7 @@
|
|||
context: "Argument 1",
|
||||
});
|
||||
|
||||
if (!HTTP_TOKEN_CODE_POINT_RE.test(name)) {
|
||||
if (!RegExpPrototypeTest(HTTP_TOKEN_CODE_POINT_RE, name)) {
|
||||
throw new TypeError("Header name is not valid.");
|
||||
}
|
||||
|
||||
|
@ -303,7 +336,7 @@
|
|||
context: "Argument 1",
|
||||
});
|
||||
|
||||
if (!HTTP_TOKEN_CODE_POINT_RE.test(name)) {
|
||||
if (!RegExpPrototypeTest(HTTP_TOKEN_CODE_POINT_RE, name)) {
|
||||
throw new TypeError("Header name is not valid.");
|
||||
}
|
||||
|
||||
|
@ -337,12 +370,13 @@
|
|||
value = normalizeHeaderValue(value);
|
||||
|
||||
// 2.
|
||||
if (!HTTP_TOKEN_CODE_POINT_RE.test(name)) {
|
||||
if (!RegExpPrototypeTest(HTTP_TOKEN_CODE_POINT_RE, name)) {
|
||||
throw new TypeError("Header name is not valid.");
|
||||
}
|
||||
if (
|
||||
value.includes("\x00") || value.includes("\x0A") ||
|
||||
value.includes("\x0D")
|
||||
StringPrototypeIncludes(value, "\x00") ||
|
||||
StringPrototypeIncludes(value, "\x0A") ||
|
||||
StringPrototypeIncludes(value, "\x0D")
|
||||
) {
|
||||
throw new TypeError("Header value is not valid.");
|
||||
}
|
||||
|
@ -360,17 +394,17 @@
|
|||
list[i][1] = value;
|
||||
added = true;
|
||||
} else {
|
||||
list.splice(i, 1);
|
||||
ArrayPrototypeSplice(list, i, 1);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!added) {
|
||||
list.push([name, value]);
|
||||
ArrayPrototypePush(list, [name, value]);
|
||||
}
|
||||
}
|
||||
|
||||
[Symbol.for("Deno.privateCustomInspect")](inspect) {
|
||||
[SymbolFor("Deno.privateCustomInspect")](inspect) {
|
||||
const headers = {};
|
||||
for (const header of this) {
|
||||
headers[header[0]] = header[1];
|
||||
|
@ -378,7 +412,7 @@
|
|||
return `Headers ${inspect(headers)}`;
|
||||
}
|
||||
|
||||
get [Symbol.toStringTag]() {
|
||||
get [SymbolToStringTag]() {
|
||||
return "Headers";
|
||||
}
|
||||
}
|
||||
|
@ -390,7 +424,7 @@
|
|||
webidl.converters["HeadersInit"] = (V, opts) => {
|
||||
// Union for (sequence<sequence<ByteString>> or record<ByteString, ByteString>)
|
||||
if (webidl.type(V) === "Object" && V !== null) {
|
||||
if (V[Symbol.iterator] !== undefined) {
|
||||
if (V[SymbolIterator] !== undefined) {
|
||||
return webidl.converters["sequence<sequence<ByteString>>"](V, opts);
|
||||
}
|
||||
return webidl.converters["record<ByteString, ByteString>"](V, opts);
|
||||
|
|
|
@ -14,6 +14,31 @@
|
|||
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");
|
||||
|
||||
|
@ -47,7 +72,7 @@
|
|||
*/
|
||||
|
||||
class FormData {
|
||||
get [Symbol.toStringTag]() {
|
||||
get [SymbolToStringTag]() {
|
||||
return "FormData";
|
||||
}
|
||||
|
||||
|
@ -97,7 +122,7 @@
|
|||
|
||||
const entry = createEntry(name, valueOrBlobValue, filename);
|
||||
|
||||
this[entryList].push(entry);
|
||||
ArrayPrototypePush(this[entryList], entry);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -117,7 +142,7 @@
|
|||
const list = this[entryList];
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
if (list[i].name === name) {
|
||||
list.splice(i, 1);
|
||||
ArrayPrototypeSplice(list, i, 1);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
@ -159,7 +184,7 @@
|
|||
|
||||
const returnList = [];
|
||||
for (const entry of this[entryList]) {
|
||||
if (entry.name === name) returnList.push(entry.value);
|
||||
if (entry.name === name) ArrayPrototypePush(returnList, entry.value);
|
||||
}
|
||||
return returnList;
|
||||
}
|
||||
|
@ -227,13 +252,13 @@
|
|||
list[i] = entry;
|
||||
added = true;
|
||||
} else {
|
||||
list.splice(i, 1);
|
||||
ArrayPrototypeSplice(list, i, 1);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!added) {
|
||||
list.push(entry);
|
||||
ArrayPrototypePush(list, entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -243,29 +268,46 @@
|
|||
webidl.configurePrototype(FormData);
|
||||
|
||||
const escape = (str, isFilename) =>
|
||||
(isFilename ? str : str.replace(/\r?\n|\r/g, "\r\n"))
|
||||
.replace(/\n/g, "%0A")
|
||||
.replace(/\r/g, "%0D")
|
||||
.replace(/"/g, "%22");
|
||||
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 = `${Math.random()}${Math.random()}`
|
||||
.replaceAll(".", "").slice(-28).padStart(32, "-");
|
||||
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") {
|
||||
chunks.push(
|
||||
ArrayPrototypePush(
|
||||
chunks,
|
||||
prefix + escape(name) + '"' + CRLF + CRLF +
|
||||
value.replace(/\r(?!\n)|(?<!\r)\n/g, CRLF) + CRLF,
|
||||
StringPrototypeReplace(value, /\r(?!\n)|(?<!\r)\n/g, CRLF) + CRLF,
|
||||
);
|
||||
} else {
|
||||
chunks.push(
|
||||
ArrayPrototypePush(
|
||||
chunks,
|
||||
prefix + escape(name) + `"; filename="${escape(value.name, true)}"` +
|
||||
CRLF +
|
||||
`Content-Type: ${value.type || "application/octet-stream"}\r\n\r\n`,
|
||||
|
@ -275,7 +317,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
chunks.push(`--${boundary}--`);
|
||||
ArrayPrototypePush(chunks, `--${boundary}--`);
|
||||
|
||||
return new Blob(chunks, {
|
||||
type: "multipart/form-data; boundary=" + boundary,
|
||||
|
@ -290,19 +332,26 @@
|
|||
/** @type {Map<string, string>} */
|
||||
const params = new Map();
|
||||
// Forced to do so for some Map constructor param mismatch
|
||||
value
|
||||
.split(";")
|
||||
.slice(1)
|
||||
.map((s) => s.trim().split("="))
|
||||
.filter((arr) => arr.length > 1)
|
||||
.map(([k, v]) => [k, v.replace(/^"([^"]*)"$/, "$1")])
|
||||
.forEach(([k, v]) => params.set(k, v));
|
||||
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 = CRLF.codePointAt(1);
|
||||
const CR = CRLF.codePointAt(0);
|
||||
const LF = StringPrototypeCodePointAt(CRLF, 1);
|
||||
const CR = StringPrototypeCodePointAt(CRLF, 0);
|
||||
|
||||
class MultipartParser {
|
||||
/**
|
||||
|
@ -325,14 +374,14 @@
|
|||
*/
|
||||
#parseHeaders(headersText) {
|
||||
const headers = new Headers();
|
||||
const rawHeaders = headersText.split("\r\n");
|
||||
const rawHeaders = StringPrototypeSplit(headersText, "\r\n");
|
||||
for (const rawHeader of rawHeaders) {
|
||||
const sepIndex = rawHeader.indexOf(":");
|
||||
const sepIndex = StringPrototypeIndexOf(rawHeader, ":");
|
||||
if (sepIndex < 0) {
|
||||
continue; // Skip this header
|
||||
}
|
||||
const key = rawHeader.slice(0, sepIndex);
|
||||
const value = rawHeader.slice(sepIndex + 1);
|
||||
const key = StringPrototypeSlice(rawHeader, 0, sepIndex);
|
||||
const value = StringPrototypeSlice(rawHeader, sepIndex + 1);
|
||||
headers.set(key, value);
|
||||
}
|
||||
|
||||
|
@ -364,7 +413,7 @@
|
|||
const isNewLine = byte === LF && prevByte === CR;
|
||||
|
||||
if (state === 1 || state === 2 || state == 3) {
|
||||
headerText += String.fromCharCode(byte);
|
||||
headerText += StringFromCharCode(byte);
|
||||
}
|
||||
if (state === 0 && isNewLine) {
|
||||
state = 1;
|
||||
|
@ -390,13 +439,14 @@
|
|||
|
||||
if (boundaryIndex >= this.boundary.length) {
|
||||
const { headers, disposition } = this.#parseHeaders(headerText);
|
||||
const content = this.body.subarray(
|
||||
const content = TypedArrayPrototypeSubarray(
|
||||
this.body,
|
||||
fileStart,
|
||||
i - boundaryIndex - 1,
|
||||
);
|
||||
// https://fetch.spec.whatwg.org/#ref-for-dom-body-formdata
|
||||
const filename = disposition.get("filename");
|
||||
const name = disposition.get("name");
|
||||
const filename = MapPrototypeGet(disposition, "filename");
|
||||
const name = MapPrototypeGet(disposition, "name");
|
||||
|
||||
state = 5;
|
||||
// Reset
|
||||
|
|
|
@ -21,6 +21,18 @@
|
|||
const mimesniff = globalThis.__bootstrap.mimesniff;
|
||||
const { isReadableStreamDisturbed, errorReadableStream, createProxy } =
|
||||
globalThis.__bootstrap.streams;
|
||||
const {
|
||||
ArrayBufferIsView,
|
||||
ArrayPrototypePush,
|
||||
ArrayPrototypeMap,
|
||||
JSONParse,
|
||||
ObjectDefineProperties,
|
||||
PromiseResolve,
|
||||
TypedArrayPrototypeSet,
|
||||
TypedArrayPrototypeSlice,
|
||||
TypeError,
|
||||
Uint8Array,
|
||||
} = window.__bootstrap.primordials;
|
||||
|
||||
class InnerBody {
|
||||
/** @type {ReadableStream<Uint8Array> | { body: Uint8Array, consumed: boolean }} */
|
||||
|
@ -92,13 +104,13 @@
|
|||
while (true) {
|
||||
const { value: chunk, done } = await reader.read();
|
||||
if (done) break;
|
||||
chunks.push(chunk);
|
||||
ArrayPrototypePush(chunks, chunk);
|
||||
totalLength += chunk.byteLength;
|
||||
}
|
||||
const finalBuffer = new Uint8Array(totalLength);
|
||||
let i = 0;
|
||||
for (const chunk of chunks) {
|
||||
finalBuffer.set(chunk, i);
|
||||
TypedArrayPrototypeSet(finalBuffer, chunk, i);
|
||||
i += chunk.byteLength;
|
||||
}
|
||||
return finalBuffer;
|
||||
|
@ -165,7 +177,7 @@
|
|||
if (object[bodySymbol] !== null) {
|
||||
return object[bodySymbol].consume();
|
||||
}
|
||||
return Promise.resolve(new Uint8Array());
|
||||
return PromiseResolve(new Uint8Array());
|
||||
}
|
||||
|
||||
/** @type {PropertyDescriptorMap} */
|
||||
|
@ -255,7 +267,7 @@
|
|||
enumerable: true,
|
||||
},
|
||||
};
|
||||
return Object.defineProperties(prototype.prototype, mixin);
|
||||
return ObjectDefineProperties(prototype.prototype, mixin);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -287,7 +299,10 @@
|
|||
} else if (essence === "application/x-www-form-urlencoded") {
|
||||
const entries = parseUrlEncoded(bytes);
|
||||
return formDataFromEntries(
|
||||
entries.map((x) => ({ name: x[0], value: x[1] })),
|
||||
ArrayPrototypeMap(
|
||||
entries,
|
||||
(x) => ({ name: x[0], value: x[1] }),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -296,7 +311,7 @@
|
|||
throw new TypeError("Missing content type");
|
||||
}
|
||||
case "JSON":
|
||||
return JSON.parse(core.decode(bytes));
|
||||
return JSONParse(core.decode(bytes));
|
||||
case "text":
|
||||
return core.decode(bytes);
|
||||
}
|
||||
|
@ -319,15 +334,15 @@
|
|||
if (object.type.length !== 0) {
|
||||
contentType = object.type;
|
||||
}
|
||||
} else if (ArrayBuffer.isView(object) || object instanceof ArrayBuffer) {
|
||||
const u8 = ArrayBuffer.isView(object)
|
||||
} else if (ArrayBufferIsView(object) || object instanceof ArrayBuffer) {
|
||||
const u8 = ArrayBufferIsView(object)
|
||||
? new Uint8Array(
|
||||
object.buffer,
|
||||
object.byteOffset,
|
||||
object.byteLength,
|
||||
)
|
||||
: new Uint8Array(object);
|
||||
const copy = u8.slice(0, u8.byteLength);
|
||||
const copy = TypedArrayPrototypeSlice(u8, 0, u8.byteLength);
|
||||
source = copy;
|
||||
} else if (object instanceof FormData) {
|
||||
const res = formDataToBlob(object);
|
||||
|
@ -336,6 +351,7 @@
|
|||
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") {
|
||||
|
@ -374,7 +390,7 @@
|
|||
if (V instanceof ArrayBuffer || V instanceof SharedArrayBuffer) {
|
||||
return webidl.converters["ArrayBuffer"](V, opts);
|
||||
}
|
||||
if (ArrayBuffer.isView(V)) {
|
||||
if (ArrayBufferIsView(V)) {
|
||||
return webidl.converters["ArrayBufferView"](V, opts);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,20 @@
|
|||
} = 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");
|
||||
|
@ -81,7 +95,9 @@
|
|||
* @returns {InnerRequest}
|
||||
*/
|
||||
function cloneInnerRequest(request) {
|
||||
const headerList = [...request.headerList.map((x) => [x[0], x[1]])];
|
||||
const headerList = [
|
||||
...ArrayPrototypeMap(request.headerList, (x) => [x[0], x[1]]),
|
||||
];
|
||||
let body = null;
|
||||
if (request.body !== null) {
|
||||
body = request.body.clone();
|
||||
|
@ -129,7 +145,7 @@
|
|||
}
|
||||
|
||||
// Regular path
|
||||
if (!HTTP_TOKEN_CODE_POINT_RE.test(m)) {
|
||||
if (!RegExpPrototypeTest(HTTP_TOKEN_CODE_POINT_RE, m)) {
|
||||
throw new TypeError("Method is not valid.");
|
||||
}
|
||||
const upperCase = byteUpperCase(m);
|
||||
|
@ -166,14 +182,17 @@
|
|||
mimeType = temporaryMimeType;
|
||||
if (mimesniff.essence(mimeType) !== essence) {
|
||||
charset = null;
|
||||
const newCharset = mimeType.parameters.get("charset");
|
||||
const newCharset = MapPrototypeGet(mimeType.parameters, "charset");
|
||||
if (newCharset !== undefined) {
|
||||
charset = newCharset;
|
||||
}
|
||||
essence = mimesniff.essence(mimeType);
|
||||
} else {
|
||||
if (mimeType.parameters.has("charset") === null && charset !== null) {
|
||||
mimeType.parameters.set("charset", charset);
|
||||
if (
|
||||
MapPrototypeHas(mimeType.parameters, "charset") === null &&
|
||||
charset !== null
|
||||
) {
|
||||
MapPrototypeSet(mimeType.parameters, "charset", charset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -267,15 +286,17 @@
|
|||
this[_headers] = headersFromHeaderList(request.headerList, "request");
|
||||
|
||||
// 32.
|
||||
if (Object.keys(init).length > 0) {
|
||||
let headers = headerListFromHeaders(this[_headers]).slice(
|
||||
if (ObjectKeys(init).length > 0) {
|
||||
let headers = ArrayPrototypeSlice(
|
||||
headerListFromHeaders(this[_headers]),
|
||||
0,
|
||||
headerListFromHeaders(this[_headers]).length,
|
||||
);
|
||||
if (init.headers !== undefined) {
|
||||
headers = init.headers;
|
||||
}
|
||||
headerListFromHeaders(this[_headers]).splice(
|
||||
ArrayPrototypeSplice(
|
||||
headerListFromHeaders(this[_headers]),
|
||||
0,
|
||||
headerListFromHeaders(this[_headers]).length,
|
||||
);
|
||||
|
@ -367,11 +388,11 @@
|
|||
);
|
||||
}
|
||||
|
||||
get [Symbol.toStringTag]() {
|
||||
get [SymbolToStringTag]() {
|
||||
return "Request";
|
||||
}
|
||||
|
||||
[Symbol.for("Deno.customInspect")](inspect) {
|
||||
[SymbolFor("Deno.customInspect")](inspect) {
|
||||
const inner = {
|
||||
bodyUsed: this.bodyUsed,
|
||||
headers: this.headers,
|
||||
|
|
|
@ -25,6 +25,20 @@
|
|||
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"];
|
||||
|
@ -75,7 +89,9 @@
|
|||
*/
|
||||
function cloneInnerResponse(response) {
|
||||
const urlList = [...response.urlList];
|
||||
const headerList = [...response.headerList.map((x) => [x[0], x[1]])];
|
||||
const headerList = [
|
||||
...ArrayPrototypeMap(response.headerList, (x) => [x[0], x[1]]),
|
||||
];
|
||||
let body = null;
|
||||
if (response.body !== null) {
|
||||
body = response.body.clone();
|
||||
|
@ -162,14 +178,17 @@
|
|||
mimeType = temporaryMimeType;
|
||||
if (mimesniff.essence(mimeType) !== essence) {
|
||||
charset = null;
|
||||
const newCharset = mimeType.parameters.get("charset");
|
||||
const newCharset = MapPrototypeGet(mimeType.parameters, "charset");
|
||||
if (newCharset !== undefined) {
|
||||
charset = newCharset;
|
||||
}
|
||||
essence = mimesniff.essence(mimeType);
|
||||
} else {
|
||||
if (mimeType.parameters.has("charset") === null && charset !== null) {
|
||||
mimeType.parameters.set("charset", charset);
|
||||
if (
|
||||
MapPrototypeHas(mimeType.parameters, "charset") === null &&
|
||||
charset !== null
|
||||
) {
|
||||
MapPrototypeSet(mimeType.parameters, "charset", charset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -218,7 +237,7 @@
|
|||
}
|
||||
const inner = newInnerResponse(status);
|
||||
inner.type = "default";
|
||||
inner.headerList.push(["location", parsedURL.href]);
|
||||
ArrayPrototypePush(inner.headerList, ["location", parsedURL.href]);
|
||||
const response = webidl.createBranded(Response);
|
||||
response[_response] = inner;
|
||||
response[_headers] = headersFromHeaderList(
|
||||
|
@ -249,7 +268,7 @@
|
|||
);
|
||||
}
|
||||
|
||||
if (!REASON_PHRASE_RE.test(init.statusText)) {
|
||||
if (!RegExpPrototypeTest(REASON_PHRASE_RE, init.statusText)) {
|
||||
throw new TypeError("Status text is not valid.");
|
||||
}
|
||||
|
||||
|
@ -353,11 +372,11 @@
|
|||
return second;
|
||||
}
|
||||
|
||||
get [Symbol.toStringTag]() {
|
||||
get [SymbolToStringTag]() {
|
||||
return "Response";
|
||||
}
|
||||
|
||||
[Symbol.for("Deno.customInspect")](inspect) {
|
||||
[SymbolFor("Deno.customInspect")](inspect) {
|
||||
const inner = {
|
||||
body: this.body,
|
||||
bodyUsed: this.bodyUsed,
|
||||
|
|
|
@ -27,6 +27,19 @@
|
|||
} = 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",
|
||||
|
@ -104,7 +117,7 @@
|
|||
);
|
||||
if (read > 0) {
|
||||
// We read some data. Enqueue it onto the stream.
|
||||
controller.enqueue(chunk.subarray(0, read));
|
||||
controller.enqueue(TypedArrayPrototypeSubarray(chunk, 0, read));
|
||||
} else {
|
||||
// We have reached the end of the body, so we close the stream.
|
||||
controller.close();
|
||||
|
@ -201,20 +214,26 @@
|
|||
const reader = reqBody.getReader();
|
||||
(async () => {
|
||||
while (true) {
|
||||
const { value, done } = await reader.read().catch((err) => {
|
||||
if (terminator.aborted) return { done: true, value: undefined };
|
||||
throw err;
|
||||
});
|
||||
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 opFetchRequestWrite(requestBodyRid, value).catch((err) => {
|
||||
if (terminator.aborted) return;
|
||||
throw err;
|
||||
});
|
||||
await PromisePrototypeCatch(
|
||||
opFetchRequestWrite(requestBodyRid, value),
|
||||
(err) => {
|
||||
if (terminator.aborted) return;
|
||||
throw err;
|
||||
},
|
||||
);
|
||||
if (terminator.aborted) break;
|
||||
} catch (err) {
|
||||
await reader.cancel(err);
|
||||
|
@ -231,7 +250,7 @@
|
|||
|
||||
let resp;
|
||||
try {
|
||||
resp = await opFetchSend(requestRid).catch((err) => {
|
||||
resp = await PromisePrototypeCatch(opFetchSend(requestRid), (err) => {
|
||||
if (terminator.aborted) return;
|
||||
throw err;
|
||||
});
|
||||
|
@ -300,7 +319,8 @@
|
|||
* @returns {Promise<InnerResponse>}
|
||||
*/
|
||||
function httpRedirectFetch(request, response, terminator) {
|
||||
const locationHeaders = response.headerList.filter(
|
||||
const locationHeaders = ArrayPrototypeFilter(
|
||||
response.headerList,
|
||||
(entry) => entry[0] === "location",
|
||||
);
|
||||
if (locationHeaders.length === 0) {
|
||||
|
@ -339,8 +359,13 @@
|
|||
request.method = "GET";
|
||||
request.body = null;
|
||||
for (let i = 0; i < request.headerList.length; i++) {
|
||||
if (REQUEST_BODY_HEADER_NAMES.includes(request.headerList[i][0])) {
|
||||
request.headerList.splice(i, 1);
|
||||
if (
|
||||
ArrayPrototypeIncludes(
|
||||
REQUEST_BODY_HEADER_NAMES,
|
||||
request.headerList[i][0],
|
||||
)
|
||||
) {
|
||||
ArrayPrototypeSplice(request.headerList, i, 1);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
@ -349,7 +374,7 @@
|
|||
const res = extractBody(request.body.source);
|
||||
request.body = res.body;
|
||||
}
|
||||
request.urlList.push(locationURL.href);
|
||||
ArrayPrototypePush(request.urlList, locationURL.href);
|
||||
return mainFetch(request, true, terminator);
|
||||
}
|
||||
|
||||
|
@ -393,35 +418,41 @@
|
|||
requestObject.signal[abortSignal.add](onabort);
|
||||
|
||||
if (!requestObject.headers.has("accept")) {
|
||||
request.headerList.push(["accept", "*/*"]);
|
||||
ArrayPrototypePush(request.headerList, ["accept", "*/*"]);
|
||||
}
|
||||
|
||||
// 12.
|
||||
mainFetch(request, false, requestObject.signal).then((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"),
|
||||
);
|
||||
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;
|
||||
}
|
||||
responseObject = fromInnerResponse(response, "immutable");
|
||||
resolve(responseObject);
|
||||
requestObject.signal[abortSignal.remove](onabort);
|
||||
}).catch((err) => {
|
||||
reject(err);
|
||||
requestObject.signal[abortSignal.remove](onabort);
|
||||
});
|
||||
},
|
||||
);
|
||||
});
|
||||
return p;
|
||||
}
|
||||
|
@ -461,7 +492,8 @@
|
|||
// https://github.com/WebAssembly/spec/issues/1138. The WPT tests
|
||||
// expect the raw value of the Content-Type attribute lowercased.
|
||||
if (
|
||||
res.headers.get("Content-Type")?.toLowerCase() !== "application/wasm"
|
||||
StringPrototypeToLowerCase(res.headers.get("Content-Type")) !==
|
||||
"application/wasm"
|
||||
) {
|
||||
throw new TypeError("Invalid WebAssembly content type.");
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue