diff --git a/src/connect.rs b/src/connect.rs index d9777dbb3..263e2a835 100644 --- a/src/connect.rs +++ b/src/connect.rs @@ -22,7 +22,7 @@ pub(crate) use self::conn::{Conn, Unnameable}; use crate::{ core::{ client::{ - ConnRequest, + ConnExtra, ConnRequest, connect::{self, Connected, Connection, proxy}, }, rt::{Read, ReadBufCursor, TokioIo, Write}, @@ -258,9 +258,8 @@ impl ConnectorService { fn build_tls_connector( &self, mut http: HttpConnector, - req: &ConnRequest, + ex_data: &ConnExtra, ) -> Result, BoxError> { - let ex_data = req.ex_data(); http.set_connect_options(ex_data.tcp_connect_options().cloned()); let tls = match ex_data.tls_options() { Some(opts) => self.tls_builder.build(opts)?, @@ -284,7 +283,7 @@ impl ConnectorService { http.set_nodelay(true); } - let mut connector = self.build_tls_connector(http, &req)?; + let mut connector = self.build_tls_connector(http, req.ex_data())?; let io = connector.call(req).await?; // If the connection is HTTPS, wrap the TLS stream in a TlsConn for unified handling. @@ -319,31 +318,36 @@ impl ConnectorService { #[cfg(feature = "socks")] { - use proxy::{DnsResolve, Socks, SocksVersion}; + use proxy::socks::{DnsResolve, SocksConnector, Version}; if let Some((version, dns_resolve)) = match proxy.uri().scheme_str() { - Some("socks4") => Some((SocksVersion::V4, DnsResolve::Local)), - Some("socks4a") => Some((SocksVersion::V4, DnsResolve::Remote)), - Some("socks5") => Some((SocksVersion::V5, DnsResolve::Local)), - Some("socks5h") => Some((SocksVersion::V5, DnsResolve::Remote)), + Some("socks4") => Some((Version::V4, DnsResolve::Local)), + Some("socks4a") => Some((Version::V4, DnsResolve::Remote)), + Some("socks5") => Some((Version::V5, DnsResolve::Local)), + Some("socks5h") => Some((Version::V5, DnsResolve::Remote)), _ => None, } { trace!("connecting via SOCKS proxy: {:?}", proxy_uri); - let mut socks = Socks::new_with_resolver( + // Create a SOCKS connector with the specified version and DNS resolution strategy. + let mut socks = SocksConnector::new_with_resolver( + proxy_uri, self.http.clone(), self.resolver.clone(), - proxy_uri.clone(), ) .with_auth(proxy.raw_auth()) .with_version(version) .with_local_dns(dns_resolve); - let conn = socks.call(uri.clone()).await?; + let is_https = uri.scheme() == Some(&Scheme::HTTPS); + let conn = socks.call(uri).await?; - return if uri.scheme() == Some(&Scheme::HTTPS) { + return if is_https { trace!("socks HTTPS over proxy"); - let mut connector = self.build_tls_connector(self.http.clone(), &req)?; + + // Create a TLS connector for the established connection. + let mut connector = + self.build_tls_connector(self.http.clone(), req.ex_data())?; let established_conn = EstablishedConn::new(req, conn); let io = connector.call(established_conn).await?; @@ -367,13 +371,17 @@ impl ConnectorService { // Handle HTTPS proxy tunneling connection if uri.scheme() == Some(&Scheme::HTTPS) { trace!("tunneling HTTPS over HTTP proxy: {:?}", proxy_uri); - let mut connector = self.build_tls_connector(self.http.clone(), &req)?; - let mut tunnel = proxy::Tunnel::new(proxy_uri, connector.clone()); + // Create a tunnel connector with the proxy URI and the HTTP connector. + let mut connector = self.build_tls_connector(self.http.clone(), req.ex_data())?; + let mut tunnel = proxy::tunnel::TunnelConnector::new(proxy_uri, connector.clone()); + + // If the proxy has basic authentication, add it to the tunnel. if let Some(auth) = proxy.basic_auth() { tunnel = tunnel.with_auth(auth.clone()); } + // If the proxy has custom headers, add them to the tunnel. if let Some(headers) = proxy.custom_headers() { tunnel = tunnel.with_headers(headers.clone()); } @@ -383,6 +391,8 @@ impl ConnectorService { let tunneled = tunnel.call(uri).await?; let tunneled = TokioIo::new(tunneled); let tunneled = TokioIo::new(tunneled); + + // Create established connection with the tunneled stream. let established_conn = EstablishedConn::new(req, tunneled); let io = connector.call(established_conn).await?; diff --git a/src/core/client/connect/proxy/mod.rs b/src/core/client/connect/proxy/mod.rs index ec00922c7..79df3db75 100644 --- a/src/core/client/connect/proxy/mod.rs +++ b/src/core/client/connect/proxy/mod.rs @@ -1,8 +1,4 @@ //! Proxy helpers #[cfg(feature = "socks")] -mod socks; -mod tunnel; - -#[cfg(feature = "socks")] -pub use self::socks::{DnsResolve, Socks, SocksVersion}; -pub use self::tunnel::Tunnel; +pub mod socks; +pub mod tunnel; diff --git a/src/core/client/connect/proxy/socks.rs b/src/core/client/connect/proxy/socks.rs index 8c7a82971..8f1fe18cb 100644 --- a/src/core/client/connect/proxy/socks.rs +++ b/src/core/client/connect/proxy/socks.rs @@ -70,7 +70,7 @@ impl From for SocksError { /// Represents the SOCKS protocol version. #[derive(Clone, Copy)] #[repr(u8)] -pub enum SocksVersion { +pub enum Version { V4, V5, } @@ -110,34 +110,33 @@ where } } -pub struct Socks { +pub struct SocksConnector { inner: C, resolver: R, - proxy: Uri, + proxy_dst: Uri, auth: Option<(Bytes, Bytes)>, - version: SocksVersion, + version: Version, dns_resolve: DnsResolve, } -impl Socks +impl SocksConnector where R: Resolve + Clone, { - /// Create a new SOCKS service with the given inner service, resolver, proxy destination, - /// and optional authentication credentials. + /// Create a new SOCKS connector with the given inner service. /// - /// The `proxy` should be a valid URI with a scheme of `socks5`, `socks5h`, `socks4`, or - /// `socks4a`. + /// This wraps an underlying connector, and stores the address of a + /// SOCKS proxy server. /// - /// The `auth` parameter is optional and can be used to provide a username and password for - /// SOCKS authentication. If provided, it should be a tuple containing the username and - /// password. - pub fn new_with_resolver(inner: C, resolver: R, proxy: Uri) -> Self { - Socks { + /// A `SocksConnector` can then be called with any destination. The `proxy_dst` passed to + /// `call` will not be used to create the underlying connection, but will + /// be used in a SOCKS handshake sent to the proxy destination. + pub fn new_with_resolver(proxy_dst: Uri, inner: C, resolver: R) -> Self { + SocksConnector { inner, resolver, - proxy, - version: SocksVersion::V5, + proxy_dst, + version: Version::V5, dns_resolve: DnsResolve::Local, auth: None, } @@ -145,24 +144,24 @@ where /// Sets the authentication credentials for the SOCKS proxy connection. pub fn with_auth(self, auth: Option<(Bytes, Bytes)>) -> Self { - Socks { auth, ..self } + SocksConnector { auth, ..self } } /// Sets whether to use the SOCKS5 protocol for the proxy connection. - pub fn with_version(self, version: SocksVersion) -> Self { - Socks { version, ..self } + pub fn with_version(self, version: Version) -> Self { + SocksConnector { version, ..self } } /// Sets whether to resolve DNS locally or let the proxy handle DNS resolution. pub fn with_local_dns(self, dns_resolve: DnsResolve) -> Self { - Socks { + SocksConnector { dns_resolve, ..self } } } -impl Service for Socks +impl Service for SocksConnector where C: Service, C::Future: Send + 'static, @@ -180,7 +179,7 @@ where } fn call(&mut self, dst: Uri) -> Self::Future { - let connecting = self.inner.call(self.proxy.clone()); + let connecting = self.inner.call(self.proxy_dst.clone()); let version = self.version; let dns_resolve = self.dns_resolve; @@ -214,12 +213,12 @@ where }; match version { - SocksVersion::V4 => { + Version::V4 => { // For SOCKS4, we connect directly to the target address. let stream = Socks4Stream::connect_with_socket(socket, target_addr).await?; Ok(stream.into_inner().into_inner()) } - SocksVersion::V5 => { + Version::V5 => { // For SOCKS5, we need to handle authentication if provided. // The `auth` is an optional tuple of (username, password). let stream = match auth { diff --git a/src/core/client/connect/proxy/tunnel.rs b/src/core/client/connect/proxy/tunnel.rs index b1bc58b16..650d6e581 100644 --- a/src/core/client/connect/proxy/tunnel.rs +++ b/src/core/client/connect/proxy/tunnel.rs @@ -20,7 +20,7 @@ use crate::core::{ /// another connector, and after getting an underlying connection, it creates /// an HTTP CONNECT tunnel over it. #[derive(Debug)] -pub struct Tunnel { +pub struct TunnelConnector { headers: Headers, inner: C, proxy_dst: Uri, @@ -60,13 +60,13 @@ pin_project! { type BoxTunneling = Pin> + Send>>; -impl Tunnel { - /// Create a new Tunnel service. +impl TunnelConnector { + /// Create a new tunnel connector. /// /// This wraps an underlying connector, and stores the address of a /// tunneling proxy server. /// - /// A `Tunnel` can then be called with any destination. The `dst` passed to + /// A `TunnelConnector` can then be called with any destination. The `proxy_dst` passed to /// `call` will not be used to create the underlying connection, but will /// be used in an HTTP CONNECT request sent to the proxy destination. pub fn new(proxy_dst: Uri, connector: C) -> Self { @@ -119,7 +119,7 @@ impl Tunnel { } } -impl Service for Tunnel +impl Service for TunnelConnector where C: Service, C::Future: Send + 'static, @@ -270,7 +270,7 @@ mod tests { }; use tower::Service; - use super::Tunnel; + use super::TunnelConnector; use crate::core::client::connect::HttpConnector; #[cfg(not(miri))] @@ -280,7 +280,7 @@ mod tests { let addr = tcp.local_addr().expect("local_addr"); let proxy_dst = format!("http://{addr}").parse().expect("uri"); - let mut connector = Tunnel::new(proxy_dst, HttpConnector::new()); + let mut connector = TunnelConnector::new(proxy_dst, HttpConnector::new()); let t1 = tokio::spawn(async move { let _conn = connector .call("https://hyper.rs".parse().unwrap())