summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ext/websocket/01_websocket.js12
-rw-r--r--ext/websocket/lib.rs8
-rw-r--r--tests/unit/websocket_test.ts44
3 files changed, 62 insertions, 2 deletions
diff --git a/ext/websocket/01_websocket.js b/ext/websocket/01_websocket.js
index afe543da5..60580a56c 100644
--- a/ext/websocket/01_websocket.js
+++ b/ext/websocket/01_websocket.js
@@ -424,6 +424,18 @@ class WebSocket extends EventTarget {
const rid = this[_rid];
while (this[_readyState] !== CLOSED) {
const kind = await op_ws_next_event(rid);
+ /* close the connection if read was cancelled, and we didn't get a close frame */
+ if (
+ (this[_readyState] == CLOSING) &&
+ kind <= 3 && this[_role] !== CLIENT
+ ) {
+ this[_readyState] = CLOSED;
+
+ const event = new CloseEvent("close");
+ this.dispatchEvent(event);
+ core.tryClose(rid);
+ break;
+ }
switch (kind) {
case 0: {
diff --git a/ext/websocket/lib.rs b/ext/websocket/lib.rs
index 87503120b..5c3e8d7b1 100644
--- a/ext/websocket/lib.rs
+++ b/ext/websocket/lib.rs
@@ -699,10 +699,14 @@ pub async fn op_ws_close(
#[smi] code: Option<u16>,
#[string] reason: Option<String>,
) -> Result<(), AnyError> {
- let resource = state
+ let Ok(resource) = state
.borrow_mut()
.resource_table
- .get::<ServerWebSocket>(rid)?;
+ .get::<ServerWebSocket>(rid)
+ else {
+ return Ok(());
+ };
+
let frame = reason
.map(|reason| Frame::close(code.unwrap_or(1005), reason.as_bytes()))
.unwrap_or_else(|| Frame::close_raw(vec![].into()));
diff --git a/tests/unit/websocket_test.ts b/tests/unit/websocket_test.ts
index 74e85052e..362957b2d 100644
--- a/tests/unit/websocket_test.ts
+++ b/tests/unit/websocket_test.ts
@@ -761,3 +761,47 @@ Deno.test("Close without frame", async () => {
};
await promise;
});
+
+Deno.test("Close connection", async () => {
+ const ac = new AbortController();
+ const listeningDeferred = Promise.withResolvers<void>();
+
+ const server = Deno.serve({
+ handler: (req) => {
+ const { socket, response } = Deno.upgradeWebSocket(req);
+ socket.onmessage = function (e) {
+ socket.close(1008);
+ assertEquals(e.data, "Hello");
+ };
+ socket.onclose = () => {
+ ac.abort();
+ };
+ socket.onerror = () => fail();
+ return response;
+ },
+ signal: ac.signal,
+ onListen: () => listeningDeferred.resolve(),
+ hostname: "localhost",
+ port: servePort,
+ });
+
+ await listeningDeferred.promise;
+
+ const conn = await Deno.connect({ port: servePort, hostname: "localhost" });
+ await conn.write(
+ new TextEncoder().encode(
+ "GET / HTTP/1.1\r\nConnection: Upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nSec-WebSocket-Version: 13\r\n\r\n",
+ ),
+ );
+
+ // Write a 2 text frame saying "Hello"
+ await conn.write(new Uint8Array([0x81, 0x05]));
+ await conn.write(new TextEncoder().encode("Hello"));
+
+ // We are a bad client so we won't acknowledge the close frame
+ await conn.write(new Uint8Array([0x81, 0x05]));
+ await conn.write(new TextEncoder().encode("Hello"));
+
+ await server.finished;
+ conn.close();
+});