Skip to content

Commit

Permalink
Fix WASM clients (#93)
Browse files Browse the repository at this point in the history
* fix features
* remove tokio-tungstenite dependency from clients
* remove tokio dependency from clients for WASM builds
* add demo WASM client; bug fixes
  • Loading branch information
UkoeHB authored Oct 16, 2023
1 parent ac3b387 commit d058d57
Show file tree
Hide file tree
Showing 21 changed files with 423 additions and 155 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Use [`tokio-tungstenite-wasm`](https://github.com/TannerRogalsky/tokio-tungstenite-wasm) errors internally to better support cross-platform clients.
- Use [`enfync`](https://github.com/UkoeHB/enfync) runtime handles internally to better support cross-platform clients. Default clients continue to use tokio.
- Add `ClientConnector` abstraction for connecting clients and add `ezsockets::client::connect_with`.
- Add `ClientConnectorWasm` and `wasm_client` feature.
- Add `ClientConnectorWasm` and `wasm_client` feature. Added `chat-client-wasm` example to show a WASM client that compiles. It currently only listens to the chat and can't input anything since browser does not have a terminal.
- Refactor `Socket` and `Client` to not depend on `tokio` when compiling to WASM. This is a breaking change as the `Client` API now exposes `async_channel` error types instead of `tokio` error types, and `Client::call_with()` now takes an `async_channel::Sender` instead of a `tokio` oneshot.


Migration guide:
Expand Down
34 changes: 22 additions & 12 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,49 +7,58 @@ description = "WebSockets server & client made easy"
readme = "README.md"
repository = "https://github.com/gbaranski/ezsockets"
license = "MIT"
keywords = ["websocket", "networking", "async"]
keywords = ["websocket", "networking", "async", "wasm"]
categories = ["asynchronous", "network-programming", "web-programming::websocket"]

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
crate-type = ["cdylib", "rlib"]

[dependencies]
async-channel = "1.9.0"
async-trait = "0.1.52"
atomic_enum = "0.2.0"
base64 = "0.21.0"
enfync = "0.1.0"
futures = "0.3.21"
futures-util = { version = "0.3.25", default-features = false }
http = "0.2.8"
tokio = { version = "1.17.0", features = ["sync", "macros", "time"] }
tracing = "0.1.31"
tungstenite = "0.20.0"
url = "2.2.2"
cfg-if = "1.0.0"

axum = { version = "0.6.1", optional = true }
axum-core = { version = "0.3.0", optional = true }
bytes = { version = "1.3.0", optional = true }
futures-util = { version = "0.3.25", default-features = false, features = ["alloc"], optional = true }
fragile = { version = "2.0", optional = true }
http-body = { version = "0.4.5", optional = true }
hyper = { version = "0.14.23", optional = true }
sha-1 = { version = "0.10.1", optional = true }
tokio-tungstenite = { version = "0.20.0", optional = true }

tokio-tungstenite-wasm = { version = "0.2.0", optional = true }
tokio-tungstenite-wasm = { version = "0.2.1", optional = true }

tokio-tungstenite = { version = "0.20.0", optional = true }
tokio-rustls = { version = "0.24.1", optional = true }
tokio-native-tls = { version = "0.3.1", optional = true }

[target.'cfg(not(target_family = "wasm"))'.dependencies]
tokio = { version = "1.17.0", features = ["sync", "macros", "time"] }

[target.'cfg(target_family = "wasm")'.dependencies]
getrandom = { version = "0.2", features = ["js"] }
wasm-bindgen-futures = "0.4"
wasmtimer = "0.2.0"

[features]
default = ["native_client", "server"]

client = ["tokio-tungstenite-wasm"]
native_client = ["client", "tokio-tungstenite", "tokio/rt"]
wasm_client = ["client", "tokio-tungstenite-wasm"]

tungstenite_common = ["tokio-tungstenite"]
native_client = ["client", "tokio/rt", "tokio-tungstenite"]
wasm_client = ["client", "fragile"]

server = ["tungstenite_common", "tokio-tungstenite-wasm", "tokio/rt"]
server = ["tokio-tungstenite", "tokio-tungstenite-wasm", "tokio/rt"]
tungstenite = ["server"]
axum = ["server", "dep:axum", "axum-core", "bytes", "futures-util", "http-body", "hyper", "sha-1"]
axum = ["server", "dep:axum", "axum-core", "bytes", "futures-util/alloc", "http-body", "hyper", "sha-1"]

tls = []
native-tls = ["tls", "tokio-native-tls", "tokio-tungstenite/native-tls"]
Expand All @@ -69,6 +78,7 @@ rustdoc-args = ["--cfg", "docsrs"]
[workspace]
members = [
"examples/chat-client",
"examples/chat-client-wasm",
"examples/chat-server",
"examples/chat-server-axum",
"examples/echo-server",
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@ Creating a WebSocket server or a client in Rust can be troublesome. This crate f
View the full documentation at [docs.rs/ezsockets](http://docs.rs/ezsockets)

## Examples
- [`simple-client`](https://github.com/gbaranski/ezsockets/tree/master/examples/chat-client) - a simplest WebSocket client which uses stdin as input.
- [`simple-client`](https://github.com/gbaranski/ezsockets/tree/master/examples/simple-client) - a simplest WebSocket client which uses stdin as input.
- [`echo-server`](https://github.com/gbaranski/ezsockets/tree/master/examples/echo-server) - server that echoes back every message it receives.
- [`echo-server`](https://github.com/gbaranski/ezsockets/tree/master/examples/echo-server-native-tls) - same as `echo-server`, but with `native-tls`.
- [`counter-server`](https://github.com/gbaranski/ezsockets/tree/master/examples/counter-server) - server that increments global value every second and shares it with client
- [`chat-client`](https://github.com/gbaranski/ezsockets/tree/master/examples/chat-client) - chat client for `chat-server` and `chat-server-axum` examples
- [`wasm-client`](https://github.com/gbaranski/ezsockets/tree/master/examples/chat-client-wasm) - chat client for `chat-server` and `chat-server-axum` examples that runs in the browser (only listens to the chat)
- [`chat-server`](https://github.com/gbaranski/ezsockets/tree/master/examples/chat-server) - chat server with support of rooms
- [`chat-server-axum`](https://github.com/gbaranski/ezsockets/tree/master/examples/chat-server-axum) - same as above, but using `axum` as a back-end

Expand Down
16 changes: 16 additions & 0 deletions examples/chat-client-wasm/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[package]
name = "ezsockets-chat-client-wasm"
version = "0.1.0"
edition = "2021"

[dependencies]
async-trait = "0.1.52"
console_error_panic_hook = "0.1"
ezsockets = { path = "../../", default-features = false, features = ["wasm_client"] }
tracing = "0.1.32"
tracing-subscriber = "0.3.9"
tracing-wasm = "0.2"
url = "2.2.2"
wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4"
wasmtimer = "0.2.0"
7 changes: 7 additions & 0 deletions examples/chat-client-wasm/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
1. install [wasm-server-runner](https://github.com/jakobhellermann/wasm-server-runner)
2. `cd` to this directory
3. `cargo run --target wasm32-unknown-unknown`
4. paste the address provided into a new browser window
5. open the browser window console to view the chat logs

Note: This demo does not currently have a way to pass messages into the chat.
63 changes: 63 additions & 0 deletions examples/chat-client-wasm/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#![allow(unused_imports)]

use async_trait::async_trait;
use ezsockets::{ClientConfig, RawMessage, SocketConfig};
use std::io::BufRead;
use std::sync::Arc;
use std::time::Duration;

struct Client {}

#[async_trait]
impl ezsockets::ClientExt for Client {
type Call = ();

async fn on_text(&mut self, text: String) -> Result<(), ezsockets::Error> {
tracing::info!("received message: {text}");
Ok(())
}

async fn on_binary(&mut self, bytes: Vec<u8>) -> Result<(), ezsockets::Error> {
tracing::info!("received bytes: {bytes:?}");
Ok(())
}

async fn on_call(&mut self, call: Self::Call) -> Result<(), ezsockets::Error> {
let () = call;
Ok(())
}
}

#[cfg(target_family = "wasm")]
#[wasm_bindgen::prelude::wasm_bindgen(main)]
async fn main() -> Result<(), wasm_bindgen::JsValue> {
// setup tracing
console_error_panic_hook::set_once();
tracing_wasm::set_as_global_default();

// make client
// - note: we use a hacky custom Ping/Pong protocol to keep the client alive (see the 'chat-server' example
// for the Ping side via SessionExt::on_binary())
let config = ClientConfig::new("ws://localhost:8080/websocket").socket_config(SocketConfig {
heartbeat_ping_msg_fn: Arc::new(|_t: Duration| RawMessage::Binary("ping".into())),
..Default::default()
});
let (_client, mut handle) = ezsockets::connect_with(
|_client| Client {},
config,
ezsockets::ClientConnectorWasm::default(),
);

// collect inputs: todo

// keep main alive until it is manually terminated
handle.extract().await.unwrap().unwrap();

Ok(())
}

#[cfg(not(target_family = "wasm"))]
fn main() {
// need per-package targets https://github.com/rust-lang/cargo/issues/9406
unreachable!()
}
2 changes: 0 additions & 2 deletions examples/chat-client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ name = "ezsockets-chat-client"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
async-trait = "0.1.52"
ezsockets = { path = "../../", features = ["client"] }
Expand Down
2 changes: 0 additions & 2 deletions examples/chat-server-axum/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ name = "ezsockets-chat-server-axum"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
async-trait = "0.1.52"
axum = "0.6.1"
Expand Down
2 changes: 0 additions & 2 deletions examples/chat-server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ name = "ezsockets-chat-server"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
async-trait = "0.1.52"
ezsockets = { path = "../../", features = ["tungstenite"] }
Expand Down
11 changes: 8 additions & 3 deletions examples/chat-server/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,10 @@ impl ezsockets::ServerExt for ChatServer {
) -> Result<Session, Option<CloseFrame>> {
let id = (0..).find(|i| !self.sessions.contains_key(i)).unwrap_or(0);
let session = Session::create(
|_handle| SessionActor {
|session_handle| SessionActor {
id,
server: self.handle.clone(),
session: session_handle,
room: DEFAULT_ROOM.to_string(),
},
id,
Expand Down Expand Up @@ -129,6 +130,7 @@ impl ezsockets::ServerExt for ChatServer {
struct SessionActor {
id: SessionID,
server: Server<ChatServer>,
session: Session,
room: String,
}

Expand Down Expand Up @@ -168,8 +170,11 @@ impl ezsockets::SessionExt for SessionActor {
Ok(())
}

async fn on_binary(&mut self, _bytes: Vec<u8>) -> Result<(), Error> {
unimplemented!()
async fn on_binary(&mut self, bytes: Vec<u8>) -> Result<(), Error> {
// echo bytes back (we use this for a hacky ping/pong protocol for the wasm client demo)
tracing::info!("echoing bytes: {bytes:?}");
self.session.binary("pong".as_bytes())?;
Ok(())
}

async fn on_call(&mut self, call: Self::Call) -> Result<(), Error> {
Expand Down
2 changes: 0 additions & 2 deletions examples/counter-server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ name = "ezsockets-counter-server"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
async-trait = "0.1.52"
ezsockets = { path = "../../", features = ["tungstenite"] }
Expand Down
2 changes: 0 additions & 2 deletions examples/echo-server-native-tls/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ name = "ezsocket-echo-server-native-tls"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
async-trait = "0.1.52"
ezsockets = { path = "../../", features = ["tungstenite", "native-tls"] }
Expand Down
2 changes: 0 additions & 2 deletions examples/echo-server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ name = "ezsocket-echo-server"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
async-trait = "0.1.52"
ezsockets = { path = "../../", features = ["tungstenite"] }
Expand Down
2 changes: 0 additions & 2 deletions examples/simple-client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ name = "ezsockets-simple-client"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
async-trait = "0.1.52"
ezsockets = { path = "../../", features = ["rustls"] }
Expand Down
Loading

0 comments on commit d058d57

Please sign in to comment.