From 3cd9c29b303d98342aa03305655069b5d2382e63 Mon Sep 17 00:00:00 2001 From: Zicklag Date: Tue, 24 Nov 2020 12:34:38 -0600 Subject: [PATCH] Fix system HTTP proxy to send proxy-authorization (#1021) Previously, HTTP proxies loaded from the system settings were not respected for non-HTTPS requests. Now the PROXY_AUTHORIZATION header is supplied on HTTP requests with a system proxy. --- src/proxy.rs | 159 +++++++++++++++++++++++++++++++++++++++++++++++-- tests/proxy.rs | 42 +++++++++++++ 2 files changed, 195 insertions(+), 6 deletions(-) diff --git a/src/proxy.rs b/src/proxy.rs index e3feb2a..629cdd1 100644 --- a/src/proxy.rs +++ b/src/proxy.rs @@ -212,7 +212,9 @@ impl Proxy { pub(crate) fn system() -> Proxy { let mut proxy = if cfg!(feature = "__internal_proxy_sys_no_cache") { - Proxy::new(Intercept::System(Arc::new(get_sys_proxies(get_from_registry())))) + Proxy::new(Intercept::System(Arc::new(get_sys_proxies( + get_from_registry(), + )))) } else { Proxy::new(Intercept::System(SYS_PROXIES.clone())) }; @@ -247,10 +249,20 @@ impl Proxy { pub(crate) fn maybe_has_http_auth(&self) -> bool { match self.intercept { - Intercept::All(ProxyScheme::Http { auth: Some(..), .. }) | - Intercept::Http(ProxyScheme::Http { auth: Some(..), .. }) | + Intercept::All(ProxyScheme::Http { auth: Some(..), .. }) + | Intercept::Http(ProxyScheme::Http { auth: Some(..), .. }) // Custom *may* match 'http', so assume so. - Intercept::Custom(_) => true, + | Intercept::Custom(_) => true, + Intercept::System(ref system) => { + if let Some(proxy) = system.get("http") { + match proxy { + ProxyScheme::Http { auth, .. } => auth.is_some(), + _ => false, + } + } else { + false + } + } _ => false, } } @@ -259,6 +271,16 @@ impl Proxy { match self.intercept { Intercept::All(ProxyScheme::Http { ref auth, .. }) | Intercept::Http(ProxyScheme::Http { ref auth, .. }) => auth.clone(), + Intercept::System(ref system) => { + if let Some(proxy) = system.get("http") { + match proxy { + ProxyScheme::Http { auth, .. } => auth.clone(), + _ => None, + } + } else { + None + } + } Intercept::Custom(ref custom) => custom.call(uri).and_then(|scheme| match scheme { ProxyScheme::Http { auth, .. } => auth, ProxyScheme::Https { auth, .. } => auth, @@ -681,7 +703,9 @@ lazy_static! { /// System proxies information as a hashmap like /// {"http": Url::parse("http://127.0.0.1:80"), "https": Url::parse("https://127.0.0.1:80")} fn get_sys_proxies( - #[cfg_attr(not(target_os = "windows"), allow(unused_variables))] registry_values: Option, + #[cfg_attr(not(target_os = "windows"), allow(unused_variables))] registry_values: Option< + RegistryProxyValues, + >, ) -> SystemProxyMap { let proxies = get_from_environment(); @@ -764,7 +788,9 @@ fn get_from_registry() -> Option { } #[cfg(target_os = "windows")] -fn parse_registry_values_impl(registry_values: RegistryProxyValues) -> Result> { +fn parse_registry_values_impl( + registry_values: RegistryProxyValues, +) -> Result> { let (proxy_enable, proxy_server) = registry_values; if proxy_enable == 0 { @@ -1208,4 +1234,125 @@ mod tests { } } } + + #[test] + fn test_has_http_auth() { + let http_proxy_with_auth = Proxy { + intercept: Intercept::Http(ProxyScheme::Http { + auth: Some(HeaderValue::from_static("auth1")), + host: http::uri::Authority::from_static("authority"), + }), + no_proxy: None, + }; + assert_eq!(http_proxy_with_auth.maybe_has_http_auth(), true); + assert_eq!( + http_proxy_with_auth.http_basic_auth(&Uri::from_static("http://example.com")), + Some(HeaderValue::from_static("auth1")) + ); + + let http_proxy_without_auth = Proxy { + intercept: Intercept::Http(ProxyScheme::Http { + auth: None, + host: http::uri::Authority::from_static("authority"), + }), + no_proxy: None, + }; + assert_eq!(http_proxy_without_auth.maybe_has_http_auth(), false); + assert_eq!( + http_proxy_without_auth.http_basic_auth(&Uri::from_static("http://example.com")), + None + ); + + let https_proxy_with_auth = Proxy { + intercept: Intercept::Http(ProxyScheme::Https { + auth: Some(HeaderValue::from_static("auth2")), + host: http::uri::Authority::from_static("authority"), + }), + no_proxy: None, + }; + assert_eq!(https_proxy_with_auth.maybe_has_http_auth(), false); + assert_eq!( + https_proxy_with_auth.http_basic_auth(&Uri::from_static("http://example.com")), + None + ); + + let all_http_proxy_with_auth = Proxy { + intercept: Intercept::All(ProxyScheme::Http { + auth: Some(HeaderValue::from_static("auth3")), + host: http::uri::Authority::from_static("authority"), + }), + no_proxy: None, + }; + assert_eq!(all_http_proxy_with_auth.maybe_has_http_auth(), true); + assert_eq!( + all_http_proxy_with_auth.http_basic_auth(&Uri::from_static("http://example.com")), + Some(HeaderValue::from_static("auth3")) + ); + + let all_https_proxy_with_auth = Proxy { + intercept: Intercept::All(ProxyScheme::Https { + auth: Some(HeaderValue::from_static("auth4")), + host: http::uri::Authority::from_static("authority"), + }), + no_proxy: None, + }; + assert_eq!(all_https_proxy_with_auth.maybe_has_http_auth(), false); + assert_eq!( + all_https_proxy_with_auth.http_basic_auth(&Uri::from_static("http://example.com")), + None + ); + + let all_https_proxy_without_auth = Proxy { + intercept: Intercept::All(ProxyScheme::Https { + auth: None, + host: http::uri::Authority::from_static("authority"), + }), + no_proxy: None, + }; + assert_eq!(all_https_proxy_without_auth.maybe_has_http_auth(), false); + assert_eq!( + all_https_proxy_without_auth.http_basic_auth(&Uri::from_static("http://example.com")), + None + ); + + let system_http_proxy_with_auth = Proxy { + intercept: Intercept::System(Arc::new({ + let mut m = HashMap::new(); + m.insert( + "http".into(), + ProxyScheme::Http { + auth: Some(HeaderValue::from_static("auth5")), + host: http::uri::Authority::from_static("authority"), + }, + ); + m + })), + no_proxy: None, + }; + assert_eq!(system_http_proxy_with_auth.maybe_has_http_auth(), true); + assert_eq!( + system_http_proxy_with_auth.http_basic_auth(&Uri::from_static("http://example.com")), + Some(HeaderValue::from_static("auth5")) + ); + + let system_https_proxy_with_auth = Proxy { + intercept: Intercept::System(Arc::new({ + let mut m = HashMap::new(); + m.insert( + "https".into(), + ProxyScheme::Https { + auth: Some(HeaderValue::from_static("auth6")), + host: http::uri::Authority::from_static("authority"), + }, + ); + m + })), + no_proxy: None, + }; + assert_eq!(system_https_proxy_with_auth.maybe_has_http_auth(), false); + assert_eq!( + system_https_proxy_with_auth.http_basic_auth(&Uri::from_static("http://example.com")), + None + ); + } } diff --git a/tests/proxy.rs b/tests/proxy.rs index 1a358c7..15ea357 100644 --- a/tests/proxy.rs +++ b/tests/proxy.rs @@ -94,6 +94,48 @@ async fn http_proxy_basic_auth_parsed() { assert_eq!(res.status(), reqwest::StatusCode::OK); } +#[tokio::test] +async fn system_http_proxy_basic_auth_parsed() { + let url = "http://hyper.rs/prox"; + let server = server::http(move |req| { + assert_eq!(req.method(), "GET"); + assert_eq!(req.uri(), url); + assert_eq!(req.headers()["host"], "hyper.rs"); + assert_eq!( + req.headers()["proxy-authorization"], + "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" + ); + + async { http::Response::default() } + }); + + // save system setting first. + let system_proxy = env::var("http_proxy"); + + // set-up http proxy. + env::set_var( + "http_proxy", + format!("http://Aladdin:open sesame@{}", server.addr()), + ); + + let res = reqwest::Client::builder() + .build() + .unwrap() + .get(url) + .send() + .await + .unwrap(); + + assert_eq!(res.url().as_str(), url); + assert_eq!(res.status(), reqwest::StatusCode::OK); + + // reset user setting. + match system_proxy { + Err(_) => env::remove_var("http_proxy"), + Ok(proxy) => env::set_var("http_proxy", proxy), + } +} + #[tokio::test] async fn test_no_proxy() { let server = server::http(move |req| {