use std::path::PathBuf;
use tokio::{
io::ReadBuf,
net::{TcpListener, TcpSocket},
};
use super::{
Acceptor, Context, Server,
http::{HttpAcceptor, accept::DefaultAcceptor, tls::RustlsAcceptor},
socks::Socks5Acceptor,
};
pub struct AutoDetectServer {
listener: TcpListener,
acceptor: (
Socks5Acceptor,
HttpAcceptor<DefaultAcceptor>,
HttpAcceptor<RustlsAcceptor>,
),
}
impl AutoDetectServer {
pub fn new<P>(ctx: Context, tls_cert: P, tls_key: P) -> std::io::Result<AutoDetectServer>
where
P: Into<Option<PathBuf>>,
{
let socket = if ctx.bind.is_ipv4() {
TcpSocket::new_v4()?
} else {
TcpSocket::new_v6()?
};
socket.set_nodelay(true)?;
socket.set_reuseaddr(true)?;
socket.bind(ctx.bind)?;
socket.listen(ctx.concurrent).and_then(|listener| {
HttpAcceptor::new(ctx.clone())
.with_https(tls_cert, tls_key)
.map(|https_acceptor| AutoDetectServer {
listener,
acceptor: (
Socks5Acceptor::new(ctx.clone()),
HttpAcceptor::new(ctx),
https_acceptor,
),
})
})
}
}
impl Server for AutoDetectServer {
async fn start(mut self) -> std::io::Result<()> {
tracing::info!(
"Http(s)/Socks5 proxy server listening on {}",
self.listener.local_addr()?
);
loop {
let conn = AutoDetectServer::incoming(&mut self.listener).await;
let acceptor = self.acceptor.clone();
tokio::spawn(async move {
let mut protocol = [0u8; 1];
let mut buf = ReadBuf::new(&mut protocol);
if std::future::poll_fn(|cx| conn.0.poll_peek(cx, &mut buf))
.await
.is_ok()
{
match protocol[0] {
0x05 => {
acceptor.0.accept(conn).await;
}
0x00..0x41 => {
acceptor.2.accept(conn).await;
}
_ => {
acceptor.1.accept(conn).await;
}
}
}
});
}
}
}