mirror of
https://github.com/actix/examples
synced 2024-11-27 16:02:57 +01:00
Add example of a full HTTP proxy, proxying body, header and method (#58)
This commit is contained in:
parent
61a4b3b23b
commit
e30915d98d
@ -47,6 +47,7 @@ script:
|
||||
cd form && cargo check && cd ..
|
||||
cd hello-world && cargo check && cd ..
|
||||
cd http-proxy && cargo check && cd ..
|
||||
cd http-full-proxy && cargo check && cd ..
|
||||
cd json && cargo check && cd ..
|
||||
cd juniper && cargo check && cd ..
|
||||
cd middleware && cargo check && cd ..
|
||||
|
@ -14,6 +14,7 @@ members = [
|
||||
"form",
|
||||
"hello-world",
|
||||
"http-proxy",
|
||||
"http-full-proxy",
|
||||
"json",
|
||||
"juniper",
|
||||
"middleware",
|
||||
|
12
http-full-proxy/Cargo.toml
Normal file
12
http-full-proxy/Cargo.toml
Normal file
@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "http-full-proxy"
|
||||
version = "0.1.0"
|
||||
authors = ["Rotem Yaari"]
|
||||
|
||||
[dependencies]
|
||||
actix = "0.7.5"
|
||||
actix-web = "0.7.13"
|
||||
clap = "2.32.0"
|
||||
futures = "0.1.25"
|
||||
failure = "0.1.3"
|
||||
url = "1.7.1"
|
10
http-full-proxy/README.md
Normal file
10
http-full-proxy/README.md
Normal file
@ -0,0 +1,10 @@
|
||||
## HTTP Full proxy example
|
||||
|
||||
This proxy forwards all types of requests, including ones with body, to another HTTP server,
|
||||
returning the response to the client.
|
||||
|
||||
To start:
|
||||
|
||||
``` shell
|
||||
cargo run <listen addr> <listen port> <forward addr> <forward port>
|
||||
```
|
123
http-full-proxy/src/main.rs
Normal file
123
http-full-proxy/src/main.rs
Normal file
@ -0,0 +1,123 @@
|
||||
#![deny(warnings)]
|
||||
extern crate actix;
|
||||
extern crate actix_web;
|
||||
extern crate clap;
|
||||
extern crate failure;
|
||||
extern crate futures;
|
||||
extern crate url;
|
||||
|
||||
use actix_web::{
|
||||
client, http, server, App, AsyncResponder, Error, HttpMessage, HttpRequest,
|
||||
HttpResponse,
|
||||
};
|
||||
use clap::{value_t, Arg};
|
||||
use futures::Future;
|
||||
use std::net::ToSocketAddrs;
|
||||
use url::Url;
|
||||
|
||||
struct AppState {
|
||||
forward_url: Url,
|
||||
}
|
||||
|
||||
impl AppState {
|
||||
pub fn init(forward_url: Url) -> AppState {
|
||||
AppState { forward_url }
|
||||
}
|
||||
}
|
||||
|
||||
fn forward(
|
||||
req: &HttpRequest<AppState>,
|
||||
) -> Box<Future<Item = HttpResponse, Error = Error>> {
|
||||
let mut new_url = req.state().forward_url.clone();
|
||||
new_url.set_path(req.uri().path());
|
||||
new_url.set_query(req.uri().query());
|
||||
|
||||
let mut forwarded_req = client::ClientRequest::build_from(req)
|
||||
.no_default_headers()
|
||||
.uri(new_url)
|
||||
.streaming(req.payload())
|
||||
.unwrap();
|
||||
|
||||
if let Some(addr) = req.peer_addr() {
|
||||
match forwarded_req.headers_mut().entry("x-forwarded-for") {
|
||||
Ok(http::header::Entry::Vacant(entry)) => {
|
||||
let addr = format!("{}", addr.ip());
|
||||
entry.insert(addr.parse().unwrap());
|
||||
}
|
||||
Ok(http::header::Entry::Occupied(mut entry)) => {
|
||||
let addr = format!("{}, {}", entry.get().to_str().unwrap(), addr.ip());
|
||||
entry.insert(addr.parse().unwrap());
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
forwarded_req
|
||||
.send()
|
||||
.map_err(Error::from)
|
||||
.and_then(move |resp| {
|
||||
let mut client_resp = HttpResponse::build(resp.status());
|
||||
for (header_name, header_value) in
|
||||
resp.headers().iter().filter(|(h, _)| *h != "connection")
|
||||
{
|
||||
client_resp.header(header_name.clone(), header_value.clone());
|
||||
}
|
||||
|
||||
Ok(client_resp.streaming(resp.payload()))
|
||||
}).responder()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let matches = clap::App::new("HTTP Proxy")
|
||||
.arg(
|
||||
Arg::with_name("listen_addr")
|
||||
.takes_value(true)
|
||||
.value_name("LISTEN ADDR")
|
||||
.index(1)
|
||||
.required(true),
|
||||
).arg(
|
||||
Arg::with_name("listen_port")
|
||||
.takes_value(true)
|
||||
.value_name("LISTEN PORT")
|
||||
.index(2)
|
||||
.required(true),
|
||||
).arg(
|
||||
Arg::with_name("forward_addr")
|
||||
.takes_value(true)
|
||||
.value_name("FWD ADDR")
|
||||
.index(3)
|
||||
.required(true),
|
||||
).arg(
|
||||
Arg::with_name("forward_port")
|
||||
.takes_value(true)
|
||||
.value_name("FWD PORT")
|
||||
.index(4)
|
||||
.required(true),
|
||||
).get_matches();
|
||||
|
||||
let listen_addr = matches.value_of("listen_addr").unwrap();
|
||||
let listen_port = value_t!(matches, "listen_port", u16).unwrap_or_else(|e| e.exit());
|
||||
|
||||
let forwarded_addr = matches.value_of("forward_addr").unwrap();
|
||||
let forwarded_port =
|
||||
value_t!(matches, "forward_port", u16).unwrap_or_else(|e| e.exit());
|
||||
|
||||
let forward_url = Url::parse(&format!(
|
||||
"http://{}",
|
||||
(forwarded_addr, forwarded_port)
|
||||
.to_socket_addrs()
|
||||
.unwrap()
|
||||
.next()
|
||||
.unwrap()
|
||||
)).unwrap();
|
||||
|
||||
server::new(move || {
|
||||
App::with_state(AppState::init(forward_url.clone())).default_resource(|r| {
|
||||
r.f(forward);
|
||||
})
|
||||
}).workers(32)
|
||||
.bind((listen_addr, listen_port))
|
||||
.expect("Cannot bind listening port")
|
||||
.system_exit()
|
||||
.run();
|
||||
}
|
Loading…
Reference in New Issue
Block a user