Skip to content

Commit

Permalink
Merge pull request #102 from omjadas/chore/hyper-1
Browse files Browse the repository at this point in the history
chore: hyper v1
  • Loading branch information
omjadas authored Mar 9, 2024
2 parents f23b958 + cd3da97 commit 2f005f9
Show file tree
Hide file tree
Showing 23 changed files with 843 additions and 576 deletions.
1 change: 1 addition & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ jobs:
- stable
- beta
- nightly
- "1.75.0"

steps:
- uses: actions/checkout@v4
Expand Down
38 changes: 20 additions & 18 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
name = "hudsucker"
version = "0.21.0"
edition = "2021"
rust-version = "1.75.0"
description = "MITM HTTP/S proxy"
documentation = "https://docs.rs/hudsucker"
readme = "README.md"
Expand All @@ -18,44 +19,45 @@ rustdoc-args = ["--cfg", "docsrs"]

[dependencies]
async-compression = { version = "0.4.0", features = ["tokio", "brotli", "gzip", "zlib", "zstd"], optional = true }
async-trait = "0.1.67"
bstr = "1.0.0"
bytes = "1.0.0"
futures = "0.3.11"
http = "0.2.0"
hyper = { version = "0.14.15", features = ["client", "http1", "server", "tcp"] }
hyper-rustls = { version = "0.24.0", default-features = false, features = ["http1", "logging", "tls12", "webpki-tokio"], optional = true }
hyper-tls = { version = "0.5.0", optional = true }
hyper-tungstenite = "0.11.1"
http = "1.1.0"
http-body-util = "0.1.0"
hyper = "1.1.0"
hyper-rustls = { version = "0.26.0", default-features = false, features = ["http1", "logging", "ring", "tls12", "webpki-tokio"], optional = true }
hyper-tls = { version = "0.6.0", optional = true }
hyper-tungstenite = "0.13.0"
hyper-util = { version="0.1.3", features = ["client-legacy", "server", "http1"] }
moka = { version = "0.12.0", features = ["future"], optional = true }
openssl = { version = "0.10.39", optional = true }
openssl = { version = "0.10.46", optional = true }
rand = { version = "0.8.0", optional = true }
rcgen = { version = "0.12.0", features = ["x509-parser"], optional = true }
thiserror = "1.0.30"
time = { version = "0.3.20", optional = true }
tokio = { version = "1.24.2", features = ["rt"] }
tokio-rustls = "0.24.0"
tokio-tungstenite = "0.20.0"
tokio-util = { version = "0.7.0", features = ["io"], optional = true }
tracing = { version = "0.1.23", features = ["log"] }
tokio = { version = "1.24.2", features = ["macros", "rt"] }
tokio-graceful = "0.1.6"
tokio-rustls = "0.25.0"
tokio-tungstenite = "0.21.0"
tokio-util = { version = "0.7.1", features = ["io"], optional = true }
tracing = { version = "0.1.35", features = ["log"] }

[dev-dependencies]
async-http-proxy = { version = "1.2.5", features = ["runtime-tokio"] }
criterion = { version = "0.5.0", features = ["async_tokio"] }
reqwest = "0.11.10"
rustls-native-certs = "0.6.2"
rustls-native-certs = "0.7.0"
rustls-pemfile = "2.0.0"
tls-listener = { version = "0.8.0", features = ["rustls", "hyper-h1", "hyper-h2"] }
tls-listener = { version = "0.9.1", features = ["rustls"] }
tokio = { version = "1.24.2", features = ["full"] }
tokio-native-tls = "0.3.1"
tracing-subscriber = "0.3.0"
tracing-subscriber = "0.3.8"
x509-parser = "0.16.0"

[features]
decoder = ["dep:async-compression", "dep:tokio-util", "hyper/stream", "tokio/io-util"]
decoder = ["dep:async-compression", "dep:tokio-util", "tokio/io-util"]
default = ["decoder", "rcgen-ca", "rustls-client"]
full = ["decoder", "http2", "native-tls-client", "openssl-ca", "rcgen-ca", "rustls-client"]
http2 = ["hyper/http2", "hyper-rustls?/http2"]
http2 = ["hyper-util/http2", "hyper-rustls?/http2"]
native-tls-client = ["dep:hyper-tls", "tokio-tungstenite/native-tls"]
openssl-ca = ["dep:openssl", "dep:moka"]
rcgen-ca = ["dep:rcgen", "dep:moka", "dep:time", "dep:rand"]
Expand Down
23 changes: 7 additions & 16 deletions benches/certificate_authorities.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ use http::uri::Authority;
use hudsucker::{
certificate_authority::{CertificateAuthority, OpensslAuthority, RcgenAuthority},
openssl::{hash::MessageDigest, pkey::PKey, x509::X509},
rustls,
};
use rustls_pemfile as pemfile;

Expand All @@ -16,21 +15,13 @@ fn runtime() -> tokio::runtime::Runtime {
fn build_rcgen_ca(cache_size: u64) -> RcgenAuthority {
let mut private_key_bytes: &[u8] = include_bytes!("../examples/ca/hudsucker.key");
let mut ca_cert_bytes: &[u8] = include_bytes!("../examples/ca/hudsucker.cer");
let private_key = rustls::PrivateKey(
pemfile::pkcs8_private_keys(&mut private_key_bytes)
.next()
.unwrap()
.expect("Failed to parse private key")
.secret_pkcs8_der()
.to_vec(),
);
let ca_cert = rustls::Certificate(
pemfile::certs(&mut ca_cert_bytes)
.next()
.unwrap()
.expect("Failed to parse CA certificate")
.to_vec(),
);
let private_key = pemfile::private_key(&mut private_key_bytes)
.unwrap()
.expect("Failed to parse private key");
let ca_cert = pemfile::certs(&mut ca_cert_bytes)
.next()
.unwrap()
.expect("Failed to parse CA certificate");

RcgenAuthority::new(private_key, ca_cert, cache_size)
.expect("Failed to create Certificate Authority")
Expand Down
3 changes: 2 additions & 1 deletion benches/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ use hudsucker::{
decode_request, decode_response,
hyper::{
header::{CONTENT_ENCODING, CONTENT_LENGTH},
Body, Request, Response,
Request, Response,
},
Body,
};
use tokio::io::BufReader;
use tokio_util::io::ReaderStream;
Expand Down
161 changes: 98 additions & 63 deletions benches/proxy.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
use criterion::{criterion_group, criterion_main, Criterion, Throughput};
use http_body_util::Empty;
use hudsucker::{
certificate_authority::{CertificateAuthority, RcgenAuthority},
hyper::{
client::connect::HttpConnector,
service::{make_service_fn, service_fn},
Body, Method, Request, Response, Server,
hyper::{body::Incoming, service::service_fn, Method, Request, Response},
hyper_util::client::legacy::{connect::HttpConnector, Client},
hyper_util::{
rt::{TokioExecutor, TokioIo},
server::conn::auto,
},
rustls, Proxy,
Body, Proxy,
};
use reqwest::Certificate;
use rustls_pemfile as pemfile;
use std::{
convert::Infallible,
net::{SocketAddr, TcpListener},
};
use std::{convert::Infallible, net::SocketAddr};
use tls_listener::TlsListener;
use tokio::sync::oneshot::Sender;
use tokio::{net::TcpListener, sync::oneshot::Sender};
use tokio_graceful::Shutdown;
use tokio_native_tls::native_tls;

fn runtime() -> tokio::runtime::Runtime {
Expand All @@ -29,74 +29,109 @@ fn runtime() -> tokio::runtime::Runtime {
fn build_ca() -> RcgenAuthority {
let mut private_key_bytes: &[u8] = include_bytes!("../examples/ca/hudsucker.key");
let mut ca_cert_bytes: &[u8] = include_bytes!("../examples/ca/hudsucker.cer");
let private_key = rustls::PrivateKey(
pemfile::pkcs8_private_keys(&mut private_key_bytes)
.next()
.unwrap()
.expect("Failed to parse private key")
.secret_pkcs8_der()
.to_vec(),
);
let ca_cert = rustls::Certificate(
pemfile::certs(&mut ca_cert_bytes)
.next()
.unwrap()
.expect("Failed to parse CA certificate")
.to_vec(),
);
let private_key = pemfile::private_key(&mut private_key_bytes)
.unwrap()
.expect("Failed to parse private key");
let ca_cert = pemfile::certs(&mut ca_cert_bytes)
.next()
.unwrap()
.expect("Failed to parse CA certificate");

RcgenAuthority::new(private_key, ca_cert, 1_000)
.expect("Failed to create Certificate Authority")
}

async fn test_server(req: Request<Body>) -> Result<Response<Body>, Infallible> {
async fn test_server(req: Request<Incoming>) -> Result<Response<Body>, Infallible> {
match (req.method(), req.uri().path()) {
(&Method::GET, "/hello") => Ok(Response::new(Body::from("hello, world"))),
_ => Ok(Response::new(Body::empty())),
_ => Ok(Response::new(Body::from(Empty::new()))),
}
}

fn start_http_server() -> Result<(SocketAddr, Sender<()>), Box<dyn std::error::Error>> {
let make_svc = make_service_fn(|_| async { Ok::<_, Infallible>(service_fn(test_server)) });

let listener = TcpListener::bind(SocketAddr::from(([127, 0, 0, 1], 0)))?;
pub async fn start_http_server() -> Result<(SocketAddr, Sender<()>), Box<dyn std::error::Error>> {
let listener = TcpListener::bind(SocketAddr::from(([127, 0, 0, 1], 0))).await?;
let addr = listener.local_addr()?;

let (tx, rx) = tokio::sync::oneshot::channel();

tokio::spawn(
Server::from_tcp(listener)?
.serve(make_svc)
.with_graceful_shutdown(async { rx.await.unwrap_or_default() }),
);
tokio::spawn(async move {
let server = auto::Builder::new(TokioExecutor::new());
let shutdown = Shutdown::new(async { rx.await.unwrap_or_default() });
let guard = shutdown.guard_weak();

loop {
tokio::select! {
res = listener.accept() => {
let Ok((tcp, _)) = res else {
continue;
};

let server = server.clone();

shutdown.spawn_task(async move {
server
.serve_connection_with_upgrades(TokioIo::new(tcp), service_fn(test_server))
.await
.unwrap();
});
}
_ = guard.cancelled() => {
break;
}
}
}

shutdown.shutdown().await;
});

Ok((addr, tx))
}

async fn start_https_server() -> Result<(SocketAddr, Sender<()>), Box<dyn std::error::Error>> {
let make_svc = make_service_fn(|_| async { Ok::<_, Infallible>(service_fn(test_server)) });

let listener = TcpListener::bind(SocketAddr::from(([127, 0, 0, 1], 0)))?;
listener.set_nonblocking(true)?;
pub async fn start_https_server(
ca: impl CertificateAuthority,
) -> Result<(SocketAddr, Sender<()>), Box<dyn std::error::Error>> {
let listener = TcpListener::bind(SocketAddr::from(([127, 0, 0, 1], 0))).await?;
let addr = listener.local_addr()?;
let acceptor: tokio_rustls::TlsAcceptor = build_ca()
.gen_server_config(&format!("localhost:{}", addr.port()).parse().unwrap())
let acceptor: tokio_rustls::TlsAcceptor = ca
.gen_server_config(&"localhost".parse().unwrap())
.await
.into();
let listener = TlsListener::new(acceptor, tokio::net::TcpListener::from_std(listener)?);

let mut listener = TlsListener::new(acceptor, listener);
let (tx, rx) = tokio::sync::oneshot::channel();

tokio::spawn(
Server::builder(listener)
.serve(make_svc)
.with_graceful_shutdown(async { rx.await.unwrap_or_default() }),
);
tokio::spawn(async move {
let server = auto::Builder::new(TokioExecutor::new());
let shutdown = Shutdown::new(async { rx.await.unwrap_or_default() });
let guard = shutdown.guard_weak();

loop {
tokio::select! {
res = listener.accept() => {
let Ok((tcp, _)) = res else {
continue;
};

let server = server.clone();

shutdown.spawn_task(async move {
server
.serve_connection_with_upgrades(TokioIo::new(tcp), service_fn(test_server))
.await
.unwrap();
});
}
_ = guard.cancelled() => {
break;
}
}
}

shutdown.shutdown().await;
});

Ok((addr, tx))
}

fn native_tls_client() -> hyper::client::Client<hyper_tls::HttpsConnector<HttpConnector>> {
fn native_tls_client() -> Client<hyper_tls::HttpsConnector<HttpConnector>, Body> {
let mut http = HttpConnector::new();
http.enforce_http(false);
let ca_cert =
Expand All @@ -110,25 +145,25 @@ fn native_tls_client() -> hyper::client::Client<hyper_tls::HttpsConnector<HttpCo

let https: hyper_tls::HttpsConnector<HttpConnector> = (http, tls).into();

hyper::Client::builder().build(https)
Client::builder(TokioExecutor::new()).build(https)
}

fn start_proxy(
async fn start_proxy(
ca: impl CertificateAuthority,
) -> Result<(SocketAddr, Sender<()>), Box<dyn std::error::Error>> {
let listener = TcpListener::bind(SocketAddr::from(([127, 0, 0, 1], 0)))?;
let listener = TcpListener::bind(SocketAddr::from(([127, 0, 0, 1], 0))).await?;
let addr = listener.local_addr()?;
let (tx, rx) = tokio::sync::oneshot::channel();

let proxy = Proxy::builder()
.with_listener(listener)
.with_client(native_tls_client())
.with_ca(ca)
.with_graceful_shutdown(async {
rx.await.unwrap_or_default();
})
.build();

tokio::spawn(proxy.start(async {
rx.await.unwrap_or_default();
}));
tokio::spawn(proxy.start());

Ok((addr, tx))
}
Expand Down Expand Up @@ -157,9 +192,9 @@ fn bench_local(c: &mut Criterion) {
let runtime = runtime();
let _guard = runtime.enter();

let (proxy_addr, stop_proxy) = start_proxy(build_ca()).unwrap();
let (http_addr, stop_http) = start_http_server().unwrap();
let (https_addr, stop_https) = runtime.block_on(start_https_server()).unwrap();
let (proxy_addr, stop_proxy) = runtime.block_on(start_proxy(build_ca())).unwrap();
let (http_addr, stop_http) = runtime.block_on(start_http_server()).unwrap();
let (https_addr, stop_https) = runtime.block_on(start_https_server(build_ca())).unwrap();
let client = build_client();
let proxied_client = build_proxied_client(&proxy_addr.to_string());

Expand Down Expand Up @@ -212,7 +247,7 @@ fn bench_remote(c: &mut Criterion) {
let runtime = runtime();
let _guard = runtime.enter();

let (proxy_addr, stop_proxy) = start_proxy(build_ca()).unwrap();
let (proxy_addr, stop_proxy) = runtime.block_on(start_proxy(build_ca())).unwrap();
let client = build_client();
let proxied_client = build_proxied_client(&proxy_addr.to_string());

Expand Down
Loading

0 comments on commit 2f005f9

Please sign in to comment.