summaryrefslogtreecommitdiff
path: root/ext/http/01_http.js
diff options
context:
space:
mode:
authorDavid Sherret <dsherret@users.noreply.github.com>2023-02-12 15:51:07 -0500
committerGitHub <noreply@github.com>2023-02-12 20:51:07 +0000
commitdc66fdc11e86ae060e43dbeb417738c1b1995fe6 (patch)
tree4c66594ef13faaa28a793467e57782d7939cfbf2 /ext/http/01_http.js
parentfc843d035c0cb4936d9b87670e282667a9a8b265 (diff)
perf(http): remove allocations checking upgrade and connection header values (#17727)
Diffstat (limited to 'ext/http/01_http.js')
-rw-r--r--ext/http/01_http.js92
1 files changed, 80 insertions, 12 deletions
diff --git a/ext/http/01_http.js b/ext/http/01_http.js
index 1da371e8d..f9e15e7d5 100644
--- a/ext/http/01_http.js
+++ b/ext/http/01_http.js
@@ -1,5 +1,6 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
const core = globalThis.Deno.core;
+const internals = globalThis.__bootstrap.internals;
const primordials = globalThis.__bootstrap.primordials;
const { BadResourcePrototype, InterruptedPrototype, ops } = core;
import * as webidl from "internal:deno_webidl/00_webidl.js";
@@ -40,18 +41,18 @@ import {
} from "internal:deno_web/06_streams.js";
const {
ArrayPrototypeIncludes,
+ ArrayPrototypeMap,
ArrayPrototypePush,
- ArrayPrototypeSome,
Error,
ObjectPrototypeIsPrototypeOf,
SafeSetIterator,
Set,
SetPrototypeAdd,
SetPrototypeDelete,
+ StringPrototypeCharCodeAt,
StringPrototypeIncludes,
StringPrototypeToLowerCase,
StringPrototypeSplit,
- StringPrototypeTrim,
Symbol,
SymbolAsyncIterator,
TypeError,
@@ -389,15 +390,13 @@ function createRespondWith(
}
const _ws = Symbol("[[associated_ws]]");
+const websocketCvf = buildCaseInsensitiveCommaValueFinder("websocket");
+const upgradeCvf = buildCaseInsensitiveCommaValueFinder("upgrade");
function upgradeWebSocket(request, options = {}) {
const upgrade = request.headers.get("upgrade");
const upgradeHasWebSocketOption = upgrade !== null &&
- ArrayPrototypeSome(
- StringPrototypeSplit(upgrade, ","),
- (option) =>
- StringPrototypeToLowerCase(StringPrototypeTrim(option)) === "websocket",
- );
+ websocketCvf(upgrade);
if (!upgradeHasWebSocketOption) {
throw new TypeError(
"Invalid Header: 'upgrade' header must contain 'websocket'",
@@ -406,11 +405,7 @@ function upgradeWebSocket(request, options = {}) {
const connection = request.headers.get("connection");
const connectionHasUpgradeOption = connection !== null &&
- ArrayPrototypeSome(
- StringPrototypeSplit(connection, ","),
- (option) =>
- StringPrototypeToLowerCase(StringPrototypeTrim(option)) === "upgrade",
- );
+ upgradeCvf(connection);
if (!connectionHasUpgradeOption) {
throw new TypeError(
"Invalid Header: 'connection' header must contain 'Upgrade'",
@@ -471,4 +466,77 @@ function upgradeHttp(req) {
return req[_deferred].promise;
}
+const spaceCharCode = StringPrototypeCharCodeAt(" ", 0);
+const tabCharCode = StringPrototypeCharCodeAt("\t", 0);
+const commaCharCode = StringPrototypeCharCodeAt(",", 0);
+
+/** Builds a case function that can be used to find a case insensitive
+ * value in some text that's separated by commas.
+ *
+ * This is done because it doesn't require any allocations.
+ * @param checkText {string} - The text to find. (ex. "websocket")
+ */
+function buildCaseInsensitiveCommaValueFinder(checkText) {
+ const charCodes = ArrayPrototypeMap(
+ StringPrototypeSplit(
+ StringPrototypeToLowerCase(checkText),
+ "",
+ ),
+ (c) => [c.charCodeAt(0), c.toUpperCase().charCodeAt(0)],
+ );
+ /** @type {number} */
+ let i;
+ /** @type {number} */
+ let char;
+
+ /** @param value {string} */
+ return function (value) {
+ for (i = 0; i < value.length; i++) {
+ char = value.charCodeAt(i);
+ skipWhitespace(value);
+
+ if (hasWord(value)) {
+ skipWhitespace(value);
+ if (i === value.length || char === commaCharCode) {
+ return true;
+ }
+ } else {
+ skipUntilComma(value);
+ }
+ }
+
+ return false;
+ };
+
+ /** @param value {string} */
+ function hasWord(value) {
+ for (const [cLower, cUpper] of charCodes) {
+ if (cLower === char || cUpper === char) {
+ char = StringPrototypeCharCodeAt(value, ++i);
+ } else {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /** @param value {string} */
+ function skipWhitespace(value) {
+ while (char === spaceCharCode || char === tabCharCode) {
+ char = StringPrototypeCharCodeAt(value, ++i);
+ }
+ }
+
+ /** @param value {string} */
+ function skipUntilComma(value) {
+ while (char !== commaCharCode && i < value.length) {
+ char = StringPrototypeCharCodeAt(value, ++i);
+ }
+ }
+}
+
+// Expose this function for unit tests
+internals.buildCaseInsensitiveCommaValueFinder =
+ buildCaseInsensitiveCommaValueFinder;
+
export { _ws, HttpConn, upgradeHttp, upgradeWebSocket };