2019-02-16 17:46:52 +00:00
|
|
|
|
2017-07-15 06:23:12 +00:00
|
|
|
# hyper-reverse-proxy
|
|
|
|
|
2022-04-14 00:30:41 +00:00
|
|
|
[![License][license-img]](LICENSE)
|
|
|
|
[![CI][ci-img]][ci-url]
|
|
|
|
[![docs][docs-img]][docs-url]
|
|
|
|
[![version][version-img]][version-url]
|
|
|
|
|
|
|
|
[license-img]: https://img.shields.io/crates/l/hyper-reverse-proxy.svg
|
|
|
|
[ci-img]: https://github.com/felipenoris/hyper-reverse-proxy/workflows/CI/badge.svg
|
|
|
|
[ci-url]: https://github.com/felipenoris/hyper-reverse-proxy/actions?query=workflow%3ACI
|
|
|
|
[docs-img]: https://docs.rs/hyper-reverse-proxy/badge.svg
|
|
|
|
[docs-url]: https://docs.rs/hyper-reverse-proxy
|
|
|
|
[version-img]: https://img.shields.io/crates/v/hyper-reverse-proxy.svg
|
|
|
|
[version-url]: https://crates.io/crates/hyper-reverse-proxy
|
2017-07-15 06:23:12 +00:00
|
|
|
|
2019-02-16 17:46:52 +00:00
|
|
|
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.
|
2017-07-15 06:23:12 +00:00
|
|
|
|
2019-02-16 17:46:52 +00:00
|
|
|
The implementation is based on Go's [`httputil.ReverseProxy`].
|
2017-07-15 06:23:12 +00:00
|
|
|
|
2017-07-15 08:41:28 +00:00
|
|
|
[Hyper]: http://hyper.rs/
|
2019-02-16 17:46:52 +00:00
|
|
|
[Hop-by-hop headers]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html
|
2017-07-15 06:23:12 +00:00
|
|
|
[`httputil.ReverseProxy`]: https://golang.org/pkg/net/http/httputil/#ReverseProxy
|
2019-02-16 17:46:52 +00:00
|
|
|
|
|
|
|
# Example
|
|
|
|
|
|
|
|
Add these dependencies to your `Cargo.toml` file.
|
|
|
|
|
2019-02-16 18:04:46 +00:00
|
|
|
```toml
|
2019-02-16 17:46:52 +00:00
|
|
|
[dependencies]
|
2021-02-23 05:00:53 +00:00
|
|
|
hyper-reverse-proxy = "0.5"
|
2022-02-03 22:25:12 +00:00
|
|
|
hyper = { version = "0.14", features = ["full"] }
|
2022-03-12 14:47:58 +00:00
|
|
|
tokio = { version = "1", features = ["full"] }
|
2019-02-16 17:46:52 +00:00
|
|
|
```
|
|
|
|
|
|
|
|
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`
|
2022-03-12 14:58:18 +00:00
|
|
|
|
2019-02-16 17:46:52 +00:00
|
|
|
* `"/target/second"` will be proxied to `http://127.0.0.1:13902`
|
2022-03-12 14:58:18 +00:00
|
|
|
|
2019-02-16 17:46:52 +00:00
|
|
|
* All other URLs will be handled by `debug_request` function, that will display request information.
|
|
|
|
|
|
|
|
```rust,no_run
|
|
|
|
use hyper::server::conn::AddrStream;
|
2022-03-12 14:58:18 +00:00
|
|
|
use hyper::{Body, Request, Response, Server, StatusCode};
|
2019-02-16 17:46:52 +00:00
|
|
|
use hyper::service::{service_fn, make_service_fn};
|
2022-03-12 14:58:18 +00:00
|
|
|
use std::{convert::Infallible, net::SocketAddr};
|
|
|
|
use std::net::IpAddr;
|
2019-02-16 17:46:52 +00:00
|
|
|
|
2022-03-12 14:58:18 +00:00
|
|
|
fn debug_request(req: Request<Body>) -> Result<Response<Body>, Infallible> {
|
2019-02-16 17:46:52 +00:00
|
|
|
let body_str = format!("{:?}", req);
|
2022-03-12 14:58:18 +00:00
|
|
|
Ok(Response::new(Body::from(body_str)))
|
2019-02-16 17:46:52 +00:00
|
|
|
}
|
|
|
|
|
2022-03-12 14:58:18 +00:00
|
|
|
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)}
|
2022-03-12 15:04:55 +00:00
|
|
|
Err(_error) => {Ok(Response::builder()
|
2022-03-12 14:58:18 +00:00
|
|
|
.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)
|
|
|
|
}
|
|
|
|
}
|
2022-03-12 15:48:31 +00:00
|
|
|
|
2022-03-12 14:47:58 +00:00
|
|
|
#[tokio::main]
|
2022-03-12 14:58:18 +00:00
|
|
|
async fn main() {
|
|
|
|
let bind_addr = "127.0.0.1:8000";
|
|
|
|
let addr:SocketAddr = bind_addr.parse().expect("Could not parse ip:port.");
|
2022-03-12 15:48:31 +00:00
|
|
|
|
2022-03-12 14:58:18 +00:00
|
|
|
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)))
|
|
|
|
}
|
2019-02-16 17:46:52 +00:00
|
|
|
});
|
2022-03-12 15:48:31 +00:00
|
|
|
|
2022-03-12 14:47:58 +00:00
|
|
|
let server = Server::bind(&addr).serve(make_svc);
|
2022-03-12 15:48:31 +00:00
|
|
|
|
2019-02-16 17:46:52 +00:00
|
|
|
println!("Running server on {:?}", addr);
|
2022-03-12 15:48:31 +00:00
|
|
|
|
2022-03-12 14:47:58 +00:00
|
|
|
if let Err(e) = server.await {
|
|
|
|
eprintln!("server error: {}", e);
|
|
|
|
}
|
2019-02-16 17:46:52 +00:00
|
|
|
}
|
|
|
|
```
|