summaryrefslogtreecommitdiff
path: root/ext/http/01_http.js
diff options
context:
space:
mode:
authorLeo Kettmeir <crowlkats@toaxl.com>2023-02-07 20:22:46 +0100
committerGitHub <noreply@github.com>2023-02-07 20:22:46 +0100
commitb4aa1530970f7b9cc4e6f2f27e077852c4e178d3 (patch)
tree3d008912affe8550692183bd2697a386db5e3c79 /ext/http/01_http.js
parent65500f36e870b4ada3996b06aa287e30177d21a3 (diff)
refactor: Use ES modules for internal runtime code (#17648)
This PR refactors all internal js files (except core) to be written as ES modules. `__bootstrap`has been mostly replaced with static imports in form in `internal:[path to file from repo root]`. To specify if files are ESM, an `esm` method has been added to `Extension`, similar to the `js` method. A new ModuleLoader called `InternalModuleLoader` has been added to enable the loading of internal specifiers, which is used in all situations except when a snapshot is only loaded, and not a new one is created from it. --------- Co-authored-by: Bartek IwaƄczuk <biwanczuk@gmail.com>
Diffstat (limited to 'ext/http/01_http.js')
-rw-r--r--ext/http/01_http.js815
1 files changed, 404 insertions, 411 deletions
diff --git a/ext/http/01_http.js b/ext/http/01_http.js
index cb98d246d..7ce5f9172 100644
--- a/ext/http/01_http.js
+++ b/ext/http/01_http.js
@@ -1,296 +1,318 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
-"use strict";
-
-((window) => {
- const webidl = window.__bootstrap.webidl;
- const { InnerBody } = window.__bootstrap.fetchBody;
- const { Event } = window.__bootstrap.event;
- const { setEventTargetData } = window.__bootstrap.eventTarget;
- const { BlobPrototype } = window.__bootstrap.file;
- const {
- ResponsePrototype,
- fromInnerRequest,
- toInnerResponse,
- newInnerRequest,
- newInnerResponse,
- fromInnerResponse,
- _flash,
- } = window.__bootstrap.fetch;
- const core = window.Deno.core;
- const { BadResourcePrototype, InterruptedPrototype, ops } = core;
- const { ReadableStreamPrototype } = window.__bootstrap.streams;
- const abortSignal = window.__bootstrap.abortSignal;
- const {
- WebSocket,
- _rid,
- _readyState,
- _eventLoop,
- _protocol,
- _server,
- _idleTimeoutDuration,
- _idleTimeoutTimeout,
- _serverHandleIdleTimeout,
- } = window.__bootstrap.webSocket;
- const { TcpConn, UnixConn } = window.__bootstrap.net;
- const { TlsConn } = window.__bootstrap.tls;
- const {
- Deferred,
- getReadableStreamResourceBacking,
- readableStreamForRid,
- readableStreamClose,
- } = window.__bootstrap.streams;
- const {
- ArrayPrototypeIncludes,
- ArrayPrototypePush,
- ArrayPrototypeSome,
- Error,
- ObjectPrototypeIsPrototypeOf,
- SafeSetIterator,
- Set,
- SetPrototypeAdd,
- SetPrototypeDelete,
- StringPrototypeIncludes,
- StringPrototypeToLowerCase,
- StringPrototypeSplit,
- Symbol,
- SymbolAsyncIterator,
- TypeError,
- Uint8Array,
- Uint8ArrayPrototype,
- } = window.__bootstrap.primordials;
-
- const connErrorSymbol = Symbol("connError");
- const _deferred = Symbol("upgradeHttpDeferred");
-
- class HttpConn {
- #rid = 0;
- #closed = false;
- #remoteAddr;
- #localAddr;
-
- // This set holds resource ids of resources
- // that were created during lifecycle of this request.
- // When the connection is closed these resources should be closed
- // as well.
- managedResources = new Set();
-
- constructor(rid, remoteAddr, localAddr) {
- this.#rid = rid;
- this.#remoteAddr = remoteAddr;
- this.#localAddr = localAddr;
- }
-
- /** @returns {number} */
- get rid() {
- return this.#rid;
- }
+const core = globalThis.Deno.core;
+const primordials = globalThis.__bootstrap.primordials;
+const { BadResourcePrototype, InterruptedPrototype, ops } = core;
+import * as webidl from "internal:ext/webidl/00_webidl.js";
+import { InnerBody } from "internal:ext/fetch/22_body.js";
+import { Event, setEventTargetData } from "internal:ext/web/02_event.js";
+import { BlobPrototype } from "internal:ext/web/09_file.js";
+import {
+ fromInnerResponse,
+ newInnerResponse,
+ ResponsePrototype,
+ toInnerResponse,
+} from "internal:ext/fetch/23_response.js";
+import {
+ _flash,
+ fromInnerRequest,
+ newInnerRequest,
+} from "internal:ext/fetch/23_request.js";
+import * as abortSignal from "internal:ext/web/03_abort_signal.js";
+import {
+ _eventLoop,
+ _idleTimeoutDuration,
+ _idleTimeoutTimeout,
+ _protocol,
+ _readyState,
+ _rid,
+ _server,
+ _serverHandleIdleTimeout,
+ WebSocket,
+} from "internal:ext/websocket/01_websocket.js";
+import { TcpConn, UnixConn } from "internal:ext/net/01_net.js";
+import { TlsConn } from "internal:ext/net/02_tls.js";
+import {
+ Deferred,
+ getReadableStreamResourceBacking,
+ readableStreamClose,
+ readableStreamForRid,
+ ReadableStreamPrototype,
+} from "internal:ext/web/06_streams.js";
+const {
+ ArrayPrototypeIncludes,
+ ArrayPrototypePush,
+ ArrayPrototypeSome,
+ Error,
+ ObjectPrototypeIsPrototypeOf,
+ SafeSetIterator,
+ Set,
+ SetPrototypeAdd,
+ SetPrototypeDelete,
+ StringPrototypeIncludes,
+ StringPrototypeToLowerCase,
+ StringPrototypeSplit,
+ Symbol,
+ SymbolAsyncIterator,
+ TypeError,
+ Uint8Array,
+ Uint8ArrayPrototype,
+} = primordials;
+
+const connErrorSymbol = Symbol("connError");
+const _deferred = Symbol("upgradeHttpDeferred");
+
+class HttpConn {
+ #rid = 0;
+ #closed = false;
+ #remoteAddr;
+ #localAddr;
+
+ // This set holds resource ids of resources
+ // that were created during lifecycle of this request.
+ // When the connection is closed these resources should be closed
+ // as well.
+ managedResources = new Set();
+
+ constructor(rid, remoteAddr, localAddr) {
+ this.#rid = rid;
+ this.#remoteAddr = remoteAddr;
+ this.#localAddr = localAddr;
+ }
- /** @returns {Promise<RequestEvent | null>} */
- async nextRequest() {
- let nextRequest;
- try {
- nextRequest = await core.opAsync("op_http_accept", this.#rid);
- } catch (error) {
- this.close();
- // A connection error seen here would cause disrupted responses to throw
- // a generic `BadResource` error. Instead store this error and replace
- // those with it.
- this[connErrorSymbol] = error;
- if (
- ObjectPrototypeIsPrototypeOf(BadResourcePrototype, error) ||
- ObjectPrototypeIsPrototypeOf(InterruptedPrototype, error) ||
- StringPrototypeIncludes(error.message, "connection closed")
- ) {
- return null;
- }
- throw error;
- }
- if (nextRequest == null) {
- // Work-around for servers (deno_std/http in particular) that call
- // `nextRequest()` before upgrading a previous request which has a
- // `connection: upgrade` header.
- await null;
+ /** @returns {number} */
+ get rid() {
+ return this.#rid;
+ }
- this.close();
+ /** @returns {Promise<RequestEvent | null>} */
+ async nextRequest() {
+ let nextRequest;
+ try {
+ nextRequest = await core.opAsync("op_http_accept", this.#rid);
+ } catch (error) {
+ this.close();
+ // A connection error seen here would cause disrupted responses to throw
+ // a generic `BadResource` error. Instead store this error and replace
+ // those with it.
+ this[connErrorSymbol] = error;
+ if (
+ ObjectPrototypeIsPrototypeOf(BadResourcePrototype, error) ||
+ ObjectPrototypeIsPrototypeOf(InterruptedPrototype, error) ||
+ StringPrototypeIncludes(error.message, "connection closed")
+ ) {
return null;
}
+ throw error;
+ }
+ if (nextRequest == null) {
+ // Work-around for servers (deno_std/http in particular) that call
+ // `nextRequest()` before upgrading a previous request which has a
+ // `connection: upgrade` header.
+ await null;
+
+ this.close();
+ return null;
+ }
- const { 0: streamRid, 1: method, 2: url } = nextRequest;
- SetPrototypeAdd(this.managedResources, streamRid);
-
- /** @type {ReadableStream<Uint8Array> | undefined} */
- let body = null;
- // There might be a body, but we don't expose it for GET/HEAD requests.
- // It will be closed automatically once the request has been handled and
- // the response has been sent.
- if (method !== "GET" && method !== "HEAD") {
- body = readableStreamForRid(streamRid, false);
- }
-
- const innerRequest = newInnerRequest(
- () => method,
- url,
- () => ops.op_http_headers(streamRid),
- body !== null ? new InnerBody(body) : null,
- false,
- );
- const signal = abortSignal.newSignal();
- const request = fromInnerRequest(
- innerRequest,
- signal,
- "immutable",
- false,
- );
-
- const respondWith = createRespondWith(
- this,
- streamRid,
- request,
- this.#remoteAddr,
- this.#localAddr,
- );
+ const { 0: streamRid, 1: method, 2: url } = nextRequest;
+ SetPrototypeAdd(this.managedResources, streamRid);
- return { request, respondWith };
+ /** @type {ReadableStream<Uint8Array> | undefined} */
+ let body = null;
+ // There might be a body, but we don't expose it for GET/HEAD requests.
+ // It will be closed automatically once the request has been handled and
+ // the response has been sent.
+ if (method !== "GET" && method !== "HEAD") {
+ body = readableStreamForRid(streamRid, false);
}
- /** @returns {void} */
- close() {
- if (!this.#closed) {
- this.#closed = true;
- core.close(this.#rid);
- for (const rid of new SafeSetIterator(this.managedResources)) {
- SetPrototypeDelete(this.managedResources, rid);
- core.close(rid);
- }
- }
- }
+ const innerRequest = newInnerRequest(
+ () => method,
+ url,
+ () => ops.op_http_headers(streamRid),
+ body !== null ? new InnerBody(body) : null,
+ false,
+ );
+ const signal = abortSignal.newSignal();
+ const request = fromInnerRequest(
+ innerRequest,
+ signal,
+ "immutable",
+ false,
+ );
+
+ const respondWith = createRespondWith(
+ this,
+ streamRid,
+ request,
+ this.#remoteAddr,
+ this.#localAddr,
+ );
+
+ return { request, respondWith };
+ }
- [SymbolAsyncIterator]() {
- // deno-lint-ignore no-this-alias
- const httpConn = this;
- return {
- async next() {
- const reqEvt = await httpConn.nextRequest();
- // Change with caution, current form avoids a v8 deopt
- return { value: reqEvt ?? undefined, done: reqEvt === null };
- },
- };
+ /** @returns {void} */
+ close() {
+ if (!this.#closed) {
+ this.#closed = true;
+ core.close(this.#rid);
+ for (const rid of new SafeSetIterator(this.managedResources)) {
+ SetPrototypeDelete(this.managedResources, rid);
+ core.close(rid);
+ }
}
}
- function createRespondWith(
- httpConn,
- streamRid,
- request,
- remoteAddr,
- localAddr,
- ) {
- return async function respondWith(resp) {
- try {
- resp = await resp;
- if (!(ObjectPrototypeIsPrototypeOf(ResponsePrototype, resp))) {
- throw new TypeError(
- "First argument to respondWith must be a Response or a promise resolving to a Response.",
- );
- }
+ [SymbolAsyncIterator]() {
+ // deno-lint-ignore no-this-alias
+ const httpConn = this;
+ return {
+ async next() {
+ const reqEvt = await httpConn.nextRequest();
+ // Change with caution, current form avoids a v8 deopt
+ return { value: reqEvt ?? undefined, done: reqEvt === null };
+ },
+ };
+ }
+}
+
+function createRespondWith(
+ httpConn,
+ streamRid,
+ request,
+ remoteAddr,
+ localAddr,
+) {
+ return async function respondWith(resp) {
+ try {
+ resp = await resp;
+ if (!(ObjectPrototypeIsPrototypeOf(ResponsePrototype, resp))) {
+ throw new TypeError(
+ "First argument to respondWith must be a Response or a promise resolving to a Response.",
+ );
+ }
- const innerResp = toInnerResponse(resp);
+ const innerResp = toInnerResponse(resp);
- // If response body length is known, it will be sent synchronously in a
- // single op, in other case a "response body" resource will be created and
- // we'll be streaming it.
- /** @type {ReadableStream<Uint8Array> | Uint8Array | null} */
- let respBody = null;
- if (innerResp.body !== null) {
- if (innerResp.body.unusable()) {
- throw new TypeError("Body is unusable.");
- }
+ // If response body length is known, it will be sent synchronously in a
+ // single op, in other case a "response body" resource will be created and
+ // we'll be streaming it.
+ /** @type {ReadableStream<Uint8Array> | Uint8Array | null} */
+ let respBody = null;
+ if (innerResp.body !== null) {
+ if (innerResp.body.unusable()) {
+ throw new TypeError("Body is unusable.");
+ }
+ if (
+ ObjectPrototypeIsPrototypeOf(
+ ReadableStreamPrototype,
+ innerResp.body.streamOrStatic,
+ )
+ ) {
if (
+ innerResp.body.length === null ||
ObjectPrototypeIsPrototypeOf(
- ReadableStreamPrototype,
- innerResp.body.streamOrStatic,
+ BlobPrototype,
+ innerResp.body.source,
)
) {
- if (
- innerResp.body.length === null ||
- ObjectPrototypeIsPrototypeOf(
- BlobPrototype,
- innerResp.body.source,
- )
- ) {
- respBody = innerResp.body.stream;
+ respBody = innerResp.body.stream;
+ } else {
+ const reader = innerResp.body.stream.getReader();
+ const r1 = await reader.read();
+ if (r1.done) {
+ respBody = new Uint8Array(0);
} else {
- const reader = innerResp.body.stream.getReader();
- const r1 = await reader.read();
- if (r1.done) {
- respBody = new Uint8Array(0);
- } else {
- respBody = r1.value;
- const r2 = await reader.read();
- if (!r2.done) throw new TypeError("Unreachable");
- }
+ respBody = r1.value;
+ const r2 = await reader.read();
+ if (!r2.done) throw new TypeError("Unreachable");
}
- } else {
- innerResp.body.streamOrStatic.consumed = true;
- respBody = innerResp.body.streamOrStatic.body;
}
} else {
- respBody = new Uint8Array(0);
+ innerResp.body.streamOrStatic.consumed = true;
+ respBody = innerResp.body.streamOrStatic.body;
}
- const isStreamingResponseBody = !(
- typeof respBody === "string" ||
- ObjectPrototypeIsPrototypeOf(Uint8ArrayPrototype, respBody)
+ } else {
+ respBody = new Uint8Array(0);
+ }
+ const isStreamingResponseBody = !(
+ typeof respBody === "string" ||
+ ObjectPrototypeIsPrototypeOf(Uint8ArrayPrototype, respBody)
+ );
+ try {
+ await core.opAsync(
+ "op_http_write_headers",
+ streamRid,
+ innerResp.status ?? 200,
+ innerResp.headerList,
+ isStreamingResponseBody ? null : respBody,
);
- try {
- await core.opAsync(
- "op_http_write_headers",
- streamRid,
- innerResp.status ?? 200,
- innerResp.headerList,
- isStreamingResponseBody ? null : respBody,
- );
- } catch (error) {
- const connError = httpConn[connErrorSymbol];
- if (
- ObjectPrototypeIsPrototypeOf(BadResourcePrototype, error) &&
- connError != null
- ) {
- // deno-lint-ignore no-ex-assign
- error = new connError.constructor(connError.message);
- }
- if (
- respBody !== null &&
- ObjectPrototypeIsPrototypeOf(ReadableStreamPrototype, respBody)
- ) {
- await respBody.cancel(error);
- }
- throw error;
+ } catch (error) {
+ const connError = httpConn[connErrorSymbol];
+ if (
+ ObjectPrototypeIsPrototypeOf(BadResourcePrototype, error) &&
+ connError != null
+ ) {
+ // deno-lint-ignore no-ex-assign
+ error = new connError.constructor(connError.message);
+ }
+ if (
+ respBody !== null &&
+ ObjectPrototypeIsPrototypeOf(ReadableStreamPrototype, respBody)
+ ) {
+ await respBody.cancel(error);
}
+ throw error;
+ }
- if (isStreamingResponseBody) {
- let success = false;
- if (
- respBody === null ||
- !ObjectPrototypeIsPrototypeOf(ReadableStreamPrototype, respBody)
- ) {
- throw new TypeError("Unreachable");
+ if (isStreamingResponseBody) {
+ let success = false;
+ if (
+ respBody === null ||
+ !ObjectPrototypeIsPrototypeOf(ReadableStreamPrototype, respBody)
+ ) {
+ throw new TypeError("Unreachable");
+ }
+ const resourceBacking = getReadableStreamResourceBacking(respBody);
+ let reader;
+ if (resourceBacking) {
+ if (respBody.locked) {
+ throw new TypeError("ReadableStream is locked.");
+ }
+ reader = respBody.getReader(); // Aquire JS lock.
+ try {
+ await core.opAsync(
+ "op_http_write_resource",
+ streamRid,
+ resourceBacking.rid,
+ );
+ if (resourceBacking.autoClose) core.tryClose(resourceBacking.rid);
+ readableStreamClose(respBody); // Release JS lock.
+ success = true;
+ } catch (error) {
+ const connError = httpConn[connErrorSymbol];
+ if (
+ ObjectPrototypeIsPrototypeOf(BadResourcePrototype, error) &&
+ connError != null
+ ) {
+ // deno-lint-ignore no-ex-assign
+ error = new connError.constructor(connError.message);
+ }
+ await reader.cancel(error);
+ throw error;
}
- const resourceBacking = getReadableStreamResourceBacking(respBody);
- let reader;
- if (resourceBacking) {
- if (respBody.locked) {
- throw new TypeError("ReadableStream is locked.");
+ } else {
+ reader = respBody.getReader();
+ while (true) {
+ const { value, done } = await reader.read();
+ if (done) break;
+ if (!ObjectPrototypeIsPrototypeOf(Uint8ArrayPrototype, value)) {
+ await reader.cancel(new TypeError("Value not a Uint8Array"));
+ break;
}
- reader = respBody.getReader(); // Aquire JS lock.
try {
- await core.opAsync(
- "op_http_write_resource",
- streamRid,
- resourceBacking.rid,
- );
- if (resourceBacking.autoClose) core.tryClose(resourceBacking.rid);
- readableStreamClose(respBody); // Release JS lock.
- success = true;
+ await core.opAsync("op_http_write", streamRid, value);
} catch (error) {
const connError = httpConn[connErrorSymbol];
if (
@@ -303,176 +325,147 @@
await reader.cancel(error);
throw error;
}
- } else {
- reader = respBody.getReader();
- while (true) {
- const { value, done } = await reader.read();
- if (done) break;
- if (!ObjectPrototypeIsPrototypeOf(Uint8ArrayPrototype, value)) {
- await reader.cancel(new TypeError("Value not a Uint8Array"));
- break;
- }
- try {
- await core.opAsync("op_http_write", streamRid, value);
- } catch (error) {
- const connError = httpConn[connErrorSymbol];
- if (
- ObjectPrototypeIsPrototypeOf(BadResourcePrototype, error) &&
- connError != null
- ) {
- // deno-lint-ignore no-ex-assign
- error = new connError.constructor(connError.message);
- }
- await reader.cancel(error);
- throw error;
- }
- }
- success = true;
- }
-
- if (success) {
- try {
- await core.opAsync("op_http_shutdown", streamRid);
- } catch (error) {
- await reader.cancel(error);
- throw error;
- }
}
+ success = true;
}
- const deferred = request[_deferred];
- if (deferred) {
- const res = await core.opAsync("op_http_upgrade", streamRid);
- let conn;
- if (res.connType === "tcp") {
- conn = new TcpConn(res.connRid, remoteAddr, localAddr);
- } else if (res.connType === "tls") {
- conn = new TlsConn(res.connRid, remoteAddr, localAddr);
- } else if (res.connType === "unix") {
- conn = new UnixConn(res.connRid, remoteAddr, localAddr);
- } else {
- throw new Error("unreachable");
+ if (success) {
+ try {
+ await core.opAsync("op_http_shutdown", streamRid);
+ } catch (error) {
+ await reader.cancel(error);
+ throw error;
}
+ }
+ }
- deferred.resolve([conn, res.readBuf]);
+ const deferred = request[_deferred];
+ if (deferred) {
+ const res = await core.opAsync("op_http_upgrade", streamRid);
+ let conn;
+ if (res.connType === "tcp") {
+ conn = new TcpConn(res.connRid, remoteAddr, localAddr);
+ } else if (res.connType === "tls") {
+ conn = new TlsConn(res.connRid, remoteAddr, localAddr);
+ } else if (res.connType === "unix") {
+ conn = new UnixConn(res.connRid, remoteAddr, localAddr);
+ } else {
+ throw new Error("unreachable");
}
- const ws = resp[_ws];
- if (ws) {
- const wsRid = await core.opAsync(
- "op_http_upgrade_websocket",
- streamRid,
- );
- ws[_rid] = wsRid;
- ws[_protocol] = resp.headers.get("sec-websocket-protocol");
- httpConn.close();
+ deferred.resolve([conn, res.readBuf]);
+ }
+ const ws = resp[_ws];
+ if (ws) {
+ const wsRid = await core.opAsync(
+ "op_http_upgrade_websocket",
+ streamRid,
+ );
+ ws[_rid] = wsRid;
+ ws[_protocol] = resp.headers.get("sec-websocket-protocol");
- ws[_readyState] = WebSocket.OPEN;
- const event = new Event("open");
- ws.dispatchEvent(event);
+ httpConn.close();
- ws[_eventLoop]();
- if (ws[_idleTimeoutDuration]) {
- ws.addEventListener(
- "close",
- () => clearTimeout(ws[_idleTimeoutTimeout]),
- );
- }
- ws[_serverHandleIdleTimeout]();
- }
- } finally {
- if (SetPrototypeDelete(httpConn.managedResources, streamRid)) {
- core.close(streamRid);
+ ws[_readyState] = WebSocket.OPEN;
+ const event = new Event("open");
+ ws.dispatchEvent(event);
+
+ ws[_eventLoop]();
+ if (ws[_idleTimeoutDuration]) {
+ ws.addEventListener(
+ "close",
+ () => clearTimeout(ws[_idleTimeoutTimeout]),
+ );
}
+ ws[_serverHandleIdleTimeout]();
}
- };
+ } finally {
+ if (SetPrototypeDelete(httpConn.managedResources, streamRid)) {
+ core.close(streamRid);
+ }
+ }
+ };
+}
+
+const _ws = Symbol("[[associated_ws]]");
+
+function upgradeWebSocket(request, options = {}) {
+ const upgrade = request.headers.get("upgrade");
+ const upgradeHasWebSocketOption = upgrade !== null &&
+ ArrayPrototypeSome(
+ StringPrototypeSplit(upgrade, /\s*,\s*/),
+ (option) => StringPrototypeToLowerCase(option) === "websocket",
+ );
+ if (!upgradeHasWebSocketOption) {
+ throw new TypeError(
+ "Invalid Header: 'upgrade' header must contain 'websocket'",
+ );
}
- const _ws = Symbol("[[associated_ws]]");
-
- function upgradeWebSocket(request, options = {}) {
- const upgrade = request.headers.get("upgrade");
- const upgradeHasWebSocketOption = upgrade !== null &&
- ArrayPrototypeSome(
- StringPrototypeSplit(upgrade, /\s*,\s*/),
- (option) => StringPrototypeToLowerCase(option) === "websocket",
- );
- if (!upgradeHasWebSocketOption) {
- throw new TypeError(
- "Invalid Header: 'upgrade' header must contain 'websocket'",
- );
- }
+ const connection = request.headers.get("connection");
+ const connectionHasUpgradeOption = connection !== null &&
+ ArrayPrototypeSome(
+ StringPrototypeSplit(connection, /\s*,\s*/),
+ (option) => StringPrototypeToLowerCase(option) === "upgrade",
+ );
+ if (!connectionHasUpgradeOption) {
+ throw new TypeError(
+ "Invalid Header: 'connection' header must contain 'Upgrade'",
+ );
+ }
- const connection = request.headers.get("connection");
- const connectionHasUpgradeOption = connection !== null &&
- ArrayPrototypeSome(
- StringPrototypeSplit(connection, /\s*,\s*/),
- (option) => StringPrototypeToLowerCase(option) === "upgrade",
- );
- 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 websocketKey = request.headers.get("sec-websocket-key");
- if (websocketKey === null) {
+ const accept = ops.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(
- "Invalid Header: 'sec-websocket-key' header must be set",
+ `Protocol '${options.protocol}' not in the request's protocol list (non negotiable)`,
);
}
+ }
- const accept = ops.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 response = fromInnerResponse(r, "immutable");
- const response = fromInnerResponse(r, "immutable");
+ const socket = webidl.createBranded(WebSocket);
+ setEventTargetData(socket);
+ socket[_server] = true;
+ response[_ws] = socket;
+ socket[_idleTimeoutDuration] = options.idleTimeout ?? 120;
+ socket[_idleTimeoutTimeout] = null;
- const socket = webidl.createBranded(WebSocket);
- setEventTargetData(socket);
- socket[_server] = true;
- response[_ws] = socket;
- socket[_idleTimeoutDuration] = options.idleTimeout ?? 120;
- socket[_idleTimeoutTimeout] = null;
+ return { response, socket };
+}
- return { response, socket };
+function upgradeHttp(req) {
+ if (req[_flash]) {
+ throw new TypeError(
+ "Flash requests can not be upgraded with `upgradeHttp`. Use `upgradeHttpRaw` instead.",
+ );
}
- function upgradeHttp(req) {
- if (req[_flash]) {
- throw new TypeError(
- "Flash requests can not be upgraded with `upgradeHttp`. Use `upgradeHttpRaw` instead.",
- );
- }
+ req[_deferred] = new Deferred();
+ return req[_deferred].promise;
+}
- req[_deferred] = new Deferred();
- return req[_deferred].promise;
- }
-
- window.__bootstrap.http = {
- HttpConn,
- upgradeWebSocket,
- upgradeHttp,
- _ws,
- };
-})(this);
+export { _ws, HttpConn, upgradeHttp, upgradeWebSocket };