https_proxy 0.3.0

Stealth HTTPS forward proxy with automatic Let's Encrypt TLS and nginx camouflage
Documentation
#![allow(dead_code)]

use std::collections::HashMap;
use std::net::SocketAddr;

use http_body_util::Full;
use hyper::body::{Bytes, Incoming};
use hyper::server::conn::http1;
use hyper::service::service_fn;
use hyper::{Request, Response};
use hyper_util::rt::TokioIo;
use tokio::net::TcpListener;
use tokio_util::sync::CancellationToken;

pub struct EchoServer {
    pub addr: SocketAddr,
    shutdown: CancellationToken,
}

impl EchoServer {
    pub async fn start() -> Self {
        let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
        let addr = listener.local_addr().unwrap();
        let shutdown = CancellationToken::new();
        let token = shutdown.clone();

        tokio::spawn(async move {
            loop {
                tokio::select! {
                    _ = token.cancelled() => break,
                    result = listener.accept() => {
                        let (stream, _) = match result {
                            Ok(v) => v,
                            Err(_) => continue,
                        };
                        let token = token.clone();
                        tokio::spawn(async move {
                            let io = TokioIo::new(stream);
                            let service = service_fn(echo_handler);
                            let conn = http1::Builder::new()
                                .serve_connection(io, service);
                            tokio::select! {
                                _ = token.cancelled() => {}
                                result = conn => {
                                    if let Err(e) = result {
                                        eprintln!("echo server error: {e}");
                                    }
                                }
                            }
                        });
                    }
                }
            }
        });

        EchoServer { addr, shutdown }
    }
}

impl Drop for EchoServer {
    fn drop(&mut self) {
        self.shutdown.cancel();
    }
}

async fn echo_handler(req: Request<Incoming>) -> Result<Response<Full<Bytes>>, hyper::Error> {
    let mut headers: HashMap<String, String> = HashMap::new();
    for (name, value) in req.headers() {
        headers.insert(
            name.to_string(),
            value.to_str().unwrap_or("<binary>").to_string(),
        );
    }

    let info = serde_json::json!({
        "method": req.method().to_string(),
        "uri": req.uri().to_string(),
        "headers": headers,
    });

    let body = serde_json::to_string_pretty(&info).unwrap();

    Ok(Response::builder()
        .status(200)
        .header("Content-Type", "application/json")
        .body(Full::new(Bytes::from(body)))
        .unwrap())
}