diff --git a/src/client/mod.rs b/src/client/mod.rs index fef3e13257..14a1fb401a 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -68,10 +68,11 @@ use url::ParseError as UrlError; use header::{Headers, Header, HeaderFormat}; use header::{ContentLength, Host, Location}; use method::Method; -use net::{HttpConnector, NetworkConnector, NetworkStream, SslClient}; +use net::{NetworkConnector, NetworkStream, SslClient}; use Error; use self::proxy::{Proxy, tunnel}; +use self::scheme::Scheme; pub use self::pool::Pool; pub use self::request::Request; pub use self::response::Response; @@ -84,10 +85,6 @@ pub mod response; use http::Protocol; use http::h1::Http11Protocol; -/// Proxy server configuration with a custom TLS wrapper. -pub struct ProxyConfig(pub H, pub u16, pub S) -where H: Into>, - S: SslClient<::Stream> + Send + Sync + 'static; /// A Client to use additional features with Requests. /// @@ -97,7 +94,7 @@ pub struct Client { redirect_policy: RedirectPolicy, read_timeout: Option, write_timeout: Option, - proxy: Option<(Cow<'static, str>, u16)> + proxy: Option<(Scheme, Cow<'static, str>, u16)> } impl fmt::Debug for Client { @@ -123,27 +120,36 @@ impl Client { Client::with_connector(Pool::new(config)) } + /// Create a Client with an HTTP proxy to a (host, port). pub fn with_http_proxy(host: H, port: u16) -> Client where H: Into> { let host = host.into(); - let proxy = tunnel((host.clone(), port)); + let proxy = tunnel((Scheme::Http, host.clone(), port)); let mut client = Client::with_connector(Pool::with_connector(Default::default(), proxy)); - client.proxy = Some((host, port)); + client.proxy = Some((Scheme::Http, host, port)); client } - pub fn with_proxy_config(proxy_config: ProxyConfig) -> Client - where H: Into>, - S: SslClient<::Stream> + Send + Sync + 'static { - let host = proxy_config.0.into(); - let port = proxy_config.1; + /// Create a Client using a proxy with a custom connector and SSL client. + pub fn with_proxy_config(proxy_config: ProxyConfig) -> Client + where C: NetworkConnector + Send + Sync + 'static, + C::Stream: NetworkStream + Send + Clone, + S: SslClient + Send + Sync + 'static { + + let scheme = proxy_config.scheme; + let host = proxy_config.host; + let port = proxy_config.port; let proxy = Proxy { - connector: HttpConnector, - proxy: (host.clone(), port), - ssl: proxy_config.2 + proxy: (scheme.clone(), host.clone(), port), + connector: proxy_config.connector, + ssl: proxy_config.ssl, }; - let mut client = Client::with_connector(Pool::with_connector(Default::default(), proxy)); - client.proxy = Some((host, port)); + + let mut client = match proxy_config.pool_config { + Some(pool_config) => Client::with_connector(Pool::with_connector(pool_config, proxy)), + None => Client::with_connector(proxy), + }; + client.proxy = Some((scheme, host, port)); client } @@ -450,6 +456,47 @@ impl<'a> IntoUrl for &'a String { } } +/// Proxy server configuration with a custom connector and TLS wrapper. +pub struct ProxyConfig +where C: NetworkConnector + Send + Sync + 'static, + C::Stream: NetworkStream + Clone + Send, + S: SslClient + Send + Sync + 'static { + scheme: Scheme, + host: Cow<'static, str>, + port: u16, + pool_config: Option, + connector: C, + ssl: S, +} + +impl ProxyConfig +where C: NetworkConnector + Send + Sync + 'static, + C::Stream: NetworkStream + Clone + Send, + S: SslClient + Send + Sync + 'static { + + /// Create a new `ProxyConfig`. + #[inline] + pub fn new>>(scheme: &str, host: H, port: u16, connector: C, ssl: S) -> ProxyConfig { + ProxyConfig { + scheme: scheme.into(), + host: host.into(), + port: port, + pool_config: Some(pool::Config::default()), + connector: connector, + ssl: ssl, + } + } + + /// Change the `pool::Config` for the proxy. + /// + /// Passing `None` disables the `Pool`. + /// + /// The default is enabled, with the default `pool::Config`. + pub fn set_pool_config(&mut self, pool_config: Option) { + self.pool_config = pool_config; + } +} + /// Behavior regarding how to handle redirects within a Client. #[derive(Copy)] pub enum RedirectPolicy { @@ -499,6 +546,37 @@ fn get_host_and_port(url: &Url) -> ::Result<(&str, u16)> { Ok((host, port)) } +mod scheme { + + #[derive(Clone, PartialEq, Eq, Debug, Hash)] + pub enum Scheme { + Http, + Https, + Other(String) + } + + impl<'a> From<&'a str> for Scheme { + fn from(s: &'a str) -> Scheme { + match s { + "http" => Scheme::Http, + "https" => Scheme::Https, + s => Scheme::Other(String::from(s)) + } + } + } + + impl AsRef for Scheme { + fn as_ref(&self) -> &str { + match *self { + Scheme::Http => "http", + Scheme::Https => "https", + Scheme::Other(ref s) => s, + } + } + } + +} + #[cfg(test)] mod tests { use std::io::Read; @@ -506,6 +584,7 @@ mod tests { use http::h1::Http11Message; use mock::{MockStream, MockSsl}; use super::{Client, RedirectPolicy}; + use super::scheme::Scheme; use super::proxy::Proxy; use super::pool::Pool; use url::Url; @@ -537,11 +616,11 @@ mod tests { }); let tunnel = Proxy { connector: ProxyConnector, - proxy: ("example.proxy".into(), 8008), + proxy: (Scheme::Http, "example.proxy".into(), 8008), ssl: MockSsl, }; let mut client = Client::with_connector(Pool::with_connector(Default::default(), tunnel)); - client.proxy = Some(("example.proxy".into(), 8008)); + client.proxy = Some((Scheme::Http, "example.proxy".into(), 8008)); let mut dump = vec![]; client.get("http://127.0.0.1/foo/bar").send().unwrap().read_to_end(&mut dump).unwrap(); @@ -566,11 +645,11 @@ mod tests { }); let tunnel = Proxy { connector: ProxyConnector, - proxy: ("example.proxy".into(), 8008), + proxy: (Scheme::Http, "example.proxy".into(), 8008), ssl: MockSsl, }; let mut client = Client::with_connector(Pool::with_connector(Default::default(), tunnel)); - client.proxy = Some(("example.proxy".into(), 8008)); + client.proxy = Some((Scheme::Http, "example.proxy".into(), 8008)); let mut dump = vec![]; client.get("https://127.0.0.1/foo/bar").send().unwrap().read_to_end(&mut dump).unwrap(); diff --git a/src/client/pool.rs b/src/client/pool.rs index ecf6db22e6..ca80cd2401 100644 --- a/src/client/pool.rs +++ b/src/client/pool.rs @@ -10,6 +10,7 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::time::Duration; use net::{NetworkConnector, NetworkStream, DefaultConnector}; +use client::scheme::Scheme; /// The `NetworkConnector` that behaves as a connection pool used by hyper's `Client`. pub struct Pool { @@ -45,23 +46,6 @@ fn key>(host: &str, port: u16, scheme: T) -> Key { (host.to_owned(), port, scheme.into()) } -#[derive(Clone, PartialEq, Eq, Debug, Hash)] -enum Scheme { - Http, - Https, - Other(String) -} - -impl<'a> From<&'a str> for Scheme { - fn from(s: &'a str) -> Scheme { - match s { - "http" => Scheme::Http, - "https" => Scheme::Https, - s => Scheme::Other(String::from(s)) - } - } -} - impl Pool { /// Creates a `Pool` with a `DefaultConnector`. #[inline] diff --git a/src/client/proxy.rs b/src/client/proxy.rs index ace8d2b456..7d4f9722d2 100644 --- a/src/client/proxy.rs +++ b/src/client/proxy.rs @@ -3,11 +3,12 @@ use std::io; use std::net::{SocketAddr, Shutdown}; use std::time::Duration; +use client::scheme::Scheme; use method::Method; use net::{NetworkConnector, HttpConnector, NetworkStream, SslClient}; #[cfg(all(feature = "openssl", not(feature = "security-framework")))] -pub fn tunnel(proxy: (Cow<'static, str>, u16)) -> Proxy { +pub fn tunnel(proxy: (Scheme, Cow<'static, str>, u16)) -> Proxy { Proxy { connector: HttpConnector, proxy: proxy, @@ -16,7 +17,7 @@ pub fn tunnel(proxy: (Cow<'static, str>, u16)) -> Proxy, u16)) -> Proxy { +pub fn tunnel(proxy: (Scheme, Cow<'static, str>, u16)) -> Proxy { Proxy { connector: HttpConnector, proxy: proxy, @@ -25,7 +26,7 @@ pub fn tunnel(proxy: (Cow<'static, str>, u16)) -> Proxy, u16)) -> Proxy { +pub fn tunnel(proxy: (Scheme, Cow<'static, str>, u16)) -> Proxy { Proxy { connector: HttpConnector, proxy: proxy, @@ -39,11 +40,10 @@ where C: NetworkConnector + Send + Sync + 'static, C::Stream: NetworkStream + Send + Clone, S: SslClient { pub connector: C, - pub proxy: (Cow<'static, str>, u16), + pub proxy: (Scheme, Cow<'static, str>, u16), pub ssl: S, } - impl NetworkConnector for Proxy where C: NetworkConnector + Send + Sync + 'static, C::Stream: NetworkStream + Send + Clone, @@ -57,11 +57,11 @@ where C: NetworkConnector + Send + Sync + 'static, trace!("{:?} proxy for '{}://{}:{}'", self.proxy, scheme, host, port); match scheme { "http" => { - self.connector.connect(self.proxy.0.as_ref(), self.proxy.1, "http") + self.connector.connect(self.proxy.1.as_ref(), self.proxy.2, self.proxy.0.as_ref()) .map(Proxied::Normal) }, "https" => { - let mut stream = try!(self.connector.connect(self.proxy.0.as_ref(), self.proxy.1, "http")); + let mut stream = try!(self.connector.connect(self.proxy.1.as_ref(), self.proxy.2, self.proxy.0.as_ref())); trace!("{:?} CONNECT {}:{}", self.proxy, host, port); try!(write!(&mut stream, "{method} {host}:{port} {version}\r\nHost: {host}:{port}\r\n\r\n", method=Method::Connect, host=host, port=port, version=Http11));