mirror of
https://github.com/MercuryWorkshop/epoxy-tls.git
synced 2025-05-12 22:10:01 -04:00
various small improvements
This commit is contained in:
parent
8b2a8a3eb3
commit
5be02151e6
11 changed files with 146 additions and 105 deletions
|
@ -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.
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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<typeof wasm_bindgen>;" >> "epoxy-module-bundled.d.ts"
|
||||
echo "} export default function epoxy(maybe_memory?: WebAssembly.Memory): Promise<typeof wasm_bindgen>;" >> "epoxy-module-bundled.d.ts"
|
||||
|
||||
cp out/epoxy_client.js epoxy.js
|
||||
cp out/epoxy_client.d.ts epoxy.d.ts
|
||||
|
|
|
@ -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");
|
||||
};
|
||||
|
|
|
@ -3,8 +3,9 @@
|
|||
<head>
|
||||
<title>epoxy</title>
|
||||
<style>
|
||||
body { font-family: sans-serif }
|
||||
#logs > * { font-family: monospace }
|
||||
body { font-family: monospace; font-weight: bold; width: 100%; height: 100%; padding: 0; margin: 0 }
|
||||
body > div { padding: 1em; }
|
||||
#logs > * { margin: 0; font-weight: normal; }
|
||||
</style>
|
||||
<script>
|
||||
const params = (new URL(window.location.href)).searchParams;
|
||||
|
@ -17,8 +18,8 @@
|
|||
const should_tls_test = params.has("rawtls_test");
|
||||
const worker = new Worker("demo.js");
|
||||
worker.onmessage = (msg) => {
|
||||
let el = document.createElement("div");
|
||||
el.textContent = msg.data;
|
||||
let el = document.createElement("pre");
|
||||
el.innerHTML = msg.data;
|
||||
document.getElementById("logs").appendChild(el);
|
||||
window.scrollTo(0, document.body.scrollHeight);
|
||||
};
|
||||
|
@ -28,9 +29,10 @@
|
|||
|
||||
<body>
|
||||
<div>
|
||||
running... (wait for the browser alert if not running ws test)
|
||||
<div>running... (note: WASM is significantly slower when DevTools is open)</div>
|
||||
<div>logs:</div>
|
||||
<div id="logs"></div>
|
||||
</div>
|
||||
<div id="logs"></div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
|
@ -6,7 +6,7 @@ mod websocket;
|
|||
mod wrappers;
|
||||
|
||||
use tls_stream::EpxTlsStream;
|
||||
use utils::{ReplaceErr, UriExt};
|
||||
use utils::{Boolinator, ReplaceErr, UriExt};
|
||||
use websocket::EpxWebSocket;
|
||||
use wrappers::{IncomingBody, TlsWispService};
|
||||
|
||||
|
@ -25,7 +25,7 @@ use tokio_util::{
|
|||
either::Either,
|
||||
io::{ReaderStream, StreamReader},
|
||||
};
|
||||
use wasm_bindgen::prelude::*;
|
||||
use wasm_bindgen::{intern, prelude::*};
|
||||
use web_sys::TextEncoder;
|
||||
use wisp_mux::{tokioio::TokioIo, tower::ServiceWrapper, ClientMux, MuxStreamIo, StreamType};
|
||||
use ws_stream_wasm::{WsMessage, WsMeta, WsStream};
|
||||
|
@ -52,13 +52,16 @@ fn init() {
|
|||
console_error_panic_hook::set_once();
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
|
||||
#[wasm_bindgen(inspectable)]
|
||||
pub struct EpoxyClient {
|
||||
rustls_config: Arc<rustls::ClientConfig>,
|
||||
mux: Arc<ClientMux<SplitSink<WsStream, WsMessage>>>,
|
||||
hyper_client: Client<TlsWispService<SplitSink<WsStream, WsMessage>>, HttpBody>,
|
||||
useragent: String,
|
||||
redirect_limit: usize,
|
||||
#[wasm_bindgen(getter_with_clone)]
|
||||
pub useragent: String,
|
||||
#[wasm_bindgen(js_name = "redirectLimit")]
|
||||
pub redirect_limit: usize,
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
|
@ -81,10 +84,11 @@ impl EpoxyClient {
|
|||
}
|
||||
|
||||
debug!("connecting to ws {:?}", ws_url);
|
||||
let (_, ws) = WsMeta::connect(ws_url, vec!["wisp-v1"])
|
||||
let (_, ws) = WsMeta::connect(ws_url, vec![])
|
||||
.await
|
||||
.replace_err("Failed to connect to websocket")?;
|
||||
debug!("connected!");
|
||||
|
||||
let (wtx, wrx) = ws.split();
|
||||
let (mux, fut) = ClientMux::new(wrx, wtx).await?;
|
||||
let mux = Arc::new(mux);
|
||||
|
@ -128,19 +132,17 @@ impl EpoxyClient {
|
|||
.replace_err("Failed to create multiplexor channel")?
|
||||
.into_io()
|
||||
.into_asyncrw();
|
||||
let cloned_uri = url_host.to_string().clone();
|
||||
let connector = TlsConnector::from(self.rustls_config.clone());
|
||||
debug!("connecting channel");
|
||||
let io = connector
|
||||
.connect(
|
||||
cloned_uri
|
||||
url_host
|
||||
.to_string()
|
||||
.try_into()
|
||||
.replace_err("Failed to parse URL (rustls)")?,
|
||||
channel,
|
||||
)
|
||||
.await
|
||||
.replace_err("Failed to perform TLS handshake")?;
|
||||
debug!("connected channel");
|
||||
Ok(io)
|
||||
}
|
||||
|
||||
|
@ -155,24 +157,18 @@ impl EpoxyClient {
|
|||
None
|
||||
};
|
||||
|
||||
debug!("sending req");
|
||||
let res = self
|
||||
.hyper_client
|
||||
.request(req)
|
||||
.await
|
||||
.replace_err("Failed to send request");
|
||||
debug!("recieved res");
|
||||
match res {
|
||||
Ok(res) => {
|
||||
if utils::is_redirect(res.status().as_u16())
|
||||
&& let Some(mut new_req) = new_req
|
||||
&& let Some(location) = res.headers().get("Location")
|
||||
&& let Ok(redirect_url) = new_req.uri().get_redirect(location)
|
||||
&& let Some(redirect_url_authority) = redirect_url
|
||||
.clone()
|
||||
.authority()
|
||||
.replace_err("Redirect URL must have an authority")
|
||||
.ok()
|
||||
&& let Some(redirect_url_authority) = redirect_url.clone().authority()
|
||||
{
|
||||
*new_req.uri_mut() = redirect_url;
|
||||
new_req.headers_mut().insert(
|
||||
|
@ -246,19 +242,18 @@ impl EpoxyClient {
|
|||
let uri = url.parse::<uri::Uri>().replace_err("Failed to parse URL")?;
|
||||
let uri_scheme = uri.scheme().replace_err("URL must have a scheme")?;
|
||||
if *uri_scheme != uri::Scheme::HTTP && *uri_scheme != uri::Scheme::HTTPS {
|
||||
return Err(jerr!("Scheme must be either `http` or `https`"));
|
||||
return Err(jerri!("Scheme must be either `http` or `https`"));
|
||||
}
|
||||
let uri_host = uri.host().replace_err("URL must have a host")?;
|
||||
|
||||
let req_method_string: String = match Reflect::get(&options, &jval!("method")) {
|
||||
let req_method_string: String = match Reflect::get(&options, &jvali!("method")) {
|
||||
Ok(val) => val.as_string().unwrap_or("GET".to_string()),
|
||||
Err(_) => "GET".to_string(),
|
||||
};
|
||||
let req_method: http::Method =
|
||||
http::Method::try_from(<String as AsRef<str>>::as_ref(&req_method_string))
|
||||
.replace_err("Invalid http method")?;
|
||||
let req_method: http::Method = http::Method::try_from(req_method_string.as_str())
|
||||
.replace_err("Invalid http method")?;
|
||||
|
||||
let req_should_redirect = match Reflect::get(&options, &jval!("redirect")) {
|
||||
let req_should_redirect = match Reflect::get(&options, &jvali!("redirect")) {
|
||||
Ok(val) => !matches!(
|
||||
val.as_string().unwrap_or_default().as_str(),
|
||||
"error" | "manual"
|
||||
|
@ -266,7 +261,7 @@ impl EpoxyClient {
|
|||
Err(_) => true,
|
||||
};
|
||||
|
||||
let body_jsvalue: Option<JsValue> = Reflect::get(&options, &jval!("body")).ok();
|
||||
let body_jsvalue: Option<JsValue> = Reflect::get(&options, &jvali!("body")).ok();
|
||||
let body = if let Some(val) = body_jsvalue {
|
||||
if val.is_string() {
|
||||
let str = val
|
||||
|
@ -288,7 +283,7 @@ impl EpoxyClient {
|
|||
None => Bytes::new(),
|
||||
};
|
||||
|
||||
let headers: Option<Vec<Vec<String>>> = Reflect::get(&options, &jval!("headers"))
|
||||
let headers = Reflect::get(&options, &jvali!("headers"))
|
||||
.map(|val| {
|
||||
if val.is_truthy() {
|
||||
Some(utils::entries_of_object(&Object::from(val)))
|
||||
|
@ -301,12 +296,12 @@ impl EpoxyClient {
|
|||
let mut builder = Request::builder().uri(uri.clone()).method(req_method);
|
||||
|
||||
let headers_map = builder.headers_mut().replace_err("Failed to get headers")?;
|
||||
headers_map.insert("Accept-Encoding", HeaderValue::from_str("gzip, br")?);
|
||||
headers_map.insert("Connection", HeaderValue::from_str("keep-alive")?);
|
||||
headers_map.insert("Accept-Encoding", HeaderValue::from_static("gzip, br"));
|
||||
headers_map.insert("Connection", HeaderValue::from_static("keep-alive"));
|
||||
headers_map.insert("User-Agent", HeaderValue::from_str(&self.useragent)?);
|
||||
headers_map.insert("Host", HeaderValue::from_str(uri_host)?);
|
||||
if body_bytes.is_empty() {
|
||||
headers_map.insert("Content-Length", HeaderValue::from_str("0")?);
|
||||
headers_map.insert("Content-Length", HeaderValue::from_static("0"));
|
||||
}
|
||||
|
||||
if let Some(headers) = headers {
|
||||
|
@ -314,7 +309,7 @@ impl EpoxyClient {
|
|||
headers_map.insert(
|
||||
HeaderName::from_bytes(hdr[0].as_bytes())
|
||||
.replace_err("Failed to get hdr name")?,
|
||||
HeaderValue::from_str(hdr[1].clone().as_ref())
|
||||
HeaderValue::from_bytes(hdr[1].as_bytes())
|
||||
.replace_err("Failed to get hdr value")?,
|
||||
);
|
||||
}
|
||||
|
@ -387,45 +382,41 @@ impl EpoxyClient {
|
|||
|
||||
Object::define_property(
|
||||
&resp,
|
||||
&jval!("url"),
|
||||
&jvali!("url"),
|
||||
&utils::define_property_obj(jval!(resp_uri.to_string()), false)
|
||||
.replace_err("Failed to make define_property object for url")?,
|
||||
);
|
||||
|
||||
Object::define_property(
|
||||
&resp,
|
||||
&jval!("redirected"),
|
||||
&jvali!("redirected"),
|
||||
&utils::define_property_obj(jval!(req_redirected), false)
|
||||
.replace_err("Failed to make define_property object for redirected")?,
|
||||
);
|
||||
|
||||
let raw_headers = Object::new();
|
||||
for (k, v) in resp_headers_raw.iter() {
|
||||
if let Ok(jv) = Reflect::get(&raw_headers, &jval!(k.to_string())) {
|
||||
let k = jval!(k.to_string());
|
||||
let v = jval!(v.to_str()?.to_string());
|
||||
if let Ok(jv) = Reflect::get(&raw_headers, &k) {
|
||||
if jv.is_array() {
|
||||
let arr = Array::from(&jv);
|
||||
|
||||
arr.push(&jval!(v.to_str()?.to_string()));
|
||||
let _ = Reflect::set(&raw_headers, &jval!(k.to_string()), &arr);
|
||||
arr.push(&v);
|
||||
Reflect::set(&raw_headers, &k, &arr).flatten("Failed to set rawHeader")?;
|
||||
} else if jv.is_truthy() {
|
||||
let _ = Reflect::set(
|
||||
&raw_headers,
|
||||
&jval!(k.to_string()),
|
||||
&Array::of2(&jv, &jval!(v.to_str()?.to_string())),
|
||||
);
|
||||
Reflect::set(&raw_headers, &k, &Array::of2(&jv, &v))
|
||||
.flatten("Failed to set rawHeader")?;
|
||||
} else {
|
||||
let _ = Reflect::set(
|
||||
&raw_headers,
|
||||
&jval!(k.to_string()),
|
||||
&jval!(v.to_str()?.to_string()),
|
||||
);
|
||||
Reflect::set(&raw_headers, &k, &v).flatten("Failed to set rawHeader")?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Object::define_property(
|
||||
&resp,
|
||||
&jval!("rawHeaders"),
|
||||
&utils::define_property_obj(jval!(&raw_headers), false).replace_err("wjat!!")?,
|
||||
&jvali!("rawHeaders"),
|
||||
&utils::define_property_obj(jval!(&raw_headers), false)
|
||||
.replace_err("Failed to make define_property object for rawHeaders")?,
|
||||
);
|
||||
|
||||
Ok(resp)
|
||||
|
|
|
@ -4,10 +4,12 @@ use js_sys::Function;
|
|||
use tokio::io::{split, AsyncWriteExt, WriteHalf};
|
||||
use tokio_util::io::ReaderStream;
|
||||
|
||||
#[wasm_bindgen]
|
||||
#[wasm_bindgen(inspectable)]
|
||||
pub struct EpxTlsStream {
|
||||
tx: WriteHalf<EpxIoTlsStream>,
|
||||
onerror: Function,
|
||||
#[wasm_bindgen(readonly, getter_with_clone)]
|
||||
pub url: String,
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
|
@ -51,7 +53,7 @@ impl EpxTlsStream {
|
|||
.call0(&Object::default())
|
||||
.replace_err("Failed to call onopen")?;
|
||||
|
||||
Ok(Self { tx, onerror })
|
||||
Ok(Self { tx, onerror, url: url.to_string() })
|
||||
}
|
||||
.await;
|
||||
if let Err(ret) = ret {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use wasm_bindgen::prelude::*;
|
||||
use wasm_bindgen::{intern, prelude::*};
|
||||
|
||||
use hyper::rt::Executor;
|
||||
use hyper::{header::HeaderValue, Uri};
|
||||
|
@ -15,7 +15,6 @@ extern "C" {
|
|||
pub fn console_error(s: &str);
|
||||
}
|
||||
|
||||
#[allow(unused_macros)]
|
||||
macro_rules! debug {
|
||||
($($t:tt)*) => (utils::console_debug(&format_args!($($t)*).to_string()))
|
||||
}
|
||||
|
@ -25,25 +24,34 @@ macro_rules! log {
|
|||
($($t:tt)*) => (utils::console_log(&format_args!($($t)*).to_string()))
|
||||
}
|
||||
|
||||
#[allow(unused_macros)]
|
||||
macro_rules! error {
|
||||
($($t:tt)*) => (utils::console_error(&format_args!($($t)*).to_string()))
|
||||
}
|
||||
|
||||
#[allow(unused_macros)]
|
||||
macro_rules! jerr {
|
||||
($expr:expr) => {
|
||||
JsError::new($expr)
|
||||
};
|
||||
}
|
||||
|
||||
#[allow(unused_macros)]
|
||||
macro_rules! jval {
|
||||
($expr:expr) => {
|
||||
JsValue::from($expr)
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! jerri {
|
||||
($expr:expr) => {
|
||||
JsError::new(intern($expr))
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! jvali {
|
||||
($expr:expr) => {
|
||||
JsValue::from(intern($expr))
|
||||
};
|
||||
}
|
||||
|
||||
pub trait ReplaceErr {
|
||||
type Ok;
|
||||
|
||||
|
@ -55,11 +63,11 @@ impl<T, E: std::fmt::Debug> ReplaceErr for Result<T, E> {
|
|||
type Ok = T;
|
||||
|
||||
fn replace_err(self, err: &str) -> Result<<Self as ReplaceErr>::Ok, JsError> {
|
||||
self.map_err(|oe| jerr!(&format!("{}, original error: {:?}", err, oe)))
|
||||
self.map_err(|_| jerri!(err))
|
||||
}
|
||||
|
||||
fn replace_err_jv(self, err: &str) -> Result<<Self as ReplaceErr>::Ok, JsValue> {
|
||||
self.map_err(|oe| jval!(&format!("{}, original error: {:?}", err, oe)))
|
||||
self.map_err(|_| jvali!(err))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -67,17 +75,52 @@ impl<T> ReplaceErr for Option<T> {
|
|||
type Ok = T;
|
||||
|
||||
fn replace_err(self, err: &str) -> Result<<Self as ReplaceErr>::Ok, JsError> {
|
||||
self.ok_or_else(|| jerr!(err))
|
||||
self.ok_or_else(|| jerri!(err))
|
||||
}
|
||||
|
||||
fn replace_err_jv(self, err: &str) -> Result<<Self as ReplaceErr>::Ok, JsValue> {
|
||||
self.ok_or_else(|| jval!(err))
|
||||
self.ok_or_else(|| jvali!(err))
|
||||
}
|
||||
}
|
||||
|
||||
// the... BOOLINATOR!
|
||||
impl ReplaceErr for bool {
|
||||
type Ok = ();
|
||||
|
||||
fn replace_err(self, err: &str) -> Result<(), JsError> {
|
||||
if !self {
|
||||
Err(jerri!(err))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn replace_err_jv(self, err: &str) -> Result<(), JsValue> {
|
||||
if !self {
|
||||
Err(jvali!(err))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// the... BOOLINATOR!
|
||||
pub trait Boolinator {
|
||||
fn flatten(self, err: &str) -> Result<(), JsError>;
|
||||
}
|
||||
|
||||
impl Boolinator for Result<bool, JsValue> {
|
||||
fn flatten(self, err: &str) -> Result<(), JsError> {
|
||||
if !self.replace_err(err)? {
|
||||
Err(jerri!(err))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait UriExt {
|
||||
fn get_redirect(&self, location: &HeaderValue) -> Result<Uri, JsError>;
|
||||
fn is_same_host(&self, other: &Uri) -> bool;
|
||||
}
|
||||
|
||||
impl UriExt for Uri {
|
||||
|
@ -93,9 +136,6 @@ impl UriExt for Uri {
|
|||
|
||||
Ok(Uri::from_parts(new_parts)?)
|
||||
}
|
||||
fn is_same_host(&self, other: &Uri) -> bool {
|
||||
self.host() == other.host() && self.port() == other.port()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -129,8 +169,8 @@ pub fn entries_of_object(obj: &Object) -> Vec<Vec<String>> {
|
|||
|
||||
pub fn define_property_obj(value: JsValue, writable: bool) -> Result<Object, JsValue> {
|
||||
let entries: Array = [
|
||||
Array::of2(&jval!("value"), &jval!(value)),
|
||||
Array::of2(&jval!("writable"), &jval!(writable)),
|
||||
Array::of2(&jval!(intern("value")), &value),
|
||||
Array::of2(&jval!(intern("writable")), &jval!(writable)),
|
||||
]
|
||||
.iter()
|
||||
.collect::<Array>();
|
||||
|
|
|
@ -15,10 +15,16 @@ use js_sys::Function;
|
|||
use std::str::from_utf8;
|
||||
use tokio::io::WriteHalf;
|
||||
|
||||
#[wasm_bindgen]
|
||||
#[wasm_bindgen(inspectable)]
|
||||
pub struct EpxWebSocket {
|
||||
tx: Arc<Mutex<WebSocketWrite<WriteHalf<TokioIo<Upgraded>>>>>,
|
||||
onerror: Function,
|
||||
#[wasm_bindgen(readonly, getter_with_clone)]
|
||||
pub url: String,
|
||||
#[wasm_bindgen(readonly, getter_with_clone)]
|
||||
pub protocols: Vec<String>,
|
||||
#[wasm_bindgen(readonly, getter_with_clone)]
|
||||
pub origin: String,
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
|
@ -53,7 +59,7 @@ impl EpxWebSocket {
|
|||
.method("GET")
|
||||
.uri(url.clone())
|
||||
.header("Host", host)
|
||||
.header("Origin", origin)
|
||||
.header("Origin", origin.clone())
|
||||
.header(UPGRADE, "websocket")
|
||||
.header(CONNECTION, "upgrade")
|
||||
.header("Sec-WebSocket-Key", key)
|
||||
|
@ -109,7 +115,7 @@ impl EpxWebSocket {
|
|||
.call0(&Object::default())
|
||||
.replace_err("Failed to call onopen")?;
|
||||
|
||||
Ok(Self { tx, onerror })
|
||||
Ok(Self { tx, onerror, origin, protocols, url: url.to_string() })
|
||||
}
|
||||
.await;
|
||||
if let Err(ret) = ret {
|
||||
|
|
|
@ -9,8 +9,7 @@ use fastwebsockets::{
|
|||
};
|
||||
use futures_util::{SinkExt, StreamExt, TryFutureExt};
|
||||
use hyper::{
|
||||
body::Incoming, header::HeaderValue, server::conn::http1, service::service_fn, Request,
|
||||
Response, StatusCode,
|
||||
body::Incoming, server::conn::http1, service::service_fn, Request, Response, StatusCode,
|
||||
};
|
||||
use hyper_util::rt::TokioIo;
|
||||
use tokio::net::{TcpListener, TcpStream, UdpSocket};
|
||||
|
@ -88,24 +87,10 @@ async fn accept_http(
|
|||
if upgrade::is_upgrade_request(&req)
|
||||
&& let Some(uri) = uri.strip_prefix(&prefix)
|
||||
{
|
||||
let (mut res, fut) = upgrade::upgrade(&mut req)?;
|
||||
let (res, fut) = upgrade::upgrade(&mut req)?;
|
||||
|
||||
if let Some(protocols) = req.headers().get("Sec-Websocket-Protocol").and_then(|x| {
|
||||
Some(
|
||||
x.to_str()
|
||||
.ok()?
|
||||
.split(',')
|
||||
.map(|x| x.trim())
|
||||
.collect::<Vec<&str>>(),
|
||||
)
|
||||
}) && protocols.contains(&"wisp-v1")
|
||||
&& (uri.is_empty() || uri == "/")
|
||||
{
|
||||
if uri.is_empty() || uri == "/" {
|
||||
tokio::spawn(async move { accept_ws(fut, addr.clone()).await });
|
||||
res.headers_mut().insert(
|
||||
"Sec-Websocket-Protocol",
|
||||
HeaderValue::from_str("wisp-v1").unwrap(),
|
||||
);
|
||||
} else {
|
||||
let uri = uri.strip_prefix('/').unwrap_or(uri).to_string();
|
||||
tokio::spawn(async move { accept_wsproxy(fut, uri, addr.clone()).await });
|
||||
|
@ -119,7 +104,7 @@ async fn accept_http(
|
|||
println!("random request to path {:?}", uri);
|
||||
Ok(Response::builder()
|
||||
.status(StatusCode::OK)
|
||||
.body(HttpBody::new(":3".to_string().into()))
|
||||
.body(HttpBody::new(":3".into()))
|
||||
.unwrap())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -74,7 +74,7 @@ async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
|
|||
};
|
||||
let req = Request::builder()
|
||||
.method("GET")
|
||||
.uri(format!("wss://{}:{}/", &addr, addr_port))
|
||||
.uri("/")
|
||||
.header("Host", &addr)
|
||||
.header(UPGRADE, "websocket")
|
||||
.header(CONNECTION, "upgrade")
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue