From 80526fbc683d5fe82c2d3985ea0ef32ea196ec26 Mon Sep 17 00:00:00 2001 From: Sebastian Imlay Date: Mon, 29 Jul 2024 14:45:20 -0400 Subject: [PATCH 1/4] Initial barebones for browser wasm support --- Cargo.toml | 8 ++++++-- src/connect_wasm.rs | 42 ++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 18 +++++++++++++----- 3 files changed, 61 insertions(+), 7 deletions(-) create mode 100644 src/connect_wasm.rs diff --git a/Cargo.toml b/Cargo.toml index dec1fa2c..d3170d83 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,11 +27,15 @@ rustls-tls-webpki-roots = ["__rustls-tls", "webpki-roots"] __rustls-tls = ["rustls", "rustls-pki-types", "tokio-rustls", "stream", "tungstenite/__rustls-tls", "handshake"] stream = [] url = ["tungstenite/url"] +js-connect = ["stream", "handshake", "ws_stream_wasm", "async_io_stream", "getrandom/js" ] [dependencies] log = "0.4.17" futures-util = { version = "0.3.28", default-features = false, features = ["sink", "std"] } -tokio = { version = "1.0.0", default-features = false, features = ["io-util"] } +tokio = { version = "1.39.0", default-features = false } +ws_stream_wasm = { version = "0.7.4", optional = true } +async_io_stream = { version = "0.3.3", features = ["tokio_io"], optional = true } +getrandom = { version = "0.2", optional = true } [dependencies.tungstenite] version = "0.23.0" @@ -68,7 +72,7 @@ default-features = false optional = true version = "0.26.0" -[dev-dependencies] +[target.'cfg(not(target_family = "wasm"))'.dev-dependencies] futures-channel = "0.3.28" hyper = { version = "1.0", default-features = false, features = ["http1", "server"] } hyper-util = { version = "0.1", features = ["tokio"] } diff --git a/src/connect_wasm.rs b/src/connect_wasm.rs new file mode 100644 index 00000000..f3d130bd --- /dev/null +++ b/src/connect_wasm.rs @@ -0,0 +1,42 @@ +use crate::{stream::MaybeTlsStream, Connector, WebSocketStream}; + +use tungstenite::{ + error::{Error, UrlError}, + handshake::client::{Request, Response}, + protocol::WebSocketConfig, + //client::IntoClientRequest, +}; +//use web_sys::WebSocket; +use ws_stream_wasm::WsStreamIo; +use async_io_stream::IoStream; + + + +pub async fn connect( + request: Request, + config: Option, + disable_nagle: bool, + //connector: Option, +) -> Result<(WebSocketStream>>>, Response), Error> { + //let domain = domain(&request)?; + let domain = request.uri().host().unwrap(); + let port = request + .uri() + .port_u16() + .or_else(|| match request.uri().scheme_str() { + Some("wss") => Some(443), + Some("ws") => Some(80), + _ => None, + }) + .ok_or(Error::Url(UrlError::UnsupportedUrlScheme))?; + + let addr = format!("{domain}:{port}"); + //let socket = TcpStream::connect(addr).await.map_err(Error::Io)?; + + if disable_nagle { + //socket.set_nodelay(true)?; + } + todo!(); + + //crate::tls::client_async_tls_with_config(request, socket, config, connector).await +} diff --git a/src/lib.rs b/src/lib.rs index 734fc648..ef6b5143 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,17 +8,22 @@ //! Each WebSocket stream implements the required `Stream` and `Sink` traits, //! so the socket is just a stream of messages coming in and going out. -#![deny(missing_docs, unused_must_use, unused_mut, unused_imports, unused_import_braces)] +//#![deny(missing_docs, unused_must_use, unused_mut, unused_imports, unused_import_braces)] pub use tungstenite; mod compat; -#[cfg(feature = "connect")] +#[cfg(all(feature = "connect", not(target_family = "wasm")))] mod connect; + +#[cfg(all(feature = "js-connect", target_family = "wasm"))] +mod connect_wasm; + +#[cfg(feature = "handshake")] mod handshake; #[cfg(feature = "stream")] mod stream; -#[cfg(any(feature = "native-tls", feature = "__rustls-tls", feature = "connect"))] +#[cfg(any(feature = "native-tls", feature = "__rustls-tls", feature = "connect", feature = "js-connect"))] mod tls; use std::io::{Read, Write}; @@ -49,14 +54,17 @@ use tungstenite::{ protocol::{Message, Role, WebSocket, WebSocketConfig}, }; -#[cfg(any(feature = "native-tls", feature = "__rustls-tls", feature = "connect"))] +#[cfg(any(feature = "native-tls", feature = "__rustls-tls", feature = "connect", feature = "js-connect"))] pub use tls::Connector; #[cfg(any(feature = "native-tls", feature = "__rustls-tls"))] pub use tls::{client_async_tls, client_async_tls_with_config}; -#[cfg(feature = "connect")] +#[cfg(all(feature = "connect", not(target_family = "wasm")))] pub use connect::{connect_async, connect_async_with_config}; +#[cfg(all(feature = "js-connect", target_family = "wasm"))] +pub use connect_wasm::connect; + #[cfg(all(any(feature = "native-tls", feature = "__rustls-tls"), feature = "connect"))] pub use connect::connect_async_tls_with_config; From fef45acd1d4da3f6b55767f9c8558b8402e8e61b Mon Sep 17 00:00:00 2001 From: Sebastian Imlay Date: Sun, 25 Aug 2024 11:45:29 -0400 Subject: [PATCH 2/4] Finished out implementation using ws-stream-wasm --- src/connect_wasm.rs | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/src/connect_wasm.rs b/src/connect_wasm.rs index f3d130bd..53c00a52 100644 --- a/src/connect_wasm.rs +++ b/src/connect_wasm.rs @@ -1,17 +1,20 @@ -use crate::{stream::MaybeTlsStream, Connector, WebSocketStream}; +use crate::{stream::MaybeTlsStream, Connector, WebSocketStream, AllowStd}; use tungstenite::{ error::{Error, UrlError}, handshake::client::{Request, Response}, - protocol::WebSocketConfig, - //client::IntoClientRequest, + protocol::{ + WebSocketConfig, + WebSocket, + Role, + }, +}; +use ws_stream_wasm::{ + WsMeta, + WsStreamIo, }; -//use web_sys::WebSocket; -use ws_stream_wasm::WsStreamIo; use async_io_stream::IoStream; - - pub async fn connect( request: Request, config: Option, @@ -19,6 +22,7 @@ pub async fn connect( //connector: Option, ) -> Result<(WebSocketStream>>>, Response), Error> { //let domain = domain(&request)?; + let domain = request.uri().host().unwrap(); let port = request .uri() @@ -30,13 +34,17 @@ pub async fn connect( }) .ok_or(Error::Url(UrlError::UnsupportedUrlScheme))?; - let addr = format!("{domain}:{port}"); - //let socket = TcpStream::connect(addr).await.map_err(Error::Io)?; + //let addr = format!("ws://{domain}:{port}"); + let addr = request.uri().to_string(); + + let (mut _ws, wsio) = WsMeta::connect(addr, None ).await.expect("assume the connection succeeds"); if disable_nagle { //socket.set_nodelay(true)?; } - todo!(); + let io = wsio.into_io(); - //crate::tls::client_async_tls_with_config(request, socket, config, connector).await + let result = WebSocketStream::from_raw_socket(MaybeTlsStream::Plain(io), Role::Client, None).await; + let response = Response::new(None); + Ok((result, response)) } From cc8027d481ad92fe38447ec92eeb8a1246c3ce55 Mon Sep 17 00:00:00 2001 From: Sebastian Imlay Date: Wed, 28 Aug 2024 19:54:47 -0400 Subject: [PATCH 3/4] Cargo fmt --- src/connect_wasm.rs | 21 ++++++++------------- src/lib.rs | 14 ++++++++++++-- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/src/connect_wasm.rs b/src/connect_wasm.rs index 53c00a52..afb73784 100644 --- a/src/connect_wasm.rs +++ b/src/connect_wasm.rs @@ -1,19 +1,12 @@ -use crate::{stream::MaybeTlsStream, Connector, WebSocketStream, AllowStd}; +use crate::{stream::MaybeTlsStream, AllowStd, Connector, WebSocketStream}; +use async_io_stream::IoStream; use tungstenite::{ error::{Error, UrlError}, handshake::client::{Request, Response}, - protocol::{ - WebSocketConfig, - WebSocket, - Role, - }, -}; -use ws_stream_wasm::{ - WsMeta, - WsStreamIo, + protocol::{Role, WebSocket, WebSocketConfig}, }; -use async_io_stream::IoStream; +use ws_stream_wasm::{WsMeta, WsStreamIo}; pub async fn connect( request: Request, @@ -37,14 +30,16 @@ pub async fn connect( //let addr = format!("ws://{domain}:{port}"); let addr = request.uri().to_string(); - let (mut _ws, wsio) = WsMeta::connect(addr, None ).await.expect("assume the connection succeeds"); + let (mut _ws, wsio) = + WsMeta::connect(addr, None).await.expect("assume the connection succeeds"); if disable_nagle { //socket.set_nodelay(true)?; } let io = wsio.into_io(); - let result = WebSocketStream::from_raw_socket(MaybeTlsStream::Plain(io), Role::Client, None).await; + let result = + WebSocketStream::from_raw_socket(MaybeTlsStream::Plain(io), Role::Client, None).await; let response = Response::new(None); Ok((result, response)) } diff --git a/src/lib.rs b/src/lib.rs index ef6b5143..43fa3d0c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,7 +23,12 @@ mod connect_wasm; mod handshake; #[cfg(feature = "stream")] mod stream; -#[cfg(any(feature = "native-tls", feature = "__rustls-tls", feature = "connect", feature = "js-connect"))] +#[cfg(any( + feature = "native-tls", + feature = "__rustls-tls", + feature = "connect", + feature = "js-connect" +))] mod tls; use std::io::{Read, Write}; @@ -54,7 +59,12 @@ use tungstenite::{ protocol::{Message, Role, WebSocket, WebSocketConfig}, }; -#[cfg(any(feature = "native-tls", feature = "__rustls-tls", feature = "connect", feature = "js-connect"))] +#[cfg(any( + feature = "native-tls", + feature = "__rustls-tls", + feature = "connect", + feature = "js-connect" +))] pub use tls::Connector; #[cfg(any(feature = "native-tls", feature = "__rustls-tls"))] pub use tls::{client_async_tls, client_async_tls_with_config}; From 0a001acc0cd8a3d45abf41240acef999717188e2 Mon Sep 17 00:00:00 2001 From: Sebastian Imlay Date: Thu, 29 Aug 2024 11:00:54 -0400 Subject: [PATCH 4/4] Fix cargo hack ci --- Cargo.toml | 2 +- src/lib.rs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index d3170d83..dc870f0f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,7 @@ all-features = true [features] default = ["connect", "handshake"] -connect = ["stream", "tokio/net", "handshake"] +connect = ["stream", "tokio/net", "handshake", "tokio/io-util"] handshake = ["tungstenite/handshake"] native-tls = ["native-tls-crate", "tokio-native-tls", "stream", "tungstenite/native-tls", "handshake"] native-tls-vendored = ["native-tls", "native-tls-crate/vendored", "tungstenite/native-tls-vendored"] diff --git a/src/lib.rs b/src/lib.rs index 43fa3d0c..585c6586 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -221,6 +221,7 @@ pub struct WebSocketStream { impl WebSocketStream { /// Convert a raw socket into a WebSocketStream without performing a /// handshake. + #[cfg(feature = "handshake")] pub async fn from_raw_socket(stream: S, role: Role, config: Option) -> Self where S: AsyncRead + AsyncWrite + Unpin, @@ -233,6 +234,7 @@ impl WebSocketStream { /// Convert a raw socket into a WebSocketStream without performing a /// handshake. + #[cfg(feature = "handshake")] pub async fn from_partially_read( stream: S, part: Vec,