1 use std::fmt;
2 #[cfg(feature = "socks")]
3 use std::net::SocketAddr;
4 use std::sync::Arc;
5 
6 use crate::{IntoUrl, Url};
7 use http::{header::HeaderValue, Uri};
8 use ipnet::IpNet;
9 use percent_encoding::percent_decode;
10 use std::collections::HashMap;
11 use std::env;
12 #[cfg(target_os = "windows")]
13 use std::error::Error;
14 use std::net::IpAddr;
15 #[cfg(target_os = "windows")]
16 use winreg::enums::HKEY_CURRENT_USER;
17 #[cfg(target_os = "windows")]
18 use winreg::RegKey;
19 
20 /// Configuration of a proxy that a `Client` should pass requests to.
21 ///
22 /// A `Proxy` has a couple pieces to it:
23 ///
24 /// - a URL of how to talk to the proxy
25 /// - rules on what `Client` requests should be directed to the proxy
26 ///
27 /// For instance, let's look at `Proxy::http`:
28 ///
29 /// ```rust
30 /// # fn run() -> Result<(), Box<std::error::Error>> {
31 /// let proxy = reqwest::Proxy::http("https://secure.example")?;
32 /// # Ok(())
33 /// # }
34 /// ```
35 ///
36 /// This proxy will intercept all HTTP requests, and make use of the proxy
37 /// at `https://secure.example`. A request to `http://hyper.rs` will talk
38 /// to your proxy. A request to `https://hyper.rs` will not.
39 ///
40 /// Multiple `Proxy` rules can be configured for a `Client`. The `Client` will
41 /// check each `Proxy` in the order it was added. This could mean that a
42 /// `Proxy` added first with eager intercept rules, such as `Proxy::all`,
43 /// would prevent a `Proxy` later in the list from ever working, so take care.
44 ///
45 /// By enabling the `"socks"` feature it is possible to use a socks proxy:
46 /// ```rust
47 /// # fn run() -> Result<(), Box<std::error::Error>> {
48 /// let proxy = reqwest::Proxy::http("socks5://192.168.1.1:9000")?;
49 /// # Ok(())
50 /// # }
51 /// ```
52 #[derive(Clone)]
53 pub struct Proxy {
54     intercept: Intercept,
55     no_proxy: Option<NoProxy>,
56 }
57 
58 /// Represents a possible matching entry for an IP address
59 #[derive(Clone, Debug)]
60 enum Ip {
61     Address(IpAddr),
62     Network(IpNet),
63 }
64 
65 /// A wrapper around a list of IP cidr blocks or addresses with a [IpMatcher::contains] method for
66 /// checking if an IP address is contained within the matcher
67 #[derive(Clone, Debug, Default)]
68 struct IpMatcher(Vec<Ip>);
69 
70 /// A wrapper around a list of domains with a [DomainMatcher::contains] method for checking if a
71 /// domain is contained within the matcher
72 #[derive(Clone, Debug, Default)]
73 struct DomainMatcher(Vec<String>);
74 
75 /// A configuration for filtering out requests that shouldn't be proxied
76 #[derive(Clone, Debug, Default)]
77 struct NoProxy {
78     ips: IpMatcher,
79     domains: DomainMatcher,
80 }
81 
82 /// A particular scheme used for proxying requests.
83 ///
84 /// For example, HTTP vs SOCKS5
85 #[derive(Clone)]
86 pub enum ProxyScheme {
87     Http {
88         auth: Option<HeaderValue>,
89         host: http::uri::Authority,
90     },
91     Https {
92         auth: Option<HeaderValue>,
93         host: http::uri::Authority,
94     },
95     #[cfg(feature = "socks")]
96     Socks5 {
97         addr: SocketAddr,
98         auth: Option<(String, String)>,
99         remote_dns: bool,
100     },
101 }
102 
103 /// Trait used for converting into a proxy scheme. This trait supports
104 /// parsing from a URL-like type, whilst also supporting proxy schemes
105 /// built directly using the factory methods.
106 pub trait IntoProxyScheme {
into_proxy_scheme(self) -> crate::Result<ProxyScheme>107     fn into_proxy_scheme(self) -> crate::Result<ProxyScheme>;
108 }
109 
110 impl<T: IntoUrl> IntoProxyScheme for T {
into_proxy_scheme(self) -> crate::Result<ProxyScheme>111     fn into_proxy_scheme(self) -> crate::Result<ProxyScheme> {
112         ProxyScheme::parse(self.into_url()?)
113     }
114 }
115 
116 impl IntoProxyScheme for ProxyScheme {
into_proxy_scheme(self) -> crate::Result<ProxyScheme>117     fn into_proxy_scheme(self) -> crate::Result<ProxyScheme> {
118         Ok(self)
119     }
120 }
121 
122 impl Proxy {
123     /// Proxy all HTTP traffic to the passed URL.
124     ///
125     /// # Example
126     ///
127     /// ```
128     /// # extern crate reqwest;
129     /// # fn run() -> Result<(), Box<std::error::Error>> {
130     /// let client = reqwest::Client::builder()
131     ///     .proxy(reqwest::Proxy::http("https://my.prox")?)
132     ///     .build()?;
133     /// # Ok(())
134     /// # }
135     /// # fn main() {}
136     /// ```
http<U: IntoProxyScheme>(proxy_scheme: U) -> crate::Result<Proxy>137     pub fn http<U: IntoProxyScheme>(proxy_scheme: U) -> crate::Result<Proxy> {
138         Ok(Proxy::new(Intercept::Http(
139             proxy_scheme.into_proxy_scheme()?,
140         )))
141     }
142 
143     /// Proxy all HTTPS traffic to the passed URL.
144     ///
145     /// # Example
146     ///
147     /// ```
148     /// # extern crate reqwest;
149     /// # fn run() -> Result<(), Box<std::error::Error>> {
150     /// let client = reqwest::Client::builder()
151     ///     .proxy(reqwest::Proxy::https("https://example.prox:4545")?)
152     ///     .build()?;
153     /// # Ok(())
154     /// # }
155     /// # fn main() {}
156     /// ```
https<U: IntoProxyScheme>(proxy_scheme: U) -> crate::Result<Proxy>157     pub fn https<U: IntoProxyScheme>(proxy_scheme: U) -> crate::Result<Proxy> {
158         Ok(Proxy::new(Intercept::Https(
159             proxy_scheme.into_proxy_scheme()?,
160         )))
161     }
162 
163     /// Proxy **all** traffic to the passed URL.
164     ///
165     /// # Example
166     ///
167     /// ```
168     /// # extern crate reqwest;
169     /// # fn run() -> Result<(), Box<std::error::Error>> {
170     /// let client = reqwest::Client::builder()
171     ///     .proxy(reqwest::Proxy::all("http://pro.xy")?)
172     ///     .build()?;
173     /// # Ok(())
174     /// # }
175     /// # fn main() {}
176     /// ```
all<U: IntoProxyScheme>(proxy_scheme: U) -> crate::Result<Proxy>177     pub fn all<U: IntoProxyScheme>(proxy_scheme: U) -> crate::Result<Proxy> {
178         Ok(Proxy::new(Intercept::All(
179             proxy_scheme.into_proxy_scheme()?,
180         )))
181     }
182 
183     /// Provide a custom function to determine what traffix to proxy to where.
184     ///
185     /// # Example
186     ///
187     /// ```
188     /// # extern crate reqwest;
189     /// # fn run() -> Result<(), Box<std::error::Error>> {
190     /// let target = reqwest::Url::parse("https://my.prox")?;
191     /// let client = reqwest::Client::builder()
192     ///     .proxy(reqwest::Proxy::custom(move |url| {
193     ///         if url.host_str() == Some("hyper.rs") {
194     ///             Some(target.clone())
195     ///         } else {
196     ///             None
197     ///         }
198     ///     }))
199     ///     .build()?;
200     /// # Ok(())
201     /// # }
202     /// # fn main() {}
custom<F, U: IntoProxyScheme>(fun: F) -> Proxy where F: Fn(&Url) -> Option<U> + Send + Sync + 'static,203     pub fn custom<F, U: IntoProxyScheme>(fun: F) -> Proxy
204     where
205         F: Fn(&Url) -> Option<U> + Send + Sync + 'static,
206     {
207         Proxy::new(Intercept::Custom(Custom {
208             auth: None,
209             func: Arc::new(move |url| fun(url).map(IntoProxyScheme::into_proxy_scheme)),
210         }))
211     }
212 
system() -> Proxy213     pub(crate) fn system() -> Proxy {
214         let mut proxy = if cfg!(feature = "__internal_proxy_sys_no_cache") {
215             Proxy::new(Intercept::System(Arc::new(get_sys_proxies(
216                 get_from_registry(),
217             ))))
218         } else {
219             Proxy::new(Intercept::System(SYS_PROXIES.clone()))
220         };
221         proxy.no_proxy = NoProxy::new();
222         proxy
223     }
224 
new(intercept: Intercept) -> Proxy225     fn new(intercept: Intercept) -> Proxy {
226         Proxy {
227             intercept,
228             no_proxy: None,
229         }
230     }
231 
232     /// Set the `Proxy-Authorization` header using Basic auth.
233     ///
234     /// # Example
235     ///
236     /// ```
237     /// # extern crate reqwest;
238     /// # fn run() -> Result<(), Box<std::error::Error>> {
239     /// let proxy = reqwest::Proxy::https("http://localhost:1234")?
240     ///     .basic_auth("Aladdin", "open sesame");
241     /// # Ok(())
242     /// # }
243     /// # fn main() {}
244     /// ```
basic_auth(mut self, username: &str, password: &str) -> Proxy245     pub fn basic_auth(mut self, username: &str, password: &str) -> Proxy {
246         self.intercept.set_basic_auth(username, password);
247         self
248     }
249 
maybe_has_http_auth(&self) -> bool250     pub(crate) fn maybe_has_http_auth(&self) -> bool {
251         match self.intercept {
252             Intercept::All(ProxyScheme::Http { auth: Some(..), .. })
253             | Intercept::Http(ProxyScheme::Http { auth: Some(..), .. })
254             // Custom *may* match 'http', so assume so.
255             | Intercept::Custom(_) => true,
256             Intercept::System(ref system) => {
257                 if let Some(proxy) = system.get("http") {
258                     match proxy {
259                         ProxyScheme::Http { auth, .. } => auth.is_some(),
260                         _ => false,
261                     }
262                 } else {
263                     false
264                 }
265             }
266             _ => false,
267         }
268     }
269 
http_basic_auth<D: Dst>(&self, uri: &D) -> Option<HeaderValue>270     pub(crate) fn http_basic_auth<D: Dst>(&self, uri: &D) -> Option<HeaderValue> {
271         match self.intercept {
272             Intercept::All(ProxyScheme::Http { ref auth, .. })
273             | Intercept::Http(ProxyScheme::Http { ref auth, .. }) => auth.clone(),
274             Intercept::System(ref system) => {
275                 if let Some(proxy) = system.get("http") {
276                     match proxy {
277                         ProxyScheme::Http { auth, .. } => auth.clone(),
278                         _ => None,
279                     }
280                 } else {
281                     None
282                 }
283             }
284             Intercept::Custom(ref custom) => custom.call(uri).and_then(|scheme| match scheme {
285                 ProxyScheme::Http { auth, .. } => auth,
286                 ProxyScheme::Https { auth, .. } => auth,
287                 #[cfg(feature = "socks")]
288                 _ => None,
289             }),
290             _ => None,
291         }
292     }
293 
intercept<D: Dst>(&self, uri: &D) -> Option<ProxyScheme>294     pub(crate) fn intercept<D: Dst>(&self, uri: &D) -> Option<ProxyScheme> {
295         match self.intercept {
296             Intercept::All(ref u) => Some(u.clone()),
297             Intercept::Http(ref u) => {
298                 if uri.scheme() == "http" {
299                     Some(u.clone())
300                 } else {
301                     None
302                 }
303             }
304             Intercept::Https(ref u) => {
305                 if uri.scheme() == "https" {
306                     Some(u.clone())
307                 } else {
308                     None
309                 }
310             }
311             Intercept::System(ref map) => {
312                 let in_no_proxy = self
313                     .no_proxy
314                     .as_ref()
315                     .map_or(false, |np| np.contains(uri.host()));
316                 if in_no_proxy {
317                     None
318                 } else {
319                     map.get(uri.scheme()).cloned()
320                 }
321             }
322             Intercept::Custom(ref custom) => custom.call(uri),
323         }
324     }
325 
is_match<D: Dst>(&self, uri: &D) -> bool326     pub(crate) fn is_match<D: Dst>(&self, uri: &D) -> bool {
327         match self.intercept {
328             Intercept::All(_) => true,
329             Intercept::Http(_) => uri.scheme() == "http",
330             Intercept::Https(_) => uri.scheme() == "https",
331             Intercept::System(ref map) => map.contains_key(uri.scheme()),
332             Intercept::Custom(ref custom) => custom.call(uri).is_some(),
333         }
334     }
335 }
336 
337 impl fmt::Debug for Proxy {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result338     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
339         f.debug_tuple("Proxy")
340             .field(&self.intercept)
341             .field(&self.no_proxy)
342             .finish()
343     }
344 }
345 
346 impl NoProxy {
347     /// Returns a new no proxy configration if the NO_PROXY/no_proxy environment variable is set.
348     /// Returns None otherwise
new() -> Option<Self>349     fn new() -> Option<Self> {
350         let raw = env::var("NO_PROXY")
351             .or_else(|_| env::var("no_proxy"))
352             .unwrap_or_default();
353         if raw.is_empty() {
354             return None;
355         }
356         let mut ips = Vec::new();
357         let mut domains = Vec::new();
358         let parts = raw.split(',');
359         for part in parts {
360             match part.parse::<IpNet>() {
361                 // If we can parse an IP net or address, then use it, otherwise, assume it is a domain
362                 Ok(ip) => ips.push(Ip::Network(ip)),
363                 Err(_) => match part.parse::<IpAddr>() {
364                     Ok(addr) => ips.push(Ip::Address(addr)),
365                     Err(_) => domains.push(part.to_owned()),
366                 },
367             }
368         }
369         Some(NoProxy {
370             ips: IpMatcher(ips),
371             domains: DomainMatcher(domains),
372         })
373     }
374 
contains(&self, host: &str) -> bool375     fn contains(&self, host: &str) -> bool {
376         // According to RFC3986, raw IPv6 hosts will be wrapped in []. So we need to strip those off
377         // the end in order to parse correctly
378         let host = if host.starts_with('[') {
379             let x: &[_] = &['[', ']'];
380             host.trim_matches(x)
381         } else {
382             host
383         };
384         match host.parse::<IpAddr>() {
385             // If we can parse an IP addr, then use it, otherwise, assume it is a domain
386             Ok(ip) => self.ips.contains(ip),
387             Err(_) => self.domains.contains(host),
388         }
389     }
390 }
391 
392 impl IpMatcher {
contains(&self, addr: IpAddr) -> bool393     fn contains(&self, addr: IpAddr) -> bool {
394         for ip in self.0.iter() {
395             match ip {
396                 Ip::Address(address) => {
397                     if &addr == address {
398                         return true;
399                     }
400                 }
401                 Ip::Network(net) => {
402                     if net.contains(&addr) {
403                         return true;
404                     }
405                 }
406             }
407         }
408         false
409     }
410 }
411 
412 impl DomainMatcher {
contains(&self, domain: &str) -> bool413     fn contains(&self, domain: &str) -> bool {
414         for d in self.0.iter() {
415             // First check for a "wildcard" domain match. A single "." will match anything.
416             // Otherwise, check that the domains are equal
417             if (d.starts_with('.') && domain.ends_with(d.get(1..).unwrap_or_default()))
418                 || d == domain
419             {
420                 return true;
421             }
422         }
423         false
424     }
425 }
426 
427 impl ProxyScheme {
428     // To start conservative, keep builders private for now.
429 
430     /// Proxy traffic via the specified URL over HTTP
http(host: &str) -> crate::Result<Self>431     fn http(host: &str) -> crate::Result<Self> {
432         Ok(ProxyScheme::Http {
433             auth: None,
434             host: host.parse().map_err(crate::error::builder)?,
435         })
436     }
437 
438     /// Proxy traffic via the specified URL over HTTPS
https(host: &str) -> crate::Result<Self>439     fn https(host: &str) -> crate::Result<Self> {
440         Ok(ProxyScheme::Https {
441             auth: None,
442             host: host.parse().map_err(crate::error::builder)?,
443         })
444     }
445 
446     /// Proxy traffic via the specified socket address over SOCKS5
447     ///
448     /// # Note
449     ///
450     /// Current SOCKS5 support is provided via blocking IO.
451     #[cfg(feature = "socks")]
socks5(addr: SocketAddr) -> crate::Result<Self>452     fn socks5(addr: SocketAddr) -> crate::Result<Self> {
453         Ok(ProxyScheme::Socks5 {
454             addr,
455             auth: None,
456             remote_dns: false,
457         })
458     }
459 
460     /// Proxy traffic via the specified socket address over SOCKS5H
461     ///
462     /// This differs from SOCKS5 in that DNS resolution is also performed via the proxy.
463     ///
464     /// # Note
465     ///
466     /// Current SOCKS5 support is provided via blocking IO.
467     #[cfg(feature = "socks")]
socks5h(addr: SocketAddr) -> crate::Result<Self>468     fn socks5h(addr: SocketAddr) -> crate::Result<Self> {
469         Ok(ProxyScheme::Socks5 {
470             addr,
471             auth: None,
472             remote_dns: true,
473         })
474     }
475 
476     /// Use a username and password when connecting to the proxy server
with_basic_auth<T: Into<String>, U: Into<String>>( mut self, username: T, password: U, ) -> Self477     fn with_basic_auth<T: Into<String>, U: Into<String>>(
478         mut self,
479         username: T,
480         password: U,
481     ) -> Self {
482         self.set_basic_auth(username, password);
483         self
484     }
485 
set_basic_auth<T: Into<String>, U: Into<String>>(&mut self, username: T, password: U)486     fn set_basic_auth<T: Into<String>, U: Into<String>>(&mut self, username: T, password: U) {
487         match *self {
488             ProxyScheme::Http { ref mut auth, .. } => {
489                 let header = encode_basic_auth(&username.into(), &password.into());
490                 *auth = Some(header);
491             }
492             ProxyScheme::Https { ref mut auth, .. } => {
493                 let header = encode_basic_auth(&username.into(), &password.into());
494                 *auth = Some(header);
495             }
496             #[cfg(feature = "socks")]
497             ProxyScheme::Socks5 { ref mut auth, .. } => {
498                 *auth = Some((username.into(), password.into()));
499             }
500         }
501     }
502 
if_no_auth(mut self, update: &Option<HeaderValue>) -> Self503     fn if_no_auth(mut self, update: &Option<HeaderValue>) -> Self {
504         match self {
505             ProxyScheme::Http { ref mut auth, .. } => {
506                 if auth.is_none() {
507                     *auth = update.clone();
508                 }
509             }
510             ProxyScheme::Https { ref mut auth, .. } => {
511                 if auth.is_none() {
512                     *auth = update.clone();
513                 }
514             }
515             #[cfg(feature = "socks")]
516             ProxyScheme::Socks5 { .. } => {}
517         }
518 
519         self
520     }
521 
522     /// Convert a URL into a proxy scheme
523     ///
524     /// Supported schemes: HTTP, HTTPS, (SOCKS5, SOCKS5H if `socks` feature is enabled).
525     // Private for now...
parse(url: Url) -> crate::Result<Self>526     fn parse(url: Url) -> crate::Result<Self> {
527         use url::Position;
528 
529         // Resolve URL to a host and port
530         #[cfg(feature = "socks")]
531         let to_addr = || {
532             let addrs = url
533                 .socket_addrs(|| match url.scheme() {
534                     "socks5" | "socks5h" => Some(1080),
535                     _ => None,
536                 })
537                 .map_err(crate::error::builder)?;
538             addrs
539                 .into_iter()
540                 .next()
541                 .ok_or_else(|| crate::error::builder("unknown proxy scheme"))
542         };
543 
544         let mut scheme = match url.scheme() {
545             "http" => Self::http(&url[Position::BeforeHost..Position::AfterPort])?,
546             "https" => Self::https(&url[Position::BeforeHost..Position::AfterPort])?,
547             #[cfg(feature = "socks")]
548             "socks5" => Self::socks5(to_addr()?)?,
549             #[cfg(feature = "socks")]
550             "socks5h" => Self::socks5h(to_addr()?)?,
551             _ => return Err(crate::error::builder("unknown proxy scheme")),
552         };
553 
554         if let Some(pwd) = url.password() {
555             let decoded_username = percent_decode(url.username().as_bytes()).decode_utf8_lossy();
556             let decoded_password = percent_decode(pwd.as_bytes()).decode_utf8_lossy();
557             scheme = scheme.with_basic_auth(decoded_username, decoded_password);
558         }
559 
560         Ok(scheme)
561     }
562 
563     #[cfg(test)]
scheme(&self) -> &str564     fn scheme(&self) -> &str {
565         match self {
566             ProxyScheme::Http { .. } => "http",
567             ProxyScheme::Https { .. } => "https",
568             #[cfg(feature = "socks")]
569             ProxyScheme::Socks5 { .. } => "socks5",
570         }
571     }
572 
573     #[cfg(test)]
host(&self) -> &str574     fn host(&self) -> &str {
575         match self {
576             ProxyScheme::Http { host, .. } => host.as_str(),
577             ProxyScheme::Https { host, .. } => host.as_str(),
578             #[cfg(feature = "socks")]
579             ProxyScheme::Socks5 { .. } => panic!("socks5"),
580         }
581     }
582 }
583 
584 impl fmt::Debug for ProxyScheme {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result585     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
586         match self {
587             ProxyScheme::Http { auth: _auth, host } => write!(f, "http://{}", host),
588             ProxyScheme::Https { auth: _auth, host } => write!(f, "https://{}", host),
589             #[cfg(feature = "socks")]
590             ProxyScheme::Socks5 {
591                 addr,
592                 auth: _auth,
593                 remote_dns,
594             } => {
595                 let h = if *remote_dns { "h" } else { "" };
596                 write!(f, "socks5{}://{}", h, addr)
597             }
598         }
599     }
600 }
601 
602 type SystemProxyMap = HashMap<String, ProxyScheme>;
603 type RegistryProxyValues = (u32, String);
604 
605 #[derive(Clone, Debug)]
606 enum Intercept {
607     All(ProxyScheme),
608     Http(ProxyScheme),
609     Https(ProxyScheme),
610     System(Arc<SystemProxyMap>),
611     Custom(Custom),
612 }
613 
614 impl Intercept {
set_basic_auth(&mut self, username: &str, password: &str)615     fn set_basic_auth(&mut self, username: &str, password: &str) {
616         match self {
617             Intercept::All(ref mut s)
618             | Intercept::Http(ref mut s)
619             | Intercept::Https(ref mut s) => s.set_basic_auth(username, password),
620             Intercept::System(_) => unimplemented!(),
621             Intercept::Custom(ref mut custom) => {
622                 let header = encode_basic_auth(username, password);
623                 custom.auth = Some(header);
624             }
625         }
626     }
627 }
628 
629 #[derive(Clone)]
630 struct Custom {
631     // This auth only applies if the returned ProxyScheme doesn't have an auth...
632     auth: Option<HeaderValue>,
633     func: Arc<dyn Fn(&Url) -> Option<crate::Result<ProxyScheme>> + Send + Sync + 'static>,
634 }
635 
636 impl Custom {
call<D: Dst>(&self, uri: &D) -> Option<ProxyScheme>637     fn call<D: Dst>(&self, uri: &D) -> Option<ProxyScheme> {
638         let url = format!(
639             "{}://{}{}{}",
640             uri.scheme(),
641             uri.host(),
642             uri.port().map(|_| ":").unwrap_or(""),
643             uri.port().map(|p| p.to_string()).unwrap_or_default()
644         )
645         .parse()
646         .expect("should be valid Url");
647 
648         (self.func)(&url)
649             .and_then(|result| result.ok())
650             .map(|scheme| scheme.if_no_auth(&self.auth))
651     }
652 }
653 
654 impl fmt::Debug for Custom {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result655     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
656         f.write_str("_")
657     }
658 }
659 
encode_basic_auth(username: &str, password: &str) -> HeaderValue660 pub(crate) fn encode_basic_auth(username: &str, password: &str) -> HeaderValue {
661     let val = format!("{}:{}", username, password);
662     let mut header = format!("Basic {}", base64::encode(&val))
663         .parse::<HeaderValue>()
664         .expect("base64 is always valid HeaderValue");
665     header.set_sensitive(true);
666     header
667 }
668 
669 /// A helper trait to allow testing `Proxy::intercept` without having to
670 /// construct `hyper::client::connect::Destination`s.
671 pub(crate) trait Dst {
scheme(&self) -> &str672     fn scheme(&self) -> &str;
host(&self) -> &str673     fn host(&self) -> &str;
port(&self) -> Option<u16>674     fn port(&self) -> Option<u16>;
675 }
676 
677 #[doc(hidden)]
678 impl Dst for Uri {
scheme(&self) -> &str679     fn scheme(&self) -> &str {
680         self.scheme().expect("Uri should have a scheme").as_str()
681     }
682 
host(&self) -> &str683     fn host(&self) -> &str {
684         Uri::host(self).expect("<Uri as Dst>::host should have a str")
685     }
686 
port(&self) -> Option<u16>687     fn port(&self) -> Option<u16> {
688         self.port().map(|p| p.as_u16())
689     }
690 }
691 
692 lazy_static! {
693     static ref SYS_PROXIES: Arc<SystemProxyMap> = Arc::new(get_sys_proxies(get_from_registry()));
694 }
695 
696 /// Get system proxies information.
697 ///
698 /// It can only support Linux, Unix like, and windows system.  Note that it will always
699 /// return a HashMap, even if something runs into error when find registry information in
700 /// Windows system.  Note that invalid proxy url in the system setting will be ignored.
701 ///
702 /// Returns:
703 ///     System proxies information as a hashmap like
704 ///     {"http": Url::parse("http://127.0.0.1:80"), "https": Url::parse("https://127.0.0.1:80")}
get_sys_proxies( #[cfg_attr(not(target_os = "windows"), allow(unused_variables))] registry_values: Option< RegistryProxyValues, >, ) -> SystemProxyMap705 fn get_sys_proxies(
706     #[cfg_attr(not(target_os = "windows"), allow(unused_variables))] registry_values: Option<
707         RegistryProxyValues,
708     >,
709 ) -> SystemProxyMap {
710     let proxies = get_from_environment();
711 
712     // TODO: move the following #[cfg] to `if expression` when attributes on `if` expressions allowed
713     #[cfg(target_os = "windows")]
714     {
715         if proxies.is_empty() {
716             // don't care errors if can't get proxies from registry, just return an empty HashMap.
717             if let Some(registry_values) = registry_values {
718                 return parse_registry_values(registry_values);
719             }
720         }
721     }
722     proxies
723 }
724 
insert_proxy(proxies: &mut SystemProxyMap, scheme: impl Into<String>, addr: String) -> bool725 fn insert_proxy(proxies: &mut SystemProxyMap, scheme: impl Into<String>, addr: String) -> bool {
726     if let Ok(valid_addr) = addr.into_proxy_scheme() {
727         proxies.insert(scheme.into(), valid_addr);
728         true
729     } else {
730         false
731     }
732 }
733 
get_from_environment() -> SystemProxyMap734 fn get_from_environment() -> SystemProxyMap {
735     let mut proxies = HashMap::new();
736 
737     if is_cgi() {
738         if log::log_enabled!(log::Level::Warn) && env::var_os("HTTP_PROXY").is_some() {
739             log::warn!("HTTP_PROXY environment variable ignored in CGI");
740         }
741     } else if !insert_from_env(&mut proxies, "http", "HTTP_PROXY") {
742         insert_from_env(&mut proxies, "http", "http_proxy");
743     }
744 
745     if !insert_from_env(&mut proxies, "https", "HTTPS_PROXY") {
746         insert_from_env(&mut proxies, "https", "https_proxy");
747     }
748 
749     proxies
750 }
751 
insert_from_env(proxies: &mut SystemProxyMap, scheme: &str, var: &str) -> bool752 fn insert_from_env(proxies: &mut SystemProxyMap, scheme: &str, var: &str) -> bool {
753     if let Ok(val) = env::var(var) {
754         insert_proxy(proxies, scheme, val)
755     } else {
756         false
757     }
758 }
759 
760 /// Check if we are being executed in a CGI context.
761 ///
762 /// If so, a malicious client can send the `Proxy:` header, and it will
763 /// be in the `HTTP_PROXY` env var. So we don't use it :)
is_cgi() -> bool764 fn is_cgi() -> bool {
765     env::var_os("REQUEST_METHOD").is_some()
766 }
767 
768 #[cfg(target_os = "windows")]
get_from_registry_impl() -> Result<RegistryProxyValues, Box<dyn Error>>769 fn get_from_registry_impl() -> Result<RegistryProxyValues, Box<dyn Error>> {
770     let hkcu = RegKey::predef(HKEY_CURRENT_USER);
771     let internet_setting: RegKey =
772         hkcu.open_subkey("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings")?;
773     // ensure the proxy is enable, if the value doesn't exist, an error will returned.
774     let proxy_enable: u32 = internet_setting.get_value("ProxyEnable")?;
775     let proxy_server: String = internet_setting.get_value("ProxyServer")?;
776 
777     Ok((proxy_enable, proxy_server))
778 }
779 
780 #[cfg(target_os = "windows")]
get_from_registry() -> Option<RegistryProxyValues>781 fn get_from_registry() -> Option<RegistryProxyValues> {
782     get_from_registry_impl().ok()
783 }
784 
785 #[cfg(not(target_os = "windows"))]
get_from_registry() -> Option<RegistryProxyValues>786 fn get_from_registry() -> Option<RegistryProxyValues> {
787     None
788 }
789 
790 #[cfg(target_os = "windows")]
parse_registry_values_impl( registry_values: RegistryProxyValues, ) -> Result<SystemProxyMap, Box<dyn Error>>791 fn parse_registry_values_impl(
792     registry_values: RegistryProxyValues,
793 ) -> Result<SystemProxyMap, Box<dyn Error>> {
794     let (proxy_enable, proxy_server) = registry_values;
795 
796     if proxy_enable == 0 {
797         return Ok(HashMap::new());
798     }
799 
800     let mut proxies = HashMap::new();
801     if proxy_server.contains("=") {
802         // per-protocol settings.
803         for p in proxy_server.split(";") {
804             let protocol_parts: Vec<&str> = p.split("=").collect();
805             match protocol_parts.as_slice() {
806                 [protocol, address] => {
807                     // If address doesn't specify an explicit protocol as protocol://address
808                     // then default to HTTP
809                     let address = if extract_type_prefix(*address).is_some() {
810                         String::from(*address)
811                     } else {
812                         format!("http://{}", address)
813                     };
814 
815                     insert_proxy(&mut proxies, *protocol, address);
816                 }
817                 _ => {
818                     // Contains invalid protocol setting, just break the loop
819                     // And make proxies to be empty.
820                     proxies.clear();
821                     break;
822                 }
823             }
824         }
825     } else {
826         if let Some(scheme) = extract_type_prefix(&proxy_server) {
827             // Explicit protocol has been specified
828             insert_proxy(&mut proxies, scheme, proxy_server.to_owned());
829         } else {
830             // No explicit protocol has been specified, default to HTTP
831             insert_proxy(&mut proxies, "http", format!("http://{}", proxy_server));
832             insert_proxy(&mut proxies, "https", format!("http://{}", proxy_server));
833         }
834     }
835     Ok(proxies)
836 }
837 
838 /// Extract the protocol from the given address, if present
839 /// For example, "https://example.com" will return Some("https")
840 #[cfg(target_os = "windows")]
extract_type_prefix(address: &str) -> Option<&str>841 fn extract_type_prefix(address: &str) -> Option<&str> {
842     if let Some(indice) = address.find("://") {
843         if indice == 0 {
844             None
845         }
846         else {
847             let prefix = &address[..indice];
848             let contains_banned = prefix.contains(|c| c == ':' || c == '/');
849 
850             if !contains_banned {
851                 Some(prefix)
852             } else {
853                 None
854             }
855         }
856     }
857     else {
858         None
859     }
860 }
861 
862 #[cfg(target_os = "windows")]
parse_registry_values(registry_values: RegistryProxyValues) -> SystemProxyMap863 fn parse_registry_values(registry_values: RegistryProxyValues) -> SystemProxyMap {
864     parse_registry_values_impl(registry_values).unwrap_or(HashMap::new())
865 }
866 
867 #[cfg(test)]
868 mod tests {
869     use super::*;
870     use lazy_static::lazy_static;
871     use std::sync::Mutex;
872 
873     impl Dst for Url {
scheme(&self) -> &str874         fn scheme(&self) -> &str {
875             Url::scheme(self)
876         }
877 
host(&self) -> &str878         fn host(&self) -> &str {
879             Url::host_str(self).expect("<Url as Dst>::host should have a str")
880         }
881 
port(&self) -> Option<u16>882         fn port(&self) -> Option<u16> {
883             Url::port(self)
884         }
885     }
886 
url(s: &str) -> Url887     fn url(s: &str) -> Url {
888         s.parse().unwrap()
889     }
890 
intercepted_uri(p: &Proxy, s: &str) -> Uri891     fn intercepted_uri(p: &Proxy, s: &str) -> Uri {
892         let (scheme, host) = match p.intercept(&url(s)).unwrap() {
893             ProxyScheme::Http { host, .. } => ("http", host),
894             ProxyScheme::Https { host, .. } => ("https", host),
895             #[cfg(feature = "socks")]
896             _ => panic!("intercepted as socks"),
897         };
898         http::Uri::builder()
899             .scheme(scheme)
900             .authority(host)
901             .path_and_query("/")
902             .build()
903             .expect("intercepted_uri")
904     }
905 
906     #[test]
test_http()907     fn test_http() {
908         let target = "http://example.domain/";
909         let p = Proxy::http(target).unwrap();
910 
911         let http = "http://hyper.rs";
912         let other = "https://hyper.rs";
913 
914         assert_eq!(intercepted_uri(&p, http), target);
915         assert!(p.intercept(&url(other)).is_none());
916     }
917 
918     #[test]
test_https()919     fn test_https() {
920         let target = "http://example.domain/";
921         let p = Proxy::https(target).unwrap();
922 
923         let http = "http://hyper.rs";
924         let other = "https://hyper.rs";
925 
926         assert!(p.intercept(&url(http)).is_none());
927         assert_eq!(intercepted_uri(&p, other), target);
928     }
929 
930     #[test]
test_all()931     fn test_all() {
932         let target = "http://example.domain/";
933         let p = Proxy::all(target).unwrap();
934 
935         let http = "http://hyper.rs";
936         let https = "https://hyper.rs";
937         let other = "x-youve-never-heard-of-me-mr-proxy://hyper.rs";
938 
939         assert_eq!(intercepted_uri(&p, http), target);
940         assert_eq!(intercepted_uri(&p, https), target);
941         assert_eq!(intercepted_uri(&p, other), target);
942     }
943 
944     #[test]
test_custom()945     fn test_custom() {
946         let target1 = "http://example.domain/";
947         let target2 = "https://example.domain/";
948         let p = Proxy::custom(move |url| {
949             if url.host_str() == Some("hyper.rs") {
950                 target1.parse().ok()
951             } else if url.scheme() == "http" {
952                 target2.parse().ok()
953             } else {
954                 None::<Url>
955             }
956         });
957 
958         let http = "http://seanmonstar.com";
959         let https = "https://hyper.rs";
960         let other = "x-youve-never-heard-of-me-mr-proxy://seanmonstar.com";
961 
962         assert_eq!(intercepted_uri(&p, http), target2);
963         assert_eq!(intercepted_uri(&p, https), target1);
964         assert!(p.intercept(&url(other)).is_none());
965     }
966 
967     #[test]
test_proxy_scheme_parse()968     fn test_proxy_scheme_parse() {
969         let ps = "http://foo:bar@localhost:1239".into_proxy_scheme().unwrap();
970 
971         match ps {
972             ProxyScheme::Http { auth, host } => {
973                 assert_eq!(auth.unwrap(), encode_basic_auth("foo", "bar"));
974                 assert_eq!(host, "localhost:1239");
975             }
976             other => panic!("unexpected: {:?}", other),
977         }
978     }
979 
980     // Smallest possible content for a mutex
981     struct MutexInner;
982 
983     lazy_static! {
984         static ref ENVLOCK: Mutex<MutexInner> = Mutex::new(MutexInner);
985     }
986 
987     #[test]
test_get_sys_proxies_parsing()988     fn test_get_sys_proxies_parsing() {
989         // Stop other threads from modifying process-global ENV while we are.
990         let _lock = ENVLOCK.lock();
991         // save system setting first.
992         let _g1 = env_guard("HTTP_PROXY");
993         let _g2 = env_guard("http_proxy");
994 
995         // Mock ENV, get the results, before doing assertions
996         // to avoid assert! -> panic! -> Mutex Poisoned.
997         let baseline_proxies = get_sys_proxies(None);
998         // the system proxy setting url is invalid.
999         env::set_var("http_proxy", "123465");
1000         let invalid_proxies = get_sys_proxies(None);
1001         // set valid proxy
1002         env::set_var("http_proxy", "http://127.0.0.1/");
1003         let valid_proxies = get_sys_proxies(None);
1004 
1005         // reset user setting when guards drop
1006         drop(_g1);
1007         drop(_g2);
1008         // Let other threads run now
1009         drop(_lock);
1010 
1011         assert_eq!(baseline_proxies.contains_key("http"), false);
1012         assert_eq!(invalid_proxies.contains_key("http"), false);
1013 
1014         let p = &valid_proxies["http"];
1015         assert_eq!(p.scheme(), "http");
1016         assert_eq!(p.host(), "127.0.0.1");
1017     }
1018 
1019     #[cfg(target_os = "windows")]
1020     #[test]
test_get_sys_proxies_registry_parsing()1021     fn test_get_sys_proxies_registry_parsing() {
1022         // Stop other threads from modifying process-global ENV while we are.
1023         let _lock = ENVLOCK.lock();
1024         // save system setting first.
1025         let _g1 = env_guard("HTTP_PROXY");
1026         let _g2 = env_guard("http_proxy");
1027 
1028         // Mock ENV, get the results, before doing assertions
1029         // to avoid assert! -> panic! -> Mutex Poisoned.
1030         let baseline_proxies = get_sys_proxies(None);
1031         // the system proxy in the registry has been disabled
1032         let disabled_proxies = get_sys_proxies(Some((0, String::from("http://127.0.0.1/"))));
1033         // set valid proxy
1034         let valid_proxies = get_sys_proxies(Some((1, String::from("http://127.0.0.1/"))));
1035         let valid_proxies_no_schema = get_sys_proxies(Some((1, String::from("127.0.0.1"))));
1036         let valid_proxies_explicit_https = get_sys_proxies(Some((1, String::from("https://127.0.0.1/"))));
1037         let multiple_proxies = get_sys_proxies(Some((1, String::from("http=127.0.0.1:8888;https=127.0.0.2:8888"))));
1038         let multiple_proxies_explicit_schema = get_sys_proxies(Some((1, String::from("http=http://127.0.0.1:8888;https=https://127.0.0.2:8888"))));
1039 
1040         // reset user setting when guards drop
1041         drop(_g1);
1042         drop(_g2);
1043         // Let other threads run now
1044         drop(_lock);
1045 
1046         assert_eq!(baseline_proxies.contains_key("http"), false);
1047         assert_eq!(disabled_proxies.contains_key("http"), false);
1048 
1049         let p = &valid_proxies["http"];
1050         assert_eq!(p.scheme(), "http");
1051         assert_eq!(p.host(), "127.0.0.1");
1052 
1053         let p = &valid_proxies_no_schema["http"];
1054         assert_eq!(p.scheme(), "http");
1055         assert_eq!(p.host(), "127.0.0.1");
1056 
1057         let p = &valid_proxies_no_schema["https"];
1058         assert_eq!(p.scheme(), "http");
1059         assert_eq!(p.host(), "127.0.0.1");
1060 
1061         let p = &valid_proxies_explicit_https["https"];
1062         assert_eq!(p.scheme(), "https");
1063         assert_eq!(p.host(), "127.0.0.1");
1064 
1065         let p = &multiple_proxies["http"];
1066         assert_eq!(p.scheme(), "http");
1067         assert_eq!(p.host(), "127.0.0.1:8888");
1068 
1069         let p = &multiple_proxies["https"];
1070         assert_eq!(p.scheme(), "http");
1071         assert_eq!(p.host(), "127.0.0.2:8888");
1072 
1073         let p = &multiple_proxies_explicit_schema["http"];
1074         assert_eq!(p.scheme(), "http");
1075         assert_eq!(p.host(), "127.0.0.1:8888");
1076 
1077         let p = &multiple_proxies_explicit_schema["https"];
1078         assert_eq!(p.scheme(), "https");
1079         assert_eq!(p.host(), "127.0.0.2:8888");
1080     }
1081 
1082     #[test]
test_get_sys_proxies_in_cgi()1083     fn test_get_sys_proxies_in_cgi() {
1084         // Stop other threads from modifying process-global ENV while we are.
1085         let _lock = ENVLOCK.lock();
1086         // save system setting first.
1087         let _g1 = env_guard("REQUEST_METHOD");
1088         let _g2 = env_guard("HTTP_PROXY");
1089 
1090         // Mock ENV, get the results, before doing assertions
1091         // to avoid assert! -> panic! -> Mutex Poisoned.
1092         env::set_var("HTTP_PROXY", "http://evil/");
1093 
1094         let baseline_proxies = get_sys_proxies(None);
1095         // set like we're in CGI
1096         env::set_var("REQUEST_METHOD", "GET");
1097 
1098         let cgi_proxies = get_sys_proxies(None);
1099 
1100         // reset user setting when guards drop
1101         drop(_g1);
1102         drop(_g2);
1103         // Let other threads run now
1104         drop(_lock);
1105 
1106         // not in CGI yet
1107         assert_eq!(baseline_proxies["http"].host(), "evil");
1108         // In CGI
1109         assert!(!cgi_proxies.contains_key("http"));
1110     }
1111 
1112     #[test]
test_sys_no_proxy()1113     fn test_sys_no_proxy() {
1114         // Stop other threads from modifying process-global ENV while we are.
1115         let _lock = ENVLOCK.lock();
1116         // save system setting first.
1117         let _g1 = env_guard("HTTP_PROXY");
1118         let _g2 = env_guard("NO_PROXY");
1119 
1120         let target = "http://example.domain/";
1121         env::set_var("HTTP_PROXY", target);
1122 
1123         env::set_var(
1124             "NO_PROXY",
1125             ".foo.bar,bar.baz,10.42.1.1/24,::1,10.124.7.8,2001::/17",
1126         );
1127 
1128         // Manually construct this so we aren't use the cache
1129         let mut p = Proxy::new(Intercept::System(Arc::new(get_sys_proxies(None))));
1130         p.no_proxy = NoProxy::new();
1131 
1132         assert_eq!(intercepted_uri(&p, "http://hyper.rs"), target);
1133         assert_eq!(intercepted_uri(&p, "http://foo.bar.baz"), target);
1134         assert_eq!(intercepted_uri(&p, "http://10.43.1.1"), target);
1135         assert_eq!(intercepted_uri(&p, "http://10.124.7.7"), target);
1136         assert_eq!(intercepted_uri(&p, "http://[ffff:db8:a0b:12f0::1]"), target);
1137         assert_eq!(intercepted_uri(&p, "http://[2005:db8:a0b:12f0::1]"), target);
1138 
1139         assert!(p.intercept(&url("http://hello.foo.bar")).is_none());
1140         assert!(p.intercept(&url("http://bar.baz")).is_none());
1141         assert!(p.intercept(&url("http://10.42.1.100")).is_none());
1142         assert!(p.intercept(&url("http://[::1]")).is_none());
1143         assert!(p.intercept(&url("http://[2001:db8:a0b:12f0::1]")).is_none());
1144         assert!(p.intercept(&url("http://10.124.7.8")).is_none());
1145 
1146         // reset user setting when guards drop
1147         drop(_g1);
1148         drop(_g2);
1149         // Let other threads run now
1150         drop(_lock);
1151     }
1152 
1153     #[test]
test_no_proxy_load()1154     fn test_no_proxy_load() {
1155         // Stop other threads from modifying process-global ENV while we are.
1156         let _lock = ENVLOCK.lock();
1157 
1158         let _g1 = env_guard("no_proxy");
1159         let domain = "lower.case";
1160         env::set_var("no_proxy", domain);
1161         // Manually construct this so we aren't use the cache
1162         let mut p = Proxy::new(Intercept::System(Arc::new(get_sys_proxies(None))));
1163         p.no_proxy = NoProxy::new();
1164         assert_eq!(
1165             p.no_proxy.expect("should have a no proxy set").domains.0[0],
1166             domain
1167         );
1168 
1169         env::remove_var("no_proxy");
1170         let _g2 = env_guard("NO_PROXY");
1171         let domain = "upper.case";
1172         env::set_var("NO_PROXY", domain);
1173         // Manually construct this so we aren't use the cache
1174         let mut p = Proxy::new(Intercept::System(Arc::new(get_sys_proxies(None))));
1175         p.no_proxy = NoProxy::new();
1176         assert_eq!(
1177             p.no_proxy.expect("should have a no proxy set").domains.0[0],
1178             domain
1179         );
1180 
1181         let _g3 = env_guard("HTTP_PROXY");
1182         env::remove_var("NO_PROXY");
1183         env::remove_var("no_proxy");
1184         let target = "http://example.domain/";
1185         env::set_var("HTTP_PROXY", target);
1186 
1187         // Manually construct this so we aren't use the cache
1188         let mut p = Proxy::new(Intercept::System(Arc::new(get_sys_proxies(None))));
1189         p.no_proxy = NoProxy::new();
1190         assert!(p.no_proxy.is_none(), "NoProxy shouldn't have been created");
1191 
1192         assert_eq!(intercepted_uri(&p, "http://hyper.rs"), target);
1193 
1194         // reset user setting when guards drop
1195         drop(_g1);
1196         drop(_g2);
1197         drop(_g3);
1198         // Let other threads run now
1199         drop(_lock);
1200     }
1201 
1202     #[cfg(target_os = "windows")]
1203     #[test]
test_type_prefix_extraction()1204     fn test_type_prefix_extraction() {
1205         assert!(extract_type_prefix("test").is_none());
1206         assert!(extract_type_prefix("://test").is_none());
1207         assert!(extract_type_prefix("some:prefix://test").is_none());
1208         assert!(extract_type_prefix("some/prefix://test").is_none());
1209 
1210         assert_eq!(extract_type_prefix("http://test").unwrap(), "http");
1211         assert_eq!(extract_type_prefix("a://test").unwrap(), "a");
1212     }
1213 
1214     /// Guard an environment variable, resetting it to the original value
1215     /// when dropped.
env_guard(name: impl Into<String>) -> EnvGuard1216     fn env_guard(name: impl Into<String>) -> EnvGuard {
1217         let name = name.into();
1218         let orig_val = env::var(&name).ok();
1219         env::remove_var(&name);
1220         EnvGuard { name, orig_val }
1221     }
1222 
1223     struct EnvGuard {
1224         name: String,
1225         orig_val: Option<String>,
1226     }
1227 
1228     impl Drop for EnvGuard {
drop(&mut self)1229         fn drop(&mut self) {
1230             if let Some(val) = self.orig_val.take() {
1231                 env::set_var(&self.name, val);
1232             } else {
1233                 env::remove_var(&self.name);
1234             }
1235         }
1236     }
1237 
1238     #[test]
test_has_http_auth()1239     fn test_has_http_auth() {
1240         let http_proxy_with_auth = Proxy {
1241             intercept: Intercept::Http(ProxyScheme::Http {
1242                 auth: Some(HeaderValue::from_static("auth1")),
1243                 host: http::uri::Authority::from_static("authority"),
1244             }),
1245             no_proxy: None,
1246         };
1247         assert_eq!(http_proxy_with_auth.maybe_has_http_auth(), true);
1248         assert_eq!(
1249             http_proxy_with_auth.http_basic_auth(&Uri::from_static("http://example.com")),
1250             Some(HeaderValue::from_static("auth1"))
1251         );
1252 
1253         let http_proxy_without_auth = Proxy {
1254             intercept: Intercept::Http(ProxyScheme::Http {
1255                 auth: None,
1256                 host: http::uri::Authority::from_static("authority"),
1257             }),
1258             no_proxy: None,
1259         };
1260         assert_eq!(http_proxy_without_auth.maybe_has_http_auth(), false);
1261         assert_eq!(
1262             http_proxy_without_auth.http_basic_auth(&Uri::from_static("http://example.com")),
1263             None
1264         );
1265 
1266         let https_proxy_with_auth = Proxy {
1267             intercept: Intercept::Http(ProxyScheme::Https {
1268                 auth: Some(HeaderValue::from_static("auth2")),
1269                 host: http::uri::Authority::from_static("authority"),
1270             }),
1271             no_proxy: None,
1272         };
1273         assert_eq!(https_proxy_with_auth.maybe_has_http_auth(), false);
1274         assert_eq!(
1275             https_proxy_with_auth.http_basic_auth(&Uri::from_static("http://example.com")),
1276             None
1277         );
1278 
1279         let all_http_proxy_with_auth = Proxy {
1280             intercept: Intercept::All(ProxyScheme::Http {
1281                 auth: Some(HeaderValue::from_static("auth3")),
1282                 host: http::uri::Authority::from_static("authority"),
1283             }),
1284             no_proxy: None,
1285         };
1286         assert_eq!(all_http_proxy_with_auth.maybe_has_http_auth(), true);
1287         assert_eq!(
1288             all_http_proxy_with_auth.http_basic_auth(&Uri::from_static("http://example.com")),
1289             Some(HeaderValue::from_static("auth3"))
1290         );
1291 
1292         let all_https_proxy_with_auth = Proxy {
1293             intercept: Intercept::All(ProxyScheme::Https {
1294                 auth: Some(HeaderValue::from_static("auth4")),
1295                 host: http::uri::Authority::from_static("authority"),
1296             }),
1297             no_proxy: None,
1298         };
1299         assert_eq!(all_https_proxy_with_auth.maybe_has_http_auth(), false);
1300         assert_eq!(
1301             all_https_proxy_with_auth.http_basic_auth(&Uri::from_static("http://example.com")),
1302             None
1303         );
1304 
1305         let all_https_proxy_without_auth = Proxy {
1306             intercept: Intercept::All(ProxyScheme::Https {
1307                 auth: None,
1308                 host: http::uri::Authority::from_static("authority"),
1309             }),
1310             no_proxy: None,
1311         };
1312         assert_eq!(all_https_proxy_without_auth.maybe_has_http_auth(), false);
1313         assert_eq!(
1314             all_https_proxy_without_auth.http_basic_auth(&Uri::from_static("http://example.com")),
1315             None
1316         );
1317 
1318         let system_http_proxy_with_auth = Proxy {
1319             intercept: Intercept::System(Arc::new({
1320                 let mut m = HashMap::new();
1321                 m.insert(
1322                     "http".into(),
1323                     ProxyScheme::Http {
1324                         auth: Some(HeaderValue::from_static("auth5")),
1325                         host: http::uri::Authority::from_static("authority"),
1326                     },
1327                 );
1328                 m
1329             })),
1330             no_proxy: None,
1331         };
1332         assert_eq!(system_http_proxy_with_auth.maybe_has_http_auth(), true);
1333         assert_eq!(
1334             system_http_proxy_with_auth.http_basic_auth(&Uri::from_static("http://example.com")),
1335             Some(HeaderValue::from_static("auth5"))
1336         );
1337 
1338         let system_https_proxy_with_auth = Proxy {
1339             intercept: Intercept::System(Arc::new({
1340                 let mut m = HashMap::new();
1341                 m.insert(
1342                     "https".into(),
1343                     ProxyScheme::Https {
1344                         auth: Some(HeaderValue::from_static("auth6")),
1345                         host: http::uri::Authority::from_static("authority"),
1346                     },
1347                 );
1348                 m
1349             })),
1350             no_proxy: None,
1351         };
1352         assert_eq!(system_https_proxy_with_auth.maybe_has_http_auth(), false);
1353         assert_eq!(
1354             system_https_proxy_with_auth.http_basic_auth(&Uri::from_static("http://example.com")),
1355             None
1356         );
1357     }
1358 }
1359