Repository: seanmonstar/reqwest (language: rust)

Issue:
HTTP Proxy WithAuth Not Respected for HTTP Requests ( Works With HTTPS )
I have an HTTP proxy with authentication enabled, and I noticed that the username and password are not working on HTTP requests even though they are respected with HTTPS requests. The same request made over HTTPS will work fine, but when making the request over HTTP I get a 407 Proxy Auth Required from the proxy. I have my env vars set like so:

```
export http_proxy=http://username:password@123.456.789.10:3128
export https_proxy=$http_proxy
export HTTP_PROXY=$http_proxy
export HTTPS_PROXY=$http_proxy
export ftp_proxy=$http_proxy
export FTP_PROXY=$http_proxy
```

Related: https://github.com/denoland/deno/issues/6502

**OS:** Pop!_OS ( essentially Ubuntu ) 20.04


Discussion / hints:
Thanks for the report! Interesting, checking the code again, it looks like it should be sending a header (inside `async_impl::Client::execute_request()`.

Fix patch:
diff --git a/src/proxy.rs b/src/proxy.rs
index e3feb2ad2..629cdd175 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<RegistryProxyValues>,
+    #[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<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 {
@@ -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
+        );
+    }
 }


Test patch:
diff --git a/tests/proxy.rs b/tests/proxy.rs
index 1a358c7ca..15ea3578e 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| {