diff options
-rw-r--r-- | Cargo.lock | 4 | ||||
-rw-r--r-- | cli/tests/unit/websocket_test.ts | 51 | ||||
-rw-r--r-- | ext/websocket/Cargo.toml | 2 | ||||
-rw-r--r-- | ext/websocket/lib.rs | 18 |
4 files changed, 70 insertions, 5 deletions
diff --git a/Cargo.lock b/Cargo.lock index 29f02d5c4..cb5bc2f9d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1819,9 +1819,9 @@ dependencies = [ [[package]] name = "fastwebsockets" -version = "0.2.1" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99a248d92ac4e9048a30d147d7897eaaadd0a5230f11982ab7d6935d7d268902" +checksum = "fcf2f933f24f45831bd66580a8f9394e440f1f5a23806cf0d4d8b6649e1a01e9" dependencies = [ "base64 0.21.0", "cc", diff --git a/cli/tests/unit/websocket_test.ts b/cli/tests/unit/websocket_test.ts index 948e2add2..997d8f0df 100644 --- a/cli/tests/unit/websocket_test.ts +++ b/cli/tests/unit/websocket_test.ts @@ -1,5 +1,11 @@ // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -import { assertEquals, assertThrows, deferred, fail } from "./test_util.ts"; +import { + assert, + assertEquals, + assertThrows, + deferred, + fail, +} from "./test_util.ts"; Deno.test({ permissions: "none" }, function websocketPermissionless() { assertThrows( @@ -82,3 +88,46 @@ Deno.test( ws.close(); }, ); + +// https://github.com/denoland/deno/issues/18775 +Deno.test({ + sanitizeOps: false, + sanitizeResources: false, +}, async function websocketDoubleClose() { + const promise = deferred(); + + const ac = new AbortController(); + const listeningPromise = deferred(); + + const server = Deno.serve({ + handler: (req) => { + const { response, socket } = Deno.upgradeWebSocket(req); + let called = false; + socket.onopen = () => socket.send("Hello"); + socket.onmessage = () => { + assert(!called); + called = true; + socket.send("bye"); + socket.close(); + }; + socket.onclose = () => ac.abort(); + socket.onerror = () => fail(); + return response; + }, + signal: ac.signal, + onListen: () => listeningPromise.resolve(), + hostname: "localhost", + port: 4247, + }); + + await listeningPromise; + + const ws = new WebSocket("ws://localhost:4247/"); + assertEquals(ws.url, "ws://localhost:4247/"); + ws.onerror = () => fail(); + ws.onmessage = () => ws.send("bye"); + ws.onclose = () => { + promise.resolve(); + }; + await Promise.all([promise, server]); +}); diff --git a/ext/websocket/Cargo.toml b/ext/websocket/Cargo.toml index 03cb3076a..a96b6cceb 100644 --- a/ext/websocket/Cargo.toml +++ b/ext/websocket/Cargo.toml @@ -16,7 +16,7 @@ path = "lib.rs" [dependencies] deno_core.workspace = true deno_tls.workspace = true -fastwebsockets = { version = "0.2.1", features = ["upgrade"] } +fastwebsockets = { version = "0.2.4", features = ["upgrade"] } http.workspace = true hyper.workspace = true serde.workspace = true diff --git a/ext/websocket/lib.rs b/ext/websocket/lib.rs index f63191a8e..798856bc1 100644 --- a/ext/websocket/lib.rs +++ b/ext/websocket/lib.rs @@ -34,6 +34,7 @@ use std::cell::Cell; use std::cell::RefCell; use std::convert::TryFrom; use std::fmt; +use std::future::Future; use std::path::PathBuf; use std::rc::Rc; use std::sync::Arc; @@ -239,7 +240,8 @@ where _ => unreachable!(), }; - let client = fastwebsockets::handshake::client(request, socket); + let client = + fastwebsockets::handshake::client(&LocalExecutor, request, socket); let (stream, response): (WebSocket<Upgraded>, Response<Body>) = if let Some(cancel_resource) = cancel_resource { @@ -533,3 +535,17 @@ pub fn get_network_error_class_name(e: &AnyError) -> Option<&'static str> { e.downcast_ref::<DomExceptionNetworkError>() .map(|_| "DOMExceptionNetworkError") } + +// Needed so hyper can use non Send futures +#[derive(Clone)] +struct LocalExecutor; + +impl<Fut> hyper::rt::Executor<Fut> for LocalExecutor +where + Fut: Future + 'static, + Fut::Output: 'static, +{ + fn execute(&self, fut: Fut) { + tokio::task::spawn_local(fut); + } +} |