Skip to content

Commit 14a4f1c

Browse files
committed
feat(client): change ProxyConfig to allow HTTPS proxies
Previously, you could only connect to an HTTP proxy (you could still use that proxy to connect to any HTTP or HTTPS URL), but if the proxy itself was protected by TLS, you could not connect to it. This change to ProxyConfig now allows specifying any kind of `NetworkConnector` to use when connecting to a proxy. BREAKING CHANGE: Usage of `with_proxy_config` will need to change to provide a network connector. For the same functionality, a `hyper::net::HttpConnector` can be easily created and passed.
1 parent 0701d89 commit 14a4f1c

File tree

3 files changed

+109
-46
lines changed

3 files changed

+109
-46
lines changed

src/client/mod.rs

+101-22
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,11 @@ use url::ParseError as UrlError;
6868
use header::{Headers, Header, HeaderFormat};
6969
use header::{ContentLength, Host, Location};
7070
use method::Method;
71-
use net::{HttpConnector, NetworkConnector, NetworkStream, SslClient};
71+
use net::{NetworkConnector, NetworkStream, SslClient};
7272
use Error;
7373

7474
use self::proxy::{Proxy, tunnel};
75+
use self::scheme::Scheme;
7576
pub use self::pool::Pool;
7677
pub use self::request::Request;
7778
pub use self::response::Response;
@@ -84,10 +85,6 @@ pub mod response;
8485
use http::Protocol;
8586
use http::h1::Http11Protocol;
8687

87-
/// Proxy server configuration with a custom TLS wrapper.
88-
pub struct ProxyConfig<H, S>(pub H, pub u16, pub S)
89-
where H: Into<Cow<'static, str>>,
90-
S: SslClient<<HttpConnector as NetworkConnector>::Stream> + Send + Sync + 'static;
9188

9289
/// A Client to use additional features with Requests.
9390
///
@@ -97,7 +94,7 @@ pub struct Client {
9794
redirect_policy: RedirectPolicy,
9895
read_timeout: Option<Duration>,
9996
write_timeout: Option<Duration>,
100-
proxy: Option<(Cow<'static, str>, u16)>
97+
proxy: Option<(Scheme, Cow<'static, str>, u16)>
10198
}
10299

103100
impl fmt::Debug for Client {
@@ -123,27 +120,36 @@ impl Client {
123120
Client::with_connector(Pool::new(config))
124121
}
125122

123+
/// Create a Client with an HTTP proxy to a (host, port).
126124
pub fn with_http_proxy<H>(host: H, port: u16) -> Client
127125
where H: Into<Cow<'static, str>> {
128126
let host = host.into();
129-
let proxy = tunnel((host.clone(), port));
127+
let proxy = tunnel((Scheme::Http, host.clone(), port));
130128
let mut client = Client::with_connector(Pool::with_connector(Default::default(), proxy));
131-
client.proxy = Some((host, port));
129+
client.proxy = Some((Scheme::Http, host, port));
132130
client
133131
}
134132

135-
pub fn with_proxy_config<H, S>(proxy_config: ProxyConfig<H, S>) -> Client
136-
where H: Into<Cow<'static, str>>,
137-
S: SslClient<<HttpConnector as NetworkConnector>::Stream> + Send + Sync + 'static {
138-
let host = proxy_config.0.into();
139-
let port = proxy_config.1;
133+
/// Create a Client using a proxy with a custom connector and SSL client.
134+
pub fn with_proxy_config<C, S>(proxy_config: ProxyConfig<C, S>) -> Client
135+
where C: NetworkConnector + Send + Sync + 'static,
136+
C::Stream: NetworkStream + Send + Clone,
137+
S: SslClient<C::Stream> + Send + Sync + 'static {
138+
139+
let scheme = proxy_config.scheme;
140+
let host = proxy_config.host;
141+
let port = proxy_config.port;
140142
let proxy = Proxy {
141-
connector: HttpConnector,
142-
proxy: (host.clone(), port),
143-
ssl: proxy_config.2
143+
proxy: (scheme.clone(), host.clone(), port),
144+
connector: proxy_config.connector,
145+
ssl: proxy_config.ssl,
144146
};
145-
let mut client = Client::with_connector(Pool::with_connector(Default::default(), proxy));
146-
client.proxy = Some((host, port));
147+
148+
let mut client = match proxy_config.pool_config {
149+
Some(pool_config) => Client::with_connector(Pool::with_connector(pool_config, proxy)),
150+
None => Client::with_connector(proxy),
151+
};
152+
client.proxy = Some((scheme, host, port));
147153
client
148154
}
149155

@@ -450,6 +456,47 @@ impl<'a> IntoUrl for &'a String {
450456
}
451457
}
452458

459+
/// Proxy server configuration with a custom connector and TLS wrapper.
460+
pub struct ProxyConfig<C, S>
461+
where C: NetworkConnector + Send + Sync + 'static,
462+
C::Stream: NetworkStream + Clone + Send,
463+
S: SslClient<C::Stream> + Send + Sync + 'static {
464+
scheme: Scheme,
465+
host: Cow<'static, str>,
466+
port: u16,
467+
pool_config: Option<pool::Config>,
468+
connector: C,
469+
ssl: S,
470+
}
471+
472+
impl<C, S> ProxyConfig<C, S>
473+
where C: NetworkConnector + Send + Sync + 'static,
474+
C::Stream: NetworkStream + Clone + Send,
475+
S: SslClient<C::Stream> + Send + Sync + 'static {
476+
477+
/// Create a new `ProxyConfig`.
478+
#[inline]
479+
pub fn new<H: Into<Cow<'static, str>>>(scheme: &str, host: H, port: u16, connector: C, ssl: S) -> ProxyConfig<C, S> {
480+
ProxyConfig {
481+
scheme: scheme.into(),
482+
host: host.into(),
483+
port: port,
484+
pool_config: Some(pool::Config::default()),
485+
connector: connector,
486+
ssl: ssl,
487+
}
488+
}
489+
490+
/// Change the `pool::Config` for the proxy.
491+
///
492+
/// Passing `None` disables the `Pool`.
493+
///
494+
/// The default is enabled, with the default `pool::Config`.
495+
pub fn set_pool_config(&mut self, pool_config: Option<pool::Config>) {
496+
self.pool_config = pool_config;
497+
}
498+
}
499+
453500
/// Behavior regarding how to handle redirects within a Client.
454501
#[derive(Copy)]
455502
pub enum RedirectPolicy {
@@ -499,13 +546,45 @@ fn get_host_and_port(url: &Url) -> ::Result<(&str, u16)> {
499546
Ok((host, port))
500547
}
501548

549+
mod scheme {
550+
551+
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
552+
pub enum Scheme {
553+
Http,
554+
Https,
555+
Other(String)
556+
}
557+
558+
impl<'a> From<&'a str> for Scheme {
559+
fn from(s: &'a str) -> Scheme {
560+
match s {
561+
"http" => Scheme::Http,
562+
"https" => Scheme::Https,
563+
s => Scheme::Other(String::from(s))
564+
}
565+
}
566+
}
567+
568+
impl AsRef<str> for Scheme {
569+
fn as_ref(&self) -> &str {
570+
match *self {
571+
Scheme::Http => "http",
572+
Scheme::Https => "https",
573+
Scheme::Other(ref s) => s,
574+
}
575+
}
576+
}
577+
578+
}
579+
502580
#[cfg(test)]
503581
mod tests {
504582
use std::io::Read;
505583
use header::Server;
506584
use http::h1::Http11Message;
507585
use mock::{MockStream, MockSsl};
508586
use super::{Client, RedirectPolicy};
587+
use super::scheme::Scheme;
509588
use super::proxy::Proxy;
510589
use super::pool::Pool;
511590
use url::Url;
@@ -537,11 +616,11 @@ mod tests {
537616
});
538617
let tunnel = Proxy {
539618
connector: ProxyConnector,
540-
proxy: ("example.proxy".into(), 8008),
619+
proxy: (Scheme::Http, "example.proxy".into(), 8008),
541620
ssl: MockSsl,
542621
};
543622
let mut client = Client::with_connector(Pool::with_connector(Default::default(), tunnel));
544-
client.proxy = Some(("example.proxy".into(), 8008));
623+
client.proxy = Some((Scheme::Http, "example.proxy".into(), 8008));
545624
let mut dump = vec![];
546625
client.get("http://127.0.0.1/foo/bar").send().unwrap().read_to_end(&mut dump).unwrap();
547626

@@ -566,11 +645,11 @@ mod tests {
566645
});
567646
let tunnel = Proxy {
568647
connector: ProxyConnector,
569-
proxy: ("example.proxy".into(), 8008),
648+
proxy: (Scheme::Http, "example.proxy".into(), 8008),
570649
ssl: MockSsl,
571650
};
572651
let mut client = Client::with_connector(Pool::with_connector(Default::default(), tunnel));
573-
client.proxy = Some(("example.proxy".into(), 8008));
652+
client.proxy = Some((Scheme::Http, "example.proxy".into(), 8008));
574653
let mut dump = vec![];
575654
client.get("https://127.0.0.1/foo/bar").send().unwrap().read_to_end(&mut dump).unwrap();
576655

src/client/pool.rs

+1-17
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use std::sync::atomic::{AtomicBool, Ordering};
1010
use std::time::Duration;
1111

1212
use net::{NetworkConnector, NetworkStream, DefaultConnector};
13+
use client::scheme::Scheme;
1314

1415
/// The `NetworkConnector` that behaves as a connection pool used by hyper's `Client`.
1516
pub struct Pool<C: NetworkConnector> {
@@ -45,23 +46,6 @@ fn key<T: Into<Scheme>>(host: &str, port: u16, scheme: T) -> Key {
4546
(host.to_owned(), port, scheme.into())
4647
}
4748

48-
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
49-
enum Scheme {
50-
Http,
51-
Https,
52-
Other(String)
53-
}
54-
55-
impl<'a> From<&'a str> for Scheme {
56-
fn from(s: &'a str) -> Scheme {
57-
match s {
58-
"http" => Scheme::Http,
59-
"https" => Scheme::Https,
60-
s => Scheme::Other(String::from(s))
61-
}
62-
}
63-
}
64-
6549
impl Pool<DefaultConnector> {
6650
/// Creates a `Pool` with a `DefaultConnector`.
6751
#[inline]

src/client/proxy.rs

+7-7
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@ use std::io;
33
use std::net::{SocketAddr, Shutdown};
44
use std::time::Duration;
55

6+
use client::scheme::Scheme;
67
use method::Method;
78
use net::{NetworkConnector, HttpConnector, NetworkStream, SslClient};
89

910
#[cfg(all(feature = "openssl", not(feature = "security-framework")))]
10-
pub fn tunnel(proxy: (Cow<'static, str>, u16)) -> Proxy<HttpConnector, ::net::Openssl> {
11+
pub fn tunnel(proxy: (Scheme, Cow<'static, str>, u16)) -> Proxy<HttpConnector, ::net::Openssl> {
1112
Proxy {
1213
connector: HttpConnector,
1314
proxy: proxy,
@@ -16,7 +17,7 @@ pub fn tunnel(proxy: (Cow<'static, str>, u16)) -> Proxy<HttpConnector, ::net::Op
1617
}
1718

1819
#[cfg(feature = "security-framework")]
19-
pub fn tunnel(proxy: (Cow<'static, str>, u16)) -> Proxy<HttpConnector, ::net::ClientWrapper> {
20+
pub fn tunnel(proxy: (Scheme, Cow<'static, str>, u16)) -> Proxy<HttpConnector, ::net::ClientWrapper> {
2021
Proxy {
2122
connector: HttpConnector,
2223
proxy: proxy,
@@ -25,7 +26,7 @@ pub fn tunnel(proxy: (Cow<'static, str>, u16)) -> Proxy<HttpConnector, ::net::Cl
2526
}
2627

2728
#[cfg(not(any(feature = "openssl", feature = "security-framework")))]
28-
pub fn tunnel(proxy: (Cow<'static, str>, u16)) -> Proxy<HttpConnector, self::no_ssl::Plaintext> {
29+
pub fn tunnel(proxy: (Scheme, Cow<'static, str>, u16)) -> Proxy<HttpConnector, self::no_ssl::Plaintext> {
2930
Proxy {
3031
connector: HttpConnector,
3132
proxy: proxy,
@@ -39,11 +40,10 @@ where C: NetworkConnector + Send + Sync + 'static,
3940
C::Stream: NetworkStream + Send + Clone,
4041
S: SslClient<C::Stream> {
4142
pub connector: C,
42-
pub proxy: (Cow<'static, str>, u16),
43+
pub proxy: (Scheme, Cow<'static, str>, u16),
4344
pub ssl: S,
4445
}
4546

46-
4747
impl<C, S> NetworkConnector for Proxy<C, S>
4848
where C: NetworkConnector + Send + Sync + 'static,
4949
C::Stream: NetworkStream + Send + Clone,
@@ -57,11 +57,11 @@ where C: NetworkConnector + Send + Sync + 'static,
5757
trace!("{:?} proxy for '{}://{}:{}'", self.proxy, scheme, host, port);
5858
match scheme {
5959
"http" => {
60-
self.connector.connect(self.proxy.0.as_ref(), self.proxy.1, "http")
60+
self.connector.connect(self.proxy.1.as_ref(), self.proxy.2, self.proxy.0.as_ref())
6161
.map(Proxied::Normal)
6262
},
6363
"https" => {
64-
let mut stream = try!(self.connector.connect(self.proxy.0.as_ref(), self.proxy.1, "http"));
64+
let mut stream = try!(self.connector.connect(self.proxy.1.as_ref(), self.proxy.2, self.proxy.0.as_ref()));
6565
trace!("{:?} CONNECT {}:{}", self.proxy, host, port);
6666
try!(write!(&mut stream, "{method} {host}:{port} {version}\r\nHost: {host}:{port}\r\n\r\n",
6767
method=Method::Connect, host=host, port=port, version=Http11));

0 commit comments

Comments
 (0)