use std::sync::Arc;
use async_trait::async_trait;
use log::debug;
use super::proxy_connector::ProxyConnector;
use super::tcp_client_handler_factory::create_tcp_client_handler;
use crate::address::{NetLocation, ResolvedLocation};
use crate::async_stream::{AsyncMessageStream, AsyncStream};
use crate::config::ClientConfig;
use crate::resolver::Resolver;
use crate::tcp::tcp_handler::{TcpClientHandler, TcpClientSetupResult};
#[derive(Debug)]
pub struct ProxyConnectorImpl {
location: NetLocation,
client_handler: Box<dyn TcpClientHandler>,
}
impl ProxyConnectorImpl {
pub fn from_config(config: ClientConfig, resolver: Arc<dyn Resolver>) -> Option<Self> {
if config.protocol.is_direct() {
return None;
}
let default_sni_hostname = config.address.address().hostname().map(ToString::to_string);
Some(Self {
location: config.address,
client_handler: create_tcp_client_handler(
config.protocol,
default_sni_hostname,
resolver,
),
})
}
}
#[async_trait]
impl ProxyConnector for ProxyConnectorImpl {
fn proxy_location(&self) -> &NetLocation {
&self.location
}
fn supports_udp_over_tcp(&self) -> bool {
self.client_handler.supports_udp_over_tcp()
}
async fn setup_tcp_stream(
&self,
stream: Box<dyn AsyncStream>,
target: &ResolvedLocation,
) -> std::io::Result<TcpClientSetupResult> {
debug!(
"[ProxyConnector] setup_tcp_stream: {} -> {}",
self.location, target
);
self.client_handler
.setup_client_tcp_stream(stream, target.clone())
.await
}
async fn setup_udp_bidirectional(
&self,
stream: Box<dyn AsyncStream>,
target: ResolvedLocation,
) -> std::io::Result<Box<dyn AsyncMessageStream>> {
debug!(
"[ProxyConnector] setup_udp_bidirectional: {} -> {}",
self.location, target
);
self.client_handler
.setup_client_udp_bidirectional(stream, target)
.await
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::config::ClientProxyConfig;
use crate::resolver::NativeResolver;
use std::net::{IpAddr, Ipv4Addr};
fn mock_resolver() -> Arc<dyn Resolver> {
Arc::new(NativeResolver::new())
}
#[test]
fn test_from_direct_config_returns_none() {
let config = ClientConfig::default();
assert!(config.protocol.is_direct());
assert!(ProxyConnectorImpl::from_config(config, mock_resolver()).is_none());
}
#[test]
fn test_from_proxy_config_returns_some() {
let config = ClientConfig {
address: NetLocation::from_ip_addr(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 1080),
protocol: ClientProxyConfig::Socks {
username: None,
password: None,
},
..Default::default()
};
let connector = ProxyConnectorImpl::from_config(config, mock_resolver());
assert!(connector.is_some());
let connector = connector.unwrap();
assert_eq!(connector.proxy_location().port(), 1080);
}
}