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.
This commit is contained in:
159
src/proxy.rs
159
src/proxy.rs
@@ -212,7 +212,9 @@ impl Proxy {
|
|||||||
|
|
||||||
pub(crate) fn system() -> Proxy {
|
pub(crate) fn system() -> Proxy {
|
||||||
let mut proxy = if cfg!(feature = "__internal_proxy_sys_no_cache") {
|
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 {
|
} else {
|
||||||
Proxy::new(Intercept::System(SYS_PROXIES.clone()))
|
Proxy::new(Intercept::System(SYS_PROXIES.clone()))
|
||||||
};
|
};
|
||||||
@@ -247,10 +249,20 @@ impl Proxy {
|
|||||||
|
|
||||||
pub(crate) fn maybe_has_http_auth(&self) -> bool {
|
pub(crate) fn maybe_has_http_auth(&self) -> bool {
|
||||||
match self.intercept {
|
match self.intercept {
|
||||||
Intercept::All(ProxyScheme::Http { auth: Some(..), .. }) |
|
Intercept::All(ProxyScheme::Http { auth: Some(..), .. })
|
||||||
Intercept::Http(ProxyScheme::Http { auth: Some(..), .. }) |
|
| Intercept::Http(ProxyScheme::Http { auth: Some(..), .. })
|
||||||
// Custom *may* match 'http', so assume so.
|
// 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,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -259,6 +271,16 @@ impl Proxy {
|
|||||||
match self.intercept {
|
match self.intercept {
|
||||||
Intercept::All(ProxyScheme::Http { ref auth, .. })
|
Intercept::All(ProxyScheme::Http { ref auth, .. })
|
||||||
| Intercept::Http(ProxyScheme::Http { ref auth, .. }) => auth.clone(),
|
| 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 {
|
Intercept::Custom(ref custom) => custom.call(uri).and_then(|scheme| match scheme {
|
||||||
ProxyScheme::Http { auth, .. } => auth,
|
ProxyScheme::Http { auth, .. } => auth,
|
||||||
ProxyScheme::Https { auth, .. } => auth,
|
ProxyScheme::Https { auth, .. } => auth,
|
||||||
@@ -681,7 +703,9 @@ lazy_static! {
|
|||||||
/// System proxies information as a hashmap like
|
/// 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")}
|
/// {"http": Url::parse("http://127.0.0.1:80"), "https": Url::parse("https://127.0.0.1:80")}
|
||||||
fn get_sys_proxies(
|
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 {
|
) -> SystemProxyMap {
|
||||||
let proxies = get_from_environment();
|
let proxies = get_from_environment();
|
||||||
|
|
||||||
@@ -764,7 +788,9 @@ fn get_from_registry() -> Option<RegistryProxyValues> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
#[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;
|
let (proxy_enable, proxy_server) = registry_values;
|
||||||
|
|
||||||
if proxy_enable == 0 {
|
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
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -94,6 +94,48 @@ async fn http_proxy_basic_auth_parsed() {
|
|||||||
assert_eq!(res.status(), reqwest::StatusCode::OK);
|
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]
|
#[tokio::test]
|
||||||
async fn test_no_proxy() {
|
async fn test_no_proxy() {
|
||||||
let server = server::http(move |req| {
|
let server = server::http(move |req| {
|
||||||
|
|||||||
Reference in New Issue
Block a user