mirror of
https://github.com/MercuryWorkshop/epoxy-tls.git
synced 2025-05-13 14:30:02 -04:00
make it fully typed and errors more verbose
This commit is contained in:
parent
e35717bf2c
commit
373d2f4a4d
5 changed files with 79 additions and 47 deletions
|
@ -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>>>,
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()))),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue