make it fully typed and errors more verbose

This commit is contained in:
Toshit Chawda 2024-10-12 15:27:29 -07:00
parent e35717bf2c
commit 373d2f4a4d
No known key found for this signature in database
GPG key ID: 91480ED99E2B3D9D
5 changed files with 79 additions and 47 deletions

View file

@ -9,23 +9,9 @@ use wasm_streams::{ReadableStream, WritableStream};
use crate::{ use crate::{
stream_provider::{ProviderAsyncRW, ProviderUnencryptedStream}, stream_provider::{ProviderAsyncRW, ProviderUnencryptedStream},
utils::{convert_body, object_set, ReaderStream}, 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<Uint8Array>,
write: WritableStream<Uint8Array>,
}
"#;
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(typescript_type = "EpoxyIoStream")]
pub type EpoxyIoStream;
}
fn create_iostream( fn create_iostream(
stream: Pin<Box<dyn Stream<Item = Result<Bytes, EpoxyError>>>>, stream: Pin<Box<dyn Stream<Item = Result<Bytes, EpoxyError>>>>,
sink: Pin<Box<dyn Sink<BytesMut, Error = EpoxyError>>>, sink: Pin<Box<dyn Sink<BytesMut, Error = EpoxyError>>>,

View file

@ -20,15 +20,15 @@ use http::{
use hyper::{body::Incoming, Uri}; use hyper::{body::Incoming, Uri};
use hyper_util_wasm::client::legacy::Client; use hyper_util_wasm::client::legacy::Client;
#[cfg(feature = "full")] #[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 js_sys::{Array, Function, Object, Promise};
use send_wrapper::SendWrapper; use send_wrapper::SendWrapper;
use stream_provider::{StreamProvider, StreamProviderService}; use stream_provider::{StreamProvider, StreamProviderService};
use thiserror::Error; use thiserror::Error;
use utils::{ use utils::{
asyncread_to_readablestream, convert_body, entries_of_object, asyncread_to_readablestream, convert_body, entries_of_object, from_entries, is_null_body,
from_entries, is_null_body, is_redirect, object_get, object_set, object_truthy, IncomingBody, is_redirect, object_get, object_set, object_truthy, IncomingBody, UriExt, WasmExecutor,
UriExt, WasmExecutor, WispTransportRead, WispTransportWrite, WispTransportRead, WispTransportWrite,
}; };
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
use wasm_bindgen_futures::JsFuture; use wasm_bindgen_futures::JsFuture;
@ -52,6 +52,28 @@ mod utils;
mod websocket; mod websocket;
mod ws_wrapper; mod ws_wrapper;
#[wasm_bindgen(typescript_custom_section)]
const EPOXYCLIENT_TYPES: &'static str = r#"
type EpoxyIoStream = {
read: ReadableStream<Uint8Array>,
write: WritableStream<Uint8Array>,
}
type EpoxyWispTransport = string | (() => { read: ReadableStream<ArrayBuffer>, write: WritableStream<Uint8Array> })
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<Bytes>; type HttpBody = http_body_util::Full<Bytes>;
#[derive(Debug, Error)] #[derive(Debug, Error)]
@ -84,10 +106,10 @@ pub enum EpoxyError {
#[error("Custom Wisp transport: {0}")] #[error("Custom Wisp transport: {0}")]
WispTransport(String), WispTransport(String),
#[error("Invalid Wisp transport")] #[error("Invalid Wisp transport: {0}")]
InvalidWispTransport, InvalidWispTransport(String),
#[error("Invalid Wisp transport packet")] #[error("Invalid Wisp transport packet: {0}")]
InvalidWispTransportPacket, InvalidWispTransportPacket(String),
#[error("Wisp transport already closed")] #[error("Wisp transport already closed")]
WispTransportClosed, WispTransportClosed,
@ -104,11 +126,11 @@ pub enum EpoxyError {
#[error("Invalid websocket connection header: {0:?} != \"Upgrade\"")] #[error("Invalid websocket connection header: {0:?} != \"Upgrade\"")]
WsInvalidConnectionHeader(String), WsInvalidConnectionHeader(String),
#[cfg(feature = "full")] #[cfg(feature = "full")]
#[error("Invalid websocket payload, only String/ArrayBuffer accepted")] #[error("Invalid websocket payload: {0}")]
WsInvalidPayload, WsInvalidPayload(String),
#[error("Invalid URL scheme")] #[error("Invalid URL scheme: {0:?}")]
InvalidUrlScheme, InvalidUrlScheme(Option<String>),
#[error("No URL host found")] #[error("No URL host found")]
NoUrlHost, NoUrlHost,
#[error("No URL port found")] #[error("No URL port found")]
@ -292,11 +314,16 @@ pub struct EpoxyClient {
#[wasm_bindgen] #[wasm_bindgen]
impl EpoxyClient { impl EpoxyClient {
#[wasm_bindgen(constructor)] #[wasm_bindgen(constructor)]
pub fn new(wisp_url: JsValue, options: EpoxyClientOptions) -> Result<EpoxyClient, EpoxyError> { pub fn new(
let stream_provider = if let Some(wisp_url) = wisp_url.as_string() { transport: EpoxyWispTransport,
options: EpoxyClientOptions,
) -> Result<EpoxyClient, EpoxyError> {
let stream_provider = if let Some(wisp_url) = transport.as_string() {
let wisp_uri: Uri = wisp_url.clone().try_into()?; let wisp_uri: Uri = wisp_url.clone().try_into()?;
if wisp_uri.scheme_str() != Some("wss") && wisp_uri.scheme_str() != Some("ws") { 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(); let ws_protocols = options.websocket_protocols.clone();
@ -318,8 +345,8 @@ impl EpoxyClient {
}), }),
&options, &options,
)?) )?)
} else if let Ok(wisp_transport) = wisp_url.dyn_into::<Function>() { } else if let Some(wisp_transport) = transport.dyn_ref::<Function>() {
let wisp_transport = SendWrapper::new(wisp_transport); let wisp_transport = SendWrapper::new(wisp_transport.clone());
Arc::new(StreamProvider::new( Arc::new(StreamProvider::new(
Box::new(move || { Box::new(move || {
let wisp_transport = wisp_transport.clone(); let wisp_transport = wisp_transport.clone();
@ -361,7 +388,10 @@ impl EpoxyClient {
&options, &options,
)?) )?)
} else { } else {
return Err(EpoxyError::InvalidWispTransport); return Err(EpoxyError::InvalidWispTransport(format!(
"{:?}",
JsValue::from(transport)
)));
}; };
let service = StreamProviderService(stream_provider.clone()); let service = StreamProviderService(stream_provider.clone());
@ -394,7 +424,7 @@ impl EpoxyClient {
handlers: EpoxyHandlers, handlers: EpoxyHandlers,
url: String, url: String,
protocols: Vec<String>, protocols: Vec<String>,
headers: JsValue, headers: EpoxyWebSocketHeadersInput,
) -> Result<EpoxyWebSocket, EpoxyError> { ) -> Result<EpoxyWebSocket, EpoxyError> {
EpoxyWebSocket::connect(self, handlers, url, protocols, headers, &self.user_agent).await EpoxyWebSocket::connect(self, handlers, url, protocols, headers, &self.user_agent).await
} }
@ -408,7 +438,10 @@ impl EpoxyClient {
.stream_provider .stream_provider
.get_asyncread(StreamType::Tcp, host.to_string(), port) .get_asyncread(StreamType::Tcp, host.to_string(), port)
.await?; .await?;
Ok(iostream_from_asyncrw(Either::Right(stream), self.buffer_size)) Ok(iostream_from_asyncrw(
Either::Right(stream),
self.buffer_size,
))
} }
#[cfg(feature = "full")] #[cfg(feature = "full")]
@ -420,7 +453,10 @@ impl EpoxyClient {
.stream_provider .stream_provider
.get_tls_stream(host.to_string(), port) .get_tls_stream(host.to_string(), port)
.await?; .await?;
Ok(iostream_from_asyncrw(Either::Left(stream), self.buffer_size)) Ok(iostream_from_asyncrw(
Either::Left(stream),
self.buffer_size,
))
} }
#[cfg(feature = "full")] #[cfg(feature = "full")]
@ -501,7 +537,9 @@ impl EpoxyClient {
) -> Result<web_sys::Response, EpoxyError> { ) -> Result<web_sys::Response, EpoxyError> {
let url: Uri = url.try_into()?; let url: Uri = url.try_into()?;
// only valid `Scheme`s are HTTP and HTTPS, which are the ones we support // 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 host = url.host().ok_or(EpoxyError::NoUrlHost)?;
let port_str = url let port_str = url

View file

@ -274,7 +274,7 @@ impl ConnectSvc for StreamProviderService {
fn connect(self, req: hyper::Uri) -> Self::Future { fn connect(self, req: hyper::Uri) -> Self::Future {
let provider = self.0.clone(); let provider = self.0.clone();
Box::pin(async move { 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 host = req.host().ok_or(EpoxyError::NoUrlHost)?.to_string();
let port = req.port_u16().map(Ok).unwrap_or_else(|| match scheme { let port = req.port_u16().map(Ok).unwrap_or_else(|| match scheme {
"https" | "wss" => Ok(443), "https" | "wss" => Ok(443),
@ -287,7 +287,7 @@ impl ConnectSvc for StreamProviderService {
"http" | "ws" => { "http" | "ws" => {
Either::Right(provider.get_asyncread(StreamType::Tcp, host, port).await?) Either::Right(provider.get_asyncread(StreamType::Tcp, host, port).await?)
} }
_ => return Err(EpoxyError::InvalidUrlScheme), _ => return Err(EpoxyError::InvalidUrlScheme(Some(scheme.to_string()))),
}, },
}) })
}) })

View file

@ -198,8 +198,11 @@ impl WebSocketRead for WispTransportRead {
if let Some(pkt) = obj { if let Some(pkt) = obj {
let pkt = let pkt =
pkt.map_err(|x| WispError::WsImplError(Box::new(EpoxyError::wisp_transport(x))))?; pkt.map_err(|x| WispError::WsImplError(Box::new(EpoxyError::wisp_transport(x))))?;
let arr: ArrayBuffer = pkt.dyn_into().map_err(|_| { let arr: ArrayBuffer = pkt.dyn_into().map_err(|x| {
WispError::WsImplError(Box::new(EpoxyError::InvalidWispTransportPacket)) WispError::WsImplError(Box::new(EpoxyError::InvalidWispTransportPacket(format!(
"{:?}",
x
))))
})?; })?;
Ok(Frame::binary(Payload::Bytes( Ok(Frame::binary(Payload::Bytes(

View file

@ -24,7 +24,8 @@ use wasm_bindgen_futures::spawn_local;
use crate::{ use crate::{
tokioio::TokioIo, tokioio::TokioIo,
utils::{entries_of_object, from_entries, ws_key}, utils::{entries_of_object, from_entries, ws_key},
EpoxyClient, EpoxyError, EpoxyHandlers, HttpBody, EpoxyClient, EpoxyError, EpoxyHandlers, EpoxyWebSocketHeadersInput, EpoxyWebSocketInput,
HttpBody,
}; };
#[wasm_bindgen] #[wasm_bindgen]
@ -40,9 +41,10 @@ impl EpoxyWebSocket {
handlers: EpoxyHandlers, handlers: EpoxyHandlers,
url: String, url: String,
protocols: Vec<String>, protocols: Vec<String>,
headers: JsValue, headers: EpoxyWebSocketHeadersInput,
user_agent: &str, user_agent: &str,
) -> Result<Self, EpoxyError> { ) -> Result<Self, EpoxyError> {
let headers = JsValue::from(headers);
let EpoxyHandlers { let EpoxyHandlers {
onopen, onopen,
onclose, 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() { let ret = if let Some(str) = payload.as_string() {
self.tx self.tx
.lock() .lock()
@ -159,17 +161,20 @@ impl EpoxyWebSocket {
.write_frame(Frame::text(Payload::Owned(str.as_bytes().to_vec()))) .write_frame(Frame::text(Payload::Owned(str.as_bytes().to_vec())))
.await .await
.map_err(EpoxyError::from) .map_err(EpoxyError::from)
} else if let Ok(binary) = payload.dyn_into::<ArrayBuffer>() { } else if let Some(binary) = payload.dyn_ref::<ArrayBuffer>() {
self.tx self.tx
.lock() .lock()
.await .await
.write_frame(Frame::binary(Payload::Owned( .write_frame(Frame::binary(Payload::Owned(
Uint8Array::new(&binary).to_vec(), Uint8Array::new(binary).to_vec(),
))) )))
.await .await
.map_err(EpoxyError::from) .map_err(EpoxyError::from)
} else { } else {
Err(EpoxyError::WsInvalidPayload) Err(EpoxyError::WsInvalidPayload(format!(
"{:?}",
JsValue::from(payload)
)))
}; };
match ret { match ret {