mirror of
https://github.com/MercuryWorkshop/epoxy-tls.git
synced 2025-05-12 22:10:01 -04:00
use a separate hyper connection for websockets
This commit is contained in:
parent
06a5ec08dc
commit
bfee4dc078
3 changed files with 65 additions and 14 deletions
|
@ -238,9 +238,11 @@ cfg_if! {
|
||||||
pub wisp_v2: bool,
|
pub wisp_v2: bool,
|
||||||
pub udp_extension_required: bool,
|
pub udp_extension_required: bool,
|
||||||
pub title_case_headers: bool,
|
pub title_case_headers: bool,
|
||||||
|
pub ws_title_case_headers: bool,
|
||||||
#[wasm_bindgen(getter_with_clone)]
|
#[wasm_bindgen(getter_with_clone)]
|
||||||
pub websocket_protocols: Vec<String>,
|
pub websocket_protocols: Vec<String>,
|
||||||
pub redirect_limit: usize,
|
pub redirect_limit: usize,
|
||||||
|
pub header_limit: usize,
|
||||||
#[wasm_bindgen(getter_with_clone)]
|
#[wasm_bindgen(getter_with_clone)]
|
||||||
pub user_agent: String,
|
pub user_agent: String,
|
||||||
#[wasm_bindgen(getter_with_clone)]
|
#[wasm_bindgen(getter_with_clone)]
|
||||||
|
@ -257,6 +259,7 @@ cfg_if! {
|
||||||
#[wasm_bindgen(getter_with_clone)]
|
#[wasm_bindgen(getter_with_clone)]
|
||||||
pub websocket_protocols: Vec<String>,
|
pub websocket_protocols: Vec<String>,
|
||||||
pub redirect_limit: usize,
|
pub redirect_limit: usize,
|
||||||
|
pub header_limit: usize,
|
||||||
#[wasm_bindgen(getter_with_clone)]
|
#[wasm_bindgen(getter_with_clone)]
|
||||||
pub user_agent: String,
|
pub user_agent: String,
|
||||||
pub disable_certificate_validation: bool,
|
pub disable_certificate_validation: bool,
|
||||||
|
@ -279,8 +282,11 @@ impl Default for EpoxyClientOptions {
|
||||||
wisp_v2: false,
|
wisp_v2: false,
|
||||||
udp_extension_required: false,
|
udp_extension_required: false,
|
||||||
title_case_headers: false,
|
title_case_headers: false,
|
||||||
|
#[cfg(feature = "full")]
|
||||||
|
ws_title_case_headers: true,
|
||||||
websocket_protocols: Vec::new(),
|
websocket_protocols: Vec::new(),
|
||||||
redirect_limit: 10,
|
redirect_limit: 10,
|
||||||
|
header_limit: 200,
|
||||||
user_agent: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36".to_string(),
|
user_agent: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36".to_string(),
|
||||||
#[cfg(feature = "full")]
|
#[cfg(feature = "full")]
|
||||||
pem_files: Vec::new(),
|
pem_files: Vec::new(),
|
||||||
|
@ -325,6 +331,9 @@ pub struct EpoxyClient {
|
||||||
certs_tampered: bool,
|
certs_tampered: bool,
|
||||||
|
|
||||||
pub redirect_limit: usize,
|
pub redirect_limit: usize,
|
||||||
|
header_limit: usize,
|
||||||
|
#[cfg(feature = "full")]
|
||||||
|
ws_title_case_headers: bool,
|
||||||
#[wasm_bindgen(getter_with_clone)]
|
#[wasm_bindgen(getter_with_clone)]
|
||||||
pub user_agent: String,
|
pub user_agent: String,
|
||||||
pub buffer_size: usize,
|
pub buffer_size: usize,
|
||||||
|
@ -417,19 +426,23 @@ impl EpoxyClient {
|
||||||
let client = Client::builder(WasmExecutor)
|
let client = Client::builder(WasmExecutor)
|
||||||
.http09_responses(true)
|
.http09_responses(true)
|
||||||
.http1_title_case_headers(options.title_case_headers)
|
.http1_title_case_headers(options.title_case_headers)
|
||||||
.http1_max_headers(200)
|
.http1_max_headers(options.header_limit)
|
||||||
.build(service);
|
.build(service);
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
stream_provider,
|
stream_provider,
|
||||||
client,
|
client,
|
||||||
redirect_limit: options.redirect_limit,
|
redirect_limit: options.redirect_limit,
|
||||||
|
header_limit: options.header_limit,
|
||||||
user_agent: options.user_agent,
|
user_agent: options.user_agent,
|
||||||
|
buffer_size: options.buffer_size,
|
||||||
|
|
||||||
|
#[cfg(feature = "full")]
|
||||||
|
ws_title_case_headers: options.ws_title_case_headers,
|
||||||
#[cfg(feature = "full")]
|
#[cfg(feature = "full")]
|
||||||
certs_tampered: options.disable_certificate_validation || !options.pem_files.is_empty(),
|
certs_tampered: options.disable_certificate_validation || !options.pem_files.is_empty(),
|
||||||
#[cfg(not(feature = "full"))]
|
#[cfg(not(feature = "full"))]
|
||||||
certs_tampered: options.disable_certificate_validation,
|
certs_tampered: options.disable_certificate_validation,
|
||||||
buffer_size: options.buffer_size,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,9 @@ use crate::{stream_provider::ProviderUnencryptedAsyncRW, EpoxyError};
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#[wasm_bindgen(js_namespace = console, js_name = log)]
|
#[wasm_bindgen(js_namespace = console, js_name = log)]
|
||||||
pub fn js_console_log(s: &str);
|
pub fn js_console_log(s: &str);
|
||||||
|
|
||||||
|
#[wasm_bindgen(js_namespace = console, js_name = error)]
|
||||||
|
pub fn js_console_error(s: &str);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
|
@ -46,6 +49,13 @@ macro_rules! console_log {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! console_error {
|
||||||
|
($($expr:expr),*) => {
|
||||||
|
$crate::utils::js_console_error(&format!($($expr),*));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
pub trait UriExt {
|
pub trait UriExt {
|
||||||
fn get_redirect(&self, location: &HeaderValue) -> Result<Uri, EpoxyError>;
|
fn get_redirect(&self, location: &HeaderValue) -> Result<Uri, EpoxyError>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,20 +12,25 @@ use http::{
|
||||||
},
|
},
|
||||||
Method, Request, Response, StatusCode, Uri,
|
Method, Request, Response, StatusCode, Uri,
|
||||||
};
|
};
|
||||||
|
use http_body_util::Empty;
|
||||||
use hyper::{
|
use hyper::{
|
||||||
body::Incoming,
|
body::Incoming,
|
||||||
|
client::conn::http1,
|
||||||
upgrade::{self, Upgraded},
|
upgrade::{self, Upgraded},
|
||||||
};
|
};
|
||||||
|
use hyper_util_wasm::client::legacy::connect::ConnectSvc;
|
||||||
use js_sys::{ArrayBuffer, Function, Uint8Array};
|
use js_sys::{ArrayBuffer, Function, Uint8Array};
|
||||||
use tokio::io::WriteHalf;
|
use tokio::io::WriteHalf;
|
||||||
use wasm_bindgen::{prelude::*, JsError, JsValue};
|
use wasm_bindgen::{prelude::*, JsError, JsValue};
|
||||||
use wasm_bindgen_futures::spawn_local;
|
use wasm_bindgen_futures::spawn_local;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
console_error,
|
||||||
|
stream_provider::StreamProviderService,
|
||||||
tokioio::TokioIo,
|
tokioio::TokioIo,
|
||||||
utils::{entries_of_object, from_entries, ws_key},
|
utils::{entries_of_object, from_entries, ws_key},
|
||||||
EpoxyClient, EpoxyError, EpoxyHandlers, EpoxyUrlInput, EpoxyWebSocketHeadersInput,
|
EpoxyClient, EpoxyError, EpoxyHandlers, EpoxyUrlInput, EpoxyWebSocketHeadersInput,
|
||||||
EpoxyWebSocketInput, HttpBody,
|
EpoxyWebSocketInput,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
|
@ -56,7 +61,7 @@ impl EpoxyWebSocket {
|
||||||
let url: Uri = url.try_into()?;
|
let url: Uri = url.try_into()?;
|
||||||
let host = url.host().ok_or(EpoxyError::NoUrlHost)?;
|
let host = url.host().ok_or(EpoxyError::NoUrlHost)?;
|
||||||
|
|
||||||
let mut request = Request::builder()
|
let mut req = Request::builder()
|
||||||
.method(Method::GET)
|
.method(Method::GET)
|
||||||
.uri(url.clone())
|
.uri(url.clone())
|
||||||
.header(HOST, host)
|
.header(HOST, host)
|
||||||
|
@ -67,24 +72,24 @@ impl EpoxyWebSocket {
|
||||||
.header(USER_AGENT, user_agent);
|
.header(USER_AGENT, user_agent);
|
||||||
|
|
||||||
if !protocols.is_empty() {
|
if !protocols.is_empty() {
|
||||||
request = request.header(SEC_WEBSOCKET_PROTOCOL, protocols.join(","));
|
req = req.header(SEC_WEBSOCKET_PROTOCOL, protocols.join(","));
|
||||||
}
|
}
|
||||||
|
|
||||||
if web_sys::Headers::instanceof(&headers)
|
if web_sys::Headers::instanceof(&headers)
|
||||||
&& let Ok(entries) = from_entries(&headers)
|
&& let Ok(entries) = from_entries(&headers)
|
||||||
{
|
{
|
||||||
for header in entries_of_object(&entries) {
|
for header in entries_of_object(&entries) {
|
||||||
request = request.header(&header[0], &header[1]);
|
req = req.header(&header[0], &header[1]);
|
||||||
}
|
}
|
||||||
} else if headers.is_truthy() {
|
} else if headers.is_truthy() {
|
||||||
for header in entries_of_object(&headers.into()) {
|
for header in entries_of_object(&headers.into()) {
|
||||||
request = request.header(&header[0], &header[1]);
|
req = req.header(&header[0], &header[1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let request = request.body(HttpBody::new(Bytes::new()))?;
|
let req = req.body(Empty::new())?;
|
||||||
|
|
||||||
let mut response = client.client.request(request).await?;
|
let mut response = request(req, client).await?;
|
||||||
verify(&response)?;
|
verify(&response)?;
|
||||||
|
|
||||||
let websocket = WebSocket::after_handshake(
|
let websocket = WebSocket::after_handshake(
|
||||||
|
@ -207,6 +212,29 @@ impl EpoxyWebSocket {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn request(
|
||||||
|
req: Request<Empty<Bytes>>,
|
||||||
|
client: &EpoxyClient,
|
||||||
|
) -> Result<Response<Incoming>, EpoxyError> {
|
||||||
|
let stream = StreamProviderService(client.stream_provider.clone())
|
||||||
|
.connect(req.uri().clone())
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let (mut sender, conn) = http1::Builder::new()
|
||||||
|
.title_case_headers(client.ws_title_case_headers)
|
||||||
|
.max_headers(client.header_limit)
|
||||||
|
.handshake(stream)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
spawn_local(async move {
|
||||||
|
if let Err(err) = conn.with_upgrades().await {
|
||||||
|
console_error!("websocket connection future failed: {:?}", err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(sender.send_request(req).await?)
|
||||||
|
}
|
||||||
|
|
||||||
// https://github.com/snapview/tungstenite-rs/blob/314feea3055a93e585882fb769854a912a7e6dae/src/handshake/client.rs#L189
|
// https://github.com/snapview/tungstenite-rs/blob/314feea3055a93e585882fb769854a912a7e6dae/src/handshake/client.rs#L189
|
||||||
fn verify(response: &Response<Incoming>) -> Result<(), EpoxyError> {
|
fn verify(response: &Response<Incoming>) -> Result<(), EpoxyError> {
|
||||||
if response.status() != StatusCode::SWITCHING_PROTOCOLS {
|
if response.status() != StatusCode::SWITCHING_PROTOCOLS {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue