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::{
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<Uint8Array>,
write: WritableStream<Uint8Array>,
}
"#;
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(typescript_type = "EpoxyIoStream")]
pub type EpoxyIoStream;
}
fn create_iostream(
stream: Pin<Box<dyn Stream<Item = Result<Bytes, EpoxyError>>>>,
sink: Pin<Box<dyn Sink<BytesMut, Error = EpoxyError>>>,

View file

@ -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<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>;
#[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<String>),
#[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<EpoxyClient, EpoxyError> {
let stream_provider = if let Some(wisp_url) = wisp_url.as_string() {
pub fn new(
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()?;
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::<Function>() {
let wisp_transport = SendWrapper::new(wisp_transport);
} else if let Some(wisp_transport) = transport.dyn_ref::<Function>() {
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<String>,
headers: JsValue,
headers: EpoxyWebSocketHeadersInput,
) -> Result<EpoxyWebSocket, EpoxyError> {
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<web_sys::Response, EpoxyError> {
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

View file

@ -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()))),
},
})
})

View file

@ -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(

View file

@ -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<String>,
headers: JsValue,
headers: EpoxyWebSocketHeadersInput,
user_agent: &str,
) -> Result<Self, EpoxyError> {
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::<ArrayBuffer>() {
} else if let Some(binary) = payload.dyn_ref::<ArrayBuffer>() {
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 {