summaryrefslogtreecommitdiff
path: root/ext/websocket
diff options
context:
space:
mode:
Diffstat (limited to 'ext/websocket')
-rw-r--r--ext/websocket/01_websocket.js1017
-rw-r--r--ext/websocket/02_websocketstream.js778
-rw-r--r--ext/websocket/lib.rs2
3 files changed, 896 insertions, 901 deletions
diff --git a/ext/websocket/01_websocket.js b/ext/websocket/01_websocket.js
index 9b7c45e70..305cf75a7 100644
--- a/ext/websocket/01_websocket.js
+++ b/ext/websocket/01_websocket.js
@@ -1,579 +1,576 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
-"use strict";
/// <reference path="../../core/internal.d.ts" />
-((window) => {
- const core = window.Deno.core;
- const ops = core.ops;
- const { URL } = window.__bootstrap.url;
- const webidl = window.__bootstrap.webidl;
- const { HTTP_TOKEN_CODE_POINT_RE } = window.__bootstrap.infra;
- const { DOMException } = window.__bootstrap.domException;
- const {
- Event,
- ErrorEvent,
- CloseEvent,
- MessageEvent,
- defineEventHandler,
- _skipInternalInit,
- } = window.__bootstrap.event;
- const { EventTarget } = window.__bootstrap.eventTarget;
- const { Blob, BlobPrototype } = globalThis.__bootstrap.file;
- const {
- ArrayBufferPrototype,
- ArrayBufferIsView,
- ArrayPrototypeJoin,
- ArrayPrototypeMap,
- ArrayPrototypeSome,
- DataView,
- ErrorPrototypeToString,
- ObjectDefineProperties,
- ObjectPrototypeIsPrototypeOf,
- PromisePrototypeThen,
- RegExpPrototypeTest,
- Set,
- // TODO(lucacasonato): add SharedArrayBuffer to primordials
- // SharedArrayBufferPrototype
- String,
- StringPrototypeEndsWith,
- StringPrototypeToLowerCase,
- Symbol,
- SymbolIterator,
- PromisePrototypeCatch,
- SymbolFor,
- } = window.__bootstrap.primordials;
-
- webidl.converters["sequence<DOMString> or DOMString"] = (V, opts) => {
- // Union for (sequence<DOMString> or DOMString)
- if (webidl.type(V) === "Object" && V !== null) {
- if (V[SymbolIterator] !== undefined) {
- return webidl.converters["sequence<DOMString>"](V, opts);
- }
+const core = globalThis.Deno.core;
+const ops = core.ops;
+import { URL } from "internal:ext/url/00_url.js";
+import * as webidl from "internal:ext/webidl/00_webidl.js";
+import { HTTP_TOKEN_CODE_POINT_RE } from "internal:ext/web/00_infra.js";
+import DOMException from "internal:ext/web/01_dom_exception.js";
+import {
+ _skipInternalInit,
+ CloseEvent,
+ defineEventHandler,
+ ErrorEvent,
+ Event,
+ EventTarget,
+ MessageEvent,
+} from "internal:ext/web/02_event.js";
+import { Blob, BlobPrototype } from "internal:ext/web/09_file.js";
+const primordials = globalThis.__bootstrap.primordials;
+const {
+ ArrayBufferPrototype,
+ ArrayBufferIsView,
+ ArrayPrototypeJoin,
+ ArrayPrototypeMap,
+ ArrayPrototypeSome,
+ DataView,
+ ErrorPrototypeToString,
+ ObjectDefineProperties,
+ ObjectPrototypeIsPrototypeOf,
+ PromisePrototypeThen,
+ RegExpPrototypeTest,
+ Set,
+ // TODO(lucacasonato): add SharedArrayBuffer to primordials
+ // SharedArrayBufferPrototype
+ String,
+ StringPrototypeEndsWith,
+ StringPrototypeToLowerCase,
+ Symbol,
+ SymbolIterator,
+ PromisePrototypeCatch,
+ SymbolFor,
+} = primordials;
+
+webidl.converters["sequence<DOMString> or DOMString"] = (V, opts) => {
+ // Union for (sequence<DOMString> or DOMString)
+ if (webidl.type(V) === "Object" && V !== null) {
+ if (V[SymbolIterator] !== undefined) {
+ return webidl.converters["sequence<DOMString>"](V, opts);
}
- return webidl.converters.DOMString(V, opts);
- };
+ }
+ return webidl.converters.DOMString(V, opts);
+};
- webidl.converters["WebSocketSend"] = (V, opts) => {
- // Union for (Blob or ArrayBufferView or ArrayBuffer or USVString)
- if (ObjectPrototypeIsPrototypeOf(BlobPrototype, V)) {
- return webidl.converters["Blob"](V, opts);
- }
- if (typeof V === "object") {
- if (
- ObjectPrototypeIsPrototypeOf(ArrayBufferPrototype, V) ||
- // deno-lint-ignore prefer-primordials
- ObjectPrototypeIsPrototypeOf(SharedArrayBuffer.prototype, V)
- ) {
- return webidl.converters["ArrayBuffer"](V, opts);
- }
- if (ArrayBufferIsView(V)) {
- return webidl.converters["ArrayBufferView"](V, opts);
- }
+webidl.converters["WebSocketSend"] = (V, opts) => {
+ // Union for (Blob or ArrayBufferView or ArrayBuffer or USVString)
+ if (ObjectPrototypeIsPrototypeOf(BlobPrototype, V)) {
+ return webidl.converters["Blob"](V, opts);
+ }
+ if (typeof V === "object") {
+ if (
+ ObjectPrototypeIsPrototypeOf(ArrayBufferPrototype, V) ||
+ // deno-lint-ignore prefer-primordials
+ ObjectPrototypeIsPrototypeOf(SharedArrayBuffer.prototype, V)
+ ) {
+ return webidl.converters["ArrayBuffer"](V, opts);
}
- return webidl.converters["USVString"](V, opts);
- };
-
- const CONNECTING = 0;
- const OPEN = 1;
- const CLOSING = 2;
- const CLOSED = 3;
-
- const _readyState = Symbol("[[readyState]]");
- const _url = Symbol("[[url]]");
- const _rid = Symbol("[[rid]]");
- const _extensions = Symbol("[[extensions]]");
- const _protocol = Symbol("[[protocol]]");
- const _binaryType = Symbol("[[binaryType]]");
- const _bufferedAmount = Symbol("[[bufferedAmount]]");
- const _eventLoop = Symbol("[[eventLoop]]");
-
- const _server = Symbol("[[server]]");
- const _idleTimeoutDuration = Symbol("[[idleTimeout]]");
- const _idleTimeoutTimeout = Symbol("[[idleTimeoutTimeout]]");
- const _serverHandleIdleTimeout = Symbol("[[serverHandleIdleTimeout]]");
- class WebSocket extends EventTarget {
- [_rid];
-
- [_readyState] = CONNECTING;
- get readyState() {
- webidl.assertBranded(this, WebSocketPrototype);
- return this[_readyState];
+ if (ArrayBufferIsView(V)) {
+ return webidl.converters["ArrayBufferView"](V, opts);
}
+ }
+ return webidl.converters["USVString"](V, opts);
+};
+
+const CONNECTING = 0;
+const OPEN = 1;
+const CLOSING = 2;
+const CLOSED = 3;
+
+const _readyState = Symbol("[[readyState]]");
+const _url = Symbol("[[url]]");
+const _rid = Symbol("[[rid]]");
+const _extensions = Symbol("[[extensions]]");
+const _protocol = Symbol("[[protocol]]");
+const _binaryType = Symbol("[[binaryType]]");
+const _bufferedAmount = Symbol("[[bufferedAmount]]");
+const _eventLoop = Symbol("[[eventLoop]]");
+
+const _server = Symbol("[[server]]");
+const _idleTimeoutDuration = Symbol("[[idleTimeout]]");
+const _idleTimeoutTimeout = Symbol("[[idleTimeoutTimeout]]");
+const _serverHandleIdleTimeout = Symbol("[[serverHandleIdleTimeout]]");
+class WebSocket extends EventTarget {
+ [_rid];
+
+ [_readyState] = CONNECTING;
+ get readyState() {
+ webidl.assertBranded(this, WebSocketPrototype);
+ return this[_readyState];
+ }
- get CONNECTING() {
- webidl.assertBranded(this, WebSocketPrototype);
- return CONNECTING;
- }
- get OPEN() {
- webidl.assertBranded(this, WebSocketPrototype);
- return OPEN;
- }
- get CLOSING() {
- webidl.assertBranded(this, WebSocketPrototype);
- return CLOSING;
- }
- get CLOSED() {
- webidl.assertBranded(this, WebSocketPrototype);
- return CLOSED;
- }
+ get CONNECTING() {
+ webidl.assertBranded(this, WebSocketPrototype);
+ return CONNECTING;
+ }
+ get OPEN() {
+ webidl.assertBranded(this, WebSocketPrototype);
+ return OPEN;
+ }
+ get CLOSING() {
+ webidl.assertBranded(this, WebSocketPrototype);
+ return CLOSING;
+ }
+ get CLOSED() {
+ webidl.assertBranded(this, WebSocketPrototype);
+ return CLOSED;
+ }
+
+ [_extensions] = "";
+ get extensions() {
+ webidl.assertBranded(this, WebSocketPrototype);
+ return this[_extensions];
+ }
- [_extensions] = "";
- get extensions() {
- webidl.assertBranded(this, WebSocketPrototype);
- return this[_extensions];
+ [_protocol] = "";
+ get protocol() {
+ webidl.assertBranded(this, WebSocketPrototype);
+ return this[_protocol];
+ }
+
+ [_url] = "";
+ get url() {
+ webidl.assertBranded(this, WebSocketPrototype);
+ return this[_url];
+ }
+
+ [_binaryType] = "blob";
+ get binaryType() {
+ webidl.assertBranded(this, WebSocketPrototype);
+ return this[_binaryType];
+ }
+ set binaryType(value) {
+ webidl.assertBranded(this, WebSocketPrototype);
+ value = webidl.converters.DOMString(value, {
+ prefix: "Failed to set 'binaryType' on 'WebSocket'",
+ });
+ if (value === "blob" || value === "arraybuffer") {
+ this[_binaryType] = value;
}
+ }
+
+ [_bufferedAmount] = 0;
+ get bufferedAmount() {
+ webidl.assertBranded(this, WebSocketPrototype);
+ return this[_bufferedAmount];
+ }
+
+ constructor(url, protocols = []) {
+ super();
+ this[webidl.brand] = webidl.brand;
+ const prefix = "Failed to construct 'WebSocket'";
+ webidl.requiredArguments(arguments.length, 1, {
+ prefix,
+ });
+ url = webidl.converters.USVString(url, {
+ prefix,
+ context: "Argument 1",
+ });
+ protocols = webidl.converters["sequence<DOMString> or DOMString"](
+ protocols,
+ {
+ prefix,
+ context: "Argument 2",
+ },
+ );
- [_protocol] = "";
- get protocol() {
- webidl.assertBranded(this, WebSocketPrototype);
- return this[_protocol];
+ let wsURL;
+
+ try {
+ wsURL = new URL(url);
+ } catch (e) {
+ throw new DOMException(e.message, "SyntaxError");
}
- [_url] = "";
- get url() {
- webidl.assertBranded(this, WebSocketPrototype);
- return this[_url];
+ if (wsURL.protocol !== "ws:" && wsURL.protocol !== "wss:") {
+ throw new DOMException(
+ "Only ws & wss schemes are allowed in a WebSocket URL.",
+ "SyntaxError",
+ );
}
- [_binaryType] = "blob";
- get binaryType() {
- webidl.assertBranded(this, WebSocketPrototype);
- return this[_binaryType];
+ if (wsURL.hash !== "" || StringPrototypeEndsWith(wsURL.href, "#")) {
+ throw new DOMException(
+ "Fragments are not allowed in a WebSocket URL.",
+ "SyntaxError",
+ );
}
- set binaryType(value) {
- webidl.assertBranded(this, WebSocketPrototype);
- value = webidl.converters.DOMString(value, {
- prefix: "Failed to set 'binaryType' on 'WebSocket'",
- });
- if (value === "blob" || value === "arraybuffer") {
- this[_binaryType] = value;
- }
+
+ this[_url] = wsURL.href;
+
+ ops.op_ws_check_permission_and_cancel_handle(
+ "WebSocket.abort()",
+ this[_url],
+ false,
+ );
+
+ if (typeof protocols === "string") {
+ protocols = [protocols];
}
- [_bufferedAmount] = 0;
- get bufferedAmount() {
- webidl.assertBranded(this, WebSocketPrototype);
- return this[_bufferedAmount];
+ if (
+ protocols.length !==
+ new Set(
+ ArrayPrototypeMap(protocols, (p) => StringPrototypeToLowerCase(p)),
+ ).size
+ ) {
+ throw new DOMException(
+ "Can't supply multiple times the same protocol.",
+ "SyntaxError",
+ );
}
- constructor(url, protocols = []) {
- super();
- this[webidl.brand] = webidl.brand;
- const prefix = "Failed to construct 'WebSocket'";
- webidl.requiredArguments(arguments.length, 1, {
- prefix,
- });
- url = webidl.converters.USVString(url, {
- prefix,
- context: "Argument 1",
- });
- protocols = webidl.converters["sequence<DOMString> or DOMString"](
+ if (
+ ArrayPrototypeSome(
protocols,
- {
- prefix,
- context: "Argument 2",
- },
+ (protocol) => !RegExpPrototypeTest(HTTP_TOKEN_CODE_POINT_RE, protocol),
+ )
+ ) {
+ throw new DOMException(
+ "Invalid protocol value.",
+ "SyntaxError",
);
+ }
- let wsURL;
-
- try {
- wsURL = new URL(url);
- } catch (e) {
- throw new DOMException(e.message, "SyntaxError");
- }
+ PromisePrototypeThen(
+ core.opAsync(
+ "op_ws_create",
+ "new WebSocket()",
+ wsURL.href,
+ ArrayPrototypeJoin(protocols, ", "),
+ ),
+ (create) => {
+ this[_rid] = create.rid;
+ this[_extensions] = create.extensions;
+ this[_protocol] = create.protocol;
+
+ if (this[_readyState] === CLOSING) {
+ PromisePrototypeThen(
+ core.opAsync("op_ws_close", this[_rid]),
+ () => {
+ this[_readyState] = CLOSED;
+
+ const errEvent = new ErrorEvent("error");
+ this.dispatchEvent(errEvent);
+
+ const event = new CloseEvent("close");
+ this.dispatchEvent(event);
+ core.tryClose(this[_rid]);
+ },
+ );
+ } else {
+ this[_readyState] = OPEN;
+ const event = new Event("open");
+ this.dispatchEvent(event);
- if (wsURL.protocol !== "ws:" && wsURL.protocol !== "wss:") {
- throw new DOMException(
- "Only ws & wss schemes are allowed in a WebSocket URL.",
- "SyntaxError",
- );
- }
+ this[_eventLoop]();
+ }
+ },
+ (err) => {
+ this[_readyState] = CLOSED;
- if (wsURL.hash !== "" || StringPrototypeEndsWith(wsURL.href, "#")) {
- throw new DOMException(
- "Fragments are not allowed in a WebSocket URL.",
- "SyntaxError",
+ const errorEv = new ErrorEvent(
+ "error",
+ { error: err, message: ErrorPrototypeToString(err) },
);
- }
-
- this[_url] = wsURL.href;
+ this.dispatchEvent(errorEv);
- ops.op_ws_check_permission_and_cancel_handle(
- "WebSocket.abort()",
- this[_url],
- false,
- );
+ const closeEv = new CloseEvent("close");
+ this.dispatchEvent(closeEv);
+ },
+ );
+ }
- if (typeof protocols === "string") {
- protocols = [protocols];
- }
+ send(data) {
+ webidl.assertBranded(this, WebSocketPrototype);
+ const prefix = "Failed to execute 'send' on 'WebSocket'";
- if (
- protocols.length !==
- new Set(
- ArrayPrototypeMap(protocols, (p) => StringPrototypeToLowerCase(p)),
- ).size
- ) {
- throw new DOMException(
- "Can't supply multiple times the same protocol.",
- "SyntaxError",
- );
- }
+ webidl.requiredArguments(arguments.length, 1, {
+ prefix,
+ });
+ data = webidl.converters.WebSocketSend(data, {
+ prefix,
+ context: "Argument 1",
+ });
- if (
- ArrayPrototypeSome(
- protocols,
- (protocol) =>
- !RegExpPrototypeTest(HTTP_TOKEN_CODE_POINT_RE, protocol),
- )
- ) {
- throw new DOMException(
- "Invalid protocol value.",
- "SyntaxError",
- );
- }
+ if (this[_readyState] !== OPEN) {
+ throw new DOMException("readyState not OPEN", "InvalidStateError");
+ }
+ const sendTypedArray = (ta) => {
+ this[_bufferedAmount] += ta.byteLength;
PromisePrototypeThen(
- core.opAsync(
- "op_ws_create",
- "new WebSocket()",
- wsURL.href,
- ArrayPrototypeJoin(protocols, ", "),
- ),
- (create) => {
- this[_rid] = create.rid;
- this[_extensions] = create.extensions;
- this[_protocol] = create.protocol;
-
- if (this[_readyState] === CLOSING) {
- PromisePrototypeThen(
- core.opAsync("op_ws_close", this[_rid]),
- () => {
- this[_readyState] = CLOSED;
-
- const errEvent = new ErrorEvent("error");
- this.dispatchEvent(errEvent);
-
- const event = new CloseEvent("close");
- this.dispatchEvent(event);
- core.tryClose(this[_rid]);
- },
- );
- } else {
- this[_readyState] = OPEN;
- const event = new Event("open");
- this.dispatchEvent(event);
-
- this[_eventLoop]();
- }
+ core.opAsync("op_ws_send", this[_rid], {
+ kind: "binary",
+ value: ta,
+ }),
+ () => {
+ this[_bufferedAmount] -= ta.byteLength;
},
- (err) => {
- this[_readyState] = CLOSED;
-
- const errorEv = new ErrorEvent(
- "error",
- { error: err, message: ErrorPrototypeToString(err) },
- );
- this.dispatchEvent(errorEv);
+ );
+ };
- const closeEv = new CloseEvent("close");
- this.dispatchEvent(closeEv);
+ if (ObjectPrototypeIsPrototypeOf(BlobPrototype, data)) {
+ PromisePrototypeThen(
+ data.slice().arrayBuffer(),
+ (ab) => sendTypedArray(new DataView(ab)),
+ );
+ } else if (ArrayBufferIsView(data)) {
+ sendTypedArray(data);
+ } else if (ObjectPrototypeIsPrototypeOf(ArrayBufferPrototype, data)) {
+ sendTypedArray(new DataView(data));
+ } else {
+ const string = String(data);
+ const d = core.encode(string);
+ this[_bufferedAmount] += d.byteLength;
+ PromisePrototypeThen(
+ core.opAsync("op_ws_send", this[_rid], {
+ kind: "text",
+ value: string,
+ }),
+ () => {
+ this[_bufferedAmount] -= d.byteLength;
},
);
}
+ }
- send(data) {
- webidl.assertBranded(this, WebSocketPrototype);
- const prefix = "Failed to execute 'send' on 'WebSocket'";
+ close(code = undefined, reason = undefined) {
+ webidl.assertBranded(this, WebSocketPrototype);
+ const prefix = "Failed to execute 'close' on 'WebSocket'";
- webidl.requiredArguments(arguments.length, 1, {
- prefix,
- });
- data = webidl.converters.WebSocketSend(data, {
+ if (code !== undefined) {
+ code = webidl.converters["unsigned short"](code, {
prefix,
+ clamp: true,
context: "Argument 1",
});
+ }
- if (this[_readyState] !== OPEN) {
- throw new DOMException("readyState not OPEN", "InvalidStateError");
- }
-
- const sendTypedArray = (ta) => {
- this[_bufferedAmount] += ta.byteLength;
- PromisePrototypeThen(
- core.opAsync("op_ws_send", this[_rid], {
- kind: "binary",
- value: ta,
- }),
- () => {
- this[_bufferedAmount] -= ta.byteLength;
- },
- );
- };
+ if (reason !== undefined) {
+ reason = webidl.converters.USVString(reason, {
+ prefix,
+ context: "Argument 2",
+ });
+ }
- if (ObjectPrototypeIsPrototypeOf(BlobPrototype, data)) {
- PromisePrototypeThen(
- data.slice().arrayBuffer(),
- (ab) => sendTypedArray(new DataView(ab)),
- );
- } else if (ArrayBufferIsView(data)) {
- sendTypedArray(data);
- } else if (ObjectPrototypeIsPrototypeOf(ArrayBufferPrototype, data)) {
- sendTypedArray(new DataView(data));
- } else {
- const string = String(data);
- const d = core.encode(string);
- this[_bufferedAmount] += d.byteLength;
- PromisePrototypeThen(
- core.opAsync("op_ws_send", this[_rid], {
- kind: "text",
- value: string,
- }),
- () => {
- this[_bufferedAmount] -= d.byteLength;
- },
+ if (!this[_server]) {
+ if (
+ code !== undefined &&
+ !(code === 1000 || (3000 <= code && code < 5000))
+ ) {
+ throw new DOMException(
+ "The close code must be either 1000 or in the range of 3000 to 4999.",
+ "InvalidAccessError",
);
}
}
- close(code = undefined, reason = undefined) {
- webidl.assertBranded(this, WebSocketPrototype);
- const prefix = "Failed to execute 'close' on 'WebSocket'";
-
- if (code !== undefined) {
- code = webidl.converters["unsigned short"](code, {
- prefix,
- clamp: true,
- context: "Argument 1",
- });
- }
+ if (reason !== undefined && core.encode(reason).byteLength > 123) {
+ throw new DOMException(
+ "The close reason may not be longer than 123 bytes.",
+ "SyntaxError",
+ );
+ }
- if (reason !== undefined) {
- reason = webidl.converters.USVString(reason, {
- prefix,
- context: "Argument 2",
- });
- }
+ if (this[_readyState] === CONNECTING) {
+ this[_readyState] = CLOSING;
+ } else if (this[_readyState] === OPEN) {
+ this[_readyState] = CLOSING;
- if (!this[_server]) {
- if (
- code !== undefined &&
- !(code === 1000 || (3000 <= code && code < 5000))
- ) {
- throw new DOMException(
- "The close code must be either 1000 or in the range of 3000 to 4999.",
- "InvalidAccessError",
- );
- }
- }
+ PromisePrototypeCatch(
+ core.opAsync("op_ws_close", this[_rid], code, reason),
+ (err) => {
+ this[_readyState] = CLOSED;
- if (reason !== undefined && core.encode(reason).byteLength > 123) {
- throw new DOMException(
- "The close reason may not be longer than 123 bytes.",
- "SyntaxError",
- );
- }
+ const errorEv = new ErrorEvent("error", {
+ error: err,
+ message: ErrorPrototypeToString(err),
+ });
+ this.dispatchEvent(errorEv);
- if (this[_readyState] === CONNECTING) {
- this[_readyState] = CLOSING;
- } else if (this[_readyState] === OPEN) {
- this[_readyState] = CLOSING;
-
- PromisePrototypeCatch(
- core.opAsync("op_ws_close", this[_rid], code, reason),
- (err) => {
- this[_readyState] = CLOSED;
-
- const errorEv = new ErrorEvent("error", {
- error: err,
- message: ErrorPrototypeToString(err),
- });
- this.dispatchEvent(errorEv);
-
- const closeEv = new CloseEvent("close");
- this.dispatchEvent(closeEv);
- core.tryClose(this[_rid]);
- },
- );
- }
+ const closeEv = new CloseEvent("close");
+ this.dispatchEvent(closeEv);
+ core.tryClose(this[_rid]);
+ },
+ );
}
+ }
- async [_eventLoop]() {
- while (this[_readyState] !== CLOSED) {
- const { kind, value } = await core.opAsync(
- "op_ws_next_event",
- this[_rid],
- );
+ async [_eventLoop]() {
+ while (this[_readyState] !== CLOSED) {
+ const { kind, value } = await core.opAsync(
+ "op_ws_next_event",
+ this[_rid],
+ );
+
+ switch (kind) {
+ case "string": {
+ this[_serverHandleIdleTimeout]();
+ const event = new MessageEvent("message", {
+ data: value,
+ origin: this[_url],
+ });
+ this.dispatchEvent(event);
+ break;
+ }
+ case "binary": {
+ this[_serverHandleIdleTimeout]();
+ let data;
- switch (kind) {
- case "string": {
- this[_serverHandleIdleTimeout]();
- const event = new MessageEvent("message", {
- data: value,
- origin: this[_url],
- });
- this.dispatchEvent(event);
- break;
+ if (this.binaryType === "blob") {
+ data = new Blob([value]);
+ } else {
+ data = value.buffer;
}
- case "binary": {
- this[_serverHandleIdleTimeout]();
- let data;
- if (this.binaryType === "blob") {
- data = new Blob([value]);
- } else {
- data = value.buffer;
+ const event = new MessageEvent("message", {
+ data,
+ origin: this[_url],
+ [_skipInternalInit]: true,
+ });
+ this.dispatchEvent(event);
+ break;
+ }
+ case "ping": {
+ core.opAsync("op_ws_send", this[_rid], {
+ kind: "pong",
+ });
+ break;
+ }
+ case "pong": {
+ this[_serverHandleIdleTimeout]();
+ break;
+ }
+ case "closed":
+ case "close": {
+ const prevState = this[_readyState];
+ this[_readyState] = CLOSED;
+ clearTimeout(this[_idleTimeoutTimeout]);
+
+ if (prevState === OPEN) {
+ try {
+ await core.opAsync(
+ "op_ws_close",
+ this[_rid],
+ value.code,
+ value.reason,
+ );
+ } catch {
+ // ignore failures
}
-
- const event = new MessageEvent("message", {
- data,
- origin: this[_url],
- [_skipInternalInit]: true,
- });
- this.dispatchEvent(event);
- break;
- }
- case "ping": {
- core.opAsync("op_ws_send", this[_rid], {
- kind: "pong",
- });
- break;
}
- case "pong": {
- this[_serverHandleIdleTimeout]();
- break;
- }
- case "closed":
- case "close": {
- const prevState = this[_readyState];
- this[_readyState] = CLOSED;
- clearTimeout(this[_idleTimeoutTimeout]);
-
- if (prevState === OPEN) {
- try {
- await core.opAsync(
- "op_ws_close",
- this[_rid],
- value.code,
- value.reason,
- );
- } catch {
- // ignore failures
- }
- }
- const event = new CloseEvent("close", {
- wasClean: true,
- code: value.code,
- reason: value.reason,
- });
- this.dispatchEvent(event);
- core.tryClose(this[_rid]);
- break;
- }
- case "error": {
- this[_readyState] = CLOSED;
-
- const errorEv = new ErrorEvent("error", {
- message: value,
- });
- this.dispatchEvent(errorEv);
-
- const closeEv = new CloseEvent("close");
- this.dispatchEvent(closeEv);
- core.tryClose(this[_rid]);
- break;
- }
+ const event = new CloseEvent("close", {
+ wasClean: true,
+ code: value.code,
+ reason: value.reason,
+ });
+ this.dispatchEvent(event);
+ core.tryClose(this[_rid]);
+ break;
}
- }
- }
+ case "error": {
+ this[_readyState] = CLOSED;
- [_serverHandleIdleTimeout]() {
- if (this[_idleTimeoutDuration]) {
- clearTimeout(this[_idleTimeoutTimeout]);
- this[_idleTimeoutTimeout] = setTimeout(async () => {
- if (this[_readyState] === OPEN) {
- await core.opAsync("op_ws_send", this[_rid], {
- kind: "ping",
- });
- this[_idleTimeoutTimeout] = setTimeout(async () => {
- if (this[_readyState] === OPEN) {
- this[_readyState] = CLOSING;
- const reason = "No response from ping frame.";
- await core.opAsync("op_ws_close", this[_rid], 1001, reason);
- this[_readyState] = CLOSED;
-
- const errEvent = new ErrorEvent("error", {
- message: reason,
- });
- this.dispatchEvent(errEvent);
-
- const event = new CloseEvent("close", {
- wasClean: false,
- code: 1001,
- reason,
- });
- this.dispatchEvent(event);
- core.tryClose(this[_rid]);
- } else {
- clearTimeout(this[_idleTimeoutTimeout]);
- }
- }, (this[_idleTimeoutDuration] / 2) * 1000);
- } else {
- clearTimeout(this[_idleTimeoutTimeout]);
- }
- }, (this[_idleTimeoutDuration] / 2) * 1000);
+ const errorEv = new ErrorEvent("error", {
+ message: value,
+ });
+ this.dispatchEvent(errorEv);
+
+ const closeEv = new CloseEvent("close");
+ this.dispatchEvent(closeEv);
+ core.tryClose(this[_rid]);
+ break;
+ }
}
}
+ }
- [SymbolFor("Deno.customInspect")](inspect) {
- return `${this.constructor.name} ${
- inspect({
- url: this.url,
- readyState: this.readyState,
- extensions: this.extensions,
- protocol: this.protocol,
- binaryType: this.binaryType,
- bufferedAmount: this.bufferedAmount,
- })
- }`;
+ [_serverHandleIdleTimeout]() {
+ if (this[_idleTimeoutDuration]) {
+ clearTimeout(this[_idleTimeoutTimeout]);
+ this[_idleTimeoutTimeout] = setTimeout(async () => {
+ if (this[_readyState] === OPEN) {
+ await core.opAsync("op_ws_send", this[_rid], {
+ kind: "ping",
+ });
+ this[_idleTimeoutTimeout] = setTimeout(async () => {
+ if (this[_readyState] === OPEN) {
+ this[_readyState] = CLOSING;
+ const reason = "No response from ping frame.";
+ await core.opAsync("op_ws_close", this[_rid], 1001, reason);
+ this[_readyState] = CLOSED;
+
+ const errEvent = new ErrorEvent("error", {
+ message: reason,
+ });
+ this.dispatchEvent(errEvent);
+
+ const event = new CloseEvent("close", {
+ wasClean: false,
+ code: 1001,
+ reason,
+ });
+ this.dispatchEvent(event);
+ core.tryClose(this[_rid]);
+ } else {
+ clearTimeout(this[_idleTimeoutTimeout]);
+ }
+ }, (this[_idleTimeoutDuration] / 2) * 1000);
+ } else {
+ clearTimeout(this[_idleTimeoutTimeout]);
+ }
+ }, (this[_idleTimeoutDuration] / 2) * 1000);
}
}
- ObjectDefineProperties(WebSocket, {
- CONNECTING: {
- value: 0,
- },
- OPEN: {
- value: 1,
- },
- CLOSING: {
- value: 2,
- },
- CLOSED: {
- value: 3,
- },
- });
-
- defineEventHandler(WebSocket.prototype, "message");
- defineEventHandler(WebSocket.prototype, "error");
- defineEventHandler(WebSocket.prototype, "close");
- defineEventHandler(WebSocket.prototype, "open");
-
- webidl.configurePrototype(WebSocket);
- const WebSocketPrototype = WebSocket.prototype;
-
- window.__bootstrap.webSocket = {
- WebSocket,
- _rid,
- _readyState,
- _eventLoop,
- _protocol,
- _server,
- _idleTimeoutDuration,
- _idleTimeoutTimeout,
- _serverHandleIdleTimeout,
- };
-})(this);
+ [SymbolFor("Deno.customInspect")](inspect) {
+ return `${this.constructor.name} ${
+ inspect({
+ url: this.url,
+ readyState: this.readyState,
+ extensions: this.extensions,
+ protocol: this.protocol,
+ binaryType: this.binaryType,
+ bufferedAmount: this.bufferedAmount,
+ })
+ }`;
+ }
+}
+
+ObjectDefineProperties(WebSocket, {
+ CONNECTING: {
+ value: 0,
+ },
+ OPEN: {
+ value: 1,
+ },
+ CLOSING: {
+ value: 2,
+ },
+ CLOSED: {
+ value: 3,
+ },
+});
+
+defineEventHandler(WebSocket.prototype, "message");
+defineEventHandler(WebSocket.prototype, "error");
+defineEventHandler(WebSocket.prototype, "close");
+defineEventHandler(WebSocket.prototype, "open");
+
+webidl.configurePrototype(WebSocket);
+const WebSocketPrototype = WebSocket.prototype;
+
+export {
+ _eventLoop,
+ _idleTimeoutDuration,
+ _idleTimeoutTimeout,
+ _protocol,
+ _readyState,
+ _rid,
+ _server,
+ _serverHandleIdleTimeout,
+ WebSocket,
+};
diff --git a/ext/websocket/02_websocketstream.js b/ext/websocket/02_websocketstream.js
index 5d7e47cc4..b3d21297f 100644
--- a/ext/websocket/02_websocketstream.js
+++ b/ext/websocket/02_websocketstream.js
@@ -1,426 +1,424 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
-"use strict";
/// <reference path="../../core/internal.d.ts" />
-((window) => {
- const core = window.Deno.core;
- const ops = core.ops;
- const webidl = window.__bootstrap.webidl;
- const { writableStreamClose, Deferred } = window.__bootstrap.streams;
- const { DOMException } = window.__bootstrap.domException;
- const { add, remove } = window.__bootstrap.abortSignal;
- const { headersFromHeaderList, headerListFromHeaders, fillHeaders } =
- window.__bootstrap.headers;
-
- const {
- ArrayPrototypeJoin,
- ArrayPrototypeMap,
- Error,
- ObjectPrototypeIsPrototypeOf,
- PromisePrototypeCatch,
- PromisePrototypeThen,
- Set,
- StringPrototypeEndsWith,
- StringPrototypeToLowerCase,
- Symbol,
- SymbolFor,
- TypeError,
- Uint8ArrayPrototype,
- } = window.__bootstrap.primordials;
-
- webidl.converters.WebSocketStreamOptions = webidl.createDictionaryConverter(
- "WebSocketStreamOptions",
- [
- {
- key: "protocols",
- converter: webidl.converters["sequence<USVString>"],
- get defaultValue() {
- return [];
- },
- },
- {
- key: "signal",
- converter: webidl.converters.AbortSignal,
- },
- {
- key: "headers",
- converter: webidl.converters.HeadersInit,
+const core = globalThis.Deno.core;
+const ops = core.ops;
+import * as webidl from "internal:ext/webidl/00_webidl.js";
+import { Deferred, writableStreamClose } from "internal:ext/web/06_streams.js";
+import DOMException from "internal:ext/web/01_dom_exception.js";
+import { add, remove } from "internal:ext/web/03_abort_signal.js";
+import {
+ fillHeaders,
+ headerListFromHeaders,
+ headersFromHeaderList,
+} from "internal:ext/fetch/20_headers.js";
+const primordials = globalThis.__bootstrap.primordials;
+const {
+ ArrayPrototypeJoin,
+ ArrayPrototypeMap,
+ Error,
+ ObjectPrototypeIsPrototypeOf,
+ PromisePrototypeCatch,
+ PromisePrototypeThen,
+ Set,
+ StringPrototypeEndsWith,
+ StringPrototypeToLowerCase,
+ Symbol,
+ SymbolFor,
+ TypeError,
+ Uint8ArrayPrototype,
+} = primordials;
+
+webidl.converters.WebSocketStreamOptions = webidl.createDictionaryConverter(
+ "WebSocketStreamOptions",
+ [
+ {
+ key: "protocols",
+ converter: webidl.converters["sequence<USVString>"],
+ get defaultValue() {
+ return [];
},
- ],
- );
- webidl.converters.WebSocketCloseInfo = webidl.createDictionaryConverter(
- "WebSocketCloseInfo",
- [
- {
- key: "code",
- converter: webidl.converters["unsigned short"],
- },
- {
- key: "reason",
- converter: webidl.converters.USVString,
- defaultValue: "",
- },
- ],
- );
-
- const CLOSE_RESPONSE_TIMEOUT = 5000;
-
- const _rid = Symbol("[[rid]]");
- const _url = Symbol("[[url]]");
- const _connection = Symbol("[[connection]]");
- const _closed = Symbol("[[closed]]");
- const _earlyClose = Symbol("[[earlyClose]]");
- const _closeSent = Symbol("[[closeSent]]");
- class WebSocketStream {
- [_rid];
-
- [_url];
- get url() {
- webidl.assertBranded(this, WebSocketStreamPrototype);
- return this[_url];
+ },
+ {
+ key: "signal",
+ converter: webidl.converters.AbortSignal,
+ },
+ {
+ key: "headers",
+ converter: webidl.converters.HeadersInit,
+ },
+ ],
+);
+webidl.converters.WebSocketCloseInfo = webidl.createDictionaryConverter(
+ "WebSocketCloseInfo",
+ [
+ {
+ key: "code",
+ converter: webidl.converters["unsigned short"],
+ },
+ {
+ key: "reason",
+ converter: webidl.converters.USVString,
+ defaultValue: "",
+ },
+ ],
+);
+
+const CLOSE_RESPONSE_TIMEOUT = 5000;
+
+const _rid = Symbol("[[rid]]");
+const _url = Symbol("[[url]]");
+const _connection = Symbol("[[connection]]");
+const _closed = Symbol("[[closed]]");
+const _earlyClose = Symbol("[[earlyClose]]");
+const _closeSent = Symbol("[[closeSent]]");
+class WebSocketStream {
+ [_rid];
+
+ [_url];
+ get url() {
+ webidl.assertBranded(this, WebSocketStreamPrototype);
+ return this[_url];
+ }
+
+ constructor(url, options) {
+ this[webidl.brand] = webidl.brand;
+ const prefix = "Failed to construct 'WebSocketStream'";
+ webidl.requiredArguments(arguments.length, 1, { prefix });
+ url = webidl.converters.USVString(url, {
+ prefix,
+ context: "Argument 1",
+ });
+ options = webidl.converters.WebSocketStreamOptions(options, {
+ prefix,
+ context: "Argument 2",
+ });
+
+ const wsURL = new URL(url);
+
+ if (wsURL.protocol !== "ws:" && wsURL.protocol !== "wss:") {
+ throw new DOMException(
+ "Only ws & wss schemes are allowed in a WebSocket URL.",
+ "SyntaxError",
+ );
}
- constructor(url, options) {
- this[webidl.brand] = webidl.brand;
- const prefix = "Failed to construct 'WebSocketStream'";
- webidl.requiredArguments(arguments.length, 1, { prefix });
- url = webidl.converters.USVString(url, {
- prefix,
- context: "Argument 1",
- });
- options = webidl.converters.WebSocketStreamOptions(options, {
- prefix,
- context: "Argument 2",
- });
-
- const wsURL = new URL(url);
-
- if (wsURL.protocol !== "ws:" && wsURL.protocol !== "wss:") {
- throw new DOMException(
- "Only ws & wss schemes are allowed in a WebSocket URL.",
- "SyntaxError",
- );
- }
-
- if (wsURL.hash !== "" || StringPrototypeEndsWith(wsURL.href, "#")) {
- throw new DOMException(
- "Fragments are not allowed in a WebSocket URL.",
- "SyntaxError",
- );
- }
-
- this[_url] = wsURL.href;
-
- if (
- options.protocols.length !==
- new Set(
- ArrayPrototypeMap(
- options.protocols,
- (p) => StringPrototypeToLowerCase(p),
- ),
- ).size
- ) {
- throw new DOMException(
- "Can't supply multiple times the same protocol.",
- "SyntaxError",
- );
- }
-
- const headers = headersFromHeaderList([], "request");
- if (options.headers !== undefined) {
- fillHeaders(headers, options.headers);
- }
-
- const cancelRid = ops.op_ws_check_permission_and_cancel_handle(
- "WebSocketStream.abort()",
- this[_url],
- true,
+ if (wsURL.hash !== "" || StringPrototypeEndsWith(wsURL.href, "#")) {
+ throw new DOMException(
+ "Fragments are not allowed in a WebSocket URL.",
+ "SyntaxError",
);
+ }
- if (options.signal?.aborted) {
- core.close(cancelRid);
- const err = options.signal.reason;
- this[_connection].reject(err);
- this[_closed].reject(err);
- } else {
- const abort = () => {
- core.close(cancelRid);
- };
- options.signal?.[add](abort);
- PromisePrototypeThen(
- core.opAsync(
- "op_ws_create",
- "new WebSocketStream()",
- this[_url],
- options.protocols
- ? ArrayPrototypeJoin(options.protocols, ", ")
- : "",
- cancelRid,
- headerListFromHeaders(headers),
+ this[_url] = wsURL.href;
+
+ if (
+ options.protocols.length !==
+ new Set(
+ ArrayPrototypeMap(
+ options.protocols,
+ (p) => StringPrototypeToLowerCase(p),
),
- (create) => {
- options.signal?.[remove](abort);
- if (this[_earlyClose]) {
- PromisePrototypeThen(
- core.opAsync("op_ws_close", create.rid),
- () => {
- PromisePrototypeThen(
- (async () => {
- while (true) {
- const { kind } = await core.opAsync(
- "op_ws_next_event",
- create.rid,
- );
-
- if (kind === "close") {
- break;
- }
- }
- })(),
- () => {
- const err = new DOMException(
- "Closed while connecting",
- "NetworkError",
+ ).size
+ ) {
+ throw new DOMException(
+ "Can't supply multiple times the same protocol.",
+ "SyntaxError",
+ );
+ }
+
+ const headers = headersFromHeaderList([], "request");
+ if (options.headers !== undefined) {
+ fillHeaders(headers, options.headers);
+ }
+
+ const cancelRid = ops.op_ws_check_permission_and_cancel_handle(
+ "WebSocketStream.abort()",
+ this[_url],
+ true,
+ );
+
+ if (options.signal?.aborted) {
+ core.close(cancelRid);
+ const err = options.signal.reason;
+ this[_connection].reject(err);
+ this[_closed].reject(err);
+ } else {
+ const abort = () => {
+ core.close(cancelRid);
+ };
+ options.signal?.[add](abort);
+ PromisePrototypeThen(
+ core.opAsync(
+ "op_ws_create",
+ "new WebSocketStream()",
+ this[_url],
+ options.protocols ? ArrayPrototypeJoin(options.protocols, ", ") : "",
+ cancelRid,
+ headerListFromHeaders(headers),
+ ),
+ (create) => {
+ options.signal?.[remove](abort);
+ if (this[_earlyClose]) {
+ PromisePrototypeThen(
+ core.opAsync("op_ws_close", create.rid),
+ () => {
+ PromisePrototypeThen(
+ (async () => {
+ while (true) {
+ const { kind } = await core.opAsync(
+ "op_ws_next_event",
+ create.rid,
);
- this[_connection].reject(err);
- this[_closed].reject(err);
- },
- );
- },
- () => {
- const err = new DOMException(
- "Closed while connecting",
- "NetworkError",
+
+ if (kind === "close") {
+ break;
+ }
+ }
+ })(),
+ () => {
+ const err = new DOMException(
+ "Closed while connecting",
+ "NetworkError",
+ );
+ this[_connection].reject(err);
+ this[_closed].reject(err);
+ },
+ );
+ },
+ () => {
+ const err = new DOMException(
+ "Closed while connecting",
+ "NetworkError",
+ );
+ this[_connection].reject(err);
+ this[_closed].reject(err);
+ },
+ );
+ } else {
+ this[_rid] = create.rid;
+
+ const writable = new WritableStream({
+ write: async (chunk) => {
+ if (typeof chunk === "string") {
+ await core.opAsync("op_ws_send", this[_rid], {
+ kind: "text",
+ value: chunk,
+ });
+ } else if (
+ ObjectPrototypeIsPrototypeOf(Uint8ArrayPrototype, chunk)
+ ) {
+ await core.opAsync("op_ws_send", this[_rid], {
+ kind: "binary",
+ value: chunk,
+ }, chunk);
+ } else {
+ throw new TypeError(
+ "A chunk may only be either a string or an Uint8Array",
);
- this[_connection].reject(err);
- this[_closed].reject(err);
- },
+ }
+ },
+ close: async (reason) => {
+ try {
+ this.close(reason?.code !== undefined ? reason : {});
+ } catch (_) {
+ this.close();
+ }
+ await this.closed;
+ },
+ abort: async (reason) => {
+ try {
+ this.close(reason?.code !== undefined ? reason : {});
+ } catch (_) {
+ this.close();
+ }
+ await this.closed;
+ },
+ });
+ const pull = async (controller) => {
+ const { kind, value } = await core.opAsync(
+ "op_ws_next_event",
+ this[_rid],
);
- } else {
- this[_rid] = create.rid;
-
- const writable = new WritableStream({
- write: async (chunk) => {
- if (typeof chunk === "string") {
- await core.opAsync("op_ws_send", this[_rid], {
- kind: "text",
- value: chunk,
- });
- } else if (
- ObjectPrototypeIsPrototypeOf(Uint8ArrayPrototype, chunk)
- ) {
- await core.opAsync("op_ws_send", this[_rid], {
- kind: "binary",
- value: chunk,
- }, chunk);
- } else {
- throw new TypeError(
- "A chunk may only be either a string or an Uint8Array",
- );
- }
- },
- close: async (reason) => {
+
+ switch (kind) {
+ case "string": {
+ controller.enqueue(value);
+ break;
+ }
+ case "binary": {
+ controller.enqueue(value);
+ break;
+ }
+ case "ping": {
+ await core.opAsync("op_ws_send", this[_rid], {
+ kind: "pong",
+ });
+ await pull(controller);
+ break;
+ }
+ case "closed":
+ case "close": {
+ this[_closed].resolve(value);
+ core.tryClose(this[_rid]);
+ break;
+ }
+ case "error": {
+ const err = new Error(value);
+ this[_closed].reject(err);
+ controller.error(err);
+ core.tryClose(this[_rid]);
+ break;
+ }
+ }
+
+ if (
+ this[_closeSent].state === "fulfilled" &&
+ this[_closed].state === "pending"
+ ) {
+ if (
+ new Date().getTime() - await this[_closeSent].promise <=
+ CLOSE_RESPONSE_TIMEOUT
+ ) {
+ return pull(controller);
+ }
+
+ this[_closed].resolve(value);
+ core.tryClose(this[_rid]);
+ }
+ };
+ const readable = new ReadableStream({
+ start: (controller) => {
+ PromisePrototypeThen(this.closed, () => {
try {
- this.close(reason?.code !== undefined ? reason : {});
+ controller.close();
} catch (_) {
- this.close();
+ // needed to ignore warnings & assertions
}
- await this.closed;
- },
- abort: async (reason) => {
try {
- this.close(reason?.code !== undefined ? reason : {});
+ PromisePrototypeCatch(
+ writableStreamClose(writable),
+ () => {},
+ );
} catch (_) {
- this.close();
+ // needed to ignore warnings & assertions
}
- await this.closed;
- },
- });
- const pull = async (controller) => {
- const { kind, value } = await core.opAsync(
- "op_ws_next_event",
- this[_rid],
- );
+ });
- switch (kind) {
- case "string": {
- controller.enqueue(value);
- break;
- }
- case "binary": {
- controller.enqueue(value);
- break;
- }
- case "ping": {
- await core.opAsync("op_ws_send", this[_rid], {
- kind: "pong",
- });
- await pull(controller);
- break;
- }
- case "closed":
- case "close": {
- this[_closed].resolve(value);
- core.tryClose(this[_rid]);
- break;
- }
- case "error": {
- const err = new Error(value);
- this[_closed].reject(err);
- controller.error(err);
- core.tryClose(this[_rid]);
- break;
+ PromisePrototypeThen(this[_closeSent].promise, () => {
+ if (this[_closed].state === "pending") {
+ return pull(controller);
}
+ });
+ },
+ pull,
+ cancel: async (reason) => {
+ try {
+ this.close(reason?.code !== undefined ? reason : {});
+ } catch (_) {
+ this.close();
}
+ await this.closed;
+ },
+ });
+
+ this[_connection].resolve({
+ readable,
+ writable,
+ extensions: create.extensions ?? "",
+ protocol: create.protocol ?? "",
+ });
+ }
+ },
+ (err) => {
+ if (ObjectPrototypeIsPrototypeOf(core.InterruptedPrototype, err)) {
+ // The signal was aborted.
+ err = options.signal.reason;
+ } else {
+ core.tryClose(cancelRid);
+ }
+ this[_connection].reject(err);
+ this[_closed].reject(err);
+ },
+ );
+ }
+ }
- if (
- this[_closeSent].state === "fulfilled" &&
- this[_closed].state === "pending"
- ) {
- if (
- new Date().getTime() - await this[_closeSent].promise <=
- CLOSE_RESPONSE_TIMEOUT
- ) {
- return pull(controller);
- }
+ [_connection] = new Deferred();
+ get connection() {
+ webidl.assertBranded(this, WebSocketStreamPrototype);
+ return this[_connection].promise;
+ }
- this[_closed].resolve(value);
- core.tryClose(this[_rid]);
- }
- };
- const readable = new ReadableStream({
- start: (controller) => {
- PromisePrototypeThen(this.closed, () => {
- try {
- controller.close();
- } catch (_) {
- // needed to ignore warnings & assertions
- }
- try {
- PromisePrototypeCatch(
- writableStreamClose(writable),
- () => {},
- );
- } catch (_) {
- // needed to ignore warnings & assertions
- }
- });
+ [_earlyClose] = false;
+ [_closed] = new Deferred();
+ [_closeSent] = new Deferred();
+ get closed() {
+ webidl.assertBranded(this, WebSocketStreamPrototype);
+ return this[_closed].promise;
+ }
- PromisePrototypeThen(this[_closeSent].promise, () => {
- if (this[_closed].state === "pending") {
- return pull(controller);
- }
- });
- },
- pull,
- cancel: async (reason) => {
- try {
- this.close(reason?.code !== undefined ? reason : {});
- } catch (_) {
- this.close();
- }
- await this.closed;
- },
- });
-
- this[_connection].resolve({
- readable,
- writable,
- extensions: create.extensions ?? "",
- protocol: create.protocol ?? "",
- });
- }
- },
- (err) => {
- if (ObjectPrototypeIsPrototypeOf(core.InterruptedPrototype, err)) {
- // The signal was aborted.
- err = options.signal.reason;
- } else {
- core.tryClose(cancelRid);
- }
- this[_connection].reject(err);
- this[_closed].reject(err);
- },
- );
- }
+ close(closeInfo) {
+ webidl.assertBranded(this, WebSocketStreamPrototype);
+ closeInfo = webidl.converters.WebSocketCloseInfo(closeInfo, {
+ prefix: "Failed to execute 'close' on 'WebSocketStream'",
+ context: "Argument 1",
+ });
+
+ if (
+ closeInfo.code &&
+ !(closeInfo.code === 1000 ||
+ (3000 <= closeInfo.code && closeInfo.code < 5000))
+ ) {
+ throw new DOMException(
+ "The close code must be either 1000 or in the range of 3000 to 4999.",
+ "InvalidAccessError",
+ );
}
- [_connection] = new Deferred();
- get connection() {
- webidl.assertBranded(this, WebSocketStreamPrototype);
- return this[_connection].promise;
+ const encoder = new TextEncoder();
+ if (
+ closeInfo.reason && encoder.encode(closeInfo.reason).byteLength > 123
+ ) {
+ throw new DOMException(
+ "The close reason may not be longer than 123 bytes.",
+ "SyntaxError",
+ );
}
- [_earlyClose] = false;
- [_closed] = new Deferred();
- [_closeSent] = new Deferred();
- get closed() {
- webidl.assertBranded(this, WebSocketStreamPrototype);
- return this[_closed].promise;
+ let code = closeInfo.code;
+ if (closeInfo.reason && code === undefined) {
+ code = 1000;
}
- close(closeInfo) {
- webidl.assertBranded(this, WebSocketStreamPrototype);
- closeInfo = webidl.converters.WebSocketCloseInfo(closeInfo, {
- prefix: "Failed to execute 'close' on 'WebSocketStream'",
- context: "Argument 1",
- });
-
- if (
- closeInfo.code &&
- !(closeInfo.code === 1000 ||
- (3000 <= closeInfo.code && closeInfo.code < 5000))
- ) {
- throw new DOMException(
- "The close code must be either 1000 or in the range of 3000 to 4999.",
- "InvalidAccessError",
- );
- }
-
- const encoder = new TextEncoder();
- if (
- closeInfo.reason && encoder.encode(closeInfo.reason).byteLength > 123
- ) {
- throw new DOMException(
- "The close reason may not be longer than 123 bytes.",
- "SyntaxError",
- );
- }
-
- let code = closeInfo.code;
- if (closeInfo.reason && code === undefined) {
- code = 1000;
- }
-
- if (this[_connection].state === "pending") {
- this[_earlyClose] = true;
- } else if (this[_closed].state === "pending") {
- PromisePrototypeThen(
- core.opAsync("op_ws_close", this[_rid], code, closeInfo.reason),
- () => {
- setTimeout(() => {
- this[_closeSent].resolve(new Date().getTime());
- }, 0);
- },
- (err) => {
- this[_rid] && core.tryClose(this[_rid]);
- this[_closed].reject(err);
- },
- );
- }
+ if (this[_connection].state === "pending") {
+ this[_earlyClose] = true;
+ } else if (this[_closed].state === "pending") {
+ PromisePrototypeThen(
+ core.opAsync("op_ws_close", this[_rid], code, closeInfo.reason),
+ () => {
+ setTimeout(() => {
+ this[_closeSent].resolve(new Date().getTime());
+ }, 0);
+ },
+ (err) => {
+ this[_rid] && core.tryClose(this[_rid]);
+ this[_closed].reject(err);
+ },
+ );
}
+ }
- [SymbolFor("Deno.customInspect")](inspect) {
- return `${this.constructor.name} ${
- inspect({
- url: this.url,
- })
- }`;
- }
+ [SymbolFor("Deno.customInspect")](inspect) {
+ return `${this.constructor.name} ${
+ inspect({
+ url: this.url,
+ })
+ }`;
}
+}
- const WebSocketStreamPrototype = WebSocketStream.prototype;
+const WebSocketStreamPrototype = WebSocketStream.prototype;
- window.__bootstrap.webSocket.WebSocketStream = WebSocketStream;
-})(this);
+export { WebSocketStream };
diff --git a/ext/websocket/lib.rs b/ext/websocket/lib.rs
index 82a2c5918..da6a48e45 100644
--- a/ext/websocket/lib.rs
+++ b/ext/websocket/lib.rs
@@ -504,7 +504,7 @@ pub fn init<P: WebSocketPermissions + 'static>(
) -> Extension {
Extension::builder(env!("CARGO_PKG_NAME"))
.dependencies(vec!["deno_url", "deno_webidl"])
- .js(include_js_files!(
+ .esm(include_js_files!(
prefix "internal:ext/websocket",
"01_websocket.js",
"02_websocketstream.js",