8 releases (4 breaking)

0.5.1 Mar 12, 2022
0.5.0 Mar 12, 2022
0.4.0 Mar 11, 2019
0.3.0 Feb 16, 2019
0.1.1 Jul 15, 2017

#1643 in Network programming

Download history 257/week @ 2023-12-18 136/week @ 2023-12-25 225/week @ 2024-01-01 237/week @ 2024-01-08 212/week @ 2024-01-15 208/week @ 2024-01-22 199/week @ 2024-01-29 802/week @ 2024-02-05 141/week @ 2024-02-12 360/week @ 2024-02-19 265/week @ 2024-02-26 348/week @ 2024-03-04 306/week @ 2024-03-11 252/week @ 2024-03-18 200/week @ 2024-03-25 391/week @ 2024-04-01

1,169 downloads per month
Used in 9 crates (7 directly)

Apache-2.0

10KB
99 lines

hyper-reverse-proxy

Build Status Documentation Version License

A simple reverse proxy, to be used with Hyper.

The implementation ensures that Hop-by-hop headers are stripped correctly in both directions, and adds the client's IP address to a comma-space-separated list of forwarding addresses in the X-Forwarded-For header.

The implementation is based on Go's httputil.ReverseProxy.

Example

Add these dependencies to your Cargo.toml file.

[dependencies]
hyper-reverse-proxy = "0.5"
hyper = { version = "0.14", features = ["full"] }
tokio = { version = "1", features = ["full"] }

The following example will set up a reverse proxy listening on 127.0.0.1:13900, and will proxy these calls:

  • "/target/first" will be proxied to http://127.0.0.1:13901

  • "/target/second" will be proxied to http://127.0.0.1:13902

  • All other URLs will be handled by debug_request function, that will display request information.

use hyper::server::conn::AddrStream;
use hyper::{Body, Request, Response, Server, StatusCode};
use hyper::service::{service_fn, make_service_fn};
use std::{convert::Infallible, net::SocketAddr};
use std::net::IpAddr;

fn debug_request(req: Request<Body>) -> Result<Response<Body>, Infallible>  {
    let body_str = format!("{:?}", req);
    Ok(Response::new(Body::from(body_str)))
}

async fn handle(client_ip: IpAddr, req: Request<Body>) -> Result<Response<Body>, Infallible> {
    if req.uri().path().starts_with("/target/first") {
        // will forward requests to port 13901
        match hyper_reverse_proxy::call(client_ip, "http://127.0.0.1:13901", req).await {
            Ok(response) => {Ok(response)}
            Err(_error) => {Ok(Response::builder()
                                  .status(StatusCode::INTERNAL_SERVER_ERROR)
                                  .body(Body::empty())
                                  .unwrap())}
        }
    } else if req.uri().path().starts_with("/target/second") {
        // will forward requests to port 13902
        match hyper_reverse_proxy::call(client_ip, "http://127.0.0.1:13902", req).await {
            Ok(response) => {Ok(response)}
            Err(_error) => {Ok(Response::builder()
                                  .status(StatusCode::INTERNAL_SERVER_ERROR)
                                  .body(Body::empty())
                                  .unwrap())}
        }
    } else {
        debug_request(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.");

    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);

    println!("Running server on {:?}", addr);

    if let Err(e) = server.await {
        eprintln!("server error: {}", e);
    }
}

Dependencies

~4–5.5MB
~93K SLoC