From 5be02151e68342617cd2104dfe702091b372df4b Mon Sep 17 00:00:00 2001 From: Toshit Chawda Date: Wed, 28 Feb 2024 23:08:56 -0800 Subject: [PATCH 1/7] various small improvements --- README.md | 6 ++- client/Cargo.toml | 2 +- client/build.sh | 2 +- client/demo.js | 31 +++++++++---- client/index.html | 14 +++--- client/src/lib.rs | 83 +++++++++++++++------------------- client/src/tls_stream.rs | 6 ++- client/src/utils.rs | 70 ++++++++++++++++++++++------ client/src/websocket.rs | 12 +++-- server/src/main.rs | 23 ++-------- simple-wisp-client/src/main.rs | 2 +- 11 files changed, 146 insertions(+), 105 deletions(-) diff --git a/README.md b/README.md index f2d6674..0ccd0d8 100644 --- a/README.md +++ b/README.md @@ -9,8 +9,12 @@ importScripts("epoxy-bundled.js"); const { EpoxyClient } = await epoxy(); let client = await new EpoxyClient("wss://localhost:4000", navigator.userAgent, 10); +// You can view and change the user agent and redirect limit +console.log(client.userAgent); +client.redirect_limit = 5; + let response = await client.fetch("https://httpbin.org/get"); -await response.text(); +console.log(await response.text()); ``` See `client/demo.js` for more examples. diff --git a/client/Cargo.toml b/client/Cargo.toml index 4c336d0..1031bf6 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -12,7 +12,7 @@ http = "1.0.0" http-body-util = "0.1.0" hyper = { version = "1.1.0", features = ["client", "http1", "http2"] } pin-project-lite = "0.2.13" -wasm-bindgen = "0.2" +wasm-bindgen = { version = "0.2.91", features = ["enable-interning"] } wasm-bindgen-futures = "0.4.39" ws_stream_wasm = { version = "0.7.4", features = ["tokio_io"] } futures-util = "0.3.30" diff --git a/client/build.sh b/client/build.sh index 7f402f0..237915f 100755 --- a/client/build.sh +++ b/client/build.sh @@ -27,7 +27,7 @@ echo "module.exports = epoxy" >> epoxy-module-bundled.js AUTOGENERATED_TYPEDEFS=$(<"out/epoxy_client.d.ts") AUTOGENERATED_TYPEDEFS=${AUTOGENERATED_TYPEDEFS%%export class IntoUnderlyingByteSource*} echo "$AUTOGENERATED_TYPEDEFS" >"epoxy-module-bundled.d.ts" -echo "} export default function epoxy(): Promise;" >> "epoxy-module-bundled.d.ts" +echo "} export default function epoxy(maybe_memory?: WebAssembly.Memory): Promise;" >> "epoxy-module-bundled.d.ts" cp out/epoxy_client.js epoxy.js cp out/epoxy_client.d.ts epoxy.d.ts diff --git a/client/demo.js b/client/demo.js index 89b0bd5..694591c 100644 --- a/client/demo.js +++ b/client/demo.js @@ -1,6 +1,6 @@ importScripts("epoxy-bundled.js"); onmessage = async (msg) => { - console.debug("recieved:", msg); + console.debug("recieved demo:", msg); let [should_feature_test, should_multiparallel_test, should_parallel_test, should_multiperf_test, should_perf_test, should_ws_test, should_tls_test] = msg.data; console.log( "%cWASM is significantly slower with DevTools open!", @@ -8,10 +8,15 @@ onmessage = async (msg) => { ); const log = (str) => { - console.warn(str); + console.log(str); postMessage(str); } + const plog = (str) => { + console.log(str); + postMessage(JSON.stringify(str, null, 4)); + } + const { EpoxyClient } = await epoxy(); const tconn0 = performance.now(); @@ -20,6 +25,11 @@ onmessage = async (msg) => { const tconn1 = performance.now(); log(`conn establish took ${tconn1 - tconn0} ms or ${(tconn1 - tconn0) / 1000} s`); + // epoxy classes are inspectable + console.log(epoxy_client); + // you can change the user agent and redirect limit in JS + epoxy_client.redirectLimit = 15; + const test_mux = async (url) => { const t0 = performance.now(); await epoxy_client.fetch(url); @@ -138,23 +148,24 @@ onmessage = async (msg) => { log(`avg mux - avg native (${num_tests}): ${total_mux - total_native} ms or ${(total_mux - total_native) / 1000} s`); } else if (should_ws_test) { let ws = await epoxy_client.connect_ws( - () => console.log("opened"), - () => console.log("closed"), + () => log("opened"), + () => log("closed"), err => console.error(err), - msg => console.log(msg), + msg => log(msg), "wss://echo.websocket.events", [], "localhost" ); while (true) { + log("sending `data`"); await ws.send("data"); - await (new Promise((res, _) => setTimeout(res, 100))); + await (new Promise((res, _) => setTimeout(res, 50))); } } else if (should_tls_test) { let decoder = new TextDecoder(); let ws = await epoxy_client.connect_tls( - () => console.log("opened"), - () => console.log("closed"), + () => log("opened"), + () => log("closed"), err => console.error(err), msg => { console.log(msg); console.log(decoder.decode(msg)) }, "alicesworld.tech:443", @@ -163,8 +174,8 @@ onmessage = async (msg) => { await ws.close(); } else { let resp = await epoxy_client.fetch("https://httpbin.org/get"); - console.warn(resp, Object.fromEntries(resp.headers)); - console.warn(await resp.text()); + console.log(resp, Object.fromEntries(resp.headers)); + plog(await resp.json()); } log("done"); }; diff --git a/client/index.html b/client/index.html index 718f470..b78d4d8 100644 --- a/client/index.html +++ b/client/index.html @@ -3,8 +3,9 @@ epoxy diff --git a/client/package.json b/client/package.json index 316b643..3c3a2c6 100644 --- a/client/package.json +++ b/client/package.json @@ -1,6 +1,6 @@ { "name": "@mercuryworkshop/epoxy-tls", - "version": "1.1.1", + "version": "1.2.0", "description": "A wasm library for using raw encrypted tls/ssl/https/websocket streams on the browser", "scripts": { "build": "./build.sh" diff --git a/client/src/lib.rs b/client/src/lib.rs index cd26347..ce8e495 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -2,10 +2,12 @@ #[macro_use] mod utils; mod tls_stream; +mod udp_stream; mod websocket; mod wrappers; use tls_stream::EpxTlsStream; +use udp_stream::EpxUdpStream; use utils::{Boolinator, ReplaceErr, UriExt}; use websocket::EpxWebSocket; use wrappers::{IncomingBody, TlsWispService, WebSocketWrapper}; @@ -247,6 +249,17 @@ impl EpoxyClient { EpxTlsStream::connect(self, onopen, onclose, onerror, onmessage, url).await } + pub async fn connect_udp( + &self, + onopen: Function, + onclose: Function, + onerror: Function, + onmessage: Function, + url: String, + ) -> Result { + EpxUdpStream::connect(self, onopen, onclose, onerror, onmessage, url).await + } + pub async fn fetch(&self, url: String, options: Object) -> Result { let uri = url.parse::().replace_err("Failed to parse URL")?; let uri_scheme = uri.scheme().replace_err("URL must have a scheme")?; diff --git a/client/src/tls_stream.rs b/client/src/tls_stream.rs index e9b229e..3975982 100644 --- a/client/src/tls_stream.rs +++ b/client/src/tls_stream.rs @@ -19,8 +19,6 @@ impl EpxTlsStream { Err(jerr!("Use EpoxyClient.connect_tls() instead.")) } - // shut up - #[allow(clippy::too_many_arguments)] pub async fn connect( tcp: &EpoxyClient, onopen: Function, diff --git a/client/src/udp_stream.rs b/client/src/udp_stream.rs new file mode 100644 index 0000000..721fe48 --- /dev/null +++ b/client/src/udp_stream.rs @@ -0,0 +1,89 @@ +use crate::*; + +use futures_util::{stream::SplitSink, SinkExt}; +use js_sys::Function; + +#[wasm_bindgen(inspectable)] +pub struct EpxUdpStream { + tx: SplitSink>, + onerror: Function, + #[wasm_bindgen(readonly, getter_with_clone)] + pub url: String, +} + +#[wasm_bindgen] +impl EpxUdpStream { + #[wasm_bindgen(constructor)] + pub fn new() -> Result { + Err(jerr!("Use EpoxyClient.connect_udp() instead.")) + } + + pub async fn connect( + tcp: &EpoxyClient, + onopen: Function, + onclose: Function, + onerror: Function, + onmessage: Function, + url: String, + ) -> Result { + let onerr = onerror.clone(); + let ret: Result = async move { + let url = Uri::try_from(url).replace_err("Failed to parse URL")?; + let url_host = url.host().replace_err("URL must have a host")?; + let url_port = url.port().replace_err("URL must have a port")?.into(); + + let io = tcp + .mux + .client_new_stream(StreamType::Udp, url_host.to_string(), url_port) + .await + .replace_err("Failed to open multiplexor channel")? + .into_io(); + let (tx, mut rx) = io.split(); + + wasm_bindgen_futures::spawn_local(async move { + while let Some(Ok(data)) = rx.next().await { + let _ = onmessage.call1( + &JsValue::null(), + &jval!(Uint8Array::from(data.to_vec().as_slice())), + ); + } + let _ = onclose.call0(&JsValue::null()); + }); + + onopen + .call0(&Object::default()) + .replace_err("Failed to call onopen")?; + + Ok(Self { + tx, + onerror, + url: url.to_string(), + }) + } + .await; + if let Err(ret) = ret { + let _ = onerr.call1(&JsValue::null(), &jval!(ret.clone())); + Err(ret) + } else { + ret + } + } + + #[wasm_bindgen] + pub async fn send(&mut self, payload: Uint8Array) -> Result<(), JsError> { + let onerr = self.onerror.clone(); + let ret = self.tx.send(payload.to_vec()).await; + if let Err(ret) = ret { + let _ = onerr.call1(&JsValue::null(), &jval!(format!("{}", ret))); + Err(ret.into()) + } else { + Ok(ret?) + } + } + + #[wasm_bindgen] + pub async fn close(&mut self) -> Result<(), JsError> { + self.tx.close().await?; + Ok(()) + } +} diff --git a/server/src/main.rs b/server/src/main.rs index 3411905..dd215e7 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -108,7 +108,11 @@ async fn handle_mux( .map_err(|x| WispError::Other(Box::new(x)))?; } StreamType::Udp => { - let udp_socket = UdpSocket::bind(uri) + let udp_socket = UdpSocket::bind("0.0.0.0:0") + .await + .map_err(|x| WispError::Other(Box::new(x)))?; + udp_socket + .connect(uri) .await .map_err(|x| WispError::Other(Box::new(x)))?; let mut data = vec![0u8; 65507]; // udp standard max datagram size