#![allow(dead_code)]
use std::net::SocketAddr;
use axum::Router;
use axum::routing::get;
use axum_server::tls_rustls::RustlsConfig;
use noxy::Proxy;
use noxy::http::HttpService;
use rcgen::{CertificateParams, KeyPair};
use tokio::net::TcpListener;
pub fn install_crypto_provider() {
let _ = rustls::crypto::ring::default_provider().install_default();
}
pub async fn start_upstream(body: &'static str) -> SocketAddr {
install_crypto_provider();
let key_pair = KeyPair::generate().unwrap();
let params = CertificateParams::new(vec!["localhost".to_string()]).unwrap();
let cert = params.self_signed(&key_pair).unwrap();
let cert_der = cert.der().to_vec();
let key_der = key_pair.serialized_der().to_vec();
let config = RustlsConfig::from_der(vec![cert_der], key_der)
.await
.unwrap();
let app = Router::new().route("/", get(move || async move { body }));
let handle = axum_server::Handle::new();
let listener_handle = handle.clone();
tokio::spawn(async move {
axum_server::bind_rustls("127.0.0.1:0".parse().unwrap(), config)
.handle(handle)
.serve(app.into_make_service())
.await
.unwrap();
});
listener_handle.listening().await.unwrap()
}
pub async fn start_proxy(
layers: Vec<Box<dyn Fn(HttpService) -> HttpService + Send + Sync>>,
) -> SocketAddr {
let mut builder = Proxy::builder()
.ca_pem_files("tests/dummy-cert.pem", "tests/dummy-key.pem")
.unwrap()
.danger_accept_invalid_upstream_certs();
for layer in layers {
builder = builder.layer(BoxedLayer(layer));
}
let proxy = builder.build().unwrap();
let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
let addr = listener.local_addr().unwrap();
tokio::spawn(async move {
loop {
let (stream, client_addr) = listener.accept().await.unwrap();
let proxy = proxy.clone();
tokio::spawn(async move {
proxy.handle_connection(stream, client_addr).await.ok();
});
}
});
addr
}
pub async fn spawn_proxy(proxy: noxy::Proxy) -> SocketAddr {
let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
let addr = listener.local_addr().unwrap();
tokio::spawn(async move {
loop {
let (stream, client_addr) = listener.accept().await.unwrap();
let proxy = proxy.clone();
tokio::spawn(async move {
proxy.handle_connection(stream, client_addr).await.ok();
});
}
});
addr
}
pub fn http_client(proxy_addr: SocketAddr) -> reqwest::Client {
let ca_pem = std::fs::read("tests/dummy-cert.pem").unwrap();
let ca_cert = reqwest::tls::Certificate::from_pem(&ca_pem).unwrap();
reqwest::Client::builder()
.proxy(reqwest::Proxy::https(format!("http://{proxy_addr}")).unwrap())
.add_root_certificate(ca_cert)
.build()
.unwrap()
}
pub struct BoxedLayer(pub Box<dyn Fn(HttpService) -> HttpService + Send + Sync>);
impl tower::Layer<HttpService> for BoxedLayer {
type Service = HttpService;
fn layer(&self, inner: HttpService) -> HttpService {
(self.0)(inner)
}
}
pub async fn start_echo_upstream() -> SocketAddr {
install_crypto_provider();
let key_pair = KeyPair::generate().unwrap();
let params = CertificateParams::new(vec!["localhost".to_string()]).unwrap();
let cert = params.self_signed(&key_pair).unwrap();
let cert_der = cert.der().to_vec();
let key_der = key_pair.serialized_der().to_vec();
let config = RustlsConfig::from_der(vec![cert_der], key_der)
.await
.unwrap();
let app = Router::new().fallback(|headers: http::HeaderMap| async move {
let mut lines: Vec<String> = headers
.iter()
.map(|(name, value)| format!("{}: {}", name, value.to_str().unwrap_or("")))
.collect();
lines.sort();
lines.join("\n")
});
let handle = axum_server::Handle::new();
let listener_handle = handle.clone();
tokio::spawn(async move {
axum_server::bind_rustls("127.0.0.1:0".parse().unwrap(), config)
.handle(handle)
.serve(app.into_make_service())
.await
.unwrap();
});
listener_handle.listening().await.unwrap()
}
pub async fn start_http_upstream(body: &'static str) -> SocketAddr {
let app = Router::new().route("/", get(move || async move { body }));
let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
let addr = listener.local_addr().unwrap();
tokio::spawn(async move {
axum::serve(listener, app).await.unwrap();
});
addr
}
pub async fn start_reverse_proxy(
upstream_url: &str,
layers: Vec<Box<dyn Fn(HttpService) -> HttpService + Send + Sync>>,
) -> SocketAddr {
let mut builder = Proxy::builder()
.reverse_proxy(upstream_url)
.unwrap()
.danger_accept_invalid_upstream_certs();
for layer in layers {
builder = builder.layer(BoxedLayer(layer));
}
let proxy = builder.build().unwrap();
let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
let addr = listener.local_addr().unwrap();
tokio::spawn(async move {
proxy.listen_on(listener).await.unwrap();
});
addr
}