socks-hub 0.2.7

Convert http proxy or socks5 proxy to socks5 proxy.
Documentation
use crate::{BoxError, CONNECT_TIMEOUT, Config};
use std::{net::SocketAddr, sync::Arc};
use tokio::net::TcpListener;

#[cfg(feature = "acl")]
static ACL_CENTER: std::sync::OnceLock<Option<crate::acl::AccessControl>> = std::sync::OnceLock::new();

pub async fn main_entry<F>(config: &Config, cancel_token: tokio_util::sync::CancellationToken, callback: Option<F>) -> Result<(), BoxError>
where
    F: FnOnce(SocketAddr) + Send + Sync + 'static,
{
    #[cfg(feature = "acl")]
    ACL_CENTER.get_or_init(|| {
        config
            .acl_file
            .as_ref()
            .and_then(|acl_file| match crate::acl::AccessControl::load_from_file(acl_file) {
                Ok(ac) => Some(ac),
                Err(e) => {
                    log::warn!("Could not init ACL: {e}");
                    None
                }
            })
    });

    let listen_addr: SocketAddr = config.listen_proxy_role.addr.clone().try_into()?;

    let listener = TcpListener::bind(listen_addr).await?;

    if let Some(callback) = callback {
        callback(listener.local_addr()?);
    } else {
        log::info!("Listening on {}", config.listen_proxy_role);
    }

    let config = std::sync::Arc::new(config.clone());

    loop {
        let config = config.clone();
        tokio::select! {
            _ = cancel_token.cancelled() => {
                log::info!("quit signal received");
                break;
            }
            result = listener.accept() => {
                let (stream, incoming) = result?;
                tokio::task::spawn(async move {
                    let server = config.remote_server.clone();
                    let credentials = config.listen_proxy_role.credentials.clone().unwrap_or_default();
                    let middle_server = config.middle_server.clone();
                    let middle_server_for_conn = middle_server.clone();
                    let connector: crate::httpproxy::HttpConnector = Arc::new(move |dst| {
                        let server = server.clone();
                        let middle_server = middle_server_for_conn.clone();
                        Box::pin(async move {
                            let stream = crate::create_s5_connect(server, CONNECT_TIMEOUT, &dst, middle_server).await?;
                            Ok(Box::new(stream) as crate::httpproxy::BoxedStream)
                        })
                    });
                    if let Err(err) = crate::httpproxy::run_http_service(stream, connector, credentials).await {
                        log::error!("http service on incoming {incoming} error: {err}");
                    }
                });
            }
        }
    }
    Ok(())
}