From 373d2f4a4d902119ed944844796f4e273f264de1 Mon Sep 17 00:00:00 2001 From: Toshit Chawda Date: Sat, 12 Oct 2024 15:27:29 -0700 Subject: [PATCH] make it fully typed and errors more verbose --- client/src/io_stream.rs | 16 +------ client/src/lib.rs | 82 +++++++++++++++++++++++++---------- client/src/stream_provider.rs | 4 +- client/src/utils.rs | 7 ++- client/src/websocket.rs | 17 +++++--- 5 files changed, 79 insertions(+), 47 deletions(-) diff --git a/client/src/io_stream.rs b/client/src/io_stream.rs index 17a3aca..7ee474c 100644 --- a/client/src/io_stream.rs +++ b/client/src/io_stream.rs @@ -9,23 +9,9 @@ use wasm_streams::{ReadableStream, WritableStream}; use crate::{ stream_provider::{ProviderAsyncRW, ProviderUnencryptedStream}, utils::{convert_body, object_set, ReaderStream}, - EpoxyError, + EpoxyError, EpoxyIoStream, }; -#[wasm_bindgen(typescript_custom_section)] -const IO_STREAM_RET: &'static str = r#" -type EpoxyIoStream = { - read: ReadableStream, - write: WritableStream, -} -"#; - -#[wasm_bindgen] -extern "C" { - #[wasm_bindgen(typescript_type = "EpoxyIoStream")] - pub type EpoxyIoStream; -} - fn create_iostream( stream: Pin>>>, sink: Pin>>, diff --git a/client/src/lib.rs b/client/src/lib.rs index 471bcca..6207065 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -20,15 +20,15 @@ use http::{ use hyper::{body::Incoming, Uri}; use hyper_util_wasm::client::legacy::Client; #[cfg(feature = "full")] -use io_stream::{iostream_from_asyncrw, iostream_from_stream, EpoxyIoStream}; +use io_stream::{iostream_from_asyncrw, iostream_from_stream}; use js_sys::{Array, Function, Object, Promise}; use send_wrapper::SendWrapper; use stream_provider::{StreamProvider, StreamProviderService}; use thiserror::Error; use utils::{ - asyncread_to_readablestream, convert_body, entries_of_object, - from_entries, is_null_body, is_redirect, object_get, object_set, object_truthy, IncomingBody, - UriExt, WasmExecutor, WispTransportRead, WispTransportWrite, + asyncread_to_readablestream, convert_body, entries_of_object, from_entries, is_null_body, + is_redirect, object_get, object_set, object_truthy, IncomingBody, UriExt, WasmExecutor, + WispTransportRead, WispTransportWrite, }; use wasm_bindgen::prelude::*; use wasm_bindgen_futures::JsFuture; @@ -52,6 +52,28 @@ mod utils; mod websocket; mod ws_wrapper; +#[wasm_bindgen(typescript_custom_section)] +const EPOXYCLIENT_TYPES: &'static str = r#" +type EpoxyIoStream = { + read: ReadableStream, + write: WritableStream, +} +type EpoxyWispTransport = string | (() => { read: ReadableStream, write: WritableStream }) +type EpoxyWebSocketInput = string | ArrayBuffer; +type EpoxyWebSocketHeadersInput = Headers | { [key: string]: string } +"#; +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(typescript_type = "EpoxyWispTransport")] + pub type EpoxyWispTransport; + #[wasm_bindgen(typescript_type = "EpoxyIoStream")] + pub type EpoxyIoStream; + #[wasm_bindgen(typescript_type = "EpoxyWebSocketInput")] + pub type EpoxyWebSocketInput; + #[wasm_bindgen(typescript_type = "EpoxyWebSocketHeadersInput")] + pub type EpoxyWebSocketHeadersInput; +} + type HttpBody = http_body_util::Full; #[derive(Debug, Error)] @@ -84,10 +106,10 @@ pub enum EpoxyError { #[error("Custom Wisp transport: {0}")] WispTransport(String), - #[error("Invalid Wisp transport")] - InvalidWispTransport, - #[error("Invalid Wisp transport packet")] - InvalidWispTransportPacket, + #[error("Invalid Wisp transport: {0}")] + InvalidWispTransport(String), + #[error("Invalid Wisp transport packet: {0}")] + InvalidWispTransportPacket(String), #[error("Wisp transport already closed")] WispTransportClosed, @@ -104,11 +126,11 @@ pub enum EpoxyError { #[error("Invalid websocket connection header: {0:?} != \"Upgrade\"")] WsInvalidConnectionHeader(String), #[cfg(feature = "full")] - #[error("Invalid websocket payload, only String/ArrayBuffer accepted")] - WsInvalidPayload, + #[error("Invalid websocket payload: {0}")] + WsInvalidPayload(String), - #[error("Invalid URL scheme")] - InvalidUrlScheme, + #[error("Invalid URL scheme: {0:?}")] + InvalidUrlScheme(Option), #[error("No URL host found")] NoUrlHost, #[error("No URL port found")] @@ -292,11 +314,16 @@ pub struct EpoxyClient { #[wasm_bindgen] impl EpoxyClient { #[wasm_bindgen(constructor)] - pub fn new(wisp_url: JsValue, options: EpoxyClientOptions) -> Result { - let stream_provider = if let Some(wisp_url) = wisp_url.as_string() { + pub fn new( + transport: EpoxyWispTransport, + options: EpoxyClientOptions, + ) -> Result { + let stream_provider = if let Some(wisp_url) = transport.as_string() { let wisp_uri: Uri = wisp_url.clone().try_into()?; if wisp_uri.scheme_str() != Some("wss") && wisp_uri.scheme_str() != Some("ws") { - return Err(EpoxyError::InvalidUrlScheme); + return Err(EpoxyError::InvalidUrlScheme( + wisp_uri.scheme_str().map(ToString::to_string), + )); } let ws_protocols = options.websocket_protocols.clone(); @@ -318,8 +345,8 @@ impl EpoxyClient { }), &options, )?) - } else if let Ok(wisp_transport) = wisp_url.dyn_into::() { - let wisp_transport = SendWrapper::new(wisp_transport); + } else if let Some(wisp_transport) = transport.dyn_ref::() { + let wisp_transport = SendWrapper::new(wisp_transport.clone()); Arc::new(StreamProvider::new( Box::new(move || { let wisp_transport = wisp_transport.clone(); @@ -361,7 +388,10 @@ impl EpoxyClient { &options, )?) } else { - return Err(EpoxyError::InvalidWispTransport); + return Err(EpoxyError::InvalidWispTransport(format!( + "{:?}", + JsValue::from(transport) + ))); }; let service = StreamProviderService(stream_provider.clone()); @@ -394,7 +424,7 @@ impl EpoxyClient { handlers: EpoxyHandlers, url: String, protocols: Vec, - headers: JsValue, + headers: EpoxyWebSocketHeadersInput, ) -> Result { EpoxyWebSocket::connect(self, handlers, url, protocols, headers, &self.user_agent).await } @@ -408,7 +438,10 @@ impl EpoxyClient { .stream_provider .get_asyncread(StreamType::Tcp, host.to_string(), port) .await?; - Ok(iostream_from_asyncrw(Either::Right(stream), self.buffer_size)) + Ok(iostream_from_asyncrw( + Either::Right(stream), + self.buffer_size, + )) } #[cfg(feature = "full")] @@ -420,7 +453,10 @@ impl EpoxyClient { .stream_provider .get_tls_stream(host.to_string(), port) .await?; - Ok(iostream_from_asyncrw(Either::Left(stream), self.buffer_size)) + Ok(iostream_from_asyncrw( + Either::Left(stream), + self.buffer_size, + )) } #[cfg(feature = "full")] @@ -501,7 +537,9 @@ impl EpoxyClient { ) -> Result { let url: Uri = url.try_into()?; // only valid `Scheme`s are HTTP and HTTPS, which are the ones we support - url.scheme().ok_or(EpoxyError::InvalidUrlScheme)?; + url.scheme().ok_or(EpoxyError::InvalidUrlScheme( + url.scheme_str().map(ToString::to_string), + ))?; let host = url.host().ok_or(EpoxyError::NoUrlHost)?; let port_str = url diff --git a/client/src/stream_provider.rs b/client/src/stream_provider.rs index cc231f3..4bc6ec3 100644 --- a/client/src/stream_provider.rs +++ b/client/src/stream_provider.rs @@ -274,7 +274,7 @@ impl ConnectSvc for StreamProviderService { fn connect(self, req: hyper::Uri) -> Self::Future { let provider = self.0.clone(); Box::pin(async move { - let scheme = req.scheme_str().ok_or(EpoxyError::InvalidUrlScheme)?; + let scheme = req.scheme_str().ok_or(EpoxyError::InvalidUrlScheme(None))?; let host = req.host().ok_or(EpoxyError::NoUrlHost)?.to_string(); let port = req.port_u16().map(Ok).unwrap_or_else(|| match scheme { "https" | "wss" => Ok(443), @@ -287,7 +287,7 @@ impl ConnectSvc for StreamProviderService { "http" | "ws" => { Either::Right(provider.get_asyncread(StreamType::Tcp, host, port).await?) } - _ => return Err(EpoxyError::InvalidUrlScheme), + _ => return Err(EpoxyError::InvalidUrlScheme(Some(scheme.to_string()))), }, }) }) diff --git a/client/src/utils.rs b/client/src/utils.rs index 0c47b84..d473cde 100644 --- a/client/src/utils.rs +++ b/client/src/utils.rs @@ -198,8 +198,11 @@ impl WebSocketRead for WispTransportRead { if let Some(pkt) = obj { let pkt = pkt.map_err(|x| WispError::WsImplError(Box::new(EpoxyError::wisp_transport(x))))?; - let arr: ArrayBuffer = pkt.dyn_into().map_err(|_| { - WispError::WsImplError(Box::new(EpoxyError::InvalidWispTransportPacket)) + let arr: ArrayBuffer = pkt.dyn_into().map_err(|x| { + WispError::WsImplError(Box::new(EpoxyError::InvalidWispTransportPacket(format!( + "{:?}", + x + )))) })?; Ok(Frame::binary(Payload::Bytes( diff --git a/client/src/websocket.rs b/client/src/websocket.rs index fd930b4..25e42d0 100644 --- a/client/src/websocket.rs +++ b/client/src/websocket.rs @@ -24,7 +24,8 @@ use wasm_bindgen_futures::spawn_local; use crate::{ tokioio::TokioIo, utils::{entries_of_object, from_entries, ws_key}, - EpoxyClient, EpoxyError, EpoxyHandlers, HttpBody, + EpoxyClient, EpoxyError, EpoxyHandlers, EpoxyWebSocketHeadersInput, EpoxyWebSocketInput, + HttpBody, }; #[wasm_bindgen] @@ -40,9 +41,10 @@ impl EpoxyWebSocket { handlers: EpoxyHandlers, url: String, protocols: Vec, - headers: JsValue, + headers: EpoxyWebSocketHeadersInput, user_agent: &str, ) -> Result { + let headers = JsValue::from(headers); let EpoxyHandlers { onopen, onclose, @@ -151,7 +153,7 @@ impl EpoxyWebSocket { } } - pub async fn send(&self, payload: JsValue) -> Result<(), EpoxyError> { + pub async fn send(&self, payload: EpoxyWebSocketInput) -> Result<(), EpoxyError> { let ret = if let Some(str) = payload.as_string() { self.tx .lock() @@ -159,17 +161,20 @@ impl EpoxyWebSocket { .write_frame(Frame::text(Payload::Owned(str.as_bytes().to_vec()))) .await .map_err(EpoxyError::from) - } else if let Ok(binary) = payload.dyn_into::() { + } else if let Some(binary) = payload.dyn_ref::() { self.tx .lock() .await .write_frame(Frame::binary(Payload::Owned( - Uint8Array::new(&binary).to_vec(), + Uint8Array::new(binary).to_vec(), ))) .await .map_err(EpoxyError::from) } else { - Err(EpoxyError::WsInvalidPayload) + Err(EpoxyError::WsInvalidPayload(format!( + "{:?}", + JsValue::from(payload) + ))) }; match ret {