Remove username and password when parsing proxies (#686)
This commit is contained in:
		| @@ -1,5 +1,5 @@ | ||||
| use futures_util::FutureExt; | ||||
| use http::uri::Scheme; | ||||
| use http::uri::{Scheme, Authority}; | ||||
| use hyper::client::connect::{Connect, Connected, Destination}; | ||||
| use tokio::io::{AsyncRead, AsyncWrite}; | ||||
|  | ||||
| @@ -255,22 +255,13 @@ impl Connector { | ||||
|     ) -> Result<(Conn, Connected), io::Error> { | ||||
|         log::trace!("proxy({:?}) intercepts {:?}", proxy_scheme, dst); | ||||
|  | ||||
|         let (puri, _auth) = match proxy_scheme { | ||||
|             ProxyScheme::Http { uri, auth, .. } => (uri, auth), | ||||
|         let (proxy_dst, _auth) = match proxy_scheme { | ||||
|             ProxyScheme::Http { host, auth } => (into_dst(Scheme::HTTP, host), auth), | ||||
|             ProxyScheme::Https { host, auth } => (into_dst(Scheme::HTTPS, host), 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; | ||||
| @@ -285,7 +276,7 @@ impl Connector { | ||||
|                     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?; | ||||
|                     let (conn, connected) = http.connect(proxy_dst).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()); | ||||
| @@ -313,7 +304,7 @@ impl Connector { | ||||
|                     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?; | ||||
|                     let (conn, connected) = http.connect(proxy_dst).await?; | ||||
|                     log::trace!("tunneling HTTPS over proxy"); | ||||
|                     let maybe_dnsname = DNSNameRef::try_from_ascii_str(&host) | ||||
|                         .map(|dnsname| dnsname.to_owned()) | ||||
| @@ -336,10 +327,24 @@ impl Connector { | ||||
|             Inner::Http(_) => (), | ||||
|         } | ||||
|  | ||||
|         self.connect_with_maybe_proxy(ndst, true).await | ||||
|         self.connect_with_maybe_proxy(proxy_dst, true).await | ||||
|     } | ||||
| } | ||||
|  | ||||
| fn into_dst(scheme: Scheme, host: Authority) -> Destination { | ||||
|     use std::convert::TryInto; | ||||
|  | ||||
|     // TODO: Should the `http` crate get `From<(Scheme, Authority)> for Uri`? | ||||
|     http::Uri::builder() | ||||
|         .scheme(scheme) | ||||
|         .authority(host) | ||||
|         .path_and_query(http::uri::PathAndQuery::from_static("/")) | ||||
|         .build() | ||||
|         .expect("scheme and authority is valid Uri") | ||||
|         .try_into() | ||||
|         .expect("scheme and authority is valid Destination") | ||||
| } | ||||
|  | ||||
| //#[cfg(feature = "trust-dns")] | ||||
| //fn http_connector() -> crate::Result<HttpConnector> { | ||||
| //    TrustDnsResolver::new() | ||||
|   | ||||
							
								
								
									
										115
									
								
								src/proxy.rs
									
									
									
									
									
								
							
							
						
						
									
										115
									
								
								src/proxy.rs
									
									
									
									
									
								
							| @@ -60,7 +60,11 @@ pub struct Proxy { | ||||
| pub enum ProxyScheme { | ||||
|     Http { | ||||
|         auth: Option<HeaderValue>, | ||||
|         uri: hyper::Uri, | ||||
|         host: http::uri::Authority, | ||||
|     }, | ||||
|     Https { | ||||
|         auth: Option<HeaderValue>, | ||||
|         host: http::uri::Authority, | ||||
|     }, | ||||
|     #[cfg(feature = "socks")] | ||||
|     Socks5 { | ||||
| @@ -226,6 +230,7 @@ impl Proxy { | ||||
|             | Intercept::Http(ProxyScheme::Http { ref auth, .. }) => auth.clone(), | ||||
|             Intercept::Custom(ref custom) => custom.call(uri).and_then(|scheme| match scheme { | ||||
|                 ProxyScheme::Http { auth, .. } => auth, | ||||
|                 ProxyScheme::Https { auth, .. } => auth, | ||||
|                 #[cfg(feature = "socks")] | ||||
|                 _ => None, | ||||
|             }), | ||||
| @@ -278,10 +283,18 @@ impl ProxyScheme { | ||||
|     // To start conservative, keep builders private for now. | ||||
|  | ||||
|     /// Proxy traffic via the specified URL over HTTP | ||||
|     fn http<T: IntoUrl>(url: T) -> crate::Result<Self> { | ||||
|     fn http(host: &str) -> crate::Result<Self> { | ||||
|         Ok(ProxyScheme::Http { | ||||
|             auth: None, | ||||
|             uri: crate::into_url::expect_uri(&url.into_url()?), | ||||
|             host: host.parse().map_err(crate::error::builder)?, | ||||
|         }) | ||||
|     } | ||||
|  | ||||
|     /// Proxy traffic via the specified URL over HTTPS | ||||
|     fn https(host: &str) -> crate::Result<Self> { | ||||
|         Ok(ProxyScheme::Https { | ||||
|             auth: None, | ||||
|             host: host.parse().map_err(crate::error::builder)?, | ||||
|         }) | ||||
|     } | ||||
|  | ||||
| @@ -330,7 +343,11 @@ impl ProxyScheme { | ||||
|             ProxyScheme::Http { ref mut auth, .. } => { | ||||
|                 let header = encode_basic_auth(&username.into(), &password.into()); | ||||
|                 *auth = Some(header); | ||||
|             } | ||||
|             }, | ||||
|             ProxyScheme::Https { ref mut auth, .. } => { | ||||
|                 let header = encode_basic_auth(&username.into(), &password.into()); | ||||
|                 *auth = Some(header); | ||||
|             }, | ||||
|             #[cfg(feature = "socks")] | ||||
|             ProxyScheme::Socks5 { ref mut auth, .. } => { | ||||
|                 *auth = Some((username.into(), password.into())); | ||||
| @@ -338,11 +355,32 @@ impl ProxyScheme { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn if_no_auth(mut self, update: &Option<HeaderValue>) -> Self { | ||||
|         match self { | ||||
|             ProxyScheme::Http { ref mut auth, .. } => { | ||||
|                 if auth.is_none() { | ||||
|                     *auth = update.clone(); | ||||
|                 } | ||||
|             }, | ||||
|             ProxyScheme::Https { ref mut auth, .. } => { | ||||
|                 if auth.is_none() { | ||||
|                     *auth = update.clone(); | ||||
|                 } | ||||
|             }, | ||||
|             #[cfg(feature = "socks")] | ||||
|             ProxyScheme::Socks5 { .. } => {} | ||||
|         } | ||||
|  | ||||
|         self | ||||
|     } | ||||
|  | ||||
|     /// Convert a URL into a proxy scheme | ||||
|     /// | ||||
|     /// Supported schemes: HTTP, HTTPS, (SOCKS5, SOCKS5H if `socks` feature is enabled). | ||||
|     // Private for now... | ||||
|     fn parse(url: Url) -> crate::Result<Self> { | ||||
|         use url::Position; | ||||
|  | ||||
|         // Resolve URL to a host and port | ||||
|         #[cfg(feature = "socks")] | ||||
|         let to_addr = || { | ||||
| @@ -357,7 +395,8 @@ impl ProxyScheme { | ||||
|         }; | ||||
|  | ||||
|         let mut scheme = match url.scheme() { | ||||
|             "http" | "https" => Self::http(url.clone())?, | ||||
|             "http" => Self::http(&url[Position::BeforeHost..Position::AfterPort])?, | ||||
|             "https" => Self::https(&url[Position::BeforeHost..Position::AfterPort])?, | ||||
|             #[cfg(feature = "socks")] | ||||
|             "socks5" => Self::socks5(to_addr()?)?, | ||||
|             #[cfg(feature = "socks")] | ||||
| @@ -375,13 +414,22 @@ impl ProxyScheme { | ||||
|     } | ||||
|  | ||||
|     #[cfg(test)] | ||||
|     fn http_uri(&self) -> &http::Uri { | ||||
|     fn scheme(&self) -> &str { | ||||
|         match self { | ||||
|             ProxyScheme::Http { ref uri, .. } => { | ||||
|                 uri | ||||
|             }, | ||||
|             ProxyScheme::Http { .. } => "http", | ||||
|             ProxyScheme::Https { .. } => "https", | ||||
|             #[cfg(feature = "socks")] | ||||
|             _ => panic!("socks not expected"), | ||||
|             ProxyScheme::Socks5 => "socks5", | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     #[cfg(test)] | ||||
|     fn host(&self) -> &str { | ||||
|         match self { | ||||
|             ProxyScheme::Http { host, .. } => host.as_str(), | ||||
|             ProxyScheme::Https { host, .. } => host.as_str(), | ||||
|             #[cfg(feature = "socks")] | ||||
|             ProxyScheme::Socks5 => panic!("socks5"), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -433,20 +481,7 @@ impl Custom { | ||||
|  | ||||
|         (self.func)(&url) | ||||
|             .and_then(|result| result.ok()) | ||||
|             .map(|scheme| match scheme { | ||||
|                 ProxyScheme::Http { auth, uri } => { | ||||
|                     if auth.is_some() { | ||||
|                         ProxyScheme::Http { auth, uri } | ||||
|                     } else { | ||||
|                         ProxyScheme::Http { | ||||
|                             auth: self.auth.clone(), | ||||
|                             uri, | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 #[cfg(feature = "socks")] | ||||
|                 socks => socks, | ||||
|             }) | ||||
|             .map(|scheme| scheme.if_no_auth(&self.auth)) | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -659,11 +694,18 @@ mod tests { | ||||
|     } | ||||
|  | ||||
|     fn intercepted_uri(p: &Proxy, s: &str) -> Uri { | ||||
|         match p.intercept(&url(s)).unwrap() { | ||||
|             ProxyScheme::Http { uri, .. } => uri, | ||||
|         let (scheme, host) = match p.intercept(&url(s)).unwrap() { | ||||
|             ProxyScheme::Http { host, .. } => ("http", host), | ||||
|             ProxyScheme::Https { host, .. } => ("https", host), | ||||
|             #[cfg(feature = "socks")] | ||||
|             _ => panic!("intercepted as socks"), | ||||
|         } | ||||
|         }; | ||||
|         http::Uri::builder() | ||||
|             .scheme(scheme) | ||||
|             .authority(host) | ||||
|             .path_and_query("/") | ||||
|             .build() | ||||
|             .expect("intercepted_uri") | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
| @@ -727,6 +769,19 @@ mod tests { | ||||
|         assert!(p.intercept(&url(other)).is_none()); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_proxy_scheme_parse() { | ||||
|         let ps = "http://foo:bar@localhost:1239".into_proxy_scheme().unwrap(); | ||||
|  | ||||
|         match ps { | ||||
|             ProxyScheme::Http { auth, host } => { | ||||
|                 assert_eq!(auth.unwrap(), encode_basic_auth("foo", "bar")); | ||||
|                 assert_eq!(host, "localhost:1239"); | ||||
|             }, | ||||
|             other => panic!("unexpected: {:?}", other), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_get_sys_proxies_parsing() { | ||||
|         // save system setting first. | ||||
| @@ -744,7 +799,9 @@ mod tests { | ||||
|         env::set_var("http_proxy", "http://127.0.0.1/"); | ||||
|         let proxies = get_sys_proxies(); | ||||
|  | ||||
|         assert_eq!(proxies["http"].http_uri(), "http://127.0.0.1/"); | ||||
|         let p = &proxies["http"]; | ||||
|         assert_eq!(p.scheme(), "http"); | ||||
|         assert_eq!(p.host(), "127.0.0.1"); | ||||
|  | ||||
|         // reset user setting when guards drop | ||||
|     } | ||||
| @@ -759,7 +816,7 @@ mod tests { | ||||
|         env::set_var("HTTP_PROXY", "http://evil/"); | ||||
|  | ||||
|         // not in CGI yet | ||||
|         assert_eq!(get_sys_proxies()["http"].http_uri(), "http://evil/"); | ||||
|         assert_eq!(get_sys_proxies()["http"].host(), "evil"); | ||||
|  | ||||
|         // set like we're in CGI | ||||
|         env::set_var("REQUEST_METHOD", "GET"); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user