parent
d45b2f71d0
commit
692d287ee4
@ -0,0 +1,111 @@ |
||||
|
||||
extern crate hyper; |
||||
extern crate futures; |
||||
extern crate lazy_static; |
||||
extern crate unicase; |
||||
|
||||
use hyper::Body; |
||||
use std::net::IpAddr; |
||||
use std::str::FromStr; |
||||
use hyper::header::{HeaderMap, HeaderValue}; |
||||
use hyper::{Request, Response, Client, Uri, StatusCode}; |
||||
use futures::future::{self, Future}; |
||||
use lazy_static::lazy_static; |
||||
|
||||
type BoxFut = Box<Future<Item=Response<Body>, Error=hyper::Error> + Send>; |
||||
|
||||
fn is_hop_header(name: &str) -> bool { |
||||
use unicase::Ascii; |
||||
|
||||
// A list of the headers, using `unicase` to help us compare without
|
||||
// worrying about the case, and `lazy_static!` to prevent reallocation
|
||||
// of the vector.
|
||||
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) |
||||
} |
||||
|
||||
/// Returns a clone of the headers without the [hop-by-hop headers].
|
||||
///
|
||||
/// [hop-by-hop headers]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html
|
||||
fn remove_hop_headers(headers: &HeaderMap<HeaderValue>) -> HeaderMap<HeaderValue> { |
||||
let mut result = HeaderMap::new(); |
||||
for (k, v) in headers.iter() { |
||||
if !is_hop_header(k.as_str()) { |
||||
result.insert(k.clone(), v.clone()); |
||||
} |
||||
} |
||||
result |
||||
} |
||||
|
||||
fn create_proxied_response<B>(mut response: Response<B>) -> Response<B> { |
||||
*response.headers_mut() = remove_hop_headers(response.headers()); |
||||
response |
||||
} |
||||
|
||||
fn forward_uri<B>(forward_url: &str, req: &Request<B>) -> Uri { |
||||
let forward_uri = match req.uri().query() { |
||||
Some(query) => format!("{}{}?{}", forward_url, req.uri().path(), query), |
||||
None => format!("{}{}", forward_url, req.uri().path()), |
||||
}; |
||||
|
||||
Uri::from_str(forward_uri.as_str()).unwrap() |
||||
} |
||||
|
||||
fn create_proxied_request<B>(client_ip: IpAddr, forward_url: &str, mut request: Request<B>) -> Request<B> { |
||||
*request.headers_mut() = remove_hop_headers(request.headers()); |
||||
*request.uri_mut() = forward_uri(forward_url, &request); |
||||
|
||||
// Add forwarding information in the headers
|
||||
match request.headers_mut().entry("x-forwarded-for") { |
||||
Ok(hyper::header::Entry::Vacant(entry)) => { |
||||
let addr = format!("{}", client_ip); |
||||
entry.insert(addr.parse().unwrap()); |
||||
}, |
||||
|
||||
Ok(hyper::header::Entry::Occupied(mut entry)) => { |
||||
let addr = format!("{}, {}", entry.get().to_str().unwrap(), client_ip); |
||||
entry.insert(addr.parse().unwrap()); |
||||
}, |
||||
|
||||
_ => (), // silently fails to add x-forwarded-for header
|
||||
} |
||||
|
||||
request |
||||
} |
||||
|
||||
pub fn call(client_ip: IpAddr, forward_uri: &str, request: Request<Body>) -> BoxFut { |
||||
|
||||
let proxied_request = create_proxied_request(client_ip, forward_uri, request); |
||||
|
||||
let client = Client::new(); |
||||
let response = client.request(proxied_request).then(|response| { |
||||
|
||||
let proxied_response = match response { |
||||
Ok(response) => create_proxied_response(response), |
||||
Err(error) => { |
||||
println!("Error: {}", error); // TODO: Configurable logging
|
||||
Response::builder() |
||||
.status(StatusCode::INTERNAL_SERVER_ERROR) |
||||
.body(Body::empty()) |
||||
.unwrap() |
||||
}, |
||||
}; |
||||
|
||||
|
||||
future::ok(proxied_response) |
||||
}); |
||||
|
||||
Box::new(response) |
||||
} |
Loading…
Reference in new issue