mirror of
https://github.com/MercuryWorkshop/epoxy-tls.git
synced 2025-05-12 14:00: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();
|
const { EpoxyClient } = await epoxy();
|
||||||
let client = await new EpoxyClient("wss://localhost:4000", navigator.userAgent, 10);
|
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");
|
let response = await client.fetch("https://httpbin.org/get");
|
||||||
await response.text();
|
console.log(await response.text());
|
||||||
```
|
```
|
||||||
See `client/demo.js` for more examples.
|
See `client/demo.js` for more examples.
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ http = "1.0.0"
|
||||||
http-body-util = "0.1.0"
|
http-body-util = "0.1.0"
|
||||||
hyper = { version = "1.1.0", features = ["client", "http1", "http2"] }
|
hyper = { version = "1.1.0", features = ["client", "http1", "http2"] }
|
||||||
pin-project-lite = "0.2.13"
|
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"
|
wasm-bindgen-futures = "0.4.39"
|
||||||
ws_stream_wasm = { version = "0.7.4", features = ["tokio_io"] }
|
ws_stream_wasm = { version = "0.7.4", features = ["tokio_io"] }
|
||||||
futures-util = "0.3.30"
|
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=$(<"out/epoxy_client.d.ts")
|
||||||
AUTOGENERATED_TYPEDEFS=${AUTOGENERATED_TYPEDEFS%%export class IntoUnderlyingByteSource*}
|
AUTOGENERATED_TYPEDEFS=${AUTOGENERATED_TYPEDEFS%%export class IntoUnderlyingByteSource*}
|
||||||
echo "$AUTOGENERATED_TYPEDEFS" >"epoxy-module-bundled.d.ts"
|
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.js epoxy.js
|
||||||
cp out/epoxy_client.d.ts epoxy.d.ts
|
cp out/epoxy_client.d.ts epoxy.d.ts
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
importScripts("epoxy-bundled.js");
|
importScripts("epoxy-bundled.js");
|
||||||
onmessage = async (msg) => {
|
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;
|
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(
|
console.log(
|
||||||
"%cWASM is significantly slower with DevTools open!",
|
"%cWASM is significantly slower with DevTools open!",
|
||||||
|
@ -8,10 +8,15 @@ onmessage = async (msg) => {
|
||||||
);
|
);
|
||||||
|
|
||||||
const log = (str) => {
|
const log = (str) => {
|
||||||
console.warn(str);
|
console.log(str);
|
||||||
postMessage(str);
|
postMessage(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const plog = (str) => {
|
||||||
|
console.log(str);
|
||||||
|
postMessage(JSON.stringify(str, null, 4));
|
||||||
|
}
|
||||||
|
|
||||||
const { EpoxyClient } = await epoxy();
|
const { EpoxyClient } = await epoxy();
|
||||||
|
|
||||||
const tconn0 = performance.now();
|
const tconn0 = performance.now();
|
||||||
|
@ -20,6 +25,11 @@ onmessage = async (msg) => {
|
||||||
const tconn1 = performance.now();
|
const tconn1 = performance.now();
|
||||||
log(`conn establish took ${tconn1 - tconn0} ms or ${(tconn1 - tconn0) / 1000} s`);
|
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 test_mux = async (url) => {
|
||||||
const t0 = performance.now();
|
const t0 = performance.now();
|
||||||
await epoxy_client.fetch(url);
|
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`);
|
log(`avg mux - avg native (${num_tests}): ${total_mux - total_native} ms or ${(total_mux - total_native) / 1000} s`);
|
||||||
} else if (should_ws_test) {
|
} else if (should_ws_test) {
|
||||||
let ws = await epoxy_client.connect_ws(
|
let ws = await epoxy_client.connect_ws(
|
||||||
() => console.log("opened"),
|
() => log("opened"),
|
||||||
() => console.log("closed"),
|
() => log("closed"),
|
||||||
err => console.error(err),
|
err => console.error(err),
|
||||||
msg => console.log(msg),
|
msg => log(msg),
|
||||||
"wss://echo.websocket.events",
|
"wss://echo.websocket.events",
|
||||||
[],
|
[],
|
||||||
"localhost"
|
"localhost"
|
||||||
);
|
);
|
||||||
while (true) {
|
while (true) {
|
||||||
|
log("sending `data`");
|
||||||
await ws.send("data");
|
await ws.send("data");
|
||||||
await (new Promise((res, _) => setTimeout(res, 100)));
|
await (new Promise((res, _) => setTimeout(res, 50)));
|
||||||
}
|
}
|
||||||
} else if (should_tls_test) {
|
} else if (should_tls_test) {
|
||||||
let decoder = new TextDecoder();
|
let decoder = new TextDecoder();
|
||||||
let ws = await epoxy_client.connect_tls(
|
let ws = await epoxy_client.connect_tls(
|
||||||
() => console.log("opened"),
|
() => log("opened"),
|
||||||
() => console.log("closed"),
|
() => log("closed"),
|
||||||
err => console.error(err),
|
err => console.error(err),
|
||||||
msg => { console.log(msg); console.log(decoder.decode(msg)) },
|
msg => { console.log(msg); console.log(decoder.decode(msg)) },
|
||||||
"alicesworld.tech:443",
|
"alicesworld.tech:443",
|
||||||
|
@ -163,8 +174,8 @@ onmessage = async (msg) => {
|
||||||
await ws.close();
|
await ws.close();
|
||||||
} else {
|
} else {
|
||||||
let resp = await epoxy_client.fetch("https://httpbin.org/get");
|
let resp = await epoxy_client.fetch("https://httpbin.org/get");
|
||||||
console.warn(resp, Object.fromEntries(resp.headers));
|
console.log(resp, Object.fromEntries(resp.headers));
|
||||||
console.warn(await resp.text());
|
plog(await resp.json());
|
||||||
}
|
}
|
||||||
log("done");
|
log("done");
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,8 +3,9 @@
|
||||||
<head>
|
<head>
|
||||||
<title>epoxy</title>
|
<title>epoxy</title>
|
||||||
<style>
|
<style>
|
||||||
body { font-family: sans-serif }
|
body { font-family: monospace; font-weight: bold; width: 100%; height: 100%; padding: 0; margin: 0 }
|
||||||
#logs > * { font-family: monospace }
|
body > div { padding: 1em; }
|
||||||
|
#logs > * { margin: 0; font-weight: normal; }
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
const params = (new URL(window.location.href)).searchParams;
|
const params = (new URL(window.location.href)).searchParams;
|
||||||
|
@ -17,8 +18,8 @@
|
||||||
const should_tls_test = params.has("rawtls_test");
|
const should_tls_test = params.has("rawtls_test");
|
||||||
const worker = new Worker("demo.js");
|
const worker = new Worker("demo.js");
|
||||||
worker.onmessage = (msg) => {
|
worker.onmessage = (msg) => {
|
||||||
let el = document.createElement("div");
|
let el = document.createElement("pre");
|
||||||
el.textContent = msg.data;
|
el.innerHTML = msg.data;
|
||||||
document.getElementById("logs").appendChild(el);
|
document.getElementById("logs").appendChild(el);
|
||||||
window.scrollTo(0, document.body.scrollHeight);
|
window.scrollTo(0, document.body.scrollHeight);
|
||||||
};
|
};
|
||||||
|
@ -28,9 +29,10 @@
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div>
|
<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>
|
||||||
<div id="logs"></div>
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -6,7 +6,7 @@ mod websocket;
|
||||||
mod wrappers;
|
mod wrappers;
|
||||||
|
|
||||||
use tls_stream::EpxTlsStream;
|
use tls_stream::EpxTlsStream;
|
||||||
use utils::{ReplaceErr, UriExt};
|
use utils::{Boolinator, ReplaceErr, UriExt};
|
||||||
use websocket::EpxWebSocket;
|
use websocket::EpxWebSocket;
|
||||||
use wrappers::{IncomingBody, TlsWispService};
|
use wrappers::{IncomingBody, TlsWispService};
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ use tokio_util::{
|
||||||
either::Either,
|
either::Either,
|
||||||
io::{ReaderStream, StreamReader},
|
io::{ReaderStream, StreamReader},
|
||||||
};
|
};
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::{intern, prelude::*};
|
||||||
use web_sys::TextEncoder;
|
use web_sys::TextEncoder;
|
||||||
use wisp_mux::{tokioio::TokioIo, tower::ServiceWrapper, ClientMux, MuxStreamIo, StreamType};
|
use wisp_mux::{tokioio::TokioIo, tower::ServiceWrapper, ClientMux, MuxStreamIo, StreamType};
|
||||||
use ws_stream_wasm::{WsMessage, WsMeta, WsStream};
|
use ws_stream_wasm::{WsMessage, WsMeta, WsStream};
|
||||||
|
@ -52,13 +52,16 @@ fn init() {
|
||||||
console_error_panic_hook::set_once();
|
console_error_panic_hook::set_once();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
|
#[wasm_bindgen(inspectable)]
|
||||||
pub struct EpoxyClient {
|
pub struct EpoxyClient {
|
||||||
rustls_config: Arc<rustls::ClientConfig>,
|
rustls_config: Arc<rustls::ClientConfig>,
|
||||||
mux: Arc<ClientMux<SplitSink<WsStream, WsMessage>>>,
|
mux: Arc<ClientMux<SplitSink<WsStream, WsMessage>>>,
|
||||||
hyper_client: Client<TlsWispService<SplitSink<WsStream, WsMessage>>, HttpBody>,
|
hyper_client: Client<TlsWispService<SplitSink<WsStream, WsMessage>>, HttpBody>,
|
||||||
useragent: String,
|
#[wasm_bindgen(getter_with_clone)]
|
||||||
redirect_limit: usize,
|
pub useragent: String,
|
||||||
|
#[wasm_bindgen(js_name = "redirectLimit")]
|
||||||
|
pub redirect_limit: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
|
@ -81,10 +84,11 @@ impl EpoxyClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!("connecting to ws {:?}", ws_url);
|
debug!("connecting to ws {:?}", ws_url);
|
||||||
let (_, ws) = WsMeta::connect(ws_url, vec!["wisp-v1"])
|
let (_, ws) = WsMeta::connect(ws_url, vec![])
|
||||||
.await
|
.await
|
||||||
.replace_err("Failed to connect to websocket")?;
|
.replace_err("Failed to connect to websocket")?;
|
||||||
debug!("connected!");
|
debug!("connected!");
|
||||||
|
|
||||||
let (wtx, wrx) = ws.split();
|
let (wtx, wrx) = ws.split();
|
||||||
let (mux, fut) = ClientMux::new(wrx, wtx).await?;
|
let (mux, fut) = ClientMux::new(wrx, wtx).await?;
|
||||||
let mux = Arc::new(mux);
|
let mux = Arc::new(mux);
|
||||||
|
@ -128,19 +132,17 @@ impl EpoxyClient {
|
||||||
.replace_err("Failed to create multiplexor channel")?
|
.replace_err("Failed to create multiplexor channel")?
|
||||||
.into_io()
|
.into_io()
|
||||||
.into_asyncrw();
|
.into_asyncrw();
|
||||||
let cloned_uri = url_host.to_string().clone();
|
|
||||||
let connector = TlsConnector::from(self.rustls_config.clone());
|
let connector = TlsConnector::from(self.rustls_config.clone());
|
||||||
debug!("connecting channel");
|
|
||||||
let io = connector
|
let io = connector
|
||||||
.connect(
|
.connect(
|
||||||
cloned_uri
|
url_host
|
||||||
|
.to_string()
|
||||||
.try_into()
|
.try_into()
|
||||||
.replace_err("Failed to parse URL (rustls)")?,
|
.replace_err("Failed to parse URL (rustls)")?,
|
||||||
channel,
|
channel,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.replace_err("Failed to perform TLS handshake")?;
|
.replace_err("Failed to perform TLS handshake")?;
|
||||||
debug!("connected channel");
|
|
||||||
Ok(io)
|
Ok(io)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,24 +157,18 @@ impl EpoxyClient {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
debug!("sending req");
|
|
||||||
let res = self
|
let res = self
|
||||||
.hyper_client
|
.hyper_client
|
||||||
.request(req)
|
.request(req)
|
||||||
.await
|
.await
|
||||||
.replace_err("Failed to send request");
|
.replace_err("Failed to send request");
|
||||||
debug!("recieved res");
|
|
||||||
match res {
|
match res {
|
||||||
Ok(res) => {
|
Ok(res) => {
|
||||||
if utils::is_redirect(res.status().as_u16())
|
if utils::is_redirect(res.status().as_u16())
|
||||||
&& let Some(mut new_req) = new_req
|
&& let Some(mut new_req) = new_req
|
||||||
&& let Some(location) = res.headers().get("Location")
|
&& let Some(location) = res.headers().get("Location")
|
||||||
&& let Ok(redirect_url) = new_req.uri().get_redirect(location)
|
&& let Ok(redirect_url) = new_req.uri().get_redirect(location)
|
||||||
&& let Some(redirect_url_authority) = redirect_url
|
&& let Some(redirect_url_authority) = redirect_url.clone().authority()
|
||||||
.clone()
|
|
||||||
.authority()
|
|
||||||
.replace_err("Redirect URL must have an authority")
|
|
||||||
.ok()
|
|
||||||
{
|
{
|
||||||
*new_req.uri_mut() = redirect_url;
|
*new_req.uri_mut() = redirect_url;
|
||||||
new_req.headers_mut().insert(
|
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 = url.parse::<uri::Uri>().replace_err("Failed to parse URL")?;
|
||||||
let uri_scheme = uri.scheme().replace_err("URL must have a scheme")?;
|
let uri_scheme = uri.scheme().replace_err("URL must have a scheme")?;
|
||||||
if *uri_scheme != uri::Scheme::HTTP && *uri_scheme != uri::Scheme::HTTPS {
|
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 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()),
|
Ok(val) => val.as_string().unwrap_or("GET".to_string()),
|
||||||
Err(_) => "GET".to_string(),
|
Err(_) => "GET".to_string(),
|
||||||
};
|
};
|
||||||
let req_method: http::Method =
|
let req_method: http::Method = http::Method::try_from(req_method_string.as_str())
|
||||||
http::Method::try_from(<String as AsRef<str>>::as_ref(&req_method_string))
|
.replace_err("Invalid http method")?;
|
||||||
.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!(
|
Ok(val) => !matches!(
|
||||||
val.as_string().unwrap_or_default().as_str(),
|
val.as_string().unwrap_or_default().as_str(),
|
||||||
"error" | "manual"
|
"error" | "manual"
|
||||||
|
@ -266,7 +261,7 @@ impl EpoxyClient {
|
||||||
Err(_) => true,
|
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 {
|
let body = if let Some(val) = body_jsvalue {
|
||||||
if val.is_string() {
|
if val.is_string() {
|
||||||
let str = val
|
let str = val
|
||||||
|
@ -288,7 +283,7 @@ impl EpoxyClient {
|
||||||
None => Bytes::new(),
|
None => Bytes::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let headers: Option<Vec<Vec<String>>> = Reflect::get(&options, &jval!("headers"))
|
let headers = Reflect::get(&options, &jvali!("headers"))
|
||||||
.map(|val| {
|
.map(|val| {
|
||||||
if val.is_truthy() {
|
if val.is_truthy() {
|
||||||
Some(utils::entries_of_object(&Object::from(val)))
|
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 mut builder = Request::builder().uri(uri.clone()).method(req_method);
|
||||||
|
|
||||||
let headers_map = builder.headers_mut().replace_err("Failed to get headers")?;
|
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("Accept-Encoding", HeaderValue::from_static("gzip, br"));
|
||||||
headers_map.insert("Connection", HeaderValue::from_str("keep-alive")?);
|
headers_map.insert("Connection", HeaderValue::from_static("keep-alive"));
|
||||||
headers_map.insert("User-Agent", HeaderValue::from_str(&self.useragent)?);
|
headers_map.insert("User-Agent", HeaderValue::from_str(&self.useragent)?);
|
||||||
headers_map.insert("Host", HeaderValue::from_str(uri_host)?);
|
headers_map.insert("Host", HeaderValue::from_str(uri_host)?);
|
||||||
if body_bytes.is_empty() {
|
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 {
|
if let Some(headers) = headers {
|
||||||
|
@ -314,7 +309,7 @@ impl EpoxyClient {
|
||||||
headers_map.insert(
|
headers_map.insert(
|
||||||
HeaderName::from_bytes(hdr[0].as_bytes())
|
HeaderName::from_bytes(hdr[0].as_bytes())
|
||||||
.replace_err("Failed to get hdr name")?,
|
.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")?,
|
.replace_err("Failed to get hdr value")?,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -387,45 +382,41 @@ impl EpoxyClient {
|
||||||
|
|
||||||
Object::define_property(
|
Object::define_property(
|
||||||
&resp,
|
&resp,
|
||||||
&jval!("url"),
|
&jvali!("url"),
|
||||||
&utils::define_property_obj(jval!(resp_uri.to_string()), false)
|
&utils::define_property_obj(jval!(resp_uri.to_string()), false)
|
||||||
.replace_err("Failed to make define_property object for url")?,
|
.replace_err("Failed to make define_property object for url")?,
|
||||||
);
|
);
|
||||||
|
|
||||||
Object::define_property(
|
Object::define_property(
|
||||||
&resp,
|
&resp,
|
||||||
&jval!("redirected"),
|
&jvali!("redirected"),
|
||||||
&utils::define_property_obj(jval!(req_redirected), false)
|
&utils::define_property_obj(jval!(req_redirected), false)
|
||||||
.replace_err("Failed to make define_property object for redirected")?,
|
.replace_err("Failed to make define_property object for redirected")?,
|
||||||
);
|
);
|
||||||
|
|
||||||
let raw_headers = Object::new();
|
let raw_headers = Object::new();
|
||||||
for (k, v) in resp_headers_raw.iter() {
|
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() {
|
if jv.is_array() {
|
||||||
let arr = Array::from(&jv);
|
let arr = Array::from(&jv);
|
||||||
|
|
||||||
arr.push(&jval!(v.to_str()?.to_string()));
|
arr.push(&v);
|
||||||
let _ = Reflect::set(&raw_headers, &jval!(k.to_string()), &arr);
|
Reflect::set(&raw_headers, &k, &arr).flatten("Failed to set rawHeader")?;
|
||||||
} else if jv.is_truthy() {
|
} else if jv.is_truthy() {
|
||||||
let _ = Reflect::set(
|
Reflect::set(&raw_headers, &k, &Array::of2(&jv, &v))
|
||||||
&raw_headers,
|
.flatten("Failed to set rawHeader")?;
|
||||||
&jval!(k.to_string()),
|
|
||||||
&Array::of2(&jv, &jval!(v.to_str()?.to_string())),
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
let _ = Reflect::set(
|
Reflect::set(&raw_headers, &k, &v).flatten("Failed to set rawHeader")?;
|
||||||
&raw_headers,
|
|
||||||
&jval!(k.to_string()),
|
|
||||||
&jval!(v.to_str()?.to_string()),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Object::define_property(
|
Object::define_property(
|
||||||
&resp,
|
&resp,
|
||||||
&jval!("rawHeaders"),
|
&jvali!("rawHeaders"),
|
||||||
&utils::define_property_obj(jval!(&raw_headers), false).replace_err("wjat!!")?,
|
&utils::define_property_obj(jval!(&raw_headers), false)
|
||||||
|
.replace_err("Failed to make define_property object for rawHeaders")?,
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(resp)
|
Ok(resp)
|
||||||
|
|
|
@ -4,10 +4,12 @@ use js_sys::Function;
|
||||||
use tokio::io::{split, AsyncWriteExt, WriteHalf};
|
use tokio::io::{split, AsyncWriteExt, WriteHalf};
|
||||||
use tokio_util::io::ReaderStream;
|
use tokio_util::io::ReaderStream;
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen(inspectable)]
|
||||||
pub struct EpxTlsStream {
|
pub struct EpxTlsStream {
|
||||||
tx: WriteHalf<EpxIoTlsStream>,
|
tx: WriteHalf<EpxIoTlsStream>,
|
||||||
onerror: Function,
|
onerror: Function,
|
||||||
|
#[wasm_bindgen(readonly, getter_with_clone)]
|
||||||
|
pub url: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
|
@ -51,7 +53,7 @@ impl EpxTlsStream {
|
||||||
.call0(&Object::default())
|
.call0(&Object::default())
|
||||||
.replace_err("Failed to call onopen")?;
|
.replace_err("Failed to call onopen")?;
|
||||||
|
|
||||||
Ok(Self { tx, onerror })
|
Ok(Self { tx, onerror, url: url.to_string() })
|
||||||
}
|
}
|
||||||
.await;
|
.await;
|
||||||
if let Err(ret) = ret {
|
if let Err(ret) = ret {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::{intern, prelude::*};
|
||||||
|
|
||||||
use hyper::rt::Executor;
|
use hyper::rt::Executor;
|
||||||
use hyper::{header::HeaderValue, Uri};
|
use hyper::{header::HeaderValue, Uri};
|
||||||
|
@ -15,7 +15,6 @@ extern "C" {
|
||||||
pub fn console_error(s: &str);
|
pub fn console_error(s: &str);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused_macros)]
|
|
||||||
macro_rules! debug {
|
macro_rules! debug {
|
||||||
($($t:tt)*) => (utils::console_debug(&format_args!($($t)*).to_string()))
|
($($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()))
|
($($t:tt)*) => (utils::console_log(&format_args!($($t)*).to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused_macros)]
|
|
||||||
macro_rules! error {
|
macro_rules! error {
|
||||||
($($t:tt)*) => (utils::console_error(&format_args!($($t)*).to_string()))
|
($($t:tt)*) => (utils::console_error(&format_args!($($t)*).to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused_macros)]
|
|
||||||
macro_rules! jerr {
|
macro_rules! jerr {
|
||||||
($expr:expr) => {
|
($expr:expr) => {
|
||||||
JsError::new($expr)
|
JsError::new($expr)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused_macros)]
|
|
||||||
macro_rules! jval {
|
macro_rules! jval {
|
||||||
($expr:expr) => {
|
($expr:expr) => {
|
||||||
JsValue::from($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 {
|
pub trait ReplaceErr {
|
||||||
type Ok;
|
type Ok;
|
||||||
|
|
||||||
|
@ -55,11 +63,11 @@ impl<T, E: std::fmt::Debug> ReplaceErr for Result<T, E> {
|
||||||
type Ok = T;
|
type Ok = T;
|
||||||
|
|
||||||
fn replace_err(self, err: &str) -> Result<<Self as ReplaceErr>::Ok, JsError> {
|
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> {
|
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;
|
type Ok = T;
|
||||||
|
|
||||||
fn replace_err(self, err: &str) -> Result<<Self as ReplaceErr>::Ok, JsError> {
|
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> {
|
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 {
|
pub trait UriExt {
|
||||||
fn get_redirect(&self, location: &HeaderValue) -> Result<Uri, JsError>;
|
fn get_redirect(&self, location: &HeaderValue) -> Result<Uri, JsError>;
|
||||||
fn is_same_host(&self, other: &Uri) -> bool;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UriExt for Uri {
|
impl UriExt for Uri {
|
||||||
|
@ -93,9 +136,6 @@ impl UriExt for Uri {
|
||||||
|
|
||||||
Ok(Uri::from_parts(new_parts)?)
|
Ok(Uri::from_parts(new_parts)?)
|
||||||
}
|
}
|
||||||
fn is_same_host(&self, other: &Uri) -> bool {
|
|
||||||
self.host() == other.host() && self.port() == other.port()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[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> {
|
pub fn define_property_obj(value: JsValue, writable: bool) -> Result<Object, JsValue> {
|
||||||
let entries: Array = [
|
let entries: Array = [
|
||||||
Array::of2(&jval!("value"), &jval!(value)),
|
Array::of2(&jval!(intern("value")), &value),
|
||||||
Array::of2(&jval!("writable"), &jval!(writable)),
|
Array::of2(&jval!(intern("writable")), &jval!(writable)),
|
||||||
]
|
]
|
||||||
.iter()
|
.iter()
|
||||||
.collect::<Array>();
|
.collect::<Array>();
|
||||||
|
|
|
@ -15,10 +15,16 @@ use js_sys::Function;
|
||||||
use std::str::from_utf8;
|
use std::str::from_utf8;
|
||||||
use tokio::io::WriteHalf;
|
use tokio::io::WriteHalf;
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen(inspectable)]
|
||||||
pub struct EpxWebSocket {
|
pub struct EpxWebSocket {
|
||||||
tx: Arc<Mutex<WebSocketWrite<WriteHalf<TokioIo<Upgraded>>>>>,
|
tx: Arc<Mutex<WebSocketWrite<WriteHalf<TokioIo<Upgraded>>>>>,
|
||||||
onerror: Function,
|
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]
|
#[wasm_bindgen]
|
||||||
|
@ -53,7 +59,7 @@ impl EpxWebSocket {
|
||||||
.method("GET")
|
.method("GET")
|
||||||
.uri(url.clone())
|
.uri(url.clone())
|
||||||
.header("Host", host)
|
.header("Host", host)
|
||||||
.header("Origin", origin)
|
.header("Origin", origin.clone())
|
||||||
.header(UPGRADE, "websocket")
|
.header(UPGRADE, "websocket")
|
||||||
.header(CONNECTION, "upgrade")
|
.header(CONNECTION, "upgrade")
|
||||||
.header("Sec-WebSocket-Key", key)
|
.header("Sec-WebSocket-Key", key)
|
||||||
|
@ -109,7 +115,7 @@ impl EpxWebSocket {
|
||||||
.call0(&Object::default())
|
.call0(&Object::default())
|
||||||
.replace_err("Failed to call onopen")?;
|
.replace_err("Failed to call onopen")?;
|
||||||
|
|
||||||
Ok(Self { tx, onerror })
|
Ok(Self { tx, onerror, origin, protocols, url: url.to_string() })
|
||||||
}
|
}
|
||||||
.await;
|
.await;
|
||||||
if let Err(ret) = ret {
|
if let Err(ret) = ret {
|
||||||
|
|
|
@ -9,8 +9,7 @@ use fastwebsockets::{
|
||||||
};
|
};
|
||||||
use futures_util::{SinkExt, StreamExt, TryFutureExt};
|
use futures_util::{SinkExt, StreamExt, TryFutureExt};
|
||||||
use hyper::{
|
use hyper::{
|
||||||
body::Incoming, header::HeaderValue, server::conn::http1, service::service_fn, Request,
|
body::Incoming, server::conn::http1, service::service_fn, Request, Response, StatusCode,
|
||||||
Response, StatusCode,
|
|
||||||
};
|
};
|
||||||
use hyper_util::rt::TokioIo;
|
use hyper_util::rt::TokioIo;
|
||||||
use tokio::net::{TcpListener, TcpStream, UdpSocket};
|
use tokio::net::{TcpListener, TcpStream, UdpSocket};
|
||||||
|
@ -88,24 +87,10 @@ async fn accept_http(
|
||||||
if upgrade::is_upgrade_request(&req)
|
if upgrade::is_upgrade_request(&req)
|
||||||
&& let Some(uri) = uri.strip_prefix(&prefix)
|
&& 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| {
|
if uri.is_empty() || uri == "/" {
|
||||||
Some(
|
|
||||||
x.to_str()
|
|
||||||
.ok()?
|
|
||||||
.split(',')
|
|
||||||
.map(|x| x.trim())
|
|
||||||
.collect::<Vec<&str>>(),
|
|
||||||
)
|
|
||||||
}) && protocols.contains(&"wisp-v1")
|
|
||||||
&& (uri.is_empty() || uri == "/")
|
|
||||||
{
|
|
||||||
tokio::spawn(async move { accept_ws(fut, addr.clone()).await });
|
tokio::spawn(async move { accept_ws(fut, addr.clone()).await });
|
||||||
res.headers_mut().insert(
|
|
||||||
"Sec-Websocket-Protocol",
|
|
||||||
HeaderValue::from_str("wisp-v1").unwrap(),
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
let uri = uri.strip_prefix('/').unwrap_or(uri).to_string();
|
let uri = uri.strip_prefix('/').unwrap_or(uri).to_string();
|
||||||
tokio::spawn(async move { accept_wsproxy(fut, uri, addr.clone()).await });
|
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);
|
println!("random request to path {:?}", uri);
|
||||||
Ok(Response::builder()
|
Ok(Response::builder()
|
||||||
.status(StatusCode::OK)
|
.status(StatusCode::OK)
|
||||||
.body(HttpBody::new(":3".to_string().into()))
|
.body(HttpBody::new(":3".into()))
|
||||||
.unwrap())
|
.unwrap())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,7 +74,7 @@ async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||||
};
|
};
|
||||||
let req = Request::builder()
|
let req = Request::builder()
|
||||||
.method("GET")
|
.method("GET")
|
||||||
.uri(format!("wss://{}:{}/", &addr, addr_port))
|
.uri("/")
|
||||||
.header("Host", &addr)
|
.header("Host", &addr)
|
||||||
.header(UPGRADE, "websocket")
|
.header(UPGRADE, "websocket")
|
||||||
.header(CONNECTION, "upgrade")
|
.header(CONNECTION, "upgrade")
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue