Send user-agent in proxy tunnel requests
This commit is contained in:
		| @@ -137,6 +137,11 @@ impl ClientBuilder { | |||||||
|         let proxies = Arc::new(config.proxies); |         let proxies = Arc::new(config.proxies); | ||||||
|  |  | ||||||
|         let mut connector = { |         let mut connector = { | ||||||
|  |             #[cfg(feature = "tls")] | ||||||
|  |             fn user_agent(headers: &HeaderMap) -> HeaderValue { | ||||||
|  |                 headers[USER_AGENT].clone() | ||||||
|  |             } | ||||||
|  |  | ||||||
|             #[cfg(feature = "tls")] |             #[cfg(feature = "tls")] | ||||||
|             match config.tls { |             match config.tls { | ||||||
|                 #[cfg(feature = "default-tls")] |                 #[cfg(feature = "default-tls")] | ||||||
| @@ -156,6 +161,7 @@ impl ClientBuilder { | |||||||
|                     Connector::new_default_tls( |                     Connector::new_default_tls( | ||||||
|                         tls, |                         tls, | ||||||
|                         proxies.clone(), |                         proxies.clone(), | ||||||
|  |                         user_agent(&config.headers), | ||||||
|                         config.local_address, |                         config.local_address, | ||||||
|                         config.nodelay, |                         config.nodelay, | ||||||
|                     )? |                     )? | ||||||
| @@ -189,6 +195,7 @@ impl ClientBuilder { | |||||||
|                     Connector::new_rustls_tls( |                     Connector::new_rustls_tls( | ||||||
|                         tls, |                         tls, | ||||||
|                         proxies.clone(), |                         proxies.clone(), | ||||||
|  |                         user_agent(&config.headers), | ||||||
|                         config.local_address, |                         config.local_address, | ||||||
|                         config.nodelay, |                         config.nodelay, | ||||||
|                     )? |                     )? | ||||||
|   | |||||||
							
								
								
									
										334
									
								
								src/connect.rs
									
									
									
									
									
								
							
							
						
						
									
										334
									
								
								src/connect.rs
									
									
									
									
									
								
							| @@ -5,6 +5,8 @@ use tokio::io::{AsyncRead, AsyncWrite}; | |||||||
|  |  | ||||||
| #[cfg(feature = "default-tls")] | #[cfg(feature = "default-tls")] | ||||||
| use native_tls::{TlsConnector, TlsConnectorBuilder}; | use native_tls::{TlsConnector, TlsConnectorBuilder}; | ||||||
|  | #[cfg(feature = "tls")] | ||||||
|  | use http::header::HeaderValue; | ||||||
|  |  | ||||||
| use std::future::Future; | use std::future::Future; | ||||||
| use std::io; | use std::io; | ||||||
| @@ -23,12 +25,15 @@ use tokio::future::FutureExt as _; | |||||||
| //#[cfg(not(feature = "trust-dns"))] | //#[cfg(not(feature = "trust-dns"))] | ||||||
| type HttpConnector = hyper::client::HttpConnector; | type HttpConnector = hyper::client::HttpConnector; | ||||||
|  |  | ||||||
|  | #[derive(Clone)] | ||||||
| pub(crate) struct Connector { | pub(crate) struct Connector { | ||||||
|     inner: Inner, |     inner: Inner, | ||||||
|     proxies: Arc<Vec<Proxy>>, |     proxies: Arc<Vec<Proxy>>, | ||||||
|     timeout: Option<Duration>, |     timeout: Option<Duration>, | ||||||
|     #[cfg(feature = "tls")] |     #[cfg(feature = "tls")] | ||||||
|     nodelay: bool, |     nodelay: bool, | ||||||
|  |     #[cfg(feature = "tls")] | ||||||
|  |     user_agent: HeaderValue, | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Clone)] | #[derive(Clone)] | ||||||
| @@ -69,6 +74,7 @@ impl Connector { | |||||||
|     pub(crate) fn new_default_tls<T>( |     pub(crate) fn new_default_tls<T>( | ||||||
|         tls: TlsConnectorBuilder, |         tls: TlsConnectorBuilder, | ||||||
|         proxies: Arc<Vec<Proxy>>, |         proxies: Arc<Vec<Proxy>>, | ||||||
|  |         user_agent: HeaderValue, | ||||||
|         local_addr: T, |         local_addr: T, | ||||||
|         nodelay: bool, |         nodelay: bool, | ||||||
|     ) -> crate::Result<Connector> |     ) -> crate::Result<Connector> | ||||||
| @@ -86,6 +92,7 @@ impl Connector { | |||||||
|             proxies, |             proxies, | ||||||
|             timeout: None, |             timeout: None, | ||||||
|             nodelay, |             nodelay, | ||||||
|  |             user_agent, | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -93,6 +100,7 @@ impl Connector { | |||||||
|     pub(crate) fn new_rustls_tls<T>( |     pub(crate) fn new_rustls_tls<T>( | ||||||
|         tls: rustls::ClientConfig, |         tls: rustls::ClientConfig, | ||||||
|         proxies: Arc<Vec<Proxy>>, |         proxies: Arc<Vec<Proxy>>, | ||||||
|  |         user_agent: HeaderValue, | ||||||
|         local_addr: T, |         local_addr: T, | ||||||
|         nodelay: bool, |         nodelay: bool, | ||||||
|     ) -> crate::Result<Connector> |     ) -> crate::Result<Connector> | ||||||
| @@ -121,6 +129,7 @@ impl Connector { | |||||||
|             proxies, |             proxies, | ||||||
|             timeout: None, |             timeout: None, | ||||||
|             nodelay, |             nodelay, | ||||||
|  |             user_agent, | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -186,6 +195,149 @@ impl Connector { | |||||||
|             Inner::Http(_) => socks::connect(proxy, dst, dns), |             Inner::Http(_) => socks::connect(proxy, dst, dns), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     async fn connect_with_maybe_proxy( | ||||||
|  |         self, | ||||||
|  |         dst: Destination, | ||||||
|  |         is_proxy: bool, | ||||||
|  |     ) -> Result<(Conn, Connected), io::Error> { | ||||||
|  |         match self.inner { | ||||||
|  |             #[cfg(not(feature = "tls"))] | ||||||
|  |             Inner::Http(http) => { | ||||||
|  |                 let (io, connected) = http.connect(dst).await?; | ||||||
|  |                 Ok((Box::new(io) as Conn, connected.proxy(is_proxy))) | ||||||
|  |             } | ||||||
|  |             #[cfg(feature = "default-tls")] | ||||||
|  |             Inner::DefaultTls(http, tls) => { | ||||||
|  |                 let mut http = http.clone(); | ||||||
|  |  | ||||||
|  |                 http.set_nodelay(self.nodelay || (dst.scheme() == "https")); | ||||||
|  |  | ||||||
|  |                 let tls_connector = tokio_tls::TlsConnector::from(tls.clone()); | ||||||
|  |                 let http = hyper_tls::HttpsConnector::from((http, tls_connector)); | ||||||
|  |                 let (io, connected) = http.connect(dst).await?; | ||||||
|  |                 //TODO: where's this at now? | ||||||
|  |                 //if let hyper_tls::MaybeHttpsStream::Https(_stream) = &io { | ||||||
|  |                 //    if !no_delay { | ||||||
|  |                 //        stream.set_nodelay(false)?; | ||||||
|  |                 //    } | ||||||
|  |                 //} | ||||||
|  |  | ||||||
|  |                 Ok((Box::new(io) as Conn, connected.proxy(is_proxy))) | ||||||
|  |             } | ||||||
|  |             #[cfg(feature = "rustls-tls")] | ||||||
|  |             Inner::RustlsTls { http, tls, .. } => { | ||||||
|  |                 let mut http = http.clone(); | ||||||
|  |  | ||||||
|  |                 // Disable Nagle's algorithm for TLS handshake | ||||||
|  |                 // | ||||||
|  |                 // https://www.openssl.org/docs/man1.1.1/man3/SSL_connect.html#NOTES | ||||||
|  |                 http.set_nodelay(no_delay || (dst.scheme() == "https")); | ||||||
|  |  | ||||||
|  |                 let http = hyper_rustls::HttpsConnector::from((http, tls.clone())); | ||||||
|  |                 let (io, connected) = http.connect(dst).await?; | ||||||
|  |                 if let hyper_rustls::MaybeHttpsStream::Https(stream) = &io { | ||||||
|  |                     if !no_delay { | ||||||
|  |                         let (io, _) = stream.get_ref(); | ||||||
|  |                         io.set_nodelay(false)?; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 Ok((Box::new(io) as Conn, connected.proxy(is_proxy))) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     async fn connect_via_proxy( | ||||||
|  |         self, | ||||||
|  |         dst: Destination, | ||||||
|  |         proxy_scheme: ProxyScheme, | ||||||
|  |     ) -> Result<(Conn, Connected), io::Error> { | ||||||
|  |         log::trace!("proxy({:?}) intercepts {:?}", proxy_scheme, dst); | ||||||
|  |  | ||||||
|  |         let (puri, _auth) = match proxy_scheme { | ||||||
|  |             ProxyScheme::Http { uri, auth, .. } => (uri, auth), | ||||||
|  |             #[cfg(feature = "socks")] | ||||||
|  |             ProxyScheme::Socks5 { .. } => return this.connect_socks(dst, proxy_scheme), | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         let mut ndst = dst.clone(); | ||||||
|  |  | ||||||
|  |         let new_scheme = puri.scheme_part().map(Scheme::as_str).unwrap_or("http"); | ||||||
|  |         ndst.set_scheme(new_scheme) | ||||||
|  |             .expect("proxy target scheme should be valid"); | ||||||
|  |  | ||||||
|  |         ndst.set_host(puri.host().expect("proxy target should have host")) | ||||||
|  |             .expect("proxy target host should be valid"); | ||||||
|  |  | ||||||
|  |         ndst.set_port(puri.port_part().map(|port| port.as_u16())); | ||||||
|  |  | ||||||
|  |         #[cfg(feature = "tls")] | ||||||
|  |         let auth = _auth; | ||||||
|  |  | ||||||
|  |         match &self.inner { | ||||||
|  |             #[cfg(feature = "default-tls")] | ||||||
|  |             Inner::DefaultTls(http, tls) => { | ||||||
|  |                 if dst.scheme() == "https" { | ||||||
|  |                     let host = dst.host().to_owned(); | ||||||
|  |                     let port = dst.port().unwrap_or(443); | ||||||
|  |                     let mut http = http.clone(); | ||||||
|  |                     http.set_nodelay(self.nodelay); | ||||||
|  |                     let tls_connector = tokio_tls::TlsConnector::from(tls.clone()); | ||||||
|  |                     let http = hyper_tls::HttpsConnector::from((http, tls_connector)); | ||||||
|  |                     let (conn, connected) = http.connect(ndst).await?; | ||||||
|  |                     log::trace!("tunneling HTTPS over proxy"); | ||||||
|  |                     let tunneled = tunnel(conn, host.clone(), port, self.user_agent.clone(), auth).await?; | ||||||
|  |                     let tls_connector = tokio_tls::TlsConnector::from(tls.clone()); | ||||||
|  |                     let io = tls_connector | ||||||
|  |                         .connect(&host, tunneled) | ||||||
|  |                         .await | ||||||
|  |                         .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; | ||||||
|  |                     return Ok((Box::new(io) as Conn, connected.proxy(true))); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             #[cfg(feature = "rustls-tls")] | ||||||
|  |             Inner::RustlsTls { | ||||||
|  |                 http, | ||||||
|  |                 tls, | ||||||
|  |                 tls_proxy, | ||||||
|  |             } => { | ||||||
|  |                 if dst.scheme() == "https" { | ||||||
|  |                     use rustls::Session; | ||||||
|  |                     use tokio_rustls::webpki::DNSNameRef; | ||||||
|  |                     use tokio_rustls::TlsConnector as RustlsConnector; | ||||||
|  |  | ||||||
|  |                     let host = dst.host().to_owned(); | ||||||
|  |                     let port = dst.port().unwrap_or(443); | ||||||
|  |                     let mut http = http.clone(); | ||||||
|  |                     http.set_nodelay(no_delay); | ||||||
|  |                     let http = hyper_rustls::HttpsConnector::from((http, tls_proxy.clone())); | ||||||
|  |                     let tls = tls.clone(); | ||||||
|  |                     let (conn, connected) = http.connect(ndst).await?; | ||||||
|  |                     log::trace!("tunneling HTTPS over proxy"); | ||||||
|  |                     let maybe_dnsname = DNSNameRef::try_from_ascii_str(&host) | ||||||
|  |                         .map(|dnsname| dnsname.to_owned()) | ||||||
|  |                         .map_err(|_| io::Error::new(io::ErrorKind::Other, "Invalid DNS Name")); | ||||||
|  |                     let tunneled = tunnel(conn, host, port, auth).await?; | ||||||
|  |                     let dnsname = maybe_dnsname?; | ||||||
|  |                     let io = RustlsConnector::from(tls) | ||||||
|  |                         .connect(dnsname.as_ref(), tunneled) | ||||||
|  |                         .await | ||||||
|  |                         .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; | ||||||
|  |                     let connected = if io.get_ref().1.get_alpn_protocol() == Some(b"h2") { | ||||||
|  |                         connected.negotiated_h2() | ||||||
|  |                     } else { | ||||||
|  |                         connected | ||||||
|  |                     }; | ||||||
|  |                     return Ok((Box::new(io) as Conn, connected.proxy(true))); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             #[cfg(not(feature = "tls"))] | ||||||
|  |             Inner::Http(_) => (), | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         self.connect_with_maybe_proxy(ndst, true).await | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| //#[cfg(feature = "trust-dns")] | //#[cfg(feature = "trust-dns")] | ||||||
| @@ -200,151 +352,6 @@ fn http_connector() -> crate::Result<HttpConnector> { | |||||||
|     Ok(HttpConnector::new()) |     Ok(HttpConnector::new()) | ||||||
| } | } | ||||||
|  |  | ||||||
| async fn connect_with_maybe_proxy( |  | ||||||
|     inner: Inner, |  | ||||||
|     dst: Destination, |  | ||||||
|     is_proxy: bool, |  | ||||||
|     no_delay: bool, |  | ||||||
| ) -> Result<(Conn, Connected), io::Error> { |  | ||||||
|     match inner { |  | ||||||
|         #[cfg(not(feature = "tls"))] |  | ||||||
|         Inner::Http(http) => { |  | ||||||
|             drop(no_delay); // only used for TLS? |  | ||||||
|             let (io, connected) = http.connect(dst).await?; |  | ||||||
|             Ok((Box::new(io) as Conn, connected.proxy(is_proxy))) |  | ||||||
|         } |  | ||||||
|         #[cfg(feature = "default-tls")] |  | ||||||
|         Inner::DefaultTls(http, tls) => { |  | ||||||
|             let mut http = http.clone(); |  | ||||||
|  |  | ||||||
|             http.set_nodelay(no_delay || (dst.scheme() == "https")); |  | ||||||
|  |  | ||||||
|             let tls_connector = tokio_tls::TlsConnector::from(tls.clone()); |  | ||||||
|             let http = hyper_tls::HttpsConnector::from((http, tls_connector)); |  | ||||||
|             let (io, connected) = http.connect(dst).await?; |  | ||||||
|             //TODO: where's this at now? |  | ||||||
|             //if let hyper_tls::MaybeHttpsStream::Https(_stream) = &io { |  | ||||||
|             //    if !no_delay { |  | ||||||
|             //        stream.set_nodelay(false)?; |  | ||||||
|             //    } |  | ||||||
|             //} |  | ||||||
|  |  | ||||||
|             Ok((Box::new(io) as Conn, connected.proxy(is_proxy))) |  | ||||||
|         } |  | ||||||
|         #[cfg(feature = "rustls-tls")] |  | ||||||
|         Inner::RustlsTls { http, tls, .. } => { |  | ||||||
|             let mut http = http.clone(); |  | ||||||
|  |  | ||||||
|             // Disable Nagle's algorithm for TLS handshake |  | ||||||
|             // |  | ||||||
|             // https://www.openssl.org/docs/man1.1.1/man3/SSL_connect.html#NOTES |  | ||||||
|             http.set_nodelay(no_delay || (dst.scheme() == "https")); |  | ||||||
|  |  | ||||||
|             let http = hyper_rustls::HttpsConnector::from((http, tls.clone())); |  | ||||||
|             let (io, connected) = http.connect(dst).await?; |  | ||||||
|             if let hyper_rustls::MaybeHttpsStream::Https(stream) = &io { |  | ||||||
|                 if !no_delay { |  | ||||||
|                     let (io, _) = stream.get_ref(); |  | ||||||
|                     io.set_nodelay(false)?; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             Ok((Box::new(io) as Conn, connected.proxy(is_proxy))) |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| async fn connect_via_proxy( |  | ||||||
|     inner: Inner, |  | ||||||
|     dst: Destination, |  | ||||||
|     proxy_scheme: ProxyScheme, |  | ||||||
|     no_delay: bool, |  | ||||||
| ) -> Result<(Conn, Connected), io::Error> { |  | ||||||
|     log::trace!("proxy({:?}) intercepts {:?}", proxy_scheme, dst); |  | ||||||
|  |  | ||||||
|     let (puri, _auth) = match proxy_scheme { |  | ||||||
|         ProxyScheme::Http { uri, auth, .. } => (uri, auth), |  | ||||||
|         #[cfg(feature = "socks")] |  | ||||||
|         ProxyScheme::Socks5 { .. } => return this.connect_socks(dst, proxy_scheme), |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     let mut ndst = dst.clone(); |  | ||||||
|  |  | ||||||
|     let new_scheme = puri.scheme_part().map(Scheme::as_str).unwrap_or("http"); |  | ||||||
|     ndst.set_scheme(new_scheme) |  | ||||||
|         .expect("proxy target scheme should be valid"); |  | ||||||
|  |  | ||||||
|     ndst.set_host(puri.host().expect("proxy target should have host")) |  | ||||||
|         .expect("proxy target host should be valid"); |  | ||||||
|  |  | ||||||
|     ndst.set_port(puri.port_part().map(|port| port.as_u16())); |  | ||||||
|  |  | ||||||
|     #[cfg(feature = "tls")] |  | ||||||
|     let auth = _auth; |  | ||||||
|  |  | ||||||
|     match &inner { |  | ||||||
|         #[cfg(feature = "default-tls")] |  | ||||||
|         Inner::DefaultTls(http, tls) => { |  | ||||||
|             if dst.scheme() == "https" { |  | ||||||
|                 let host = dst.host().to_owned(); |  | ||||||
|                 let port = dst.port().unwrap_or(443); |  | ||||||
|                 let mut http = http.clone(); |  | ||||||
|                 http.set_nodelay(no_delay); |  | ||||||
|                 let tls_connector = tokio_tls::TlsConnector::from(tls.clone()); |  | ||||||
|                 let http = hyper_tls::HttpsConnector::from((http, tls_connector)); |  | ||||||
|                 let (conn, connected) = http.connect(ndst).await?; |  | ||||||
|                 log::trace!("tunneling HTTPS over proxy"); |  | ||||||
|                 let tunneled = tunnel(conn, host.clone(), port, auth).await?; |  | ||||||
|                 let tls_connector = tokio_tls::TlsConnector::from(tls.clone()); |  | ||||||
|                 let io = tls_connector |  | ||||||
|                     .connect(&host, tunneled) |  | ||||||
|                     .await |  | ||||||
|                     .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; |  | ||||||
|                 return Ok((Box::new(io) as Conn, connected.proxy(true))); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         #[cfg(feature = "rustls-tls")] |  | ||||||
|         Inner::RustlsTls { |  | ||||||
|             http, |  | ||||||
|             tls, |  | ||||||
|             tls_proxy, |  | ||||||
|         } => { |  | ||||||
|             if dst.scheme() == "https" { |  | ||||||
|                 use rustls::Session; |  | ||||||
|                 use tokio_rustls::webpki::DNSNameRef; |  | ||||||
|                 use tokio_rustls::TlsConnector as RustlsConnector; |  | ||||||
|  |  | ||||||
|                 let host = dst.host().to_owned(); |  | ||||||
|                 let port = dst.port().unwrap_or(443); |  | ||||||
|                 let mut http = http.clone(); |  | ||||||
|                 http.set_nodelay(no_delay); |  | ||||||
|                 let http = hyper_rustls::HttpsConnector::from((http, tls_proxy.clone())); |  | ||||||
|                 let tls = tls.clone(); |  | ||||||
|                 let (conn, connected) = http.connect(ndst).await?; |  | ||||||
|                 log::trace!("tunneling HTTPS over proxy"); |  | ||||||
|                 let maybe_dnsname = DNSNameRef::try_from_ascii_str(&host) |  | ||||||
|                     .map(|dnsname| dnsname.to_owned()) |  | ||||||
|                     .map_err(|_| io::Error::new(io::ErrorKind::Other, "Invalid DNS Name")); |  | ||||||
|                 let tunneled = tunnel(conn, host, port, auth).await?; |  | ||||||
|                 let dnsname = maybe_dnsname?; |  | ||||||
|                 let io = RustlsConnector::from(tls) |  | ||||||
|                     .connect(dnsname.as_ref(), tunneled) |  | ||||||
|                     .await |  | ||||||
|                     .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; |  | ||||||
|                 let connected = if io.get_ref().1.get_alpn_protocol() == Some(b"h2") { |  | ||||||
|                     connected.negotiated_h2() |  | ||||||
|                 } else { |  | ||||||
|                     connected |  | ||||||
|                 }; |  | ||||||
|                 return Ok((Box::new(io) as Conn, connected.proxy(true))); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         #[cfg(not(feature = "tls"))] |  | ||||||
|         Inner::Http(_) => (), |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     connect_with_maybe_proxy(inner, ndst, true, no_delay).await |  | ||||||
| } |  | ||||||
|  |  | ||||||
| async fn with_timeout<T, F>(f: F, timeout: Option<Duration>) -> Result<T, io::Error> | async fn with_timeout<T, F>(f: F, timeout: Option<Duration>) -> Result<T, io::Error> | ||||||
| where | where | ||||||
| @@ -366,15 +373,11 @@ impl Connect for Connector { | |||||||
|     type Future = Connecting; |     type Future = Connecting; | ||||||
|  |  | ||||||
|     fn connect(&self, dst: Destination) -> Self::Future { |     fn connect(&self, dst: Destination) -> Self::Future { | ||||||
|         #[cfg(feature = "tls")] |  | ||||||
|         let no_delay = self.nodelay; |  | ||||||
|         #[cfg(not(feature = "tls"))] |  | ||||||
|         let no_delay = false; |  | ||||||
|         let timeout = self.timeout; |         let timeout = self.timeout; | ||||||
|         for prox in self.proxies.iter() { |         for prox in self.proxies.iter() { | ||||||
|             if let Some(proxy_scheme) = prox.intercept(&dst) { |             if let Some(proxy_scheme) = prox.intercept(&dst) { | ||||||
|                 return with_timeout( |                 return with_timeout( | ||||||
|                     connect_via_proxy(self.inner.clone(), dst, proxy_scheme, no_delay), |                     self.clone().connect_via_proxy(dst, proxy_scheme), | ||||||
|                     timeout, |                     timeout, | ||||||
|                 ) |                 ) | ||||||
|                 .boxed(); |                 .boxed(); | ||||||
| @@ -382,7 +385,7 @@ impl Connect for Connector { | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         with_timeout( |         with_timeout( | ||||||
|             connect_with_maybe_proxy(self.inner.clone(), dst, false, no_delay), |             self.clone().connect_with_maybe_proxy(dst, false), | ||||||
|             timeout, |             timeout, | ||||||
|         ) |         ) | ||||||
|         .boxed() |         .boxed() | ||||||
| @@ -401,7 +404,8 @@ async fn tunnel<T>( | |||||||
|     mut conn: T, |     mut conn: T, | ||||||
|     host: String, |     host: String, | ||||||
|     port: u16, |     port: u16, | ||||||
|     auth: Option<http::header::HeaderValue>, |     user_agent: HeaderValue, | ||||||
|  |     auth: Option<HeaderValue>, | ||||||
| ) -> Result<T, io::Error> | ) -> Result<T, io::Error> | ||||||
| where | where | ||||||
|     T: AsyncRead + AsyncWrite + Unpin, |     T: AsyncRead + AsyncWrite + Unpin, | ||||||
| @@ -417,6 +421,14 @@ where | |||||||
|     ) |     ) | ||||||
|     .into_bytes(); |     .into_bytes(); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     // user-agent | ||||||
|  |     buf.extend_from_slice(b"User-Agent: "); | ||||||
|  |     buf.extend_from_slice(user_agent.as_bytes()); | ||||||
|  |     buf.extend_from_slice(b"\r\n"); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     // proxy-authorization | ||||||
|     if let Some(value) = auth { |     if let Some(value) = auth { | ||||||
|         log::debug!("tunnel to {}:{} using basic auth", host, port); |         log::debug!("tunnel to {}:{} using basic auth", host, port); | ||||||
|         buf.extend_from_slice(b"Proxy-Authorization: "); |         buf.extend_from_slice(b"Proxy-Authorization: "); | ||||||
| @@ -541,6 +553,7 @@ mod tests { | |||||||
|     use tokio::net::tcp::TcpStream; |     use tokio::net::tcp::TcpStream; | ||||||
|     use tokio::runtime::current_thread::Runtime; |     use tokio::runtime::current_thread::Runtime; | ||||||
|  |  | ||||||
|  |     static TUNNEL_UA: &'static str = "tunnel-test/x.y"; | ||||||
|     static TUNNEL_OK: &[u8] = b"\ |     static TUNNEL_OK: &[u8] = b"\ | ||||||
|         HTTP/1.1 200 OK\r\n\ |         HTTP/1.1 200 OK\r\n\ | ||||||
|         \r\n\ |         \r\n\ | ||||||
| @@ -560,11 +573,13 @@ mod tests { | |||||||
|                 "\ |                 "\ | ||||||
|                  CONNECT {0}:{1} HTTP/1.1\r\n\ |                  CONNECT {0}:{1} HTTP/1.1\r\n\ | ||||||
|                  Host: {0}:{1}\r\n\ |                  Host: {0}:{1}\r\n\ | ||||||
|                  {2}\ |                  User-Agent: {2}\r\n\ | ||||||
|  |                  {3}\ | ||||||
|                  \r\n\ |                  \r\n\ | ||||||
|                  ", |                  ", | ||||||
|                 addr.ip(), |                 addr.ip(), | ||||||
|                 addr.port(), |                 addr.port(), | ||||||
|  |                 TUNNEL_UA, | ||||||
|                 $auth |                 $auth | ||||||
|             ) |             ) | ||||||
|             .into_bytes(); |             .into_bytes(); | ||||||
| @@ -581,6 +596,10 @@ mod tests { | |||||||
|         }}; |         }}; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     fn ua() -> http::header::HeaderValue { | ||||||
|  |         http::header::HeaderValue::from_static(TUNNEL_UA) | ||||||
|  |     } | ||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_tunnel() { |     fn test_tunnel() { | ||||||
|         let addr = mock_tunnel!(); |         let addr = mock_tunnel!(); | ||||||
| @@ -590,7 +609,7 @@ mod tests { | |||||||
|             let tcp = TcpStream::connect(&addr).await?; |             let tcp = TcpStream::connect(&addr).await?; | ||||||
|             let host = addr.ip().to_string(); |             let host = addr.ip().to_string(); | ||||||
|             let port = addr.port(); |             let port = addr.port(); | ||||||
|             tunnel(tcp, host, port, None).await |             tunnel(tcp, host, port, ua(), None).await | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         rt.block_on(f).unwrap(); |         rt.block_on(f).unwrap(); | ||||||
| @@ -605,7 +624,7 @@ mod tests { | |||||||
|             let tcp = TcpStream::connect(&addr).await?; |             let tcp = TcpStream::connect(&addr).await?; | ||||||
|             let host = addr.ip().to_string(); |             let host = addr.ip().to_string(); | ||||||
|             let port = addr.port(); |             let port = addr.port(); | ||||||
|             tunnel(tcp, host, port, None).await |             tunnel(tcp, host, port, ua(), None).await | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         rt.block_on(f).unwrap_err(); |         rt.block_on(f).unwrap_err(); | ||||||
| @@ -620,7 +639,7 @@ mod tests { | |||||||
|             let tcp = TcpStream::connect(&addr).await?; |             let tcp = TcpStream::connect(&addr).await?; | ||||||
|             let host = addr.ip().to_string(); |             let host = addr.ip().to_string(); | ||||||
|             let port = addr.port(); |             let port = addr.port(); | ||||||
|             tunnel(tcp, host, port, None).await |             tunnel(tcp, host, port, ua(), None).await | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         rt.block_on(f).unwrap_err(); |         rt.block_on(f).unwrap_err(); | ||||||
| @@ -641,7 +660,7 @@ mod tests { | |||||||
|             let tcp = TcpStream::connect(&addr).await?; |             let tcp = TcpStream::connect(&addr).await?; | ||||||
|             let host = addr.ip().to_string(); |             let host = addr.ip().to_string(); | ||||||
|             let port = addr.port(); |             let port = addr.port(); | ||||||
|             tunnel(tcp, host, port, None).await |             tunnel(tcp, host, port, ua(), None).await | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         let error = rt.block_on(f).unwrap_err(); |         let error = rt.block_on(f).unwrap_err(); | ||||||
| @@ -664,6 +683,7 @@ mod tests { | |||||||
|                 tcp, |                 tcp, | ||||||
|                 host, |                 host, | ||||||
|                 port, |                 port, | ||||||
|  |                 ua(), | ||||||
|                 Some(proxy::encode_basic_auth("Aladdin", "open sesame")), |                 Some(proxy::encode_basic_auth("Aladdin", "open sesame")), | ||||||
|             ) |             ) | ||||||
|             .await |             .await | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user