Skip to content

Commit 4d3fe12

Browse files
authored
fix: proxy could use wrong credentials if many matched (#3098)
Closes #3097
1 parent 9f06fd2 commit 4d3fe12

3 files changed

Lines changed: 72 additions & 28 deletions

File tree

src/async_impl/client.rs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2714,8 +2714,11 @@ impl Client {
27142714
}
27152715

27162716
for proxy in self.inner.proxies.iter() {
2717-
if let Some(header) = proxy.http_non_tunnel_basic_auth(dst) {
2718-
headers.insert(PROXY_AUTHORIZATION, header);
2717+
if let Some(proxy) = proxy.intercept(dst) {
2718+
if let Some(header) = proxy.http_non_tunnel_basic_auth() {
2719+
headers.insert(PROXY_AUTHORIZATION, header);
2720+
}
2721+
// Use only the first matching proxy, as the connector does.
27192722
break;
27202723
}
27212724
}
@@ -2731,10 +2734,13 @@ impl Client {
27312734
}
27322735

27332736
for proxy in self.inner.proxies.iter() {
2734-
if let Some(iter) = proxy.http_non_tunnel_custom_headers(dst) {
2735-
iter.iter().for_each(|(key, value)| {
2736-
headers.insert(key, value.clone());
2737-
});
2737+
if let Some(proxy) = proxy.intercept(dst) {
2738+
if let Some(iter) = proxy.http_non_tunnel_custom_headers() {
2739+
iter.iter().for_each(|(key, value)| {
2740+
headers.insert(key, value.clone());
2741+
});
2742+
}
2743+
// Use only the first matching proxy, as the connector does.
27382744
break;
27392745
}
27402746
}

src/proxy.rs

Lines changed: 18 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -550,31 +550,9 @@ impl Matcher {
550550
self.maybe_has_http_auth
551551
}
552552

553-
pub(crate) fn http_non_tunnel_basic_auth(&self, dst: &Uri) -> Option<HeaderValue> {
554-
if let Some(proxy) = self.intercept(dst) {
555-
let scheme = proxy.uri().scheme();
556-
if scheme == Some(&Scheme::HTTP) || scheme == Some(&Scheme::HTTPS) {
557-
return proxy.basic_auth().cloned();
558-
}
559-
}
560-
561-
None
562-
}
563-
564553
pub(crate) fn maybe_has_http_custom_headers(&self) -> bool {
565554
self.maybe_has_http_custom_headers
566555
}
567-
568-
pub(crate) fn http_non_tunnel_custom_headers(&self, dst: &Uri) -> Option<HeaderMap> {
569-
if let Some(proxy) = self.intercept(dst) {
570-
let scheme = proxy.uri().scheme();
571-
if scheme == Some(&Scheme::HTTP) || scheme == Some(&Scheme::HTTPS) {
572-
return proxy.custom_headers().cloned();
573-
}
574-
}
575-
576-
None
577-
}
578556
}
579557

580558
impl fmt::Debug for Matcher {
@@ -587,6 +565,24 @@ impl fmt::Debug for Matcher {
587565
}
588566

589567
impl Intercepted {
568+
pub(crate) fn http_non_tunnel_basic_auth(&self) -> Option<HeaderValue> {
569+
let scheme = self.uri().scheme();
570+
if scheme == Some(&Scheme::HTTP) || scheme == Some(&Scheme::HTTPS) {
571+
return self.basic_auth().cloned();
572+
}
573+
574+
None
575+
}
576+
577+
pub(crate) fn http_non_tunnel_custom_headers(&self) -> Option<HeaderMap> {
578+
let scheme = self.uri().scheme();
579+
if scheme == Some(&Scheme::HTTP) || scheme == Some(&Scheme::HTTPS) {
580+
return self.custom_headers().cloned();
581+
}
582+
583+
None
584+
}
585+
590586
pub(crate) fn uri(&self) -> &http::Uri {
591587
self.inner.uri()
592588
}

tests/proxy.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,48 @@ async fn http_proxy_basic_auth() {
7171
assert_eq!(res.status(), reqwest::StatusCode::OK);
7272
}
7373

74+
#[tokio::test]
75+
async fn http_proxy_does_not_use_later_proxy_basic_auth() {
76+
http_proxy_does_not_use_later_proxy_headers(false).await;
77+
}
78+
79+
#[tokio::test]
80+
async fn http_proxy_does_not_use_later_proxy_custom_headers() {
81+
http_proxy_does_not_use_later_proxy_headers(true).await;
82+
}
83+
84+
async fn http_proxy_does_not_use_later_proxy_headers(custom: bool) {
85+
let url = "http://hyper.rs.local/prox";
86+
let server = server::http(move |req| {
87+
assert!(!req.headers().contains_key("proxy-authorization"));
88+
assert!(!req.headers().contains_key("x-proxy-secret"));
89+
90+
async { http::Response::default() }
91+
});
92+
93+
let later_proxy = reqwest::Proxy::http("http://unused.proxy.local").unwrap();
94+
let later_proxy = if custom {
95+
let mut headers = reqwest::header::HeaderMap::new();
96+
headers.insert("proxy-authorization", "secret".parse().unwrap());
97+
headers.insert("x-proxy-secret", "secret".parse().unwrap());
98+
later_proxy.headers(headers)
99+
} else {
100+
later_proxy.basic_auth("Aladdin", "open sesame")
101+
};
102+
103+
let res = reqwest::Client::builder()
104+
.proxy(reqwest::Proxy::http(format!("http://{}", server.addr())).unwrap())
105+
.proxy(later_proxy)
106+
.build()
107+
.unwrap()
108+
.get(url)
109+
.send()
110+
.await
111+
.unwrap();
112+
113+
assert_eq!(res.status(), reqwest::StatusCode::OK);
114+
}
115+
74116
#[tokio::test]
75117
async fn http_proxy_basic_auth_parsed() {
76118
let url = "http://hyper.rs.local/prox";

0 commit comments

Comments
 (0)