use std::borrow::Cow;
use std::io;
use std::io::Error as IoError;
use std::io::BufRead;
use std::io::Read;
use std::io::Write;
use std::net::TcpStream;
use std::net::ToSocketAddrs;
use Request;
use Response;
use ResponseBody;
#[derive(Debug)]
pub enum ProxyError {
BodyAlreadyExtracted,
IoError(IoError),
HttpParseError,
}
impl From<IoError> for ProxyError {
fn from(err: IoError) -> ProxyError {
ProxyError::IoError(err)
}
}
#[derive(Debug, Clone)]
pub struct ProxyConfig<A> {
pub addr: A,
pub replace_host: Option<Cow<'static, str>>,
}
pub fn proxy<A>(request: &Request, config: ProxyConfig<A>) -> Result<Response, ProxyError>
where A: ToSocketAddrs
{
let mut socket = try!(TcpStream::connect(config.addr));
let mut data = match request.data() {
Some(d) => d,
None => return Err(ProxyError::BodyAlreadyExtracted),
};
try!(socket.write_all(format!("{} {} HTTP/1.1\n", request.method(), request.raw_url()).as_bytes()));
for &(ref header, ref value) in request.headers.iter() { let value = if header == "Host" {
if let Some(ref replace) = config.replace_host {
&**replace
} else {
value
}
} else {
value
};
try!(socket.write_all(format!("{}: {}\n", header, value).as_bytes()));
}
try!(socket.write_all("Connection: close\n\n".as_bytes()));
try!(io::copy(&mut data, &mut socket));
let mut socket = io::BufReader::new(socket);
let mut headers = Vec::new();
let mut status = 200;
{
let mut lines = socket.by_ref().lines();
{
let line = try!(match lines.next() {
Some(l) => l,
None => return Err(ProxyError::HttpParseError),
});
let mut splits = line.splitn(3, ' ');
let _ = splits.next();
let status_str = match splits.next() {
Some(l) => l,
None => return Err(ProxyError::HttpParseError),
};
status = match status_str.parse() {
Ok(s) => s,
Err(_) => return Err(ProxyError::HttpParseError),
};
}
for header in lines {
let header = try!(header);
if header.is_empty() { break; }
let mut splits = header.splitn(2, ':');
let header = match splits.next() {
Some(v) => v,
None => return Err(ProxyError::HttpParseError),
};
let val = match splits.next() {
Some(v) => v,
None => return Err(ProxyError::HttpParseError),
};
let val = &val[1..];
headers.push((header.to_owned(), val.to_owned()));
}
}
Ok(Response {
status_code: status,
headers: headers,
data: ResponseBody::from_reader(socket),
})
}