mirror of
https://github.com/MercuryWorkshop/epoxy-tls.git
synced 2025-05-12 22:10:01 -04:00
better compat with fetch api
This commit is contained in:
parent
53a399856f
commit
bed942eb75
8 changed files with 99 additions and 33 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -1849,7 +1849,7 @@ checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wisp-mux"
|
name = "wisp-mux"
|
||||||
version = "1.2.1"
|
version = "1.2.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async_io_stream",
|
"async_io_stream",
|
||||||
"bytes",
|
"bytes",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "epoxy-client"
|
name = "epoxy-client"
|
||||||
version = "1.4.0"
|
version = "1.4.1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "LGPL-3.0-only"
|
license = "LGPL-3.0-only"
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ futures-util = "0.3.30"
|
||||||
js-sys = "0.3.66"
|
js-sys = "0.3.66"
|
||||||
webpki-roots = "0.26.0"
|
webpki-roots = "0.26.0"
|
||||||
tokio-rustls = "0.25.0"
|
tokio-rustls = "0.25.0"
|
||||||
web-sys = { version = "0.3.66", features = ["TextEncoder", "Response", "ResponseInit", "WebSocket", "BinaryType", "MessageEvent"] }
|
web-sys = { version = "0.3.66", features = ["Request", "RequestInit", "Headers", "Response", "ResponseInit", "WebSocket", "BinaryType", "MessageEvent"] }
|
||||||
wasm-streams = "0.4.0"
|
wasm-streams = "0.4.0"
|
||||||
tokio-util = { version = "0.7.10", features = ["io"] }
|
tokio-util = { version = "0.7.10", features = ["io"] }
|
||||||
async-compression = { version = "0.4.5", features = ["tokio", "gzip", "brotli"] }
|
async-compression = { version = "0.4.5", features = ["tokio", "gzip", "brotli"] }
|
||||||
|
|
|
@ -58,16 +58,24 @@ onmessage = async (msg) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
if (should_feature_test) {
|
if (should_feature_test) {
|
||||||
|
let formdata = new FormData();
|
||||||
|
formdata.append("a", "b");
|
||||||
for (const url of [
|
for (const url of [
|
||||||
["https://httpbin.org/get", {}],
|
["https://httpbin.org/get", {}],
|
||||||
["https://httpbin.org/gzip", {}],
|
["https://httpbin.org/gzip", {}],
|
||||||
["https://httpbin.org/brotli", {}],
|
["https://httpbin.org/brotli", {}],
|
||||||
["https://httpbin.org/redirect/11", {}],
|
["https://httpbin.org/redirect/11", {}],
|
||||||
["https://httpbin.org/redirect/1", { redirect: "manual" }],
|
["https://httpbin.org/redirect/1", { redirect: "manual" }],
|
||||||
|
["https://httpbin.org/post", { method: "POST", body: new URLSearchParams("a=b") }],
|
||||||
|
["https://httpbin.org/post", { method: "POST", body: formdata }],
|
||||||
|
["https://httpbin.org/post", { method: "POST", body: "a" }],
|
||||||
|
["https://httpbin.org/post", { method: "POST", body: (new TextEncoder()).encode("abc") }],
|
||||||
|
["https://httpbin.org/get", { headers: {"a": "b", "b": "c"} }],
|
||||||
|
["https://httpbin.org/get", { headers: new Headers({"a": "b", "b": "c"}) }]
|
||||||
]) {
|
]) {
|
||||||
let resp = await epoxy_client.fetch(url[0], url[1]);
|
let resp = await epoxy_client.fetch(url[0], url[1]);
|
||||||
console.warn(url, resp, Object.fromEntries(resp.headers));
|
console.warn(url, resp, Object.fromEntries(resp.headers));
|
||||||
console.warn(await resp.text());
|
log(await resp.text());
|
||||||
}
|
}
|
||||||
} else if (should_multiparallel_test) {
|
} else if (should_multiparallel_test) {
|
||||||
const num_tests = 10;
|
const num_tests = 10;
|
||||||
|
@ -183,7 +191,7 @@ onmessage = async (msg) => {
|
||||||
msg => { console.log(msg); log(decoder.decode(msg)) },
|
msg => { console.log(msg); log(decoder.decode(msg)) },
|
||||||
"google.com:443",
|
"google.com:443",
|
||||||
);
|
);
|
||||||
await ws.send((new TextEncoder()).encode("GET / HTTP 1.1\r\nHost: google.com\r\nConnection: close\r\n\r\n"));
|
await ws.send("GET / HTTP 1.1\r\nHost: google.com\r\nConnection: close\r\n\r\n");
|
||||||
await (new Promise((res, _) => setTimeout(res, 500)));
|
await (new Promise((res, _) => setTimeout(res, 500)));
|
||||||
await ws.close();
|
await ws.close();
|
||||||
} else if (should_udp_test) {
|
} else if (should_udp_test) {
|
||||||
|
@ -198,7 +206,7 @@ onmessage = async (msg) => {
|
||||||
);
|
);
|
||||||
while (true) {
|
while (true) {
|
||||||
log("sending `data`");
|
log("sending `data`");
|
||||||
await ws.send((new TextEncoder()).encode("data"));
|
await ws.send("data");
|
||||||
await (new Promise((res, _) => setTimeout(res, 50)));
|
await (new Promise((res, _) => setTimeout(res, 50)));
|
||||||
}
|
}
|
||||||
} else if (should_reconnect_test) {
|
} else if (should_reconnect_test) {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@mercuryworkshop/epoxy-tls",
|
"name": "@mercuryworkshop/epoxy-tls",
|
||||||
"version": "1.4.0",
|
"version": "1.4.1",
|
||||||
"description": "A wasm library for using raw encrypted tls/ssl/https/websocket streams on the browser",
|
"description": "A wasm library for using raw encrypted tls/ssl/https/websocket streams on the browser",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "./build.sh"
|
"build": "./build.sh"
|
||||||
|
|
|
@ -32,7 +32,6 @@ use tokio_util::{
|
||||||
io::{ReaderStream, StreamReader},
|
io::{ReaderStream, StreamReader},
|
||||||
};
|
};
|
||||||
use wasm_bindgen::{intern, prelude::*};
|
use wasm_bindgen::{intern, prelude::*};
|
||||||
use web_sys::TextEncoder;
|
|
||||||
use wisp_mux::{ClientMux, MuxStreamIo, StreamType};
|
use wisp_mux::{ClientMux, MuxStreamIo, StreamType};
|
||||||
|
|
||||||
type HttpBody = http_body_util::Full<Bytes>;
|
type HttpBody = http_body_util::Full<Bytes>;
|
||||||
|
@ -58,6 +57,7 @@ fn init() {
|
||||||
// utils.rs
|
// utils.rs
|
||||||
intern("value");
|
intern("value");
|
||||||
intern("writable");
|
intern("writable");
|
||||||
|
intern("POST");
|
||||||
|
|
||||||
// main.rs
|
// main.rs
|
||||||
intern("method");
|
intern("method");
|
||||||
|
@ -67,6 +67,7 @@ fn init() {
|
||||||
intern("url");
|
intern("url");
|
||||||
intern("redirected");
|
intern("redirected");
|
||||||
intern("rawHeaders");
|
intern("rawHeaders");
|
||||||
|
intern("Content-Type");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cert_to_jval(cert: &TrustAnchor) -> Result<JsValue, JsValue> {
|
fn cert_to_jval(cert: &TrustAnchor) -> Result<JsValue, JsValue> {
|
||||||
|
@ -310,31 +311,24 @@ impl EpoxyClient {
|
||||||
Err(_) => true,
|
Err(_) => true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut body_content_type: Option<String> = None;
|
||||||
let body_jsvalue: Option<JsValue> = Reflect::get(&options, &jval!("body")).ok();
|
let body_jsvalue: Option<JsValue> = Reflect::get(&options, &jval!("body")).ok();
|
||||||
let body = if let Some(val) = body_jsvalue {
|
let body_bytes: Bytes = match body_jsvalue {
|
||||||
if val.is_string() {
|
Some(buf) => {
|
||||||
let str = val
|
let (body, req) = utils::jval_to_u8_array_req(buf)
|
||||||
.as_string()
|
.await
|
||||||
.replace_err("Failed to get string from body")?;
|
.replace_err("Invalid body")?;
|
||||||
let encoder =
|
body_content_type = req.headers().get("Content-Type").ok().flatten();
|
||||||
TextEncoder::new().replace_err("Failed to create TextEncoder for body")?;
|
Bytes::from(body.to_vec())
|
||||||
let encoded = encoder.encode_with_input(str.as_ref());
|
|
||||||
Some(encoded)
|
|
||||||
} else {
|
|
||||||
Some(Uint8Array::new(&val).to_vec())
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
let body_bytes: Bytes = match body {
|
|
||||||
Some(vec) => Bytes::from(vec),
|
|
||||||
None => Bytes::new(),
|
None => Bytes::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let headers = Reflect::get(&options, &jval!("headers"))
|
let headers = Reflect::get(&options, &jval!("headers"))
|
||||||
.map(|val| {
|
.map(|val| {
|
||||||
if val.is_truthy() {
|
if web_sys::Headers::instanceof(&val) {
|
||||||
|
Some(utils::entries_of_object(&Object::from_entries(&val).ok()?))
|
||||||
|
} else if val.is_truthy() {
|
||||||
Some(utils::entries_of_object(&Object::from(val)))
|
Some(utils::entries_of_object(&Object::from(val)))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -352,6 +346,9 @@ impl EpoxyClient {
|
||||||
if body_bytes.is_empty() {
|
if body_bytes.is_empty() {
|
||||||
headers_map.insert("Content-Length", HeaderValue::from_static("0"));
|
headers_map.insert("Content-Length", HeaderValue::from_static("0"));
|
||||||
}
|
}
|
||||||
|
if let Some(content_type) = body_content_type {
|
||||||
|
headers_map.insert("Content-Type", HeaderValue::from_str(&content_type)?);
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(headers) = headers {
|
if let Some(headers) = headers {
|
||||||
for hdr in headers {
|
for hdr in headers {
|
||||||
|
|
|
@ -49,7 +49,11 @@ impl EpxTlsStream {
|
||||||
.call0(&Object::default())
|
.call0(&Object::default())
|
||||||
.replace_err("Failed to call onopen")?;
|
.replace_err("Failed to call onopen")?;
|
||||||
|
|
||||||
Ok(Self { tx, onerror, url: url.to_string() })
|
Ok(Self {
|
||||||
|
tx,
|
||||||
|
onerror,
|
||||||
|
url: url.to_string(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
.await;
|
.await;
|
||||||
if let Err(ret) = ret {
|
if let Err(ret) = ret {
|
||||||
|
@ -61,9 +65,17 @@ impl EpxTlsStream {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
pub async fn send(&mut self, payload: Uint8Array) -> Result<(), JsError> {
|
pub async fn send(&mut self, payload: JsValue) -> Result<(), JsError> {
|
||||||
let onerr = self.onerror.clone();
|
let onerr = self.onerror.clone();
|
||||||
let ret = self.tx.write_all(&payload.to_vec()).await;
|
let ret = self
|
||||||
|
.tx
|
||||||
|
.write_all(
|
||||||
|
&utils::jval_to_u8_array(payload)
|
||||||
|
.await
|
||||||
|
.replace_err("Invalid payload")?
|
||||||
|
.to_vec(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
if let Err(ret) = ret {
|
if let Err(ret) = ret {
|
||||||
let _ = onerr.call1(&JsValue::null(), &jval!(format!("{}", ret)));
|
let _ = onerr.call1(&JsValue::null(), &jval!(format!("{}", ret)));
|
||||||
Err(ret.into())
|
Err(ret.into())
|
||||||
|
|
|
@ -71,9 +71,17 @@ impl EpxUdpStream {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
pub async fn send(&mut self, payload: Uint8Array) -> Result<(), JsError> {
|
pub async fn send(&mut self, payload: JsValue) -> Result<(), JsError> {
|
||||||
let onerr = self.onerror.clone();
|
let onerr = self.onerror.clone();
|
||||||
let ret = self.tx.send(payload.to_vec()).await;
|
let ret = self
|
||||||
|
.tx
|
||||||
|
.send(
|
||||||
|
utils::jval_to_u8_array(payload)
|
||||||
|
.await
|
||||||
|
.replace_err("Invalid payload")?
|
||||||
|
.to_vec(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
if let Err(ret) = ret {
|
if let Err(ret) = ret {
|
||||||
let _ = onerr.call1(&JsValue::null(), &jval!(format!("{}", ret)));
|
let _ = onerr.call1(&JsValue::null(), &jval!(format!("{}", ret)));
|
||||||
Err(ret.into())
|
Err(ret.into())
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
use wasm_bindgen_futures::JsFuture;
|
||||||
|
|
||||||
use hyper::rt::Executor;
|
use hyper::rt::Executor;
|
||||||
|
use js_sys::ArrayBuffer;
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use wisp_mux::{CloseReason, WispError};
|
use wisp_mux::{CloseReason, WispError};
|
||||||
|
|
||||||
|
@ -188,7 +190,15 @@ pub fn get_url_port(url: &Uri) -> Result<u16, JsError> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn make_mux(url: &str) -> Result<(ClientMux<WebSocketWrapper>, impl Future<Output = Result<(), WispError>>), WispError> {
|
pub async fn make_mux(
|
||||||
|
url: &str,
|
||||||
|
) -> Result<
|
||||||
|
(
|
||||||
|
ClientMux<WebSocketWrapper>,
|
||||||
|
impl Future<Output = Result<(), WispError>>,
|
||||||
|
),
|
||||||
|
WispError,
|
||||||
|
> {
|
||||||
let (wtx, wrx) = WebSocketWrapper::connect(url, vec![])
|
let (wtx, wrx) = WebSocketWrapper::connect(url, vec![])
|
||||||
.await
|
.await
|
||||||
.map_err(|_| WispError::WsImplSocketClosed)?;
|
.map_err(|_| WispError::WsImplSocketClosed)?;
|
||||||
|
@ -198,7 +208,11 @@ pub async fn make_mux(url: &str) -> Result<(ClientMux<WebSocketWrapper>, impl Fu
|
||||||
Ok(mux)
|
Ok(mux)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn spawn_mux_fut(mux: Arc<RwLock<ClientMux<WebSocketWrapper>>>, fut: impl Future<Output = Result<(), WispError>> + 'static, url: String) {
|
pub fn spawn_mux_fut(
|
||||||
|
mux: Arc<RwLock<ClientMux<WebSocketWrapper>>>,
|
||||||
|
fut: impl Future<Output = Result<(), WispError>> + 'static,
|
||||||
|
url: String,
|
||||||
|
) {
|
||||||
wasm_bindgen_futures::spawn_local(async move {
|
wasm_bindgen_futures::spawn_local(async move {
|
||||||
if let Err(e) = fut.await {
|
if let Err(e) = fut.await {
|
||||||
error!("epoxy: error in mux future, restarting: {:?}", e);
|
error!("epoxy: error in mux future, restarting: {:?}", e);
|
||||||
|
@ -223,3 +237,30 @@ pub async fn replace_mux(
|
||||||
spawn_mux_fut(mux, fut, url.into());
|
spawn_mux_fut(mux, fut, url.into());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn jval_to_u8_array(val: JsValue) -> Result<Uint8Array, JsValue> {
|
||||||
|
JsFuture::from(
|
||||||
|
web_sys::Request::new_with_str_and_init(
|
||||||
|
"/",
|
||||||
|
web_sys::RequestInit::new().method("POST").body(Some(&val)),
|
||||||
|
)?
|
||||||
|
.array_buffer()?,
|
||||||
|
)
|
||||||
|
.await?
|
||||||
|
.dyn_into::<ArrayBuffer>()
|
||||||
|
.map(|x| Uint8Array::new(&x))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn jval_to_u8_array_req(val: JsValue) -> Result<(Uint8Array, web_sys::Request), JsValue> {
|
||||||
|
let req = web_sys::Request::new_with_str_and_init(
|
||||||
|
"/",
|
||||||
|
web_sys::RequestInit::new().method("POST").body(Some(&val)),
|
||||||
|
)?;
|
||||||
|
Ok((
|
||||||
|
JsFuture::from(req.array_buffer()?)
|
||||||
|
.await?
|
||||||
|
.dyn_into::<ArrayBuffer>()
|
||||||
|
.map(|x| Uint8Array::new(&x))?,
|
||||||
|
req,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue