1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-11 10:07:54 -05:00
denoland-deno/ext/fetch/20_headers.js

523 lines
14 KiB
JavaScript
Raw Normal View History

// Copyright 2018-2023 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" />
import * as webidl from "ext:deno_webidl/00_webidl.js";
import {
byteLowerCase,
collectHttpQuotedString,
collectSequenceOfCodepoints,
HTTP_TAB_OR_SPACE_PREFIX_RE,
HTTP_TAB_OR_SPACE_SUFFIX_RE,
HTTP_TOKEN_CODE_POINT_RE,
httpTrim,
} from "ext:deno_web/00_infra.js";
const primordials = globalThis.__bootstrap.primordials;
const {
ArrayIsArray,
ArrayPrototypePush,
ArrayPrototypeSort,
ArrayPrototypeJoin,
ArrayPrototypeSplice,
ObjectEntries,
ObjectHasOwn,
RegExpPrototypeTest,
Symbol,
SymbolFor,
SymbolIterator,
StringPrototypeReplaceAll,
StringPrototypeCharCodeAt,
TypeError,
} = primordials;
const _headerList = Symbol("header list");
const _iterableHeaders = Symbol("iterable headers");
perf(ext/headers): cache iterableHeaders for immutable Headers (#20132) This PR caches `_iterableHeaders` for immutable `Headers` increasing the performance of `fetch` & server if headers are iterated. Should close #19466 I only cached immutable headers to address this comment https://github.com/denoland/deno/issues/19466#issuecomment-1589892373 since I didn't find any occurrence of header mutation on immutable headers. We can discuss caching for non-immutable, but I think this is a great first step. ## BENCHMARK ### Server ```js const addr = Deno.args[0] ?? "127.0.0.1:4500"; const [hostname, port] = addr.split(":"); const { serve } = Deno; serve({ hostname, port: Number(port), reusePort: true }, (req) => { const headers = [...req.headers]; // req.headers are immutable, cannot set/append/delete return new Response("ok"); }); ``` Used `wrk` with 5 headers ``` wrk -d 10s --latency -H "X-Deno: true" -H "Accept: application/json" -H "X-Foo: bar" -H "User-Agent: wrk" -H "Accept-Encoding: gzip, br" http://127.0.0.1:4500 ``` **This patch** ``` Running 10s test @ http://127.0.0.1:4500 2 threads and 10 connections Thread Stats Avg Stdev Max +/- Stdev Latency 70.18us 22.89us 679.00us 81.37% Req/Sec 71.55k 9.69k 82.18k 89.60% Latency Distribution 50% 59.00us 75% 89.00us 90% 98.00us 99% 159.00us 1437891 requests in 10.10s, 193.35MB read Requests/sec: 142369.83 Transfer/sec: 19.14MB ``` **main** ``` Running 10s test @ http://127.0.0.1:4500 2 threads and 10 connections Thread Stats Avg Stdev Max +/- Stdev Latency 112.78us 36.47us 2.09ms 77.99% Req/Sec 44.30k 1.65k 49.14k 74.26% Latency Distribution 50% 99.00us 75% 136.00us 90% 162.00us 99% 213.00us 890588 requests in 10.10s, 118.91MB read Requests/sec: 88176.37 Transfer/sec: 11.77MB ``` ### fetch ```js const res = await fetch('http://127.0.0.1:4500'); Deno.bench("Headers iterator", () => { const i = [...res.headers]; // res.headers are immutable, cannot set/append/delete }); ``` **this patch** ``` cpu: 13th Gen Intel(R) Core(TM) i9-13900H runtime: deno 1.36.1 (x86_64-unknown-linux-gnu) benchmark time (avg) iter/s (min … max) p75 p99 p995 ---------------------------------------------------------------------- ----------------------------- Headers iterator 329.5 ns/iter 3,034,909.0 (318.55 ns … 364.34 ns) 331.1 ns 355.72 ns 364.34 ns ``` **main** ``` cpu: 13th Gen Intel(R) Core(TM) i9-13900H runtime: deno 1.36.1 (x86_64-unknown-linux-gnu) benchmark time (avg) iter/s (min … max) p75 p99 p995 ---------------------------------------------------------------------- ----------------------------- Headers iterator 2.59 µs/iter 386,372.1 (2.56 µs … 2.68 µs) 2.59 µs 2.68 µs 2.68 µs ```
2023-08-12 12:42:06 -04:00
const _iterableHeadersCache = Symbol("iterable headers cache");
const _guard = Symbol("guard");
/**
* @typedef Header
* @type {[string, string]}
*/
/**
* @typedef HeaderList
* @type {Header[]}
*/
/**
* @param {string} potentialValue
* @returns {string}
*/
function normalizeHeaderValue(potentialValue) {
return httpTrim(potentialValue);
}
/**
* @param {Headers} headers
* @param {HeadersInit} object
*/
function fillHeaders(headers, object) {
if (ArrayIsArray(object)) {
for (let i = 0; i < object.length; ++i) {
const header = object[i];
if (header.length !== 2) {
throw new TypeError(
`Invalid header. Length must be 2, but is ${header.length}`,
);
}
appendHeader(headers, header[0], header[1]);
}
} else {
for (const key in object) {
if (!ObjectHasOwn(object, key)) {
continue;
}
appendHeader(headers, key, object[key]);
}
}
}
function checkForInvalidValueChars(value) {
for (let i = 0; i < value.length; i++) {
const c = StringPrototypeCharCodeAt(value, i);
if (c === 0x0a || c === 0x0d || c === 0x00) {
return false;
}
}
return true;
}
let HEADER_NAME_CACHE = {};
let HEADER_CACHE_SIZE = 0;
const HEADER_NAME_CACHE_SIZE_BOUNDARY = 4096;
function checkHeaderNameForHttpTokenCodePoint(name) {
const fromCache = HEADER_NAME_CACHE[name];
if (fromCache !== undefined) {
return fromCache;
}
const valid = RegExpPrototypeTest(HTTP_TOKEN_CODE_POINT_RE, name);
if (HEADER_CACHE_SIZE > HEADER_NAME_CACHE_SIZE_BOUNDARY) {
HEADER_NAME_CACHE = {};
HEADER_CACHE_SIZE = 0;
}
HEADER_CACHE_SIZE++;
HEADER_NAME_CACHE[name] = valid;
return valid;
}
/**
* 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 (!checkHeaderNameForHttpTokenCodePoint(name)) {
throw new TypeError("Header name is not valid.");
}
if (!checkForInvalidValueChars(value)) {
throw new TypeError("Header value is not valid.");
}
// 3.
if (headers[_guard] == "immutable") {
throw new TypeError("Headers are immutable.");
}
// 7.
const list = headers[_headerList];
const lowercaseName = byteLowerCase(name);
for (let i = 0; i < list.length; i++) {
if (byteLowerCase(list[i][0]) === lowercaseName) {
name = list[i][0];
break;
}
}
ArrayPrototypePush(list, [name, value]);
}
/**
* https://fetch.spec.whatwg.org/#concept-header-list-get
* @param {HeaderList} list
* @param {string} name
*/
function getHeader(list, name) {
const lowercaseName = byteLowerCase(name);
const entries = [];
for (let i = 0; i < list.length; i++) {
if (byteLowerCase(list[i][0]) === lowercaseName) {
ArrayPrototypePush(entries, list[i][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];
perf(ext/headers): cache iterableHeaders for immutable Headers (#20132) This PR caches `_iterableHeaders` for immutable `Headers` increasing the performance of `fetch` & server if headers are iterated. Should close #19466 I only cached immutable headers to address this comment https://github.com/denoland/deno/issues/19466#issuecomment-1589892373 since I didn't find any occurrence of header mutation on immutable headers. We can discuss caching for non-immutable, but I think this is a great first step. ## BENCHMARK ### Server ```js const addr = Deno.args[0] ?? "127.0.0.1:4500"; const [hostname, port] = addr.split(":"); const { serve } = Deno; serve({ hostname, port: Number(port), reusePort: true }, (req) => { const headers = [...req.headers]; // req.headers are immutable, cannot set/append/delete return new Response("ok"); }); ``` Used `wrk` with 5 headers ``` wrk -d 10s --latency -H "X-Deno: true" -H "Accept: application/json" -H "X-Foo: bar" -H "User-Agent: wrk" -H "Accept-Encoding: gzip, br" http://127.0.0.1:4500 ``` **This patch** ``` Running 10s test @ http://127.0.0.1:4500 2 threads and 10 connections Thread Stats Avg Stdev Max +/- Stdev Latency 70.18us 22.89us 679.00us 81.37% Req/Sec 71.55k 9.69k 82.18k 89.60% Latency Distribution 50% 59.00us 75% 89.00us 90% 98.00us 99% 159.00us 1437891 requests in 10.10s, 193.35MB read Requests/sec: 142369.83 Transfer/sec: 19.14MB ``` **main** ``` Running 10s test @ http://127.0.0.1:4500 2 threads and 10 connections Thread Stats Avg Stdev Max +/- Stdev Latency 112.78us 36.47us 2.09ms 77.99% Req/Sec 44.30k 1.65k 49.14k 74.26% Latency Distribution 50% 99.00us 75% 136.00us 90% 162.00us 99% 213.00us 890588 requests in 10.10s, 118.91MB read Requests/sec: 88176.37 Transfer/sec: 11.77MB ``` ### fetch ```js const res = await fetch('http://127.0.0.1:4500'); Deno.bench("Headers iterator", () => { const i = [...res.headers]; // res.headers are immutable, cannot set/append/delete }); ``` **this patch** ``` cpu: 13th Gen Intel(R) Core(TM) i9-13900H runtime: deno 1.36.1 (x86_64-unknown-linux-gnu) benchmark time (avg) iter/s (min … max) p75 p99 p995 ---------------------------------------------------------------------- ----------------------------- Headers iterator 329.5 ns/iter 3,034,909.0 (318.55 ns … 364.34 ns) 331.1 ns 355.72 ns 364.34 ns ``` **main** ``` cpu: 13th Gen Intel(R) Core(TM) i9-13900H runtime: deno 1.36.1 (x86_64-unknown-linux-gnu) benchmark time (avg) iter/s (min … max) p75 p99 p995 ---------------------------------------------------------------------- ----------------------------- Headers iterator 2.59 µs/iter 386,372.1 (2.56 µs … 2.68 µs) 2.59 µs 2.68 µs 2.68 µs ```
2023-08-12 12:42:06 -04:00
if (
this[_guard] === "immutable" &&
this[_iterableHeadersCache] !== undefined
) {
return this[_iterableHeadersCache];
}
// The order of steps are not similar to the ones suggested by the
// spec but produce the same result.
const headers = {};
const cookies = [];
for (let i = 0; i < list.length; ++i) {
const entry = list[i];
const name = byteLowerCase(entry[0]);
const value = entry[1];
if (value === null) throw new TypeError("Unreachable");
// The following if statement is not spec compliant.
// `set-cookie` is the only header that can not be concatenated,
// so must be given to the user as multiple headers.
// The else block of the if statement is spec compliant again.
if (name === "set-cookie") {
ArrayPrototypePush(cookies, [name, value]);
} else {
// The following code has the same behaviour as getHeader()
// at the end of loop. But it avoids looping through the entire
// list to combine multiple values with same header name. It
// instead gradually combines them as they are found.
let header = headers[name];
if (header && header.length > 0) {
header += "\x2C\x20" + value;
} else {
header = value;
}
headers[name] = header;
}
}
const entries = ObjectEntries(headers);
for (let i = 0; i < cookies.length; ++i) {
ArrayPrototypePush(entries, cookies[i]);
}
perf(ext/headers): cache iterableHeaders for immutable Headers (#20132) This PR caches `_iterableHeaders` for immutable `Headers` increasing the performance of `fetch` & server if headers are iterated. Should close #19466 I only cached immutable headers to address this comment https://github.com/denoland/deno/issues/19466#issuecomment-1589892373 since I didn't find any occurrence of header mutation on immutable headers. We can discuss caching for non-immutable, but I think this is a great first step. ## BENCHMARK ### Server ```js const addr = Deno.args[0] ?? "127.0.0.1:4500"; const [hostname, port] = addr.split(":"); const { serve } = Deno; serve({ hostname, port: Number(port), reusePort: true }, (req) => { const headers = [...req.headers]; // req.headers are immutable, cannot set/append/delete return new Response("ok"); }); ``` Used `wrk` with 5 headers ``` wrk -d 10s --latency -H "X-Deno: true" -H "Accept: application/json" -H "X-Foo: bar" -H "User-Agent: wrk" -H "Accept-Encoding: gzip, br" http://127.0.0.1:4500 ``` **This patch** ``` Running 10s test @ http://127.0.0.1:4500 2 threads and 10 connections Thread Stats Avg Stdev Max +/- Stdev Latency 70.18us 22.89us 679.00us 81.37% Req/Sec 71.55k 9.69k 82.18k 89.60% Latency Distribution 50% 59.00us 75% 89.00us 90% 98.00us 99% 159.00us 1437891 requests in 10.10s, 193.35MB read Requests/sec: 142369.83 Transfer/sec: 19.14MB ``` **main** ``` Running 10s test @ http://127.0.0.1:4500 2 threads and 10 connections Thread Stats Avg Stdev Max +/- Stdev Latency 112.78us 36.47us 2.09ms 77.99% Req/Sec 44.30k 1.65k 49.14k 74.26% Latency Distribution 50% 99.00us 75% 136.00us 90% 162.00us 99% 213.00us 890588 requests in 10.10s, 118.91MB read Requests/sec: 88176.37 Transfer/sec: 11.77MB ``` ### fetch ```js const res = await fetch('http://127.0.0.1:4500'); Deno.bench("Headers iterator", () => { const i = [...res.headers]; // res.headers are immutable, cannot set/append/delete }); ``` **this patch** ``` cpu: 13th Gen Intel(R) Core(TM) i9-13900H runtime: deno 1.36.1 (x86_64-unknown-linux-gnu) benchmark time (avg) iter/s (min … max) p75 p99 p995 ---------------------------------------------------------------------- ----------------------------- Headers iterator 329.5 ns/iter 3,034,909.0 (318.55 ns … 364.34 ns) 331.1 ns 355.72 ns 364.34 ns ``` **main** ``` cpu: 13th Gen Intel(R) Core(TM) i9-13900H runtime: deno 1.36.1 (x86_64-unknown-linux-gnu) benchmark time (avg) iter/s (min … max) p75 p99 p995 ---------------------------------------------------------------------- ----------------------------- Headers iterator 2.59 µs/iter 386,372.1 (2.56 µs … 2.68 µs) 2.59 µs 2.68 µs 2.68 µs ```
2023-08-12 12:42:06 -04:00
ArrayPrototypeSort(
entries,
(a, b) => {
const akey = a[0];
const bkey = b[0];
if (akey > bkey) return 1;
if (akey < bkey) return -1;
return 0;
},
);
perf(ext/headers): cache iterableHeaders for immutable Headers (#20132) This PR caches `_iterableHeaders` for immutable `Headers` increasing the performance of `fetch` & server if headers are iterated. Should close #19466 I only cached immutable headers to address this comment https://github.com/denoland/deno/issues/19466#issuecomment-1589892373 since I didn't find any occurrence of header mutation on immutable headers. We can discuss caching for non-immutable, but I think this is a great first step. ## BENCHMARK ### Server ```js const addr = Deno.args[0] ?? "127.0.0.1:4500"; const [hostname, port] = addr.split(":"); const { serve } = Deno; serve({ hostname, port: Number(port), reusePort: true }, (req) => { const headers = [...req.headers]; // req.headers are immutable, cannot set/append/delete return new Response("ok"); }); ``` Used `wrk` with 5 headers ``` wrk -d 10s --latency -H "X-Deno: true" -H "Accept: application/json" -H "X-Foo: bar" -H "User-Agent: wrk" -H "Accept-Encoding: gzip, br" http://127.0.0.1:4500 ``` **This patch** ``` Running 10s test @ http://127.0.0.1:4500 2 threads and 10 connections Thread Stats Avg Stdev Max +/- Stdev Latency 70.18us 22.89us 679.00us 81.37% Req/Sec 71.55k 9.69k 82.18k 89.60% Latency Distribution 50% 59.00us 75% 89.00us 90% 98.00us 99% 159.00us 1437891 requests in 10.10s, 193.35MB read Requests/sec: 142369.83 Transfer/sec: 19.14MB ``` **main** ``` Running 10s test @ http://127.0.0.1:4500 2 threads and 10 connections Thread Stats Avg Stdev Max +/- Stdev Latency 112.78us 36.47us 2.09ms 77.99% Req/Sec 44.30k 1.65k 49.14k 74.26% Latency Distribution 50% 99.00us 75% 136.00us 90% 162.00us 99% 213.00us 890588 requests in 10.10s, 118.91MB read Requests/sec: 88176.37 Transfer/sec: 11.77MB ``` ### fetch ```js const res = await fetch('http://127.0.0.1:4500'); Deno.bench("Headers iterator", () => { const i = [...res.headers]; // res.headers are immutable, cannot set/append/delete }); ``` **this patch** ``` cpu: 13th Gen Intel(R) Core(TM) i9-13900H runtime: deno 1.36.1 (x86_64-unknown-linux-gnu) benchmark time (avg) iter/s (min … max) p75 p99 p995 ---------------------------------------------------------------------- ----------------------------- Headers iterator 329.5 ns/iter 3,034,909.0 (318.55 ns … 364.34 ns) 331.1 ns 355.72 ns 364.34 ns ``` **main** ``` cpu: 13th Gen Intel(R) Core(TM) i9-13900H runtime: deno 1.36.1 (x86_64-unknown-linux-gnu) benchmark time (avg) iter/s (min … max) p75 p99 p995 ---------------------------------------------------------------------- ----------------------------- Headers iterator 2.59 µs/iter 386,372.1 (2.56 µs … 2.68 µs) 2.59 µs 2.68 µs 2.68 µs ```
2023-08-12 12:42:06 -04:00
this[_iterableHeadersCache] = entries;
return entries;
}
/** @param {HeadersInit} [init] */
constructor(init = undefined) {
const prefix = "Failed to construct 'Headers'";
if (init !== undefined) {
init = webidl.converters["HeadersInit"](init, prefix, "Argument 1");
}
this[webidl.brand] = webidl.brand;
this[_guard] = "none";
if (init !== undefined) {
fillHeaders(this, init);
}
}
/**
2021-04-28 10:08:51 -04:00
* @param {string} name
* @param {string} value
*/
append(name, value) {
webidl.assertBranded(this, HeadersPrototype);
const prefix = "Failed to execute 'append' on 'Headers'";
webidl.requiredArguments(arguments.length, 2, prefix);
name = webidl.converters["ByteString"](name, prefix, "Argument 1");
value = webidl.converters["ByteString"](value, prefix, "Argument 2");
appendHeader(this, name, value);
}
/**
* @param {string} name
*/
delete(name) {
webidl.assertBranded(this, HeadersPrototype);
const prefix = "Failed to execute 'delete' on 'Headers'";
webidl.requiredArguments(arguments.length, 1, prefix);
name = webidl.converters["ByteString"](name, prefix, "Argument 1");
if (!checkHeaderNameForHttpTokenCodePoint(name)) {
throw new TypeError("Header name is not valid.");
}
if (this[_guard] == "immutable") {
throw new TypeError("Headers are immutable.");
}
const list = this[_headerList];
const lowercaseName = byteLowerCase(name);
for (let i = 0; i < list.length; i++) {
if (byteLowerCase(list[i][0]) === lowercaseName) {
ArrayPrototypeSplice(list, i, 1);
i--;
}
}
}
/**
* @param {string} name
*/
get(name) {
webidl.assertBranded(this, HeadersPrototype);
const prefix = "Failed to execute 'get' on 'Headers'";
webidl.requiredArguments(arguments.length, 1, prefix);
name = webidl.converters["ByteString"](name, prefix, "Argument 1");
if (!checkHeaderNameForHttpTokenCodePoint(name)) {
throw new TypeError("Header name is not valid.");
}
const list = this[_headerList];
return getHeader(list, name);
}
getSetCookie() {
webidl.assertBranded(this, HeadersPrototype);
const list = this[_headerList];
const entries = [];
for (let i = 0; i < list.length; i++) {
if (byteLowerCase(list[i][0]) === "set-cookie") {
ArrayPrototypePush(entries, list[i][1]);
}
}
return entries;
}
/**
* @param {string} name
*/
has(name) {
webidl.assertBranded(this, HeadersPrototype);
const prefix = "Failed to execute 'has' on 'Headers'";
webidl.requiredArguments(arguments.length, 1, prefix);
name = webidl.converters["ByteString"](name, prefix, "Argument 1");
if (!checkHeaderNameForHttpTokenCodePoint(name)) {
throw new TypeError("Header name is not valid.");
}
const list = this[_headerList];
const lowercaseName = byteLowerCase(name);
for (let i = 0; i < list.length; i++) {
if (byteLowerCase(list[i][0]) === lowercaseName) {
return true;
}
}
return false;
}
/**
* @param {string} name
* @param {string} value
*/
set(name, value) {
webidl.assertBranded(this, HeadersPrototype);
const prefix = "Failed to execute 'set' on 'Headers'";
webidl.requiredArguments(arguments.length, 2, prefix);
name = webidl.converters["ByteString"](name, prefix, "Argument 1");
value = webidl.converters["ByteString"](value, prefix, "Argument 2");
value = normalizeHeaderValue(value);
// 2.
if (!checkHeaderNameForHttpTokenCodePoint(name)) {
throw new TypeError("Header name is not valid.");
}
if (!checkForInvalidValueChars(value)) {
throw new TypeError("Header value is not valid.");
}
if (this[_guard] == "immutable") {
throw new TypeError("Headers are immutable.");
}
const list = this[_headerList];
const lowercaseName = byteLowerCase(name);
let added = false;
for (let i = 0; i < list.length; i++) {
if (byteLowerCase(list[i][0]) === lowercaseName) {
if (!added) {
list[i][1] = value;
added = true;
} else {
ArrayPrototypeSplice(list, i, 1);
i--;
}
}
}
if (!added) {
ArrayPrototypePush(list, [name, value]);
}
}
[SymbolFor("Deno.privateCustomInspect")](inspect) {
const headers = {};
// deno-lint-ignore prefer-primordials
for (const header of this) {
headers[header[0]] = header[1];
}
return `Headers ${inspect(headers)}`;
}
}
webidl.mixinPairIterable("Headers", Headers, _iterableHeaders, 0, 1);
webidl.configurePrototype(Headers);
const HeadersPrototype = Headers.prototype;
webidl.converters["HeadersInit"] = (V, prefix, context, 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,
prefix,
context,
opts,
);
}
return webidl.converters["record<ByteString, ByteString>"](
V,
prefix,
context,
opts,
);
}
throw webidl.makeException(
TypeError,
"The provided value is not of type '(sequence<sequence<ByteString>> or record<ByteString, ByteString>)'",
prefix,
context,
);
};
webidl.converters["Headers"] = webidl.createInterfaceConverter(
"Headers",
Headers.prototype,
);
/**
* @param {HeaderList} list
* @param {"immutable" | "request" | "request-no-cors" | "response" | "none"} guard
* @returns {Headers}
*/
function headersFromHeaderList(list, guard) {
const headers = webidl.createBranded(Headers);
headers[_headerList] = list;
headers[_guard] = guard;
return headers;
}
/**
* @param {Headers} headers
* @returns {HeaderList}
*/
function headerListFromHeaders(headers) {
return headers[_headerList];
}
/**
* @param {Headers} headers
* @returns {"immutable" | "request" | "request-no-cors" | "response" | "none"}
*/
function guardFromHeaders(headers) {
return headers[_guard];
}
export {
fillHeaders,
getDecodeSplitHeader,
getHeader,
guardFromHeaders,
headerListFromHeaders,
Headers,
headersFromHeaderList,
};