1use std::convert::{TryFrom, TryInto};
2use std::fmt;
3use std::sync::Arc;
4use ureq_proto::http::uri::{PathAndQuery, Scheme};
5
6use http::Uri;
7
8use crate::http;
9use crate::util::{AuthorityExt, DebugUri};
10use crate::Error;
11
12#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
14#[non_exhaustive]
15pub enum ProxyProtocol {
16 Http,
18 Https,
20 Socks4,
22 Socks4A,
24 Socks5,
26}
27
28impl ProxyProtocol {
29 pub(crate) fn default_port(&self) -> u16 {
30 match self {
31 ProxyProtocol::Http => 80,
32 ProxyProtocol::Https => 443,
33 ProxyProtocol::Socks4 | ProxyProtocol::Socks4A | ProxyProtocol::Socks5 => 1080,
34 }
35 }
36
37 pub(crate) fn is_socks(&self) -> bool {
38 matches!(self, Self::Socks4 | Self::Socks4A | Self::Socks5)
39 }
40
41 pub(crate) fn is_connect(&self) -> bool {
42 matches!(self, Self::Http | Self::Https)
43 }
44
45 fn default_resolve_target(&self) -> bool {
46 match self {
47 ProxyProtocol::Http => false,
48 ProxyProtocol::Https => false,
49 ProxyProtocol::Socks4 => true, ProxyProtocol::Socks4A => false,
51 ProxyProtocol::Socks5 => false,
52 }
53 }
54}
55
56#[derive(Clone, Eq, Hash, PartialEq)]
106pub struct Proxy {
107 inner: Arc<ProxyInner>,
108}
109
110#[derive(Eq, Hash, PartialEq)]
111struct ProxyInner {
112 proto: ProxyProtocol,
113 uri: Uri,
114 from_env: bool,
115 resolve_target: bool,
116 no_proxy: Option<NoProxy>,
117}
118
119impl Proxy {
120 pub fn new(proxy: &str) -> Result<Self, Error> {
142 Self::new_with_flag(proxy, None, false, None)
143 }
144
145 pub fn builder(p: ProxyProtocol) -> ProxyBuilder {
147 ProxyBuilder {
148 protocol: p,
149 host: None,
150 port: None,
151 username: None,
152 password: None,
153 resolve_target: p.default_resolve_target(),
154 no_proxy: None,
155 }
156 }
157
158 fn new_with_flag(
159 proxy: &str,
160 no_proxy: Option<NoProxy>,
161 from_env: bool,
162 resolve_target: Option<bool>,
163 ) -> Result<Self, Error> {
164 let mut uri = proxy.parse::<Uri>().or(Err(Error::InvalidProxyUrl))?;
165
166 let _ = uri.authority().ok_or(Error::InvalidProxyUrl)?;
169
170 let scheme = match uri.scheme_str() {
171 Some(v) => v,
172 None => {
173 uri = insert_default_scheme(uri);
176 "http"
177 }
178 };
179
180 let proto: ProxyProtocol = scheme.try_into()?;
181 let resolve_target = resolve_target.unwrap_or(proto.default_resolve_target());
182
183 let inner = ProxyInner {
184 proto,
185 uri,
186 from_env,
187 resolve_target,
188 no_proxy,
189 };
190
191 Ok(Self {
192 inner: Arc::new(inner),
193 })
194 }
195
196 pub fn try_from_env() -> Option<Self> {
211 const TRY_ENV: &[&str] = &[
212 "ALL_PROXY",
213 "all_proxy",
214 "HTTPS_PROXY",
215 "https_proxy",
216 "HTTP_PROXY",
217 "http_proxy",
218 ];
219
220 for attempt in TRY_ENV {
221 if let Ok(env) = std::env::var(attempt) {
222 let no_proxy = NoProxy::try_from_env();
223 if let Ok(proxy) = Self::new_with_flag(&env, no_proxy, true, None) {
224 return Some(proxy);
225 }
226 }
227 }
228
229 None
230 }
231
232 pub fn protocol(&self) -> ProxyProtocol {
234 self.inner.proto
235 }
236
237 pub fn uri(&self) -> &Uri {
239 &self.inner.uri
240 }
241
242 pub fn host(&self) -> &str {
244 self.inner
245 .uri
246 .authority()
247 .map(|a| a.host())
248 .expect("constructor to ensure there is an authority")
249 }
250
251 pub fn port(&self) -> u16 {
253 self.inner
254 .uri
255 .authority()
256 .and_then(|a| a.port_u16())
257 .unwrap_or_else(|| self.inner.proto.default_port())
258 }
259
260 pub fn username(&self) -> Option<&str> {
262 self.inner.uri.authority().and_then(|a| a.username())
263 }
264
265 pub fn password(&self) -> Option<&str> {
267 self.inner.uri.authority().and_then(|a| a.password())
268 }
269
270 pub fn is_from_env(&self) -> bool {
273 self.inner.from_env
274 }
275
276 pub fn resolve_target(&self) -> bool {
284 self.inner.resolve_target
285 }
286
287 pub fn is_no_proxy(&self, uri: &Uri) -> bool {
295 if let (Some(no_proxy), Some(host)) = (&self.inner.no_proxy, uri.host()) {
296 return no_proxy.is_no_proxy(host);
297 }
298 false
299 }
300}
301
302fn insert_default_scheme(uri: Uri) -> Uri {
303 let mut parts = uri.into_parts();
304
305 parts.scheme = Some(Scheme::HTTP);
306
307 parts.path_and_query = parts
310 .path_and_query
311 .or_else(|| Some(PathAndQuery::from_static("/")));
312
313 Uri::from_parts(parts).unwrap()
314}
315
316pub struct ProxyBuilder {
320 protocol: ProxyProtocol,
321 host: Option<String>,
322 port: Option<u16>,
323 username: Option<String>,
324 password: Option<String>,
325 resolve_target: bool,
326 no_proxy: Option<NoProxy>,
327}
328
329impl ProxyBuilder {
330 pub fn host(mut self, host: &str) -> Self {
334 self.host = Some(host.to_string());
335 self
336 }
337
338 pub fn port(mut self, port: u16) -> Self {
342 self.port = Some(port);
343 self
344 }
345
346 pub fn username(mut self, v: &str) -> Self {
350 self.username = Some(v.to_string());
351 self
352 }
353
354 pub fn password(mut self, v: &str) -> Self {
361 self.password = Some(v.to_string());
362 self
363 }
364
365 pub fn resolve_target(mut self, do_resolve: bool) -> Self {
373 self.resolve_target = do_resolve;
374 self
375 }
376
377 pub fn no_proxy(mut self, expr: &str) -> Self {
388 if let Some(entry) = NoProxyEntry::try_parse(expr) {
389 if self.no_proxy.is_none() {
390 self.no_proxy = Some(NoProxy::default());
391 }
392 self.no_proxy.as_mut().unwrap().inner.push(entry);
393 }
394
395 self
396 }
397
398 pub fn build(self) -> Result<Proxy, Error> {
400 let host = self.host.as_deref().unwrap_or("localhost");
401 let port = self.port.unwrap_or(self.protocol.default_port());
402
403 let mut userpass = String::new();
404 if let Some(username) = self.username {
405 userpass.push_str(&username);
406 if let Some(password) = self.password {
407 userpass.push(':');
408 userpass.push_str(&password);
409 }
410 userpass.push('@');
411 }
412
413 let proxy = format!("{}://{}{}:{}", self.protocol, userpass, host, port);
417 Proxy::new_with_flag(&proxy, self.no_proxy, false, Some(self.resolve_target))
418 }
419}
420
421impl TryFrom<&str> for ProxyProtocol {
422 type Error = Error;
423
424 fn try_from(scheme: &str) -> Result<Self, Self::Error> {
425 match scheme.to_ascii_lowercase().as_str() {
426 "http" => Ok(ProxyProtocol::Http),
427 "https" => Ok(ProxyProtocol::Https),
428 "socks4" => Ok(ProxyProtocol::Socks4),
429 "socks4a" => Ok(ProxyProtocol::Socks4A),
430 "socks" => Ok(ProxyProtocol::Socks5),
431 "socks5" => Ok(ProxyProtocol::Socks5),
432 _ => Err(Error::InvalidProxyUrl),
433 }
434 }
435}
436
437impl fmt::Debug for Proxy {
438 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
439 f.debug_struct("Proxy")
440 .field("proto", &self.inner.proto)
441 .field("uri", &DebugUri(&self.inner.uri))
442 .field("from_env", &self.inner.from_env)
443 .finish()
444 }
445}
446
447impl fmt::Display for ProxyProtocol {
448 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
449 match self {
450 ProxyProtocol::Http => write!(f, "HTTP"),
451 ProxyProtocol::Https => write!(f, "HTTPS"),
452 ProxyProtocol::Socks4 => write!(f, "SOCKS4"),
453 ProxyProtocol::Socks4A => write!(f, "SOCKS4a"),
454 ProxyProtocol::Socks5 => write!(f, "SOCKS5"),
455 }
456 }
457}
458
459#[derive(Debug, Clone, Eq, PartialEq, Hash)]
460enum NoProxyEntry {
461 ExactHost(String),
462 HostSuffix(String),
463 MatchAll,
464}
465
466#[derive(Debug, Clone, Eq, PartialEq, Hash, Default)]
467struct NoProxy {
468 inner: Vec<NoProxyEntry>,
469}
470
471impl NoProxy {
472 pub fn try_from_env() -> Option<Self> {
502 const TRY_ENV: &[&str] = &["NO_PROXY", "no_proxy"];
503
504 for attempt in TRY_ENV {
505 if let Ok(env) = std::env::var(attempt) {
506 let inner = env.split(',').filter_map(NoProxyEntry::try_parse).collect();
507 return Some(Self { inner });
508 }
509 }
510
511 None
512 }
513
514 pub fn is_no_proxy(&self, host: &str) -> bool {
515 self.inner.iter().any(|entry| entry.matches(host))
516 }
517}
518
519impl NoProxyEntry {
520 fn try_parse(u: &str) -> Option<Self> {
521 let entry = match u {
522 "*" => Self::MatchAll,
523 u if u.starts_with("*") => {
524 Self::HostSuffix(u.chars().skip(1).collect::<String>().to_ascii_lowercase())
525 }
526 u if u.starts_with(".") => Self::HostSuffix(u.to_ascii_lowercase()),
527 _ => Self::ExactHost(u.to_ascii_lowercase()),
528 };
529 Some(entry)
530 }
531
532 fn matches(&self, host: &str) -> bool {
533 match self {
534 NoProxyEntry::MatchAll => true,
535 NoProxyEntry::ExactHost(pattern) => {
536 if host.chars().all(|c| !c.is_ascii_uppercase()) {
538 pattern == host
539 } else {
540 pattern == &host.to_ascii_lowercase()
542 }
543 }
544 NoProxyEntry::HostSuffix(suffix) => {
545 if host.len() < suffix.len() {
546 return false;
547 }
548 let host_suffix = &host[host.len() - suffix.len()..];
549 if host_suffix.chars().all(|c| !c.is_ascii_uppercase()) {
551 suffix == host_suffix
552 } else {
553 suffix == &host_suffix.to_ascii_lowercase()
555 }
556 }
557 }
558 }
559}
560
561#[cfg(test)]
562mod tests {
563 use assert_no_alloc::*;
564 use std::str::FromStr;
565
566 use super::*;
567
568 #[test]
569 fn parse_proxy_fakeproto() {
570 assert!(Proxy::new("fakeproto://localhost").is_err());
571 }
572
573 #[test]
574 fn parse_proxy_http_user_pass_server_port() {
575 let proxy = Proxy::new("http://user:p@ssw0rd@localhost:9999").unwrap();
576 assert_eq!(proxy.username(), Some("user"));
577 assert_eq!(proxy.password(), Some("p@ssw0rd"));
578 assert_eq!(proxy.host(), "localhost");
579 assert_eq!(proxy.port(), 9999);
580 assert_eq!(proxy.inner.proto, ProxyProtocol::Http);
581 }
582
583 #[test]
584 fn parse_proxy_http_user_pass_server_port_trailing_slash() {
585 let proxy = Proxy::new("http://user:p@ssw0rd@localhost:9999/").unwrap();
586 assert_eq!(proxy.username(), Some("user"));
587 assert_eq!(proxy.password(), Some("p@ssw0rd"));
588 assert_eq!(proxy.host(), "localhost");
589 assert_eq!(proxy.port(), 9999);
590 assert_eq!(proxy.inner.proto, ProxyProtocol::Http);
591 }
592
593 #[test]
594 fn parse_proxy_socks4_user_pass_server_port() {
595 let proxy = Proxy::new("socks4://user:p@ssw0rd@localhost:9999").unwrap();
596 assert_eq!(proxy.username(), Some("user"));
597 assert_eq!(proxy.password(), Some("p@ssw0rd"));
598 assert_eq!(proxy.host(), "localhost");
599 assert_eq!(proxy.port(), 9999);
600 assert_eq!(proxy.inner.proto, ProxyProtocol::Socks4);
601 }
602
603 #[test]
604 fn parse_proxy_socks4a_user_pass_server_port() {
605 let proxy = Proxy::new("socks4a://user:p@ssw0rd@localhost:9999").unwrap();
606 assert_eq!(proxy.username(), Some("user"));
607 assert_eq!(proxy.password(), Some("p@ssw0rd"));
608 assert_eq!(proxy.host(), "localhost");
609 assert_eq!(proxy.port(), 9999);
610 assert_eq!(proxy.inner.proto, ProxyProtocol::Socks4A);
611 }
612
613 #[test]
614 fn parse_proxy_socks_user_pass_server_port() {
615 let proxy = Proxy::new("socks://user:p@ssw0rd@localhost:9999").unwrap();
616 assert_eq!(proxy.username(), Some("user"));
617 assert_eq!(proxy.password(), Some("p@ssw0rd"));
618 assert_eq!(proxy.host(), "localhost");
619 assert_eq!(proxy.port(), 9999);
620 assert_eq!(proxy.inner.proto, ProxyProtocol::Socks5);
621 }
622
623 #[test]
624 fn parse_proxy_socks5_user_pass_server_port() {
625 let proxy = Proxy::new("socks5://user:p@ssw0rd@localhost:9999").unwrap();
626 assert_eq!(proxy.username(), Some("user"));
627 assert_eq!(proxy.password(), Some("p@ssw0rd"));
628 assert_eq!(proxy.host(), "localhost");
629 assert_eq!(proxy.port(), 9999);
630 assert_eq!(proxy.inner.proto, ProxyProtocol::Socks5);
631 }
632
633 #[test]
634 fn parse_proxy_user_pass_server_port() {
635 let proxy = Proxy::new("user:p@ssw0rd@localhost:9999").unwrap();
636 assert_eq!(proxy.username(), Some("user"));
637 assert_eq!(proxy.password(), Some("p@ssw0rd"));
638 assert_eq!(proxy.host(), "localhost");
639 assert_eq!(proxy.port(), 9999);
640 assert_eq!(proxy.inner.proto, ProxyProtocol::Http);
641 }
642
643 #[test]
644 fn parse_proxy_server_port() {
645 let proxy = Proxy::new("localhost:9999").unwrap();
646 assert_eq!(proxy.username(), None);
647 assert_eq!(proxy.password(), None);
648 assert_eq!(proxy.host(), "localhost");
649 assert_eq!(proxy.port(), 9999);
650 assert_eq!(proxy.inner.proto, ProxyProtocol::Http);
651 }
652
653 #[test]
654 fn parse_proxy_server() {
655 let proxy = Proxy::new("localhost").unwrap();
656 assert_eq!(proxy.username(), None);
657 assert_eq!(proxy.password(), None);
658 assert_eq!(proxy.host(), "localhost");
659 assert_eq!(proxy.port(), 80);
660 assert_eq!(proxy.inner.proto, ProxyProtocol::Http);
661 }
662
663 #[test]
664 fn no_proxy_exact_host_matching() {
665 let p = Proxy::builder(ProxyProtocol::Http)
666 .host("proxy.example.com")
667 .port(8080)
668 .no_proxy("localhost")
669 .no_proxy("127.0.0.1")
670 .no_proxy("api.internal.com")
671 .build()
672 .unwrap();
673
674 fn is_no_proxy(p: &Proxy, host: &str) -> bool {
675 let uri = Uri::from_str(&format!("http://{}", host)).unwrap();
676 p.is_no_proxy(&uri)
677 }
678
679 assert!(is_no_proxy(&p, "localhost"));
681 assert!(is_no_proxy(&p, "127.0.0.1"));
682 assert!(is_no_proxy(&p, "api.internal.com"));
683
684 assert!(!is_no_proxy(&p, "mylocalhost"));
686 assert!(!is_no_proxy(&p, "localhost.example.com"));
687 assert!(!is_no_proxy(&p, "127.0.0.2"));
688 assert!(!is_no_proxy(&p, "api.internal.com.evil.com"));
689 assert!(!is_no_proxy(&p, "docs.rs"));
690 }
691
692 #[test]
693 fn no_proxy_wildcard_suffix_matching() {
694 let p = Proxy::builder(ProxyProtocol::Http)
695 .host("proxy.example.com")
696 .port(8080)
697 .no_proxy("*.internal.com")
698 .no_proxy("*.dev")
699 .build()
700 .unwrap();
701
702 fn is_no_proxy(p: &Proxy, host: &str) -> bool {
703 let uri = Uri::from_str(&format!("http://{}", host)).unwrap();
704 p.is_no_proxy(&uri)
705 }
706
707 assert!(is_no_proxy(&p, "api.internal.com"));
709 assert!(is_no_proxy(&p, "auth.internal.com"));
710 assert!(is_no_proxy(&p, "db.internal.com"));
711 assert!(is_no_proxy(&p, "app.dev"));
712 assert!(is_no_proxy(&p, "test.dev"));
713
714 assert!(!is_no_proxy(&p, "internal.com"));
716 assert!(!is_no_proxy(&p, "dev"));
717 assert!(!is_no_proxy(&p, "api.external.com"));
718 assert!(!is_no_proxy(&p, "app.prod"));
719 assert!(!is_no_proxy(&p, "docs.rs"));
720 }
721
722 #[test]
723 fn no_proxy_dot_suffix_matching() {
724 let p = Proxy::builder(ProxyProtocol::Http)
725 .host("proxy.example.com")
726 .port(8080)
727 .no_proxy(".internal.com")
728 .no_proxy(".staging")
729 .build()
730 .unwrap();
731
732 fn is_no_proxy(p: &Proxy, host: &str) -> bool {
733 let uri = Uri::from_str(&format!("http://{}", host)).unwrap();
734 p.is_no_proxy(&uri)
735 }
736
737 assert!(is_no_proxy(&p, "api.internal.com"));
739 assert!(is_no_proxy(&p, "auth.internal.com"));
740 assert!(is_no_proxy(&p, "db.sub.internal.com"));
741 assert!(is_no_proxy(&p, "app.staging"));
742 assert!(is_no_proxy(&p, "test.staging"));
743
744 assert!(!is_no_proxy(&p, "internal.com"));
746 assert!(!is_no_proxy(&p, "staging"));
747
748 assert!(!is_no_proxy(&p, "api.external.com"));
750 assert!(!is_no_proxy(&p, "prod"));
751 assert!(!is_no_proxy(&p, "docs.rs"));
752 }
753
754 #[test]
755 fn no_proxy_match_all_wildcard() {
756 let p = Proxy::builder(ProxyProtocol::Http)
757 .host("proxy.example.com")
758 .port(8080)
759 .no_proxy("*")
760 .build()
761 .unwrap();
762
763 fn is_no_proxy(p: &Proxy, host: &str) -> bool {
764 let uri = Uri::from_str(&format!("http://{}", host)).unwrap();
765 p.is_no_proxy(&uri)
766 }
767
768 assert!(is_no_proxy(&p, "localhost"));
770 assert!(is_no_proxy(&p, "127.0.0.1"));
771 assert!(is_no_proxy(&p, "api.example.com"));
772 assert!(is_no_proxy(&p, "docs.rs"));
773 assert!(is_no_proxy(&p, "github.com"));
774 assert!(is_no_proxy(&p, "any.random.domain"));
775 }
776
777 #[test]
778 fn no_proxy_mixed_patterns() {
779 let p = Proxy::builder(ProxyProtocol::Http)
780 .host("proxy.example.com")
781 .port(8080)
782 .no_proxy("localhost") .no_proxy("*.dev") .no_proxy(".staging") .no_proxy("127.0.0.1") .build()
787 .unwrap();
788
789 fn is_no_proxy(p: &Proxy, host: &str) -> bool {
790 let uri = Uri::from_str(&format!("http://{}", host)).unwrap();
791 p.is_no_proxy(&uri)
792 }
793
794 assert!(is_no_proxy(&p, "localhost"));
796 assert!(is_no_proxy(&p, "127.0.0.1"));
797
798 assert!(is_no_proxy(&p, "api.dev"));
800 assert!(is_no_proxy(&p, "test.dev"));
801
802 assert!(is_no_proxy(&p, "app.staging"));
804 assert!(!is_no_proxy(&p, "staging"));
805
806 assert!(!is_no_proxy(&p, "dev")); assert!(!is_no_proxy(&p, "api.prod")); assert!(!is_no_proxy(&p, "docs.rs")); assert!(!is_no_proxy(&p, "127.0.0.2")); }
812
813 #[test]
814 fn no_proxy_case_insensitive_matching() {
815 let p = Proxy::builder(ProxyProtocol::Http)
816 .host("proxy.example.com")
817 .port(8080)
818 .no_proxy("localhost")
819 .no_proxy("*.Example.Com")
820 .no_proxy(".INTERNAL")
821 .build()
822 .unwrap();
823
824 fn is_no_proxy(p: &Proxy, host: &str) -> bool {
825 let uri = Uri::from_str(&format!("http://{}", host)).unwrap();
826 p.is_no_proxy(&uri)
827 }
828
829 assert!(is_no_proxy(&p, "localhost")); assert!(is_no_proxy(&p, "LOCALHOST")); assert!(is_no_proxy(&p, "LocalHost")); assert!(is_no_proxy(&p, "api.example.com")); assert!(is_no_proxy(&p, "api.EXAMPLE.COM")); assert!(is_no_proxy(&p, "API.example.COM")); assert!(is_no_proxy(&p, "api.Example.Com")); assert!(is_no_proxy(&p, "app.internal")); assert!(is_no_proxy(&p, "app.INTERNAL")); assert!(is_no_proxy(&p, "APP.Internal")); assert!(!is_no_proxy(&p, "INTERNAL")); assert!(!is_no_proxy(&p, "internal")); }
850
851 #[test]
852 fn no_proxy_edge_cases() {
853 let p = Proxy::builder(ProxyProtocol::Http)
854 .host("proxy.example.com")
855 .port(8080)
856 .no_proxy("") .no_proxy("single") .no_proxy("*..") .no_proxy("..") .no_proxy("192.168.1.1") .no_proxy("*.local") .build()
863 .unwrap();
864
865 fn is_no_proxy(p: &Proxy, host: &str) -> bool {
866 let uri = Uri::from_str(&format!("http://{}", host)).unwrap();
867 p.is_no_proxy(&uri)
868 }
869
870 assert!(is_no_proxy(&p, "single"));
872 assert!(is_no_proxy(&p, "192.168.1.1"));
873 assert!(!is_no_proxy(&p, "192.168.1.2"));
874
875 assert!(is_no_proxy(&p, "printer.local"));
877 assert!(is_no_proxy(&p, "router.local"));
878 assert!(!is_no_proxy(&p, "local")); assert!(is_no_proxy(&p, "something..")); assert!(!is_no_proxy(&p, "something.else"));
883
884 }
888
889 #[test]
890 fn proxy_clone_does_not_allocate() {
891 let c = Proxy::new("socks://1.2.3.4").unwrap();
892 assert_no_alloc(|| c.clone());
893 }
894
895 #[test]
896 fn proxy_new_default_scheme() {
897 let c = Proxy::new("localhost:1234").unwrap();
898 assert_eq!(c.protocol(), ProxyProtocol::Http);
899 assert_eq!(c.uri(), "http://localhost:1234");
900 }
901
902 #[test]
903 fn proxy_empty_env_url() {
904 let result = Proxy::new_with_flag("", None, false, None);
905 assert!(result.is_err());
906 }
907
908 #[test]
909 fn proxy_invalid_env_url() {
910 let result = Proxy::new_with_flag("r32/?//52:**", None, false, None);
911 assert!(result.is_err());
912 }
913
914 #[test]
915 fn proxy_builder() {
916 let proxy = Proxy::builder(ProxyProtocol::Socks4)
917 .host("my-proxy.com")
918 .port(5551)
919 .resolve_target(false)
920 .build()
921 .unwrap();
922
923 assert_eq!(proxy.protocol(), ProxyProtocol::Socks4);
924 assert_eq!(proxy.uri(), "SOCKS4://my-proxy.com:5551/");
925 assert_eq!(proxy.host(), "my-proxy.com");
926 assert_eq!(proxy.port(), 5551);
927 assert_eq!(proxy.username(), None);
928 assert_eq!(proxy.password(), None);
929 assert_eq!(proxy.is_from_env(), false);
930 assert_eq!(proxy.resolve_target(), false);
931 }
932
933 #[test]
934 fn proxy_builder_username() {
935 let proxy = Proxy::builder(ProxyProtocol::Https)
936 .username("hemligearne")
937 .build()
938 .unwrap();
939
940 assert_eq!(proxy.protocol(), ProxyProtocol::Https);
941 assert_eq!(proxy.uri(), "https://hemligearne@localhost:443/");
942 assert_eq!(proxy.host(), "localhost");
943 assert_eq!(proxy.port(), 443);
944 assert_eq!(proxy.username(), Some("hemligearne"));
945 assert_eq!(proxy.password(), None);
946 assert_eq!(proxy.is_from_env(), false);
947 assert_eq!(proxy.resolve_target(), false);
948 }
949
950 #[test]
951 fn proxy_builder_username_password() {
952 let proxy = Proxy::builder(ProxyProtocol::Https)
953 .username("hemligearne")
954 .password("kulgrej")
955 .build()
956 .unwrap();
957
958 assert_eq!(proxy.protocol(), ProxyProtocol::Https);
959 assert_eq!(proxy.uri(), "https://hemligearne:kulgrej@localhost:443/");
960 assert_eq!(proxy.host(), "localhost");
961 assert_eq!(proxy.port(), 443);
962 assert_eq!(proxy.username(), Some("hemligearne"));
963 assert_eq!(proxy.password(), Some("kulgrej"));
964 assert_eq!(proxy.is_from_env(), false);
965 assert_eq!(proxy.resolve_target(), false);
966 }
967}