add Proxy::custom to allow custom proxy functions
This commit is contained in:
		| @@ -334,7 +334,7 @@ impl Client { | ||||
|             reusable | ||||
|         }); | ||||
|  | ||||
|         if proxy::is_proxied(&self.inner.proxies, &uri) { | ||||
|         if proxy::is_proxied(&self.inner.proxies, &url) { | ||||
|             if uri.scheme() == Some("http") { | ||||
|                 req.set_proxy(true); | ||||
|             } | ||||
| @@ -453,7 +453,7 @@ impl Future for Pending { | ||||
|                             if let Some(Some(ref body)) = self.body { | ||||
|                                 req.set_body(body.clone()); | ||||
|                             } | ||||
|                             if proxy::is_proxied(&self.client.proxies, &uri) { | ||||
|                             if proxy::is_proxied(&self.client.proxies, &self.url) { | ||||
|                                 if uri.scheme() == Some("http") { | ||||
|                                     req.set_proxy(true); | ||||
|                                 } | ||||
|   | ||||
| @@ -47,7 +47,7 @@ impl Service for Connector { | ||||
|  | ||||
|     fn call(&self, uri: Uri) -> Self::Future { | ||||
|         for prox in self.proxies.iter() { | ||||
|             if let Some(puri) = proxy::proxies(prox, &uri) { | ||||
|             if let Some(puri) = proxy::intercept(prox, &uri) { | ||||
|                 if uri.scheme() == Some("https") { | ||||
|                     let host = uri.host().unwrap().to_owned(); | ||||
|                     let port = uri.port().unwrap_or(443); | ||||
|   | ||||
| @@ -36,3 +36,7 @@ impl<'a> PolyfillTryInto for &'a String { | ||||
| pub fn to_uri(url: &Url) -> ::hyper::Uri { | ||||
|     url.as_str().parse().expect("a parsed Url should always be a valid Uri") | ||||
| } | ||||
|  | ||||
| pub fn to_url(uri: &::hyper::Uri) -> Url { | ||||
|     uri.as_ref().parse().expect("reqwest Uris should only ever come from Urls") | ||||
| } | ||||
|   | ||||
							
								
								
									
										189
									
								
								src/proxy.rs
									
									
									
									
									
								
							
							
						
						
									
										189
									
								
								src/proxy.rs
									
									
									
									
									
								
							| @@ -1,5 +1,8 @@ | ||||
| use std::fmt; | ||||
| use std::sync::Arc; | ||||
|  | ||||
| use hyper::Uri; | ||||
| use {IntoUrl}; | ||||
| use {into_url, IntoUrl, Url}; | ||||
|  | ||||
| /// Configuration of a proxy that a `Client` should pass requests to. | ||||
| /// | ||||
| @@ -30,7 +33,6 @@ use {IntoUrl}; | ||||
| #[derive(Clone, Debug)] | ||||
| pub struct Proxy { | ||||
|     intercept: Intercept, | ||||
|     uri: Uri, | ||||
| } | ||||
|  | ||||
| impl Proxy { | ||||
| @@ -49,7 +51,8 @@ impl Proxy { | ||||
|     /// # fn main() {} | ||||
|     /// ``` | ||||
|     pub fn http<U: IntoUrl>(url: U) -> ::Result<Proxy> { | ||||
|         Proxy::new(Intercept::Http, url) | ||||
|         let uri = ::into_url::to_uri(&try_!(url.into_url())); | ||||
|         Ok(Proxy::new(Intercept::Http(uri))) | ||||
|     } | ||||
|  | ||||
|     /// Proxy all HTTPS traffic to the passed URL. | ||||
| @@ -67,7 +70,8 @@ impl Proxy { | ||||
|     /// # fn main() {} | ||||
|     /// ``` | ||||
|     pub fn https<U: IntoUrl>(url: U) -> ::Result<Proxy> { | ||||
|         Proxy::new(Intercept::Https, url) | ||||
|         let uri = ::into_url::to_uri(&try_!(url.into_url())); | ||||
|         Ok(Proxy::new(Intercept::Https(uri))) | ||||
|     } | ||||
|  | ||||
|     /// Proxy **all** traffic to the passed URL. | ||||
| @@ -85,7 +89,33 @@ impl Proxy { | ||||
|     /// # fn main() {} | ||||
|     /// ``` | ||||
|     pub fn all<U: IntoUrl>(url: U) -> ::Result<Proxy> { | ||||
|         Proxy::new(Intercept::All, url) | ||||
|         let uri = ::into_url::to_uri(&try_!(url.into_url())); | ||||
|         Ok(Proxy::new(Intercept::All(uri))) | ||||
|     } | ||||
|  | ||||
|     /// Provide a custom function to determine what traffix to proxy to where. | ||||
|     /// | ||||
|     /// # Example | ||||
|     /// | ||||
|     /// ``` | ||||
|     /// # extern crate reqwest; | ||||
|     /// # fn run() -> Result<(), Box<::std::error::Error>> { | ||||
|     /// let target = reqwest::Url::parse("https://my.prox")?; | ||||
|     /// let client = reqwest::Client::builder()? | ||||
|     ///     .proxy(reqwest::Proxy::custom(move |url| { | ||||
|     ///         if url.host_str() == Some("hyper.rs") { | ||||
|     ///             Some(target.clone()) | ||||
|     ///         } else { | ||||
|     ///             None | ||||
|     ///         } | ||||
|     ///     })) | ||||
|     ///     .build()?; | ||||
|     /// # Ok(()) | ||||
|     /// # } | ||||
|     /// # fn main() {} | ||||
|     pub fn custom<F>(fun: F) -> Proxy | ||||
|     where F: Fn(&Url) -> Option<Url> + Send + Sync + 'static { | ||||
|         Proxy::new(Intercept::Custom(Custom(Arc::new(fun)))) | ||||
|     } | ||||
|  | ||||
|     /* | ||||
| @@ -94,41 +124,71 @@ impl Proxy { | ||||
|     } | ||||
|     */ | ||||
|  | ||||
|     fn new<U: IntoUrl>(intercept: Intercept, url: U) -> ::Result<Proxy> { | ||||
|         let uri = ::into_url::to_uri(&try_!(url.into_url())); | ||||
|         Ok(Proxy { | ||||
|     fn new(intercept: Intercept) -> Proxy { | ||||
|         Proxy { | ||||
|             intercept: intercept, | ||||
|             uri: uri, | ||||
|         }) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn proxies(&self, uri: &Uri) -> bool { | ||||
|     fn proxies(&self, url: &Url) -> bool { | ||||
|         match self.intercept { | ||||
|             Intercept::All => true, | ||||
|             Intercept::Http => uri.scheme() == Some("http"), | ||||
|             Intercept::Https => uri.scheme() == Some("https"), | ||||
|             Intercept::All(..) => true, | ||||
|             Intercept::Http(..) => url.scheme() == "http", | ||||
|             Intercept::Https(..) => url.scheme() == "https", | ||||
|             Intercept::Custom(ref fun) => (fun.0)(url).is_some(), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     fn intercept(&self, uri: &Uri) -> Option<Uri> { | ||||
|         match self.intercept { | ||||
|             Intercept::All(ref u) => Some(u.clone()), | ||||
|             Intercept::Http(ref u) => { | ||||
|                 if uri.scheme() == Some("http") { | ||||
|                     Some(u.clone()) | ||||
|                 } else { | ||||
|                     None | ||||
|                 } | ||||
|             }, | ||||
|             Intercept::Https(ref u) => { | ||||
|                 if uri.scheme() == Some("https") { | ||||
|                     Some(u.clone()) | ||||
|                 } else { | ||||
|                     None | ||||
|                 } | ||||
|             }, | ||||
|             Intercept::Custom(ref fun) => { | ||||
|                 (fun.0)(&into_url::to_url(uri)) | ||||
|                     .map(|u| into_url::to_uri(&u)) | ||||
|             }, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Clone, Debug)] | ||||
| enum Intercept { | ||||
|     All, | ||||
|     Http, | ||||
|     Https, | ||||
|     All(Uri), | ||||
|     Http(Uri), | ||||
|     Https(Uri), | ||||
|     Custom(Custom), | ||||
| } | ||||
|  | ||||
| #[derive(Clone)] | ||||
| struct Custom(Arc<Fn(&Url) -> Option<Url> + Send + Sync + 'static>); | ||||
|  | ||||
| impl fmt::Debug for Custom { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         f.write_str("_") | ||||
|     } | ||||
| } | ||||
|  | ||||
| // pub(crate) | ||||
|  | ||||
| pub fn proxies(proxy: &Proxy, uri: &Uri) -> Option<Uri> { | ||||
|     if proxy.proxies(uri) { | ||||
|         Some(proxy.uri.clone()) | ||||
|     } else { | ||||
|         None | ||||
|     } | ||||
| pub fn intercept(proxy: &Proxy, uri: &Uri) -> Option<Uri> { | ||||
|     proxy.intercept(uri) | ||||
| } | ||||
|  | ||||
| pub fn is_proxied(proxies: &[Proxy], uri: &Uri) -> bool { | ||||
| pub fn is_proxied(proxies: &[Proxy], uri: &Url) -> bool { | ||||
|     proxies.iter().any(|p| p.proxies(uri)) | ||||
| } | ||||
|  | ||||
| @@ -136,39 +196,86 @@ pub fn is_proxied(proxies: &[Proxy], uri: &Uri) -> bool { | ||||
| mod tests { | ||||
|     use super::*; | ||||
|  | ||||
|     fn uri(s: &str) -> Uri { | ||||
|         s.parse().unwrap() | ||||
|     } | ||||
|  | ||||
|     fn url(s: &str) -> Url { | ||||
|         s.parse().unwrap() | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_http() { | ||||
|         let p = Proxy::http("http://example.domain").unwrap(); | ||||
|         let target = "http://example.domain/"; | ||||
|         let p = Proxy::http(target).unwrap(); | ||||
|  | ||||
|         let http = "http://hyper.rs".parse().unwrap(); | ||||
|         let other = "https://hyper.rs".parse().unwrap(); | ||||
|         let http = "http://hyper.rs"; | ||||
|         let other = "https://hyper.rs"; | ||||
|  | ||||
|         assert!(p.proxies(&http)); | ||||
|         assert!(!p.proxies(&other)); | ||||
|         assert!(p.proxies(&url(http))); | ||||
|         assert_eq!(p.intercept(&uri(http)).unwrap(), target); | ||||
|         assert!(!p.proxies(&url(other))); | ||||
|         assert!(p.intercept(&uri(other)).is_none()); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_https() { | ||||
|         let p = Proxy::https("http://example.domain").unwrap(); | ||||
|         let target = "http://example.domain/"; | ||||
|         let p = Proxy::https(target).unwrap(); | ||||
|  | ||||
|         let http = "http://hyper.rs".parse().unwrap(); | ||||
|         let other = "https://hyper.rs".parse().unwrap(); | ||||
|         let http = "http://hyper.rs"; | ||||
|         let other = "https://hyper.rs"; | ||||
|  | ||||
|         assert!(!p.proxies(&http)); | ||||
|         assert!(p.proxies(&other)); | ||||
|         assert!(!p.proxies(&url(http))); | ||||
|         assert!(p.intercept(&uri(http)).is_none()); | ||||
|         assert!(p.proxies(&url(other))); | ||||
|         assert_eq!(p.intercept(&uri(other)).unwrap(), target); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_all() { | ||||
|         let p = Proxy::all("http://example.domain").unwrap(); | ||||
|         let target = "http://example.domain/"; | ||||
|         let p = Proxy::all(target).unwrap(); | ||||
|  | ||||
|         let http = "http://hyper.rs".parse().unwrap(); | ||||
|         let https = "https://hyper.rs".parse().unwrap(); | ||||
|         let other = "x-youve-never-heard-of-me-mr-proxy://hyper.rs".parse().unwrap(); | ||||
|         let http = "http://hyper.rs"; | ||||
|         let https = "https://hyper.rs"; | ||||
|         let other = "x-youve-never-heard-of-me-mr-proxy://hyper.rs"; | ||||
|  | ||||
|         assert!(p.proxies(&http)); | ||||
|         assert!(p.proxies(&https)); | ||||
|         assert!(p.proxies(&other)); | ||||
|         assert!(p.proxies(&url(http))); | ||||
|         assert!(p.proxies(&url(https))); | ||||
|         assert!(p.proxies(&url(other))); | ||||
|  | ||||
|         assert_eq!(p.intercept(&uri(http)).unwrap(), target); | ||||
|         assert_eq!(p.intercept(&uri(https)).unwrap(), target); | ||||
|         assert_eq!(p.intercept(&uri(other)).unwrap(), target); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     #[test] | ||||
|     fn test_custom() { | ||||
|         let target1 = "http://example.domain/"; | ||||
|         let target2 = "https://example.domain/"; | ||||
|         let p = Proxy::custom(move |url| { | ||||
|             if url.host_str() == Some("hyper.rs") { | ||||
|                 target1.parse().ok() | ||||
|             } else if url.scheme() == "http" { | ||||
|                 target2.parse().ok() | ||||
|             } else { | ||||
|                 None | ||||
|             } | ||||
|         }); | ||||
|  | ||||
|         let http = "http://seanmonstar.com"; | ||||
|         let https = "https://hyper.rs"; | ||||
|         let other = "x-youve-never-heard-of-me-mr-proxy://seanmonstar.com"; | ||||
|  | ||||
|         assert!(p.proxies(&url(http))); | ||||
|         assert!(p.proxies(&url(https))); | ||||
|         assert!(!p.proxies(&url(other))); | ||||
|  | ||||
|         assert_eq!(p.intercept(&uri(http)).unwrap(), target2); | ||||
|         assert_eq!(p.intercept(&uri(https)).unwrap(), target1); | ||||
|         assert!(p.intercept(&uri(other)).is_none()); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|   | ||||
		Reference in New Issue
	
	Block a user