use std::io::{Read, Write};
use httparse;
use hyper;
use hyper::method::Method;
use hyper::version::HttpVersion;
use hyper::net::{HttpConnector, HttpsStream, NetworkConnector, SslClient};
use http::client::proxy::Proxy;
pub struct ProxyHttpsConnector<S: SslClient> {
proxy: Proxy,
proxy_connector: HttpConnector,
ssl_client: S,
}
impl<S: SslClient> ProxyHttpsConnector<S> {
pub fn new(proxy: Proxy, ssl: S) -> hyper::Result<Self> {
Ok(ProxyHttpsConnector {
proxy: proxy,
proxy_connector: HttpConnector,
ssl_client: ssl,
})
}
}
impl<S: SslClient> NetworkConnector for ProxyHttpsConnector<S> {
type Stream = HttpsStream<S::Stream>;
fn connect(&self, host: &str, port: u16, scheme: &str) -> hyper::Result<Self::Stream> {
let mut stream = try!(self.proxy_connector
.connect(self.proxy.host(), self.proxy.port(), "http"));
match scheme {
"https" => {
let mut connect_msg = format!("{method} {host}:{port} {version}\r\nHost \
{host}:{port}\r\n",
method = Method::Connect,
version = HttpVersion::Http11,
host = host,
port = port);
if let Some(header_value) = self.proxy.authorization_header_value() {
connect_msg.push_str(&format!("Proxy-Authorization: {}\r\n", header_value));
};
connect_msg.push_str("\r\n");
debug!("Proxy {}:{} {:?}",
self.proxy.host(),
self.proxy.port(),
connect_msg.trim().replace("\r\n", ", "));
try!(stream.write_all(connect_msg.as_bytes()));
try!(stream.flush());
let mut buf = [0; 1024];
let mut n = 0;
while n < buf.len() {
n += try!(stream.read(&mut buf[n..]));
let mut headers = [httparse::EMPTY_HEADER; 10];
let mut res = httparse::Response::new(&mut headers);
if try!(res.parse(&buf[..n])).is_complete() {
let code = res.code.expect("complete parsing lost code");
if code >= 200 && code < 300 {
debug!("Proxy {}:{} CONNECT success = {}",
self.proxy.host(),
self.proxy.port(),
code);
return self.ssl_client
.wrap_client(stream, host)
.map(HttpsStream::Https);
} else {
debug!("Proxy {}:{} CONNECT failed response = {}",
self.proxy.host(),
self.proxy.port(),
code);
return Err(hyper::Error::Status);
}
}
}
Err(hyper::Error::TooLarge)
}
_ => Ok(HttpsStream::Http(stream)),
}
}
}