diff options
author | Matt Mastracci <matthew@mastracci.com> | 2024-04-19 20:02:39 -0600 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-04-19 20:02:39 -0600 |
commit | 9425dce6dbc3a10bbde963d7a6192884c47185a5 (patch) | |
tree | 0dd4aeaf3dc3e5403095a5e37750a09adb3599a8 /ext/http/01_http.js | |
parent | 79e6751cf753612f99438ee2f158f54a1bf44815 (diff) |
refactor(ext/http): extract 02_websocket.ts from 01_http.js (#23460)
Landing part of https://github.com/denoland/deno/pull/21903
This will allow us to more easily refactor `serveHttp` to live on top of
`serve` by splitting the websocket code out. There's probably a lot more
we could do here but this helps.
Diffstat (limited to 'ext/http/01_http.js')
-rw-r--r-- | ext/http/01_http.js | 167 |
1 files changed, 3 insertions, 164 deletions
diff --git a/ext/http/01_http.js b/ext/http/01_http.js index b41c36446..580ba1166 100644 --- a/ext/http/01_http.js +++ b/ext/http/01_http.js @@ -12,45 +12,34 @@ import { op_http_shutdown, op_http_start, op_http_upgrade_websocket, - op_http_websocket_accept_header, op_http_write, op_http_write_headers, op_http_write_resource, } from "ext:core/ops"; const { - ArrayPrototypeIncludes, - ArrayPrototypeMap, - ArrayPrototypePush, ObjectPrototypeIsPrototypeOf, SafeSet, SafeSetIterator, SetPrototypeAdd, SetPrototypeDelete, - StringPrototypeCharCodeAt, StringPrototypeIncludes, - StringPrototypeSplit, - StringPrototypeToLowerCase, - StringPrototypeToUpperCase, Symbol, SymbolAsyncIterator, TypeError, TypedArrayPrototypeGetSymbolToStringTag, Uint8Array, } = primordials; - +import { _ws } from "ext:deno_http/02_websocket.ts"; import { InnerBody } from "ext:deno_fetch/22_body.js"; -import { Event, setEventTargetData } from "ext:deno_web/02_event.js"; +import { Event } from "ext:deno_web/02_event.js"; import { BlobPrototype } from "ext:deno_web/09_file.js"; import { - fromInnerResponse, - newInnerResponse, ResponsePrototype, toInnerResponse, } from "ext:deno_fetch/23_response.js"; import { fromInnerRequest, newInnerRequest, - toInnerRequest, } from "ext:deno_fetch/23_request.js"; import { AbortController } from "ext:deno_web/03_abort_signal.js"; import { @@ -63,7 +52,6 @@ import { _role, _server, _serverHandleIdleTimeout, - createWebSocketBranded, SERVER, WebSocket, } from "ext:deno_websocket/01_websocket.js"; @@ -409,155 +397,6 @@ function createRespondWith( }; } -const _ws = Symbol("[[associated_ws]]"); -const websocketCvf = buildCaseInsensitiveCommaValueFinder("websocket"); -const upgradeCvf = buildCaseInsensitiveCommaValueFinder("upgrade"); - -function upgradeWebSocket(request, options = {}) { - const inner = toInnerRequest(request); - const upgrade = request.headers.get("upgrade"); - const upgradeHasWebSocketOption = upgrade !== null && - websocketCvf(upgrade); - if (!upgradeHasWebSocketOption) { - throw new TypeError( - "Invalid Header: 'upgrade' header must contain 'websocket'", - ); - } - - const connection = request.headers.get("connection"); - const connectionHasUpgradeOption = connection !== null && - upgradeCvf(connection); - if (!connectionHasUpgradeOption) { - throw new TypeError( - "Invalid Header: 'connection' header must contain 'Upgrade'", - ); - } - - const websocketKey = request.headers.get("sec-websocket-key"); - if (websocketKey === null) { - throw new TypeError( - "Invalid Header: 'sec-websocket-key' header must be set", - ); - } - - const accept = op_http_websocket_accept_header(websocketKey); - - const r = newInnerResponse(101); - r.headerList = [ - ["upgrade", "websocket"], - ["connection", "Upgrade"], - ["sec-websocket-accept", accept], - ]; - - const protocolsStr = request.headers.get("sec-websocket-protocol") || ""; - const protocols = StringPrototypeSplit(protocolsStr, ", "); - if (protocols && options.protocol) { - if (ArrayPrototypeIncludes(protocols, options.protocol)) { - ArrayPrototypePush(r.headerList, [ - "sec-websocket-protocol", - options.protocol, - ]); - } else { - throw new TypeError( - `Protocol '${options.protocol}' not in the request's protocol list (non negotiable)`, - ); - } - } - - const socket = createWebSocketBranded(WebSocket); - setEventTargetData(socket); - socket[_server] = true; - socket[_idleTimeoutDuration] = options.idleTimeout ?? 120; - socket[_idleTimeoutTimeout] = null; - - if (inner._wantsUpgrade) { - return inner._wantsUpgrade("upgradeWebSocket", r, socket); - } - - const response = fromInnerResponse(r, "immutable"); - - response[_ws] = socket; - - return { response, socket }; -} - -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) => [ - StringPrototypeCharCodeAt(c, 0), - StringPrototypeCharCodeAt(StringPrototypeToUpperCase(c), 0), - ], - ); - /** @type {number} */ - let i; - /** @type {number} */ - let char; - - /** @param {string} value */ - return function (value) { - for (i = 0; i < value.length; i++) { - char = StringPrototypeCharCodeAt(value, 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 (let j = 0; j < charCodes.length; ++j) { - const { 0: cLower, 1: cUpper } = charCodes[j]; - 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; - function serveHttp(conn) { internals.warnOnDeprecatedApi( "Deno.serveHttp()", @@ -568,4 +407,4 @@ function serveHttp(conn) { return new HttpConn(rid, conn.remoteAddr, conn.localAddr); } -export { _ws, HttpConn, serveHttp, upgradeWebSocket }; +export { HttpConn, serveHttp }; |