2024-01-01 14:58:21 -05:00
|
|
|
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
2021-04-18 19:00:13 -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-18 19:00:13 -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-18 19:00:13 -04:00
|
|
|
/// <reference path="./lib.deno_fetch.d.ts" />
|
|
|
|
/// <reference lib="esnext" />
|
2020-07-19 13:49:44 -04:00
|
|
|
|
2023-12-07 08:21:01 -05:00
|
|
|
import { primordials } from "ext:core/mod.js";
|
2023-02-07 14:22:46 -05:00
|
|
|
const {
|
|
|
|
ArrayIsArray,
|
|
|
|
ArrayPrototypePush,
|
|
|
|
ArrayPrototypeSort,
|
|
|
|
ArrayPrototypeJoin,
|
|
|
|
ArrayPrototypeSplice,
|
2023-11-19 03:13:38 -05:00
|
|
|
ObjectFromEntries,
|
2023-05-02 06:15:45 -04:00
|
|
|
ObjectHasOwn,
|
2023-11-19 03:13:38 -05:00
|
|
|
ObjectPrototypeIsPrototypeOf,
|
perf(ext/headers): use regex.test instead of .exec (#20125)
This PR improves the performance of `Headers.get` by using `Regex.test`
instead of `.exec`. Also replaced the `Map` used for caching with an
object which is a bit faster
**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.get 124.71 ns/iter 8,018,687.3 (115.11 ns … 265.66 ns) 126.05 ns 136.12 ns 142.37 ns
```
**1.36.1**
```
cpu: 13th Gen Intel(R) Core(TM) i9-13900H
runtime: deno 1.36.0 (x86_64-unknown-linux-gnu)
benchmark time (avg) iter/s (min … max) p75 p99 p995
----------------------------------------------------------------------- -----------------------------
Headers.get 218.91 ns/iter 4,568,172.3 (165.37 ns … 264.44 ns) 241.62 ns 260.94 ns 262.67 ns
```
```js
const headers = new Headers({
"Content-Type": "application/json",
"Date": "Thu, 10 Aug 2023 07:45:10 GMT",
"X-Deno": "Deno",
"Powered-By": "Deno",
"Content-Encoding": "gzip",
"Set-Cookie": "__Secure-ID=123; Secure; Domain=example.com",
"Content-Length": "150",
"Vary": "Accept-Encoding, Accept, X-Requested-With",
});
Deno.bench("Headers.get", () => {
headers.get("x-deno");
});
```
2023-08-12 12:42:23 -04:00
|
|
|
RegExpPrototypeTest,
|
2023-02-07 14:22:46 -05:00
|
|
|
Symbol,
|
|
|
|
SymbolFor,
|
|
|
|
SymbolIterator,
|
|
|
|
StringPrototypeReplaceAll,
|
2023-06-13 15:13:34 -04:00
|
|
|
StringPrototypeCharCodeAt,
|
2023-02-07 14:22:46 -05:00
|
|
|
TypeError,
|
|
|
|
} = primordials;
|
|
|
|
|
2024-01-10 17:37:25 -05:00
|
|
|
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";
|
|
|
|
|
2023-02-07 14:22:46 -05:00
|
|
|
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");
|
2023-02-07 14:22:46 -05:00
|
|
|
const _guard = Symbol("guard");
|
2023-09-21 22:06:42 -04:00
|
|
|
const _brand = webidl.brand;
|
2023-02-07 14:22:46 -05:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @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) {
|
2023-05-02 06:15:45 -04:00
|
|
|
if (!ObjectHasOwn(object, key)) {
|
2023-02-07 14:22:46 -05:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
appendHeader(headers, key, object[key]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-13 15:13:34 -04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
perf(ext/headers): use regex.test instead of .exec (#20125)
This PR improves the performance of `Headers.get` by using `Regex.test`
instead of `.exec`. Also replaced the `Map` used for caching with an
object which is a bit faster
**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.get 124.71 ns/iter 8,018,687.3 (115.11 ns … 265.66 ns) 126.05 ns 136.12 ns 142.37 ns
```
**1.36.1**
```
cpu: 13th Gen Intel(R) Core(TM) i9-13900H
runtime: deno 1.36.0 (x86_64-unknown-linux-gnu)
benchmark time (avg) iter/s (min … max) p75 p99 p995
----------------------------------------------------------------------- -----------------------------
Headers.get 218.91 ns/iter 4,568,172.3 (165.37 ns … 264.44 ns) 241.62 ns 260.94 ns 262.67 ns
```
```js
const headers = new Headers({
"Content-Type": "application/json",
"Date": "Thu, 10 Aug 2023 07:45:10 GMT",
"X-Deno": "Deno",
"Powered-By": "Deno",
"Content-Encoding": "gzip",
"Set-Cookie": "__Secure-ID=123; Secure; Domain=example.com",
"Content-Length": "150",
"Vary": "Accept-Encoding, Accept, X-Requested-With",
});
Deno.bench("Headers.get", () => {
headers.get("x-deno");
});
```
2023-08-12 12:42:23 -04:00
|
|
|
let HEADER_NAME_CACHE = {};
|
|
|
|
let HEADER_CACHE_SIZE = 0;
|
2023-06-15 12:27:21 -04:00
|
|
|
const HEADER_NAME_CACHE_SIZE_BOUNDARY = 4096;
|
2023-06-13 15:13:34 -04:00
|
|
|
function checkHeaderNameForHttpTokenCodePoint(name) {
|
perf(ext/headers): use regex.test instead of .exec (#20125)
This PR improves the performance of `Headers.get` by using `Regex.test`
instead of `.exec`. Also replaced the `Map` used for caching with an
object which is a bit faster
**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.get 124.71 ns/iter 8,018,687.3 (115.11 ns … 265.66 ns) 126.05 ns 136.12 ns 142.37 ns
```
**1.36.1**
```
cpu: 13th Gen Intel(R) Core(TM) i9-13900H
runtime: deno 1.36.0 (x86_64-unknown-linux-gnu)
benchmark time (avg) iter/s (min … max) p75 p99 p995
----------------------------------------------------------------------- -----------------------------
Headers.get 218.91 ns/iter 4,568,172.3 (165.37 ns … 264.44 ns) 241.62 ns 260.94 ns 262.67 ns
```
```js
const headers = new Headers({
"Content-Type": "application/json",
"Date": "Thu, 10 Aug 2023 07:45:10 GMT",
"X-Deno": "Deno",
"Powered-By": "Deno",
"Content-Encoding": "gzip",
"Set-Cookie": "__Secure-ID=123; Secure; Domain=example.com",
"Content-Length": "150",
"Vary": "Accept-Encoding, Accept, X-Requested-With",
});
Deno.bench("Headers.get", () => {
headers.get("x-deno");
});
```
2023-08-12 12:42:23 -04:00
|
|
|
const fromCache = HEADER_NAME_CACHE[name];
|
|
|
|
if (fromCache !== undefined) {
|
|
|
|
return fromCache;
|
2023-06-13 15:13:34 -04:00
|
|
|
}
|
|
|
|
|
perf(ext/headers): use regex.test instead of .exec (#20125)
This PR improves the performance of `Headers.get` by using `Regex.test`
instead of `.exec`. Also replaced the `Map` used for caching with an
object which is a bit faster
**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.get 124.71 ns/iter 8,018,687.3 (115.11 ns … 265.66 ns) 126.05 ns 136.12 ns 142.37 ns
```
**1.36.1**
```
cpu: 13th Gen Intel(R) Core(TM) i9-13900H
runtime: deno 1.36.0 (x86_64-unknown-linux-gnu)
benchmark time (avg) iter/s (min … max) p75 p99 p995
----------------------------------------------------------------------- -----------------------------
Headers.get 218.91 ns/iter 4,568,172.3 (165.37 ns … 264.44 ns) 241.62 ns 260.94 ns 262.67 ns
```
```js
const headers = new Headers({
"Content-Type": "application/json",
"Date": "Thu, 10 Aug 2023 07:45:10 GMT",
"X-Deno": "Deno",
"Powered-By": "Deno",
"Content-Encoding": "gzip",
"Set-Cookie": "__Secure-ID=123; Secure; Domain=example.com",
"Content-Length": "150",
"Vary": "Accept-Encoding, Accept, X-Requested-With",
});
Deno.bench("Headers.get", () => {
headers.get("x-deno");
});
```
2023-08-12 12:42:23 -04:00
|
|
|
const valid = RegExpPrototypeTest(HTTP_TOKEN_CODE_POINT_RE, name);
|
2023-06-13 15:13:34 -04:00
|
|
|
|
perf(ext/headers): use regex.test instead of .exec (#20125)
This PR improves the performance of `Headers.get` by using `Regex.test`
instead of `.exec`. Also replaced the `Map` used for caching with an
object which is a bit faster
**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.get 124.71 ns/iter 8,018,687.3 (115.11 ns … 265.66 ns) 126.05 ns 136.12 ns 142.37 ns
```
**1.36.1**
```
cpu: 13th Gen Intel(R) Core(TM) i9-13900H
runtime: deno 1.36.0 (x86_64-unknown-linux-gnu)
benchmark time (avg) iter/s (min … max) p75 p99 p995
----------------------------------------------------------------------- -----------------------------
Headers.get 218.91 ns/iter 4,568,172.3 (165.37 ns … 264.44 ns) 241.62 ns 260.94 ns 262.67 ns
```
```js
const headers = new Headers({
"Content-Type": "application/json",
"Date": "Thu, 10 Aug 2023 07:45:10 GMT",
"X-Deno": "Deno",
"Powered-By": "Deno",
"Content-Encoding": "gzip",
"Set-Cookie": "__Secure-ID=123; Secure; Domain=example.com",
"Content-Length": "150",
"Vary": "Accept-Encoding, Accept, X-Requested-With",
});
Deno.bench("Headers.get", () => {
headers.get("x-deno");
});
```
2023-08-12 12:42:23 -04:00
|
|
|
if (HEADER_CACHE_SIZE > HEADER_NAME_CACHE_SIZE_BOUNDARY) {
|
|
|
|
HEADER_NAME_CACHE = {};
|
|
|
|
HEADER_CACHE_SIZE = 0;
|
2023-06-13 15:13:34 -04:00
|
|
|
}
|
perf(ext/headers): use regex.test instead of .exec (#20125)
This PR improves the performance of `Headers.get` by using `Regex.test`
instead of `.exec`. Also replaced the `Map` used for caching with an
object which is a bit faster
**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.get 124.71 ns/iter 8,018,687.3 (115.11 ns … 265.66 ns) 126.05 ns 136.12 ns 142.37 ns
```
**1.36.1**
```
cpu: 13th Gen Intel(R) Core(TM) i9-13900H
runtime: deno 1.36.0 (x86_64-unknown-linux-gnu)
benchmark time (avg) iter/s (min … max) p75 p99 p995
----------------------------------------------------------------------- -----------------------------
Headers.get 218.91 ns/iter 4,568,172.3 (165.37 ns … 264.44 ns) 241.62 ns 260.94 ns 262.67 ns
```
```js
const headers = new Headers({
"Content-Type": "application/json",
"Date": "Thu, 10 Aug 2023 07:45:10 GMT",
"X-Deno": "Deno",
"Powered-By": "Deno",
"Content-Encoding": "gzip",
"Set-Cookie": "__Secure-ID=123; Secure; Domain=example.com",
"Content-Length": "150",
"Vary": "Accept-Encoding, Accept, X-Requested-With",
});
Deno.bench("Headers.get", () => {
headers.get("x-deno");
});
```
2023-08-12 12:42:23 -04:00
|
|
|
HEADER_CACHE_SIZE++;
|
|
|
|
HEADER_NAME_CACHE[name] = valid;
|
2023-06-13 15:13:34 -04:00
|
|
|
|
|
|
|
return valid;
|
|
|
|
}
|
2023-02-07 14:22:46 -05:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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.
|
2023-06-13 15:13:34 -04:00
|
|
|
if (!checkHeaderNameForHttpTokenCodePoint(name)) {
|
2023-02-07 14:22:46 -05:00
|
|
|
throw new TypeError("Header name is not valid.");
|
|
|
|
}
|
2023-06-13 15:13:34 -04:00
|
|
|
if (!checkForInvalidValueChars(value)) {
|
2023-02-07 14:22:46 -05:00
|
|
|
throw new TypeError("Header value is not valid.");
|
|
|
|
}
|
2020-07-19 13:49:44 -04:00
|
|
|
|
2023-02-07 14:22:46 -05:00
|
|
|
// 3.
|
|
|
|
if (headers[_guard] == "immutable") {
|
|
|
|
throw new TypeError("Headers are immutable.");
|
2020-07-19 13:49:44 -04:00
|
|
|
}
|
|
|
|
|
2023-02-07 14:22:46 -05:00
|
|
|
// 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);
|
perf(ext/headers): optimize getHeader using for loop (#20115)
This PR optimizes the `getHeader` function by replacing `.filter` and
`.map` with a `for` loop
**this patch**
```
cpu: 13th Gen Intel(R) Core(TM) i9-13900H
runtime: deno 1.36.0 (x86_64-unknown-linux-gnu)
benchmark time (avg) iter/s (min … max) p75 p99 p995
----------------------------------------------------------------- -----------------------------
Headers.get 132.2 ns/iter 7,564,093.4 (125.81 ns … 147.66 ns) 133.79 ns 144.92 ns 145.36 ns
```
**main**
```
cpu: 13th Gen Intel(R) Core(TM) i9-13900H
runtime: deno 1.36.0 (x86_64-unknown-linux-gnu)
benchmark time (avg) iter/s (min … max) p75 p99 p995
----------------------------------------------------------------- -----------------------------
Headers.get 191.48 ns/iter 5,222,523.6 (182.75 ns … 212.22 ns) 193.5 ns 205.96 ns 211.51 ns
```
```js
const headers = new Headers({
"Content-Type": "application/json",
"Date": "Thu, 10 Aug 2023 07:45:10 GMT",
"X-Deno": "Deno",
"Powered-By": "Deno",
"Content-Encoding": "gzip",
"Set-Cookie": "__Secure-ID=123; Secure; Domain=example.com",
"Content-Length": "150",
"Vary": "Accept-Encoding, Accept, X-Requested-With",
});
Deno.bench("Headers.get", () => {
const i = headers.get("x-deno");
});
```
2023-08-10 13:41:09 -04:00
|
|
|
const entries = [];
|
|
|
|
for (let i = 0; i < list.length; i++) {
|
|
|
|
if (byteLowerCase(list[i][0]) === lowercaseName) {
|
|
|
|
ArrayPrototypePush(entries, list[i][1]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-07 14:22:46 -05:00
|
|
|
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;
|
2020-07-19 13:49:44 -04:00
|
|
|
}
|
2023-02-07 14:22:46 -05:00
|
|
|
} else {
|
|
|
|
if (input[position] !== "\u002C") throw new TypeError("Unreachable");
|
|
|
|
position += 1;
|
2021-04-18 19:00:13 -04:00
|
|
|
}
|
2023-02-07 14:22:46 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
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];
|
|
|
|
}
|
|
|
|
|
2023-02-07 14:22:46 -05:00
|
|
|
// The order of steps are not similar to the ones suggested by the
|
|
|
|
// spec but produce the same result.
|
perf(ext/headers): optimize headers iterable (#20155)
This PR makes more optimizations to headers iterable by removing
`ObjectEntries` which was consistently prominent in the flame graph when
benchmarking an express server.
**this PR**
```
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 iter 9.6 µs/iter 104,134.1 (8.74 µs … 131.31 µs) 9.47 µs 12.61 µs 17.81 µs
```
**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 iter 12.87 µs/iter 77,675.9 (11.97 µs … 132.34 µs) 12.76 µs 16.49 µs 26.4 µs
```
```js
const headers = new Headers({
"Content-Type": "application/json",
"X-Content-Type": "application/json",
"Date": "Thu, 14 Aug 2023 17:45:10 GMT",
"X-Deno": "Deno",
"Powered-By": "Deno",
"Content-Encoding": "gzip",
"Set-Cookie": "__Secure-ID=123; Secure; Domain=example.com",
"Content-Length": "150",
"Vary": "Accept-Encoding, Accept, X-Requested-With",
});
Deno.bench('headers iter', () => {
[...headers]
})
```
2023-08-14 15:13:55 -04:00
|
|
|
const seenHeaders = {};
|
|
|
|
const entries = [];
|
2023-02-07 14:22:46 -05:00
|
|
|
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") {
|
perf(ext/headers): optimize headers iterable (#20155)
This PR makes more optimizations to headers iterable by removing
`ObjectEntries` which was consistently prominent in the flame graph when
benchmarking an express server.
**this PR**
```
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 iter 9.6 µs/iter 104,134.1 (8.74 µs … 131.31 µs) 9.47 µs 12.61 µs 17.81 µs
```
**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 iter 12.87 µs/iter 77,675.9 (11.97 µs … 132.34 µs) 12.76 µs 16.49 µs 26.4 µs
```
```js
const headers = new Headers({
"Content-Type": "application/json",
"X-Content-Type": "application/json",
"Date": "Thu, 14 Aug 2023 17:45:10 GMT",
"X-Deno": "Deno",
"Powered-By": "Deno",
"Content-Encoding": "gzip",
"Set-Cookie": "__Secure-ID=123; Secure; Domain=example.com",
"Content-Length": "150",
"Vary": "Accept-Encoding, Accept, X-Requested-With",
});
Deno.bench('headers iter', () => {
[...headers]
})
```
2023-08-14 15:13:55 -04:00
|
|
|
ArrayPrototypePush(entries, [name, value]);
|
2023-02-07 14:22:46 -05:00
|
|
|
} 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.
|
perf(ext/headers): optimize headers iterable (#20155)
This PR makes more optimizations to headers iterable by removing
`ObjectEntries` which was consistently prominent in the flame graph when
benchmarking an express server.
**this PR**
```
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 iter 9.6 µs/iter 104,134.1 (8.74 µs … 131.31 µs) 9.47 µs 12.61 µs 17.81 µs
```
**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 iter 12.87 µs/iter 77,675.9 (11.97 µs … 132.34 µs) 12.76 µs 16.49 µs 26.4 µs
```
```js
const headers = new Headers({
"Content-Type": "application/json",
"X-Content-Type": "application/json",
"Date": "Thu, 14 Aug 2023 17:45:10 GMT",
"X-Deno": "Deno",
"Powered-By": "Deno",
"Content-Encoding": "gzip",
"Set-Cookie": "__Secure-ID=123; Secure; Domain=example.com",
"Content-Length": "150",
"Vary": "Accept-Encoding, Accept, X-Requested-With",
});
Deno.bench('headers iter', () => {
[...headers]
})
```
2023-08-14 15:13:55 -04:00
|
|
|
const seenHeaderIndex = seenHeaders[name];
|
|
|
|
if (seenHeaderIndex !== undefined) {
|
|
|
|
const entryValue = entries[seenHeaderIndex][1];
|
|
|
|
entries[seenHeaderIndex][1] = entryValue.length > 0
|
|
|
|
? entryValue + "\x2C\x20" + value
|
|
|
|
: value;
|
2023-02-07 14:22:46 -05:00
|
|
|
} else {
|
perf(ext/headers): optimize headers iterable (#20155)
This PR makes more optimizations to headers iterable by removing
`ObjectEntries` which was consistently prominent in the flame graph when
benchmarking an express server.
**this PR**
```
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 iter 9.6 µs/iter 104,134.1 (8.74 µs … 131.31 µs) 9.47 µs 12.61 µs 17.81 µs
```
**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 iter 12.87 µs/iter 77,675.9 (11.97 µs … 132.34 µs) 12.76 µs 16.49 µs 26.4 µs
```
```js
const headers = new Headers({
"Content-Type": "application/json",
"X-Content-Type": "application/json",
"Date": "Thu, 14 Aug 2023 17:45:10 GMT",
"X-Deno": "Deno",
"Powered-By": "Deno",
"Content-Encoding": "gzip",
"Set-Cookie": "__Secure-ID=123; Secure; Domain=example.com",
"Content-Length": "150",
"Vary": "Accept-Encoding, Accept, X-Requested-With",
});
Deno.bench('headers iter', () => {
[...headers]
})
```
2023-08-14 15:13:55 -04:00
|
|
|
seenHeaders[name] = entries.length; // store header index in entries array
|
|
|
|
ArrayPrototypePush(entries, [name, value]);
|
2021-10-01 06:17:16 -04:00
|
|
|
}
|
2020-07-19 13:49:44 -04:00
|
|
|
}
|
|
|
|
}
|
2023-02-07 14:22:46 -05:00
|
|
|
|
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(
|
2023-08-09 19:36:47 -04:00
|
|
|
entries,
|
2023-02-07 14:22:46 -05:00
|
|
|
(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;
|
2020-07-19 13:49:44 -04:00
|
|
|
}
|
|
|
|
|
2023-02-07 14:22:46 -05:00
|
|
|
/** @param {HeadersInit} [init] */
|
|
|
|
constructor(init = undefined) {
|
2023-09-21 22:06:42 -04:00
|
|
|
if (init === _brand) {
|
|
|
|
this[_brand] = _brand;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-02-07 14:22:46 -05:00
|
|
|
const prefix = "Failed to construct 'Headers'";
|
|
|
|
if (init !== undefined) {
|
2023-05-01 06:47:13 -04:00
|
|
|
init = webidl.converters["HeadersInit"](init, prefix, "Argument 1");
|
2023-02-07 14:22:46 -05:00
|
|
|
}
|
|
|
|
|
2023-09-21 22:06:42 -04:00
|
|
|
this[_brand] = _brand;
|
2023-02-07 14:22:46 -05:00
|
|
|
this[_guard] = "none";
|
|
|
|
if (init !== undefined) {
|
|
|
|
fillHeaders(this, init);
|
|
|
|
}
|
|
|
|
}
|
2021-09-26 14:19:02 -04:00
|
|
|
|
2021-04-18 19:00:13 -04:00
|
|
|
/**
|
2021-04-28 10:08:51 -04:00
|
|
|
* @param {string} name
|
|
|
|
* @param {string} value
|
2021-04-18 19:00:13 -04:00
|
|
|
*/
|
2023-02-07 14:22:46 -05:00
|
|
|
append(name, value) {
|
|
|
|
webidl.assertBranded(this, HeadersPrototype);
|
|
|
|
const prefix = "Failed to execute 'append' on 'Headers'";
|
2023-04-12 15:58:57 -04:00
|
|
|
webidl.requiredArguments(arguments.length, 2, prefix);
|
2023-05-01 06:47:13 -04:00
|
|
|
name = webidl.converters["ByteString"](name, prefix, "Argument 1");
|
|
|
|
value = webidl.converters["ByteString"](value, prefix, "Argument 2");
|
2023-02-07 14:22:46 -05:00
|
|
|
appendHeader(this, name, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {string} name
|
|
|
|
*/
|
|
|
|
delete(name) {
|
2023-07-02 07:20:56 -04:00
|
|
|
webidl.assertBranded(this, HeadersPrototype);
|
2023-02-07 14:22:46 -05:00
|
|
|
const prefix = "Failed to execute 'delete' on 'Headers'";
|
2023-04-12 15:58:57 -04:00
|
|
|
webidl.requiredArguments(arguments.length, 1, prefix);
|
2023-05-01 06:47:13 -04:00
|
|
|
name = webidl.converters["ByteString"](name, prefix, "Argument 1");
|
2021-04-18 19:00:13 -04:00
|
|
|
|
2023-06-13 15:13:34 -04:00
|
|
|
if (!checkHeaderNameForHttpTokenCodePoint(name)) {
|
2021-04-18 19:00:13 -04:00
|
|
|
throw new TypeError("Header name is not valid.");
|
2020-07-19 13:49:44 -04:00
|
|
|
}
|
2023-02-07 14:22:46 -05:00
|
|
|
if (this[_guard] == "immutable") {
|
2021-04-18 19:00:13 -04:00
|
|
|
throw new TypeError("Headers are immutable.");
|
2020-07-19 13:49:44 -04:00
|
|
|
}
|
|
|
|
|
2023-02-07 14:22:46 -05:00
|
|
|
const list = this[_headerList];
|
2021-11-22 19:23:11 -05:00
|
|
|
const lowercaseName = byteLowerCase(name);
|
|
|
|
for (let i = 0; i < list.length; i++) {
|
|
|
|
if (byteLowerCase(list[i][0]) === lowercaseName) {
|
2023-02-07 14:22:46 -05:00
|
|
|
ArrayPrototypeSplice(list, i, 1);
|
|
|
|
i--;
|
2021-11-22 19:23:11 -05:00
|
|
|
}
|
|
|
|
}
|
2020-07-19 13:49:44 -04:00
|
|
|
}
|
|
|
|
|
2021-04-18 19:00:13 -04:00
|
|
|
/**
|
|
|
|
* @param {string} name
|
|
|
|
*/
|
2023-02-07 14:22:46 -05:00
|
|
|
get(name) {
|
2023-07-02 07:20:56 -04:00
|
|
|
webidl.assertBranded(this, HeadersPrototype);
|
2023-02-07 14:22:46 -05:00
|
|
|
const prefix = "Failed to execute 'get' on 'Headers'";
|
2023-04-12 15:58:57 -04:00
|
|
|
webidl.requiredArguments(arguments.length, 1, prefix);
|
2023-05-01 06:47:13 -04:00
|
|
|
name = webidl.converters["ByteString"](name, prefix, "Argument 1");
|
2023-02-07 14:22:46 -05:00
|
|
|
|
2023-06-13 15:13:34 -04:00
|
|
|
if (!checkHeaderNameForHttpTokenCodePoint(name)) {
|
2023-02-07 14:22:46 -05:00
|
|
|
throw new TypeError("Header name is not valid.");
|
2020-07-19 13:49:44 -04:00
|
|
|
}
|
2023-02-07 14:22:46 -05:00
|
|
|
|
|
|
|
const list = this[_headerList];
|
|
|
|
return getHeader(list, name);
|
2020-07-19 13:49:44 -04:00
|
|
|
}
|
|
|
|
|
2023-07-02 07:20:56 -04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2021-04-20 08:47:22 -04:00
|
|
|
/**
|
|
|
|
* @param {string} name
|
|
|
|
*/
|
2023-02-07 14:22:46 -05:00
|
|
|
has(name) {
|
2023-07-02 07:20:56 -04:00
|
|
|
webidl.assertBranded(this, HeadersPrototype);
|
2023-02-07 14:22:46 -05:00
|
|
|
const prefix = "Failed to execute 'has' on 'Headers'";
|
2023-04-12 15:58:57 -04:00
|
|
|
webidl.requiredArguments(arguments.length, 1, prefix);
|
2023-05-01 06:47:13 -04:00
|
|
|
name = webidl.converters["ByteString"](name, prefix, "Argument 1");
|
2021-04-20 08:47:22 -04:00
|
|
|
|
2023-06-13 15:13:34 -04:00
|
|
|
if (!checkHeaderNameForHttpTokenCodePoint(name)) {
|
2023-02-07 14:22:46 -05:00
|
|
|
throw new TypeError("Header name is not valid.");
|
2021-04-20 08:47:22 -04:00
|
|
|
}
|
|
|
|
|
2023-02-07 14:22:46 -05:00
|
|
|
const list = this[_headerList];
|
|
|
|
const lowercaseName = byteLowerCase(name);
|
|
|
|
for (let i = 0; i < list.length; i++) {
|
|
|
|
if (byteLowerCase(list[i][0]) === lowercaseName) {
|
|
|
|
return true;
|
2020-07-19 13:49:44 -04:00
|
|
|
}
|
|
|
|
}
|
2023-02-07 14:22:46 -05:00
|
|
|
return false;
|
|
|
|
}
|
2020-07-19 13:49:44 -04:00
|
|
|
|
2023-02-07 14:22:46 -05:00
|
|
|
/**
|
|
|
|
* @param {string} name
|
|
|
|
* @param {string} value
|
|
|
|
*/
|
|
|
|
set(name, value) {
|
|
|
|
webidl.assertBranded(this, HeadersPrototype);
|
|
|
|
const prefix = "Failed to execute 'set' on 'Headers'";
|
2023-04-12 15:58:57 -04:00
|
|
|
webidl.requiredArguments(arguments.length, 2, prefix);
|
2023-05-01 06:47:13 -04:00
|
|
|
name = webidl.converters["ByteString"](name, prefix, "Argument 1");
|
|
|
|
value = webidl.converters["ByteString"](value, prefix, "Argument 2");
|
2021-04-18 19:00:13 -04:00
|
|
|
|
2023-02-07 14:22:46 -05:00
|
|
|
value = normalizeHeaderValue(value);
|
2020-07-19 13:49:44 -04:00
|
|
|
|
2023-02-07 14:22:46 -05:00
|
|
|
// 2.
|
2023-06-13 15:13:34 -04:00
|
|
|
if (!checkHeaderNameForHttpTokenCodePoint(name)) {
|
2023-02-07 14:22:46 -05:00
|
|
|
throw new TypeError("Header name is not valid.");
|
2020-07-19 13:49:44 -04:00
|
|
|
}
|
2023-06-13 15:13:34 -04:00
|
|
|
if (!checkForInvalidValueChars(value)) {
|
2023-02-07 14:22:46 -05:00
|
|
|
throw new TypeError("Header value is not valid.");
|
2020-07-19 13:49:44 -04:00
|
|
|
}
|
|
|
|
|
2023-02-07 14:22:46 -05:00
|
|
|
if (this[_guard] == "immutable") {
|
|
|
|
throw new TypeError("Headers are immutable.");
|
2020-07-19 13:49:44 -04:00
|
|
|
}
|
|
|
|
|
2023-02-07 14:22:46 -05:00
|
|
|
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--;
|
2021-04-18 19:00:13 -04:00
|
|
|
}
|
|
|
|
}
|
2020-07-19 13:49:44 -04:00
|
|
|
}
|
2023-02-07 14:22:46 -05:00
|
|
|
if (!added) {
|
|
|
|
ArrayPrototypePush(list, [name, value]);
|
2021-04-18 19:00:13 -04:00
|
|
|
}
|
2023-02-07 14:22:46 -05:00
|
|
|
}
|
2021-04-18 19:00:13 -04:00
|
|
|
|
2023-11-19 03:13:38 -05:00
|
|
|
[SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
|
|
|
|
if (ObjectPrototypeIsPrototypeOf(HeadersPrototype, this)) {
|
|
|
|
return `${this.constructor.name} ${
|
|
|
|
inspect(ObjectFromEntries(this), inspectOptions)
|
|
|
|
}`;
|
|
|
|
} else {
|
|
|
|
return `${this.constructor.name} ${inspect({}, inspectOptions)}`;
|
2020-07-19 13:49:44 -04:00
|
|
|
}
|
|
|
|
}
|
2023-02-07 14:22:46 -05:00
|
|
|
}
|
2020-07-19 13:49:44 -04:00
|
|
|
|
2023-02-07 14:22:46 -05:00
|
|
|
webidl.mixinPairIterable("Headers", Headers, _iterableHeaders, 0, 1);
|
2021-04-18 19:00:13 -04:00
|
|
|
|
2023-10-09 23:01:01 -04:00
|
|
|
webidl.configureInterface(Headers);
|
2023-02-07 14:22:46 -05:00
|
|
|
const HeadersPrototype = Headers.prototype;
|
2021-06-07 04:04:10 -04:00
|
|
|
|
2023-05-01 06:47:13 -04:00
|
|
|
webidl.converters["HeadersInit"] = (V, prefix, context, opts) => {
|
2023-02-07 14:22:46 -05:00
|
|
|
// Union for (sequence<sequence<ByteString>> or record<ByteString, ByteString>)
|
|
|
|
if (webidl.type(V) === "Object" && V !== null) {
|
|
|
|
if (V[SymbolIterator] !== undefined) {
|
2023-05-01 06:47:13 -04:00
|
|
|
return webidl.converters["sequence<sequence<ByteString>>"](
|
|
|
|
V,
|
|
|
|
prefix,
|
|
|
|
context,
|
|
|
|
opts,
|
|
|
|
);
|
2021-04-18 19:00:13 -04:00
|
|
|
}
|
2023-05-01 06:47:13 -04:00
|
|
|
return webidl.converters["record<ByteString, ByteString>"](
|
|
|
|
V,
|
|
|
|
prefix,
|
|
|
|
context,
|
|
|
|
opts,
|
|
|
|
);
|
2021-04-20 08:47:22 -04:00
|
|
|
}
|
2023-02-07 14:22:46 -05:00
|
|
|
throw webidl.makeException(
|
|
|
|
TypeError,
|
|
|
|
"The provided value is not of type '(sequence<sequence<ByteString>> or record<ByteString, ByteString>)'",
|
2023-05-01 06:47:13 -04:00
|
|
|
prefix,
|
|
|
|
context,
|
2023-02-07 14:22:46 -05:00
|
|
|
);
|
|
|
|
};
|
|
|
|
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) {
|
2023-09-21 22:06:42 -04:00
|
|
|
const headers = new Headers(_brand);
|
2023-02-07 14:22:46 -05:00
|
|
|
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];
|
|
|
|
}
|
|
|
|
|
2023-08-15 10:59:35 -04:00
|
|
|
/**
|
|
|
|
* @param {Headers} headers
|
|
|
|
* @returns {[string, string][]}
|
|
|
|
*/
|
|
|
|
function headersEntries(headers) {
|
|
|
|
return headers[_iterableHeaders];
|
|
|
|
}
|
|
|
|
|
2023-02-07 14:22:46 -05:00
|
|
|
export {
|
|
|
|
fillHeaders,
|
|
|
|
getDecodeSplitHeader,
|
|
|
|
getHeader,
|
|
|
|
guardFromHeaders,
|
|
|
|
headerListFromHeaders,
|
|
|
|
Headers,
|
2023-08-15 10:59:35 -04:00
|
|
|
headersEntries,
|
2023-02-07 14:22:46 -05:00
|
|
|
headersFromHeaderList,
|
|
|
|
};
|