1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-28 16:20:57 -05:00

perf(web): ~400x faster http header trimming (#12277)

Use a regex substring match with a first/last char fastpath instead of 2 regex replaces. Roughly ~400x faster (423ms vs 0.7ms in profiled runs)
This commit is contained in:
Aaron O'Mullan 2021-09-30 18:39:55 +02:00 committed by Ryan Dahl
parent df94139aa8
commit b1eeaf71ff
3 changed files with 36 additions and 14 deletions

View file

@ -15,12 +15,11 @@
const { const {
HTTP_TAB_OR_SPACE_PREFIX_RE, HTTP_TAB_OR_SPACE_PREFIX_RE,
HTTP_TAB_OR_SPACE_SUFFIX_RE, HTTP_TAB_OR_SPACE_SUFFIX_RE,
HTTP_WHITESPACE_PREFIX_RE,
HTTP_WHITESPACE_SUFFIX_RE,
HTTP_TOKEN_CODE_POINT_RE, HTTP_TOKEN_CODE_POINT_RE,
byteLowerCase, byteLowerCase,
collectSequenceOfCodepoints, collectSequenceOfCodepoints,
collectHttpQuotedString, collectHttpQuotedString,
httpTrim,
} = window.__bootstrap.infra; } = window.__bootstrap.infra;
const { const {
ArrayIsArray, ArrayIsArray,
@ -59,17 +58,7 @@
* @returns {string} * @returns {string}
*/ */
function normalizeHeaderValue(potentialValue) { function normalizeHeaderValue(potentialValue) {
potentialValue = StringPrototypeReplaceAll( return httpTrim(potentialValue);
potentialValue,
HTTP_WHITESPACE_PREFIX_RE,
"",
);
potentialValue = StringPrototypeReplaceAll(
potentialValue,
HTTP_WHITESPACE_SUFFIX_RE,
"",
);
return potentialValue;
} }
/** /**
@ -95,7 +84,7 @@
// Regex matching illegal chars in a header value // Regex matching illegal chars in a header value
// deno-lint-ignore no-control-regex // deno-lint-ignore no-control-regex
const ILLEGAL_VALUE_CHARS = /[\x00\x0A\x0D]/; const ILLEGAL_VALUE_CHARS = /[\x00\x0A\x0D]/g;
/** /**
* https://fetch.spec.whatwg.org/#concept-headers-append * https://fetch.spec.whatwg.org/#concept-headers-append

View file

@ -19,6 +19,7 @@
TypeError, TypeError,
ArrayPrototypeJoin, ArrayPrototypeJoin,
StringPrototypeCharAt, StringPrototypeCharAt,
StringPrototypeMatch,
StringPrototypeSlice, StringPrototypeSlice,
String, String,
StringPrototypeReplace, StringPrototypeReplace,
@ -75,6 +76,9 @@
"g", "g",
); );
const HTTP_WHITESPACE_MATCHER = regexMatcher(HTTP_WHITESPACE); const HTTP_WHITESPACE_MATCHER = regexMatcher(HTTP_WHITESPACE);
const HTTP_BETWEEN_WHITESPACE = new RegExp(
`^[${HTTP_WHITESPACE_MATCHER}]*(.*?)[${HTTP_WHITESPACE_MATCHER}]*$`,
);
const HTTP_WHITESPACE_PREFIX_RE = new RegExp( const HTTP_WHITESPACE_PREFIX_RE = new RegExp(
`^[${HTTP_WHITESPACE_MATCHER}]+`, `^[${HTTP_WHITESPACE_MATCHER}]+`,
"g", "g",
@ -237,6 +241,33 @@
return core.opSync("op_base64_decode", data); return core.opSync("op_base64_decode", data);
} }
/**
* @param {string} char
* @returns {boolean}
*/
function isHttpWhitespace(char) {
switch (char) {
case "\u0009":
case "\u000A":
case "\u000D":
case "\u0020":
return true;
default:
return false;
}
}
/**
* @param {string} s
* @returns {string}
*/
function httpTrim(s) {
if (!isHttpWhitespace(s[0]) && !isHttpWhitespace(s[s.length - 1])) {
return s;
}
return StringPrototypeMatch(s, HTTP_BETWEEN_WHITESPACE)?.[1] ?? "";
}
window.__bootstrap.infra = { window.__bootstrap.infra = {
collectSequenceOfCodepoints, collectSequenceOfCodepoints,
ASCII_DIGIT, ASCII_DIGIT,
@ -254,6 +285,7 @@
HTTP_TAB_OR_SPACE_SUFFIX_RE, HTTP_TAB_OR_SPACE_SUFFIX_RE,
HTTP_WHITESPACE_PREFIX_RE, HTTP_WHITESPACE_PREFIX_RE,
HTTP_WHITESPACE_SUFFIX_RE, HTTP_WHITESPACE_SUFFIX_RE,
httpTrim,
regexMatcher, regexMatcher,
byteUpperCase, byteUpperCase,
byteLowerCase, byteLowerCase,

View file

@ -29,6 +29,7 @@ declare namespace globalThis {
HTTP_TAB_OR_SPACE_SUFFIX_RE: RegExp; HTTP_TAB_OR_SPACE_SUFFIX_RE: RegExp;
HTTP_WHITESPACE_PREFIX_RE: RegExp; HTTP_WHITESPACE_PREFIX_RE: RegExp;
HTTP_WHITESPACE_SUFFIX_RE: RegExp; HTTP_WHITESPACE_SUFFIX_RE: RegExp;
httpTrim(s: string): string;
regexMatcher(chars: string[]): string; regexMatcher(chars: string[]): string;
byteUpperCase(s: string): string; byteUpperCase(s: string): string;
byteLowerCase(s: string): string; byteLowerCase(s: string): string;