From 692d287ee4b5d24a2b0dc2e1e4aef76ec89b46ef Mon Sep 17 00:00:00 2001 From: Felipe Noronha Date: Sat, 24 Nov 2018 13:27:20 -0200 Subject: [PATCH] updates to hyper 0.12.16 --- Cargo.toml | 21 ++++------ src/lib.rs | 111 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 119 insertions(+), 13 deletions(-) create mode 100644 src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 8729a58..889cf00 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,12 @@ [package] name = "hyper-reverse-proxy" -version = "0.2.1" -authors = ["Brendan Zabarauskas "] +version = "0.3.0" +authors = ["Brendan Zabarauskas ", "Felipe Noronha "] license = "Apache-2.0" description = "A simple reverse proxy, to be used with Hyper and Tokio." -homepage = "https://github.com/brendanzab/hyper-reverse-proxy" +homepage = "https://github.com/felipenoris/hyper-reverse-proxy" documentation = "https://docs.rs/hyper-reverse-proxy" -repository = "https://github.com/brendanzab/hyper-reverse-proxy" +repository = "https://github.com/felipenoris/hyper-reverse-proxy" keywords = ["http", "hyper"] categories = ["network-programming", "web-programming"] readme = "README.md" @@ -18,12 +18,7 @@ include = [ ] [dependencies] -futures = "0.1.14" -hyper = "0.11.0" -lazy_static = "0.2" -unicase = "2.0.0" - -[dev-dependencies] -error-chain = "0.10.0" -tokio-core = "0.1.8" -tokio-signal = "0.1.2" +hyper = "0.12.16" +futures = "0.1" +lazy_static = "1.1" +unicase = "2.2" diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..bcd0b6d --- /dev/null +++ b/src/lib.rs @@ -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, 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> = 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) -> HeaderMap { + 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(mut response: Response) -> Response { + *response.headers_mut() = remove_hop_headers(response.headers()); + response +} + +fn forward_uri(forward_url: &str, req: &Request) -> 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(client_ip: IpAddr, forward_url: &str, mut request: Request) -> Request { + *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) -> 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) +}