mirror of
https://github.com/denoland/deno.git
synced 2024-12-24 08:09:08 -05:00
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.
This commit is contained in:
parent
d14b0f0564
commit
e4920f4a28
1 changed files with 40 additions and 11 deletions
|
@ -32,11 +32,16 @@ const {
|
||||||
ObjectHasOwn,
|
ObjectHasOwn,
|
||||||
RegExpPrototypeExec,
|
RegExpPrototypeExec,
|
||||||
SafeArrayIterator,
|
SafeArrayIterator,
|
||||||
SafeRegExp,
|
SafeMap,
|
||||||
|
MapPrototypeGet,
|
||||||
|
MapPrototypeHas,
|
||||||
|
MapPrototypeSet,
|
||||||
|
MapPrototypeClear,
|
||||||
Symbol,
|
Symbol,
|
||||||
SymbolFor,
|
SymbolFor,
|
||||||
SymbolIterator,
|
SymbolIterator,
|
||||||
StringPrototypeReplaceAll,
|
StringPrototypeReplaceAll,
|
||||||
|
StringPrototypeCharCodeAt,
|
||||||
TypeError,
|
TypeError,
|
||||||
} = primordials;
|
} = primordials;
|
||||||
|
|
||||||
|
@ -87,9 +92,33 @@ function fillHeaders(headers, object) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Regex matching illegal chars in a header value
|
function checkForInvalidValueChars(value) {
|
||||||
// deno-lint-ignore no-control-regex
|
for (let i = 0; i < value.length; i++) {
|
||||||
const ILLEGAL_VALUE_CHARS = new SafeRegExp(/[\x00\x0A\x0D]/);
|
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
|
* https://fetch.spec.whatwg.org/#concept-headers-append
|
||||||
|
@ -102,10 +131,10 @@ function appendHeader(headers, name, value) {
|
||||||
value = normalizeHeaderValue(value);
|
value = normalizeHeaderValue(value);
|
||||||
|
|
||||||
// 2.
|
// 2.
|
||||||
if (RegExpPrototypeExec(HTTP_TOKEN_CODE_POINT_RE, name) === null) {
|
if (!checkHeaderNameForHttpTokenCodePoint(name)) {
|
||||||
throw new TypeError("Header name is not valid.");
|
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.");
|
throw new TypeError("Header value is not valid.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -282,7 +311,7 @@ class Headers {
|
||||||
webidl.requiredArguments(arguments.length, 1, prefix);
|
webidl.requiredArguments(arguments.length, 1, prefix);
|
||||||
name = webidl.converters["ByteString"](name, prefix, "Argument 1");
|
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.");
|
throw new TypeError("Header name is not valid.");
|
||||||
}
|
}
|
||||||
if (this[_guard] == "immutable") {
|
if (this[_guard] == "immutable") {
|
||||||
|
@ -307,7 +336,7 @@ class Headers {
|
||||||
webidl.requiredArguments(arguments.length, 1, prefix);
|
webidl.requiredArguments(arguments.length, 1, prefix);
|
||||||
name = webidl.converters["ByteString"](name, prefix, "Argument 1");
|
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.");
|
throw new TypeError("Header name is not valid.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -323,7 +352,7 @@ class Headers {
|
||||||
webidl.requiredArguments(arguments.length, 1, prefix);
|
webidl.requiredArguments(arguments.length, 1, prefix);
|
||||||
name = webidl.converters["ByteString"](name, prefix, "Argument 1");
|
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.");
|
throw new TypeError("Header name is not valid.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -351,10 +380,10 @@ class Headers {
|
||||||
value = normalizeHeaderValue(value);
|
value = normalizeHeaderValue(value);
|
||||||
|
|
||||||
// 2.
|
// 2.
|
||||||
if (RegExpPrototypeExec(HTTP_TOKEN_CODE_POINT_RE, name) === null) {
|
if (!checkHeaderNameForHttpTokenCodePoint(name)) {
|
||||||
throw new TypeError("Header name is not valid.");
|
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.");
|
throw new TypeError("Header value is not valid.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue