extern crate futures;
#[macro_use]
extern crate hyper;
#[macro_use]
extern crate lazy_static;
extern crate unicase;
use futures::future::Future;
use hyper::{Body, Headers, Request, Response, StatusCode};
use hyper::server::Service;
use std::marker::PhantomData;
use std::net::IpAddr;
fn is_hop_header(name: &str) -> bool {
use unicase::Ascii;
lazy_static! {
static ref HOP_HEADERS: Vec<Ascii<&'static str>> = vec![
Ascii::new("Connection"),
Ascii::new("Keep-Alive"),
Ascii::new("Proxy-Authenticate"),
Ascii::new("Proxy-Authorization"),
Ascii::new("Te"),
Ascii::new("Trailers"),
Ascii::new("Transfer-Encoding"),
Ascii::new("Upgrade"),
];
}
HOP_HEADERS.iter().any(|h| h == &name)
}
fn remove_hop_headers(headers: &Headers) -> Headers {
headers
.iter()
.filter(|header| !is_hop_header(header.name()))
.collect()
}
header! {
(XForwardedFor, "X-Forwarded-For") => (IpAddr)+
}
fn create_proxied_response<B>(mut response: Response<B>) -> Response<B> {
*response.headers_mut() = remove_hop_headers(response.headers());
response
}
pub struct ReverseProxy<C: Service, B = Body> {
client: C,
remote_ip: Option<IpAddr>,
_pantom_data: PhantomData<B>,
}
impl<C: Service, B> ReverseProxy<C, B> {
pub fn new(client: C, remote_ip: Option<IpAddr>) -> ReverseProxy<C, B> {
ReverseProxy {
client,
remote_ip,
_pantom_data: PhantomData,
}
}
fn create_proxied_request(&self, mut request: Request<B>) -> Request<B> {
*request.headers_mut() = remove_hop_headers(request.headers());
if let Some(ip) = self.remote_ip {
if request.headers().has::<XForwardedFor>() {
if let Some(prior) = request.headers_mut().get_mut::<XForwardedFor>() {
prior.push(ip);
}
} else {
let header = XForwardedFor(vec![ip]);
request.headers_mut().set(header);
}
}
request
}
}
impl<C, B> Service for ReverseProxy<C, B>
where
B: 'static,
C: Service<Request = Request<B>, Response = Response<B>>,
C::Error: 'static + std::fmt::Display,
C::Future: 'static,
{
type Request = Request<B>;
type Response = Response<B>;
type Error = hyper::Error;
type Future = Box<Future<Item = Response<B>, Error = hyper::Error>>;
fn call(&self, request: Self::Request) -> Self::Future {
let proxied_request = self.create_proxied_request(request);
Box::new(self.client.call(proxied_request).then(|response| {
Ok(match response {
Ok(response) => create_proxied_response(response),
Err(error) => {
println!("Error: {}", error); Response::new().with_status(StatusCode::InternalServerError)
}
})
}))
}
}