From 3a02156cfc7c45d1d92471a3b86d28c5eaf2b8a3 Mon Sep 17 00:00:00 2001 From: Jose Daniel Hernandez Date: Mon, 20 Nov 2023 10:36:51 -0600 Subject: [PATCH] fix(websocket-websys): Add support for different WASM environments Add support different WASM environments (such as workers, NodeJS) that don't have a `window` global. This is done by introducing a `WebContext` `enum` that abstracts and detects the `Window` vs the `WorkerGlobalScope` API. This is done due to the `web-sys` lack of interest to support this (see discussion in [this issue](https://github.com/rustwasm/wasm-bindgen/issues/1046)). --- Cargo.lock | 2 +- Cargo.toml | 2 +- transports/websocket-websys/CHANGELOG.md | 6 ++ transports/websocket-websys/Cargo.toml | 4 +- transports/websocket-websys/src/lib.rs | 16 ++++-- .../websocket-websys/src/web_context.rs | 57 +++++++++++++++++++ 6 files changed, 77 insertions(+), 10 deletions(-) create mode 100644 transports/websocket-websys/src/web_context.rs diff --git a/Cargo.lock b/Cargo.lock index b342fbb133b..b12b51cc1ec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3320,7 +3320,7 @@ dependencies = [ [[package]] name = "libp2p-websocket-websys" -version = "0.3.0" +version = "0.3.1" dependencies = [ "bytes", "futures", diff --git a/Cargo.toml b/Cargo.toml index 9417876ae01..0603a22629b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -110,7 +110,7 @@ libp2p-webrtc = { version = "0.6.1-alpha", path = "transports/webrtc" } libp2p-webrtc-utils = { version = "0.1.0", path = "misc/webrtc-utils" } libp2p-webrtc-websys = { version = "0.2.0-alpha", path = "transports/webrtc-websys" } libp2p-websocket = { version = "0.43.0", path = "transports/websocket" } -libp2p-websocket-websys = { version = "0.3.0", path = "transports/websocket-websys" } +libp2p-websocket-websys = { version = "0.3.1", path = "transports/websocket-websys" } libp2p-webtransport-websys = { version = "0.2.0", path = "transports/webtransport-websys" } libp2p-yamux = { version = "0.45.0", path = "muxers/yamux" } multiaddr = "0.18.1" diff --git a/transports/websocket-websys/CHANGELOG.md b/transports/websocket-websys/CHANGELOG.md index 17c253cb80a..3cfb1b2fbf9 100644 --- a/transports/websocket-websys/CHANGELOG.md +++ b/transports/websocket-websys/CHANGELOG.md @@ -1,3 +1,9 @@ +## 0.3.1 + +- Add support for different WASM environments by introducing a `WebContext` that + detects and abstracts the `Window` vs the `WorkerGlobalScope` API. + See [PR 4889](https://github.com/libp2p/rust-libp2p/pull/4889). + ## 0.3.0 diff --git a/transports/websocket-websys/Cargo.toml b/transports/websocket-websys/Cargo.toml index 6a6cef5f8bc..ffa2892e838 100644 --- a/transports/websocket-websys/Cargo.toml +++ b/transports/websocket-websys/Cargo.toml @@ -3,7 +3,7 @@ name = "libp2p-websocket-websys" edition = "2021" rust-version = "1.60.0" description = "WebSocket for libp2p under WASM environment" -version = "0.3.0" +version = "0.3.1" authors = ["Vince Vasta "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" @@ -20,7 +20,7 @@ parking_lot = "0.12.1" send_wrapper = "0.6.0" thiserror = "1.0.50" wasm-bindgen = "0.2.88" -web-sys = { version = "0.3.65", features = ["BinaryType", "CloseEvent", "MessageEvent", "WebSocket", "Window"] } +web-sys = { version = "0.3.65", features = ["BinaryType", "CloseEvent", "MessageEvent", "WebSocket", "Window", "WorkerGlobalScope"] } # Passing arguments to the docsrs builder in order to properly document cfg's. # More information: https://docs.rs/about/builds#cross-compiling diff --git a/transports/websocket-websys/src/lib.rs b/transports/websocket-websys/src/lib.rs index b4f7566f95e..5c1a6ebf1c4 100644 --- a/transports/websocket-websys/src/lib.rs +++ b/transports/websocket-websys/src/lib.rs @@ -20,6 +20,8 @@ //! Libp2p websocket transports built on [web-sys](https://rustwasm.github.io/wasm-bindgen/web-sys/index.html). +mod web_context; + use bytes::BytesMut; use futures::task::AtomicWaker; use futures::{future::Ready, io, prelude::*}; @@ -35,7 +37,9 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Mutex; use std::{pin::Pin, task::Context, task::Poll}; use wasm_bindgen::{prelude::*, JsCast}; -use web_sys::{window, CloseEvent, Event, MessageEvent, WebSocket}; +use web_sys::{CloseEvent, Event, MessageEvent, WebSocket}; + +use crate::web_context::WebContext; /// A Websocket transport that can be used in a wasm environment. /// @@ -300,8 +304,8 @@ impl Connection { } } }); - let buffered_amount_low_interval = window() - .expect("to have a window") + let buffered_amount_low_interval = WebContext::new() + .expect("to have a window or worker context") .set_interval_with_callback_and_timeout_and_arguments( on_buffered_amount_low_closure.as_ref().unchecked_ref(), 100, // Chosen arbitrarily and likely worth tuning. Due to low impact of the /ws transport, no further effort was invested at the time. @@ -439,8 +443,8 @@ impl Drop for Connection { .close_with_code_and_reason(GO_AWAY_STATUS_CODE, "connection dropped"); } - window() - .expect("to have a window") - .clear_interval_with_handle(self.inner.buffered_amount_low_interval) + WebContext::new() + .expect("to have a window or worker context") + .clear_interval_with_handle(self.inner.buffered_amount_low_interval); } } diff --git a/transports/websocket-websys/src/web_context.rs b/transports/websocket-websys/src/web_context.rs new file mode 100644 index 00000000000..c514435d2bb --- /dev/null +++ b/transports/websocket-websys/src/web_context.rs @@ -0,0 +1,57 @@ +use wasm_bindgen::{prelude::*, JsCast}; +use web_sys::window; + +/// Web context that abstract the window vs web worker API +#[derive(Debug)] +pub(crate) enum WebContext { + Window(web_sys::Window), + Worker(web_sys::WorkerGlobalScope), +} + +impl WebContext { + pub(crate) fn new() -> Option { + match window() { + Some(window) => Some(Self::Window(window)), + None => { + #[wasm_bindgen] + extern "C" { + type Global; + + #[wasm_bindgen(method, getter, js_name = WorkerGlobalScope)] + fn worker(this: &Global) -> JsValue; + } + let global: Global = js_sys::global().unchecked_into(); + if !global.worker().is_undefined() { + Some(Self::Worker(global.unchecked_into())) + } else { + None + } + } + } + } + + /// The `setInterval()` method. + pub(crate) fn set_interval_with_callback_and_timeout_and_arguments( + &self, + handler: &::js_sys::Function, + timeout: i32, + arguments: &::js_sys::Array, + ) -> Result { + match self { + WebContext::Window(w) => { + w.set_interval_with_callback_and_timeout_and_arguments(handler, timeout, arguments) + } + WebContext::Worker(w) => { + w.set_interval_with_callback_and_timeout_and_arguments(handler, timeout, arguments) + } + } + } + + /// The `clearInterval()` method. + pub(crate) fn clear_interval_with_handle(&self, handle: i32) { + match self { + WebContext::Window(w) => w.clear_interval_with_handle(handle), + WebContext::Worker(w) => w.clear_interval_with_handle(handle), + } + } +}