summaryrefslogtreecommitdiff
path: root/ext/fetch/20_headers.js
diff options
context:
space:
mode:
authorBartek IwaƄczuk <biwanczuk@gmail.com>2023-06-13 21:13:34 +0200
committerGitHub <noreply@github.com>2023-06-13 21:13:34 +0200
commit7e81d3c876bccd208a2e7b9c33f6fad4e3cf1b0f (patch)
tree9786f21f9f53c434bbe06bee2e6947b068d65c85 /ext/fetch/20_headers.js
parent133f9a952bc317347fa6581a6a47e59287f5b960 (diff)
perf(http): cache verified headers (#19465)
Use `Map` to cache validated HTTP headers. Cache has a capacity of 4096 elements and it's cleared once that capacity is reached. In `preactssr` benchmark it lowers the time spent when adding headers from 180ms to 2.5ms.
Diffstat (limited to 'ext/fetch/20_headers.js')
-rw-r--r--ext/fetch/20_headers.js51
1 files changed, 40 insertions, 11 deletions
diff --git a/ext/fetch/20_headers.js b/ext/fetch/20_headers.js
index 6d934a7c1..dcd3c95fc 100644
--- a/ext/fetch/20_headers.js
+++ b/ext/fetch/20_headers.js
@@ -32,11 +32,16 @@ const {
ObjectHasOwn,
RegExpPrototypeExec,
SafeArrayIterator,
- SafeRegExp,
+ SafeMap,
+ MapPrototypeGet,
+ MapPrototypeHas,
+ MapPrototypeSet,
+ MapPrototypeClear,
Symbol,
SymbolFor,
SymbolIterator,
StringPrototypeReplaceAll,
+ StringPrototypeCharCodeAt,
TypeError,
} = primordials;
@@ -87,9 +92,33 @@ function fillHeaders(headers, object) {
}
}
-// Regex matching illegal chars in a header value
-// deno-lint-ignore no-control-regex
-const ILLEGAL_VALUE_CHARS = new SafeRegExp(/[\x00\x0A\x0D]/);
+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;
+}
+
+const HEADER_NAME_CACHE = new SafeMap();
+function checkHeaderNameForHttpTokenCodePoint(name) {
+ if (MapPrototypeHas(HEADER_NAME_CACHE, name)) {
+ return MapPrototypeGet(HEADER_NAME_CACHE, name);
+ }
+
+ const valid = RegExpPrototypeExec(HTTP_TOKEN_CODE_POINT_RE, name) !== null;
+
+ if (HEADER_NAME_CACHE.size > 4096) {
+ MapPrototypeClear(HEADER_NAME_CACHE);
+ }
+ MapPrototypeSet(HEADER_NAME_CACHE, name, valid);
+
+ return valid;
+}
/**
* https://fetch.spec.whatwg.org/#concept-headers-append
@@ -102,10 +131,10 @@ function appendHeader(headers, name, value) {
value = normalizeHeaderValue(value);
// 2.
- if (RegExpPrototypeExec(HTTP_TOKEN_CODE_POINT_RE, name) === null) {
+ if (!checkHeaderNameForHttpTokenCodePoint(name)) {
throw new TypeError("Header name is not valid.");
}
- if (RegExpPrototypeExec(ILLEGAL_VALUE_CHARS, value) !== null) {
+ if (!checkForInvalidValueChars(value)) {
throw new TypeError("Header value is not valid.");
}
@@ -282,7 +311,7 @@ class Headers {
webidl.requiredArguments(arguments.length, 1, prefix);
name = webidl.converters["ByteString"](name, prefix, "Argument 1");
- if (RegExpPrototypeExec(HTTP_TOKEN_CODE_POINT_RE, name) === null) {
+ if (!checkHeaderNameForHttpTokenCodePoint(name)) {
throw new TypeError("Header name is not valid.");
}
if (this[_guard] == "immutable") {
@@ -307,7 +336,7 @@ class Headers {
webidl.requiredArguments(arguments.length, 1, prefix);
name = webidl.converters["ByteString"](name, prefix, "Argument 1");
- if (RegExpPrototypeExec(HTTP_TOKEN_CODE_POINT_RE, name) === null) {
+ if (!checkHeaderNameForHttpTokenCodePoint(name)) {
throw new TypeError("Header name is not valid.");
}
@@ -323,7 +352,7 @@ class Headers {
webidl.requiredArguments(arguments.length, 1, prefix);
name = webidl.converters["ByteString"](name, prefix, "Argument 1");
- if (RegExpPrototypeExec(HTTP_TOKEN_CODE_POINT_RE, name) === null) {
+ if (!checkHeaderNameForHttpTokenCodePoint(name)) {
throw new TypeError("Header name is not valid.");
}
@@ -351,10 +380,10 @@ class Headers {
value = normalizeHeaderValue(value);
// 2.
- if (RegExpPrototypeExec(HTTP_TOKEN_CODE_POINT_RE, name) === null) {
+ if (!checkHeaderNameForHttpTokenCodePoint(name)) {
throw new TypeError("Header name is not valid.");
}
- if (RegExpPrototypeExec(ILLEGAL_VALUE_CHARS, value) !== null) {
+ if (!checkForInvalidValueChars(value)) {
throw new TypeError("Header value is not valid.");
}