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