Skip to content

Commit

Permalink
Fix system HTTP proxy to send proxy-authorization (seanmonstar#1021)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
zicklag committed Nov 24, 2020
1 parent e7be3ed commit 3cd9c29
Show file tree
Hide file tree
Showing 2 changed files with 195 additions and 6 deletions.
159 changes: 153 additions & 6 deletions src/proxy.rs
Expand Up @@ -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()))
};
Expand Down Expand Up @@ -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,
}
}
Expand All @@ -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,
Expand Down Expand Up @@ -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<RegistryProxyValues>,
#[cfg_attr(not(target_os = "windows"), allow(unused_variables))] registry_values: Option<
RegistryProxyValues,
>,
) -> SystemProxyMap {
let proxies = get_from_environment();

Expand Down Expand Up @@ -764,7 +788,9 @@ fn get_from_registry() -> Option<RegistryProxyValues> {
}

#[cfg(target_os = "windows")]
fn parse_registry_values_impl(registry_values: RegistryProxyValues) -> Result<SystemProxyMap, Box<dyn Error>> {
fn parse_registry_values_impl(
registry_values: RegistryProxyValues,
) -> Result<SystemProxyMap, Box<dyn Error>> {
let (proxy_enable, proxy_server) = registry_values;

if proxy_enable == 0 {
Expand Down Expand Up @@ -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
);
}
}
42 changes: 42 additions & 0 deletions tests/proxy.rs
Expand Up @@ -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| {
Expand Down

0 comments on commit 3cd9c29

Please sign in to comment.