sthp 0.5.0

Convert Socks5 proxy into Http proxy
Documentation
use std::net::SocketAddr;

use hyper::StatusCode;
use sthp::proxy_request;
use tokio::net::TcpListener;

use color_eyre::Result;
use socksprox::Socks5Server;
use tokio::task::JoinHandle;

async fn start_socks_server() -> Result<(JoinHandle<()>, SocketAddr)> {
    // TODO: Currently, Socks5Server does not return the port it is bound to.
    // To work around this, we will use TcpListener to obtain a random available port.
    // After retrieving the port, we will immediately release it.
    let listener = TcpListener::bind("localhost:0").await?;
    let addr = listener.local_addr()?;
    let port = addr.port();
    eprintln!("socks proxy will listen on port {}", port);
    // release port
    drop(listener);

    let mut server = Socks5Server::new("localhost", port, None, None)
        .await
        .unwrap();
    let join_handle = tokio::task::spawn(async move {
        server.serve().await;
    });
    Ok((join_handle, addr))
}

#[tokio::test]
async fn simple_test() -> Result<()> {
    let (_, socks_proxy_addr) = start_socks_server().await?;
    let listener = TcpListener::bind("localhost:0").await?;
    let addr = listener.local_addr()?;
    let _ = tokio::task::spawn(async move {
        let (stream, proxy_addr) = listener.accept().await?;
        proxy_request(stream, proxy_addr, socks_proxy_addr, None, None, None).await?;
        eprintln!("new connection from: {:?}", proxy_addr);
        Ok::<_, color_eyre::eyre::Error>(())
    });
    eprintln!("http proxy will listen on {}", addr);

    let client = reqwest::Client::builder()
        .proxy(reqwest::Proxy::http(format!(
            "http://localhost:{}",
            addr.port()
        ))?)
        .build()?;

    assert_eq!(
        client.get("http://example.org").send().await?.status(),
        StatusCode::OK
    );
    Ok(())
}