diff --git a/.gitignore b/.gitignore index 6aa1064..44e5e18 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ /target/ **/*.rs.bk Cargo.lock +/.idea +*.swp diff --git a/Cargo.toml b/Cargo.toml index b9c4485..0d4deae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "hyper-reverse-proxy" -version = "0.4.0" -authors = ["Brendan Zabarauskas ", "Felipe Noronha "] +version = "0.5.0" +authors = ["Brendan Zabarauskas ", "Felipe Noronha ", "Jan Kantert "] license = "Apache-2.0" description = "A simple reverse proxy, to be used with Hyper and Tokio." homepage = "https://github.com/felipenoris/hyper-reverse-proxy" @@ -19,7 +19,10 @@ include = [ ] [dependencies] -hyper = "0.12" -futures = "0.1" -lazy_static = "1.3" -unicase = "2.3" +hyper = "0.13" +futures = "0.3" +lazy_static = "1.4" +unicase = "2.6" + +[dev-dependencies] +tokio = { version = "0.2", features = ["full"] } diff --git a/src/lib.rs b/src/lib.rs index ef34fe5..6170e78 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,9 +17,10 @@ //! //! ```toml //! [dependencies] -//! hyper-reverse-proxy = "0.4" -//! hyper = "0.12" -//! futures = "0.1" +//! hyper-reverse-proxy = "0.5" +//! hyper = "0.13" +//! futures = "0.3" +//! tokio = { version = "0.2", features = ["full"] } //! ``` //! //! The following example will set up a reverse proxy listening on `127.0.0.1:13900`, @@ -36,62 +37,59 @@ //! use hyper::{Body, Request, Response, Server}; //! use hyper::service::{service_fn, make_service_fn}; //! use futures::future::{self, Future}; +//! use std::{convert::Infallible, net::SocketAddr}; +//! use hyper::http::uri::InvalidUri; +//! use std::net::IpAddr; //! -//! type BoxFut = Box, Error=hyper::Error> + Send>; -//! -//! fn debug_request(req: Request) -> BoxFut { +//! fn debug_request(req: Request) -> Result, Infallible> { //! let body_str = format!("{:?}", req); -//! let response = Response::new(Body::from(body_str)); -//! Box::new(future::ok(response)) +//! Ok(Response::new(Body::from(body_str))) //! } //! -//! fn main() { -//! -//! // This is our socket address... -//! let addr = ([127, 0, 0, 1], 13900).into(); -//! -//! // A `Service` is needed for every connection. -//! let make_svc = make_service_fn(|socket: &AddrStream| { -//! let remote_addr = socket.remote_addr(); -//! service_fn(move |req: Request| { // returns BoxFut +//! async fn handle(client_ip: IpAddr, req: Request) -> Result, Infallible> { +//! if req.uri().path().starts_with("/target/first") { +//! // will forward requests to port 13901 +//! Ok(hyper_reverse_proxy::call(client_ip, "http://127.0.0.1:13901", req).await.unwrap()) //! -//! if req.uri().path().starts_with("/target/first") { +//! } else if req.uri().path().starts_with("/target/second") { //! -//! // will forward requests to port 13901 -//! return hyper_reverse_proxy::call(remote_addr.ip(), "http://127.0.0.1:13901", req) +//! // will forward requests to port 13902 +//! Ok(hyper_reverse_proxy::call(client_ip, "http://127.0.0.1:13902", req).await.unwrap()) //! -//! } else if req.uri().path().starts_with("/target/second") { +//! } else { +//! debug_request(req) +//! } +//! } //! -//! // will forward requests to port 13902 -//! return hyper_reverse_proxy::call(remote_addr.ip(), "http://127.0.0.1:13902", req) +//! #[tokio::main] +//! async fn main() { +//! let bind_addr = "127.0.0.1:8000"; +//! let addr:SocketAddr = bind_addr.parse().expect("Could not parse ip:port."); //! -//! } else { -//! debug_request(req) -//! } -//! }) +//! let make_svc = make_service_fn(|conn: &AddrStream| { +//! let remote_addr = conn.remote_addr().ip(); +//! async move { +//! Ok::<_, Infallible>(service_fn(move |req| handle(remote_addr, req))) +//! } //! }); //! -//! let server = Server::bind(&addr) -//! .serve(make_svc) -//! .map_err(|e| eprintln!("server error: {}", e)); +//! let server = Server::bind(&addr).serve(make_svc); //! -//! println!("Running server on {:?}", addr); +//! if let Err(e) = server.await { +//! eprintln!("server error: {}", e); +//! } //! -//! // Run this server for... forever! -//! hyper::rt::run(server); +//! println!("Running server on {:?}", addr); //! } //! ``` //! -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 hyper::http::uri::InvalidUri; +use hyper::{Body, Client, Request, Response, StatusCode, Uri}; use lazy_static::lazy_static; - -type BoxFut = Box, Error=hyper::Error> + Send>; +use std::net::IpAddr; +use std::str::FromStr; fn is_hop_header(name: &str) -> bool { use unicase::Ascii; @@ -133,66 +131,58 @@ fn create_proxied_response(mut response: Response) -> Response { response } -fn forward_uri(forward_url: &str, req: &Request) -> Uri { +fn forward_uri(forward_url: &str, req: &Request) -> Result { 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() + Uri::from_str(forward_uri.as_str()) } -fn create_proxied_request(client_ip: IpAddr, forward_url: &str, mut request: Request) -> Request { +fn create_proxied_request( + client_ip: IpAddr, + forward_url: &str, + mut request: Request, +) -> Result, InvalidUri> { *request.headers_mut() = remove_hop_headers(request.headers()); - *request.uri_mut() = forward_uri(forward_url, &request); + *request.uri_mut() = forward_uri(forward_url, &request)?; let x_forwarded_for_header_name = "x-forwarded-for"; // Add forwarding information in the headers match request.headers_mut().entry(x_forwarded_for_header_name) { - - Ok(header_entry) => { - match header_entry { - hyper::header::Entry::Vacant(entry) => { - let addr = format!("{}", client_ip); - entry.insert(addr.parse().unwrap()); - }, - - hyper::header::Entry::Occupied(mut entry) => { - let addr = format!("{}, {}", entry.get().to_str().unwrap(), client_ip); - entry.insert(addr.parse().unwrap()); - } - } + hyper::header::Entry::Vacant(entry) => { + entry.insert(client_ip.to_string().parse().unwrap()); } - // shouldn't happen... - Err(_) => panic!("Invalid header name: {}", x_forwarded_for_header_name), + hyper::header::Entry::Occupied(mut entry) => { + let addr = format!("{}, {}", entry.get().to_str().unwrap(), client_ip); + entry.insert(addr.parse().unwrap()); + } } - request + Ok(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) +pub async fn call( + client_ip: IpAddr, + forward_uri: &str, + request: Request, +) -> Result, InvalidUri> { + let proxied_request = create_proxied_request(client_ip, &forward_uri, request)?; + + let client = Client::new(); + let response = client.request(proxied_request).await; + let proxied_response = match response { + Ok(response) => create_proxied_response(response), + Err(error) => { + println!("Error: {}", error); // TODO: Configurable logging + Response::builder() + .status(StatusCode::SERVICE_UNAVAILABLE) + .body(Body::empty()) + .unwrap() + } + }; + Ok(proxied_response) }