support http proxy addresses with no scheme
Other implementations (curl and Go) accept HTTPS_PROXY values with no protocol scheme. When the scheme is not present, http:// is assumed. For example 192.168.1.1 is interpreted as http://192.168.1.1 This commit adds support for http proxy addresses without a scheme by retrying the URL parsing mechanisms with http:// prepended.
This commit is contained in:
		
				
					committed by
					
						 Sean McArthur
						Sean McArthur
					
				
			
			
				
	
			
			
			
						parent
						
							c27cd06a11
						
					
				
				
					commit
					f5450f534a
				
			| @@ -13,6 +13,8 @@ pub trait PolyfillTryInto { | |||||||
|     // Besides parsing as a valid `Url`, the `Url` must be a valid |     // Besides parsing as a valid `Url`, the `Url` must be a valid | ||||||
|     // `http::Uri`, in that it makes sense to use in a network request. |     // `http::Uri`, in that it makes sense to use in a network request. | ||||||
|     fn into_url(self) -> crate::Result<Url>; |     fn into_url(self) -> crate::Result<Url>; | ||||||
|  |  | ||||||
|  |     fn _as_str(&self) -> &str; | ||||||
| } | } | ||||||
|  |  | ||||||
| impl PolyfillTryInto for Url { | impl PolyfillTryInto for Url { | ||||||
| @@ -23,18 +25,30 @@ impl PolyfillTryInto for Url { | |||||||
|             Err(crate::error::url_bad_scheme(self)) |             Err(crate::error::url_bad_scheme(self)) | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     fn _as_str(&self) -> &str { | ||||||
|  |         self.as_ref() | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| impl<'a> PolyfillTryInto for &'a str { | impl<'a> PolyfillTryInto for &'a str { | ||||||
|     fn into_url(self) -> crate::Result<Url> { |     fn into_url(self) -> crate::Result<Url> { | ||||||
|         Url::parse(self).map_err(crate::error::builder)?.into_url() |         Url::parse(self).map_err(crate::error::builder)?.into_url() | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     fn _as_str(&self) -> &str { | ||||||
|  |         self.as_ref() | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| impl<'a> PolyfillTryInto for &'a String { | impl<'a> PolyfillTryInto for &'a String { | ||||||
|     fn into_url(self) -> crate::Result<Url> { |     fn into_url(self) -> crate::Result<Url> { | ||||||
|         (&**self).into_url() |         (&**self).into_url() | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     fn _as_str(&self) -> &str { | ||||||
|  |         self.as_ref() | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| if_hyper! { | if_hyper! { | ||||||
|   | |||||||
							
								
								
									
										81
									
								
								src/proxy.rs
									
									
									
									
									
								
							
							
						
						
									
										81
									
								
								src/proxy.rs
									
									
									
									
									
								
							| @@ -3,7 +3,8 @@ use std::fmt; | |||||||
| use std::net::SocketAddr; | use std::net::SocketAddr; | ||||||
| use std::sync::Arc; | use std::sync::Arc; | ||||||
|  |  | ||||||
| use crate::{IntoUrl, Url}; | use crate::into_url::{IntoUrl, PolyfillTryInto}; | ||||||
|  | use crate::Url; | ||||||
| use http::{header::HeaderValue, Uri}; | use http::{header::HeaderValue, Uri}; | ||||||
| use ipnet::IpNet; | use ipnet::IpNet; | ||||||
| use percent_encoding::percent_decode; | use percent_encoding::percent_decode; | ||||||
| @@ -107,9 +108,33 @@ pub trait IntoProxyScheme { | |||||||
|     fn into_proxy_scheme(self) -> crate::Result<ProxyScheme>; |     fn into_proxy_scheme(self) -> crate::Result<ProxyScheme>; | ||||||
| } | } | ||||||
|  |  | ||||||
| impl<T: IntoUrl> IntoProxyScheme for T { | impl<S: IntoUrl> IntoProxyScheme for S { | ||||||
|     fn into_proxy_scheme(self) -> crate::Result<ProxyScheme> { |     fn into_proxy_scheme(self) -> crate::Result<ProxyScheme> { | ||||||
|         ProxyScheme::parse(self.into_url()?) |         // validate the URL | ||||||
|  |         let url = match self._as_str().into_url() { | ||||||
|  |             Ok(ok) => ok, | ||||||
|  |             Err(e) => { | ||||||
|  |                 // the issue could have been caused by a missing scheme, so we try adding http:// | ||||||
|  |                 format!("http://{}", self._as_str()) | ||||||
|  |                     .into_url() | ||||||
|  |                     .map_err(|_| { | ||||||
|  |                         // return the original error | ||||||
|  |                         crate::error::builder(e) | ||||||
|  |                     })? | ||||||
|  |             } | ||||||
|  |         }; | ||||||
|  |         ProxyScheme::parse(url) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // These bounds are accidentally leaked by the blanket impl of IntoProxyScheme | ||||||
|  | // for all types that implement IntoUrl. So, this function exists to detect | ||||||
|  | // if we were to break those bounds for a user. | ||||||
|  | fn _implied_bounds() { | ||||||
|  |     fn prox<T: IntoProxyScheme>(_t: T) {} | ||||||
|  |  | ||||||
|  |     fn url<T: IntoUrl>(t: T) { | ||||||
|  |         prox(t); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -842,8 +867,7 @@ fn extract_type_prefix(address: &str) -> Option<&str> { | |||||||
|     if let Some(indice) = address.find("://") { |     if let Some(indice) = address.find("://") { | ||||||
|         if indice == 0 { |         if indice == 0 { | ||||||
|             None |             None | ||||||
|         } |         } else { | ||||||
|         else { |  | ||||||
|             let prefix = &address[..indice]; |             let prefix = &address[..indice]; | ||||||
|             let contains_banned = prefix.contains(|c| c == ':' || c == '/'); |             let contains_banned = prefix.contains(|c| c == ':' || c == '/'); | ||||||
|  |  | ||||||
| @@ -853,8 +877,7 @@ fn extract_type_prefix(address: &str) -> Option<&str> { | |||||||
|                 None |                 None | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } else { | ||||||
|     else { |  | ||||||
|         None |         None | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -977,6 +1000,33 @@ mod tests { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     #[test] | ||||||
|  |     fn test_proxy_scheme_ip_address_default_http() { | ||||||
|  |         let ps = "192.168.1.1:8888".into_proxy_scheme().unwrap(); | ||||||
|  |  | ||||||
|  |         match ps { | ||||||
|  |             ProxyScheme::Http { auth, host } => { | ||||||
|  |                 assert!(auth.is_none()); | ||||||
|  |                 assert_eq!(host, "192.168.1.1:8888"); | ||||||
|  |             } | ||||||
|  |             other => panic!("unexpected: {:?}", other), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #[test] | ||||||
|  |     fn test_proxy_scheme_parse_default_http_with_auth() { | ||||||
|  |         // this should fail because `foo` is interpreted as the scheme and no host can be found | ||||||
|  |         let ps = "foo:bar@localhost:1239".into_proxy_scheme().unwrap(); | ||||||
|  |  | ||||||
|  |         match ps { | ||||||
|  |             ProxyScheme::Http { auth, host } => { | ||||||
|  |                 assert_eq!(auth.unwrap(), encode_basic_auth("foo", "bar")); | ||||||
|  |                 assert_eq!(host, "localhost:1239"); | ||||||
|  |             } | ||||||
|  |             other => panic!("unexpected: {:?}", other), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     // Smallest possible content for a mutex |     // Smallest possible content for a mutex | ||||||
|     struct MutexInner; |     struct MutexInner; | ||||||
|  |  | ||||||
| @@ -996,10 +1046,10 @@ mod tests { | |||||||
|         // to avoid assert! -> panic! -> Mutex Poisoned. |         // to avoid assert! -> panic! -> Mutex Poisoned. | ||||||
|         let baseline_proxies = get_sys_proxies(None); |         let baseline_proxies = get_sys_proxies(None); | ||||||
|         // the system proxy setting url is invalid. |         // the system proxy setting url is invalid. | ||||||
|         env::set_var("http_proxy", "123465"); |         env::set_var("http_proxy", "file://123465"); | ||||||
|         let invalid_proxies = get_sys_proxies(None); |         let invalid_proxies = get_sys_proxies(None); | ||||||
|         // set valid proxy |         // set valid proxy | ||||||
|         env::set_var("http_proxy", "http://127.0.0.1/"); |         env::set_var("http_proxy", "127.0.0.1/"); | ||||||
|         let valid_proxies = get_sys_proxies(None); |         let valid_proxies = get_sys_proxies(None); | ||||||
|  |  | ||||||
|         // reset user setting when guards drop |         // reset user setting when guards drop | ||||||
| @@ -1033,9 +1083,16 @@ mod tests { | |||||||
|         // set valid proxy |         // set valid proxy | ||||||
|         let valid_proxies = get_sys_proxies(Some((1, String::from("http://127.0.0.1/")))); |         let valid_proxies = get_sys_proxies(Some((1, String::from("http://127.0.0.1/")))); | ||||||
|         let valid_proxies_no_schema = get_sys_proxies(Some((1, String::from("127.0.0.1")))); |         let valid_proxies_no_schema = get_sys_proxies(Some((1, String::from("127.0.0.1")))); | ||||||
|         let valid_proxies_explicit_https = get_sys_proxies(Some((1, String::from("https://127.0.0.1/")))); |         let valid_proxies_explicit_https = | ||||||
|         let multiple_proxies = get_sys_proxies(Some((1, String::from("http=127.0.0.1:8888;https=127.0.0.2:8888")))); |             get_sys_proxies(Some((1, String::from("https://127.0.0.1/")))); | ||||||
|         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")))); |         let multiple_proxies = get_sys_proxies(Some(( | ||||||
|  |             1, | ||||||
|  |             String::from("http=127.0.0.1:8888;https=127.0.0.2:8888"), | ||||||
|  |         ))); | ||||||
|  |         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"), | ||||||
|  |         ))); | ||||||
|  |  | ||||||
|         // reset user setting when guards drop |         // reset user setting when guards drop | ||||||
|         drop(_g1); |         drop(_g1); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user