start from scratch
This commit is contained in:
parent
2b2ef26523
commit
d45b2f71d0
@ -1,47 +0,0 @@
|
|||||||
//! A simple use of the `ReverseProxy` service.
|
|
||||||
|
|
||||||
extern crate futures;
|
|
||||||
extern crate hyper;
|
|
||||||
extern crate hyper_reverse_proxy;
|
|
||||||
extern crate tokio_core;
|
|
||||||
|
|
||||||
use futures::Stream;
|
|
||||||
use hyper::Client;
|
|
||||||
use hyper::server::Http;
|
|
||||||
use hyper_reverse_proxy::ReverseProxy;
|
|
||||||
use tokio_core::net::TcpListener;
|
|
||||||
use tokio_core::reactor::Core;
|
|
||||||
use std::net::{SocketAddr, Ipv4Addr};
|
|
||||||
|
|
||||||
fn run() -> hyper::Result<()> {
|
|
||||||
// Set up the Tokio reactor core
|
|
||||||
let mut core = Core::new()?;
|
|
||||||
let handle = core.handle();
|
|
||||||
|
|
||||||
// Set up a TCP socket to listen to
|
|
||||||
let listen_addr = SocketAddr::new(Ipv4Addr::new(127, 0, 0, 1).into(), 8080);
|
|
||||||
let listener = TcpListener::bind(&listen_addr, &handle)?;
|
|
||||||
|
|
||||||
// Listen to incoming requests over TCP, and forward them to a new `ReverseProxy`
|
|
||||||
let http = Http::new();
|
|
||||||
let server = listener.incoming().for_each(|(socket, addr)| {
|
|
||||||
let client = Client::new(&handle);
|
|
||||||
let service = ReverseProxy::new(client, Some(addr.ip()));
|
|
||||||
http.bind_connection(&handle, socket, addr, service);
|
|
||||||
Ok(())
|
|
||||||
});
|
|
||||||
|
|
||||||
// Start our server on the reactor core
|
|
||||||
core.run(server)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
use std::io::{self, Write};
|
|
||||||
|
|
||||||
if let Err(error) = run() {
|
|
||||||
write!(&mut io::stderr(), "{}", error).expect("Error writing to stderr");
|
|
||||||
std::process::exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,151 +0,0 @@
|
|||||||
//! A more involved example of using the `ReverseProxy` service.
|
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
extern crate error_chain;
|
|
||||||
extern crate futures;
|
|
||||||
extern crate hyper;
|
|
||||||
extern crate hyper_reverse_proxy;
|
|
||||||
extern crate tokio_core;
|
|
||||||
extern crate tokio_signal;
|
|
||||||
|
|
||||||
use futures::{BoxFuture, Future, Stream};
|
|
||||||
use tokio_core::reactor::Handle;
|
|
||||||
use std::net::{SocketAddr, Ipv4Addr};
|
|
||||||
|
|
||||||
error_chain! {
|
|
||||||
foreign_links {
|
|
||||||
Io(std::io::Error);
|
|
||||||
Hyper(hyper::Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn shutdown_future(handle: &Handle) -> BoxFuture<(), std::io::Error> {
|
|
||||||
use tokio_signal::unix::{Signal, SIGINT, SIGTERM};
|
|
||||||
|
|
||||||
let sigint = Signal::new(SIGINT, handle).flatten_stream();
|
|
||||||
let sigterm = Signal::new(SIGTERM, handle).flatten_stream();
|
|
||||||
|
|
||||||
Stream::select(sigint, sigterm)
|
|
||||||
.into_future()
|
|
||||||
.map(|_| ())
|
|
||||||
.map_err(|(e, _)| e)
|
|
||||||
.boxed()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run() -> Result<()> {
|
|
||||||
use futures::task::{self, Task};
|
|
||||||
use hyper::Client;
|
|
||||||
use hyper::server::{Http, Service};
|
|
||||||
use hyper_reverse_proxy::ReverseProxy;
|
|
||||||
use std::rc::{Rc, Weak};
|
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::time::Duration;
|
|
||||||
use tokio_core::net::TcpListener;
|
|
||||||
use tokio_core::reactor::{Core, Timeout};
|
|
||||||
|
|
||||||
struct Info {
|
|
||||||
active: usize,
|
|
||||||
blocker: Option<Task>,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct NotifyService<S> {
|
|
||||||
inner: S,
|
|
||||||
info: Weak<RefCell<Info>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S: Service> Service for NotifyService<S> {
|
|
||||||
type Request = S::Request;
|
|
||||||
type Response = S::Response;
|
|
||||||
type Error = S::Error;
|
|
||||||
type Future = S::Future;
|
|
||||||
|
|
||||||
fn call(&self, message: Self::Request) -> Self::Future {
|
|
||||||
self.inner.call(message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S> Drop for NotifyService<S> {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
if let Some(info) = self.info.upgrade() {
|
|
||||||
let mut info = info.borrow_mut();
|
|
||||||
info.active -= 1;
|
|
||||||
if info.active == 0 {
|
|
||||||
if let Some(task) = info.blocker.take() {
|
|
||||||
task.notify();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct WaitUntilZero {
|
|
||||||
info: Rc<RefCell<Info>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Future for WaitUntilZero {
|
|
||||||
type Item = ();
|
|
||||||
type Error = std::io::Error;
|
|
||||||
|
|
||||||
fn poll(&mut self) -> futures::Poll<(), std::io::Error> {
|
|
||||||
use futures::Async;
|
|
||||||
|
|
||||||
let mut info = self.info.borrow_mut();
|
|
||||||
if info.active == 0 {
|
|
||||||
Ok(Async::Ready(()))
|
|
||||||
} else {
|
|
||||||
info.blocker = Some(task::current());
|
|
||||||
Ok(Async::NotReady)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set up the Tokio reactor core
|
|
||||||
let mut core = Core::new()?;
|
|
||||||
let handle = core.handle();
|
|
||||||
|
|
||||||
// Set up a TCP socket to listen to
|
|
||||||
let listen_addr = SocketAddr::new(Ipv4Addr::new(127, 0, 0, 1).into(), 8080);
|
|
||||||
let listener = TcpListener::bind(&listen_addr, &handle)?;
|
|
||||||
|
|
||||||
println!("Listening on {}", listen_addr);
|
|
||||||
|
|
||||||
// Keep track of how many active connections we are managing
|
|
||||||
let info = Rc::new(RefCell::new(Info {
|
|
||||||
active: 0,
|
|
||||||
blocker: None,
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Listen to incoming requests over TCP, and forward them to a new `ReverseProxy`
|
|
||||||
let http = Http::new();
|
|
||||||
let server = listener.incoming().for_each(|(socket, addr)| {
|
|
||||||
let client = Client::new(&handle);
|
|
||||||
let service = NotifyService {
|
|
||||||
inner: ReverseProxy::new(client, Some(addr.ip())),
|
|
||||||
info: Rc::downgrade(&info),
|
|
||||||
};
|
|
||||||
|
|
||||||
info.borrow_mut().active += 1;
|
|
||||||
http.bind_connection(&handle, socket, addr, service);
|
|
||||||
Ok(())
|
|
||||||
});
|
|
||||||
|
|
||||||
let shutdown = shutdown_future(&handle);
|
|
||||||
|
|
||||||
// Start our server, blocking the main thread.
|
|
||||||
match core.run(Future::select(shutdown, server)) {
|
|
||||||
Ok(((), _next)) => {}
|
|
||||||
Err((error, _next)) => bail!(error),
|
|
||||||
}
|
|
||||||
|
|
||||||
println!("Shutting down gracefully");
|
|
||||||
|
|
||||||
// Let the outstanding requests run for 2 seconds, then shut down the server
|
|
||||||
let timeout = Timeout::new(Duration::from_secs(2), &handle)?;
|
|
||||||
let wait = WaitUntilZero { info: info.clone() };
|
|
||||||
match core.run(Future::select(wait, timeout)) {
|
|
||||||
Ok(((), _next)) => Ok(()),
|
|
||||||
Err((error, _next)) => Err(error.into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
quick_main!(run);
|
|
241
src/lib.rs
241
src/lib.rs
@ -1,241 +0,0 @@
|
|||||||
//! A simple reverse proxy, to be used with [Hyper] and [Tokio].
|
|
||||||
//!
|
|
||||||
//! 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`].
|
|
||||||
//!
|
|
||||||
//! [Hyper]: http://hyper.rs/
|
|
||||||
//! [Tokio]: http://tokio.rs/
|
|
||||||
//! [Hop-by-hop headers]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html
|
|
||||||
//! [`httputil.ReverseProxy`]: https://golang.org/pkg/net/http/httputil/#ReverseProxy
|
|
||||||
//!
|
|
||||||
//! # Example
|
|
||||||
//!
|
|
||||||
//! Because the reverse proxy needs client, we also need a [`Handle`]. This
|
|
||||||
//! means that we can't take advantage of hyper's handy [`Http::bind`] method
|
|
||||||
//! to quickly set up the server. Instead we do things manually:
|
|
||||||
//!
|
|
||||||
//! [`Handle`]: https://docs.rs/tokio-core/*/tokio_core/reactor/struct.Handle.html
|
|
||||||
//! [`Http::bind`]: https://docs.rs/hyper/*/hyper/server/struct.Http.html#method.bind
|
|
||||||
//!
|
|
||||||
//! ```rust,no_run
|
|
||||||
//! extern crate futures;
|
|
||||||
//! extern crate hyper;
|
|
||||||
//! extern crate hyper_reverse_proxy;
|
|
||||||
//! extern crate tokio_core;
|
|
||||||
//!
|
|
||||||
//! use futures::Stream;
|
|
||||||
//! use hyper::Client;
|
|
||||||
//! use hyper::server::Http;
|
|
||||||
//! use hyper_reverse_proxy::ReverseProxy;
|
|
||||||
//! use tokio_core::net::TcpListener;
|
|
||||||
//! use tokio_core::reactor::Core;
|
|
||||||
//! use std::net::{SocketAddr, Ipv4Addr};
|
|
||||||
//!
|
|
||||||
//! fn run() -> hyper::Result<()> {
|
|
||||||
//! // Set up the Tokio reactor core
|
|
||||||
//! let mut core = Core::new()?;
|
|
||||||
//! let handle = core.handle();
|
|
||||||
//!
|
|
||||||
//! // Set up a TCP socket to listen to
|
|
||||||
//! let listen_addr = SocketAddr::new(Ipv4Addr::new(127, 0, 0, 1).into(), 8080);
|
|
||||||
//! let listener = TcpListener::bind(&listen_addr, &handle)?;
|
|
||||||
//!
|
|
||||||
//! // Listen to incoming requests over TCP, and forward them to a new `ReverseProxy`
|
|
||||||
//! let http = Http::new();
|
|
||||||
//! let server = listener.incoming().for_each(|(socket, addr)| {
|
|
||||||
//! let client = Client::new(&handle);
|
|
||||||
//! let service = ReverseProxy::new(client, Some(addr.ip()));
|
|
||||||
//! http.bind_connection(&handle, socket, addr, service);
|
|
||||||
//! Ok(())
|
|
||||||
//! });
|
|
||||||
//!
|
|
||||||
//! // Start our server on the reactor core
|
|
||||||
//! core.run(server)?;
|
|
||||||
//!
|
|
||||||
//! Ok(())
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! fn main() {
|
|
||||||
//! use std::io::{self, Write};
|
|
||||||
//!
|
|
||||||
//! if let Err(error) = run() {
|
|
||||||
//! write!(&mut io::stderr(), "{}", error).expect("Error writing to stderr");
|
|
||||||
//! std::process::exit(1);
|
|
||||||
//! }
|
|
||||||
//! }
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! Note that in a production system we might also want to:
|
|
||||||
//!
|
|
||||||
//! - listen for `SIGINT` and `SIGTERM` signals using [tokio_signal]
|
|
||||||
//! - shut down the server gracefully, waiting for outstanding requests to complete
|
|
||||||
//!
|
|
||||||
//! To see an example of this, look at [examples/extended.rs].
|
|
||||||
//!
|
|
||||||
//! [examples/extended.rs]: https://github.com/brendanzab/hyper-reverse-proxy/blob/master/examples/extended.rs
|
|
||||||
//! [tokio_signal]: https://github.com/alexcrichton/tokio-signal
|
|
||||||
|
|
||||||
extern crate futures;
|
|
||||||
#[macro_use]
|
|
||||||
extern crate hyper;
|
|
||||||
#[macro_use]
|
|
||||||
extern crate lazy_static;
|
|
||||||
extern crate unicase;
|
|
||||||
|
|
||||||
use futures::future::Future;
|
|
||||||
use hyper::{Body, Headers, Request, Response, StatusCode};
|
|
||||||
use hyper::server::Service;
|
|
||||||
use std::marker::PhantomData;
|
|
||||||
use std::net::IpAddr;
|
|
||||||
|
|
||||||
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<Ascii<&'static str>> = 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: &Headers) -> Headers {
|
|
||||||
headers
|
|
||||||
.iter()
|
|
||||||
.filter(|header| !is_hop_header(header.name()))
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: use https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Forwarded ?
|
|
||||||
header! {
|
|
||||||
/// `X-Forwarded-For` header.
|
|
||||||
///
|
|
||||||
/// The `X-Forwarded-For` header describes the path of
|
|
||||||
/// proxies this request has been forwarded through.
|
|
||||||
///
|
|
||||||
/// # Example Values
|
|
||||||
///
|
|
||||||
/// * `2001:db8:85a3:8d3:1319:8a2e:370:7348`
|
|
||||||
/// * `203.0.113.195`
|
|
||||||
/// * `203.0.113.195, 70.41.3.18, 150.172.238.178`
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # extern crate hyper;
|
|
||||||
/// # extern crate hyper_reverse_proxy;
|
|
||||||
/// use hyper::Headers;
|
|
||||||
/// use hyper_reverse_proxy::XForwardedFor;
|
|
||||||
/// use std::net::{Ipv4Addr, Ipv6Addr};
|
|
||||||
///
|
|
||||||
/// # fn main() {
|
|
||||||
/// let mut headers = Headers::new();
|
|
||||||
/// headers.set(XForwardedFor(vec![
|
|
||||||
/// Ipv4Addr::new(127, 0, 0, 1).into(),
|
|
||||||
/// Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1).into(),
|
|
||||||
/// ]));
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// # References
|
|
||||||
///
|
|
||||||
/// - [MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For)
|
|
||||||
/// - [Wikipedia](https://en.wikipedia.org/wiki/X-Forwarded-For)
|
|
||||||
(XForwardedFor, "X-Forwarded-For") => (IpAddr)+
|
|
||||||
|
|
||||||
// test_x_forwarded_for {
|
|
||||||
// // Testcases from MDN
|
|
||||||
// test_header!(test1, vec![b"2001:db8:85a3:8d3:1319:8a2e:370:7348"]);
|
|
||||||
// test_header!(test2, vec![b"203.0.113.195"]);
|
|
||||||
// test_header!(test3, vec![b"203.0.113.195, 70.41.3.18, 150.172.238.178"]);
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_proxied_response<B>(mut response: Response<B>) -> Response<B> {
|
|
||||||
*response.headers_mut() = remove_hop_headers(response.headers());
|
|
||||||
response
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A `Service` that takes an incoming request, sends it to a given `Client`, then proxies back
|
|
||||||
/// the response.
|
|
||||||
pub struct ReverseProxy<C: Service, B = Body> {
|
|
||||||
client: C,
|
|
||||||
remote_ip: Option<IpAddr>,
|
|
||||||
_pantom_data: PhantomData<B>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<C: Service, B> ReverseProxy<C, B> {
|
|
||||||
/// Construct a reverse proxy that dispatches to the given client.
|
|
||||||
pub fn new(client: C, remote_ip: Option<IpAddr>) -> ReverseProxy<C, B> {
|
|
||||||
ReverseProxy {
|
|
||||||
client,
|
|
||||||
remote_ip,
|
|
||||||
_pantom_data: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_proxied_request(&self, mut request: Request<B>) -> Request<B> {
|
|
||||||
*request.headers_mut() = remove_hop_headers(request.headers());
|
|
||||||
|
|
||||||
// Add forwarding information in the headers
|
|
||||||
if let Some(ip) = self.remote_ip {
|
|
||||||
// This is kind of ugly because of borrowing. Maybe hyper's `Headers` object
|
|
||||||
// could use an entry API like `std::collections::HashMap`?
|
|
||||||
if request.headers().has::<XForwardedFor>() {
|
|
||||||
if let Some(prior) = request.headers_mut().get_mut::<XForwardedFor>() {
|
|
||||||
prior.push(ip);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let header = XForwardedFor(vec![ip]);
|
|
||||||
request.headers_mut().set(header);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
request
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<C, B> Service for ReverseProxy<C, B>
|
|
||||||
where
|
|
||||||
B: 'static,
|
|
||||||
C: Service<Request = Request<B>, Response = Response<B>>,
|
|
||||||
C::Error: 'static + std::fmt::Display,
|
|
||||||
C::Future: 'static,
|
|
||||||
{
|
|
||||||
type Request = Request<B>;
|
|
||||||
type Response = Response<B>;
|
|
||||||
type Error = hyper::Error;
|
|
||||||
type Future = Box<Future<Item = Response<B>, Error = hyper::Error>>;
|
|
||||||
|
|
||||||
fn call(&self, request: Self::Request) -> Self::Future {
|
|
||||||
let proxied_request = self.create_proxied_request(request);
|
|
||||||
|
|
||||||
Box::new(self.client.call(proxied_request).then(|response| {
|
|
||||||
Ok(match response {
|
|
||||||
Ok(response) => create_proxied_response(response),
|
|
||||||
Err(error) => {
|
|
||||||
println!("Error: {}", error); // TODO: Configurable logging
|
|
||||||
Response::new().with_status(StatusCode::InternalServerError)
|
|
||||||
// TODO: handle trailers
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
|
178
tests/lib.rs
178
tests/lib.rs
@ -1,178 +0,0 @@
|
|||||||
extern crate futures;
|
|
||||||
#[macro_use]
|
|
||||||
extern crate hyper;
|
|
||||||
extern crate hyper_reverse_proxy;
|
|
||||||
|
|
||||||
use futures::future::{self, Future, FutureResult};
|
|
||||||
use hyper::{Get, Request, Response};
|
|
||||||
use hyper::server::Service;
|
|
||||||
use hyper_reverse_proxy::ReverseProxy;
|
|
||||||
|
|
||||||
struct MockService<F: Fn(Request) -> Response>(F);
|
|
||||||
|
|
||||||
impl<F: Fn(Request) -> Response> Service for MockService<F> {
|
|
||||||
type Request = Request;
|
|
||||||
type Response = Response;
|
|
||||||
type Error = hyper::Error;
|
|
||||||
type Future = FutureResult<Response, hyper::Error>;
|
|
||||||
|
|
||||||
fn call(&self, request: Self::Request) -> Self::Future {
|
|
||||||
future::ok((self.0)(request))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn begins_forwarded_for_header() {
|
|
||||||
use hyper_reverse_proxy::XForwardedFor;
|
|
||||||
use std::net::Ipv6Addr;
|
|
||||||
|
|
||||||
let mut request = Request::new(Get, "/".parse().unwrap());
|
|
||||||
request.set_body("request");
|
|
||||||
|
|
||||||
let remote_ip = Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8);
|
|
||||||
let client = MockService(|request| {
|
|
||||||
assert_eq!(
|
|
||||||
request.headers().get::<XForwardedFor>(),
|
|
||||||
Some(&XForwardedFor(
|
|
||||||
vec![Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8).into()],
|
|
||||||
))
|
|
||||||
);
|
|
||||||
|
|
||||||
Response::new()
|
|
||||||
});
|
|
||||||
let service = ReverseProxy::new(client, Some(remote_ip.into()));
|
|
||||||
|
|
||||||
service.call(request).wait().unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn continues_forwarded_for_header() {
|
|
||||||
use hyper_reverse_proxy::XForwardedFor;
|
|
||||||
use std::net::{Ipv4Addr, Ipv6Addr};
|
|
||||||
|
|
||||||
let mut request = Request::new(Get, "/".parse().unwrap());
|
|
||||||
request.set_body("request");
|
|
||||||
request.headers_mut().set(XForwardedFor(vec![
|
|
||||||
Ipv4Addr::new(127, 0, 0, 1).into(),
|
|
||||||
Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1).into(),
|
|
||||||
]));
|
|
||||||
|
|
||||||
let remote_ip = Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8);
|
|
||||||
let client = MockService(|request| {
|
|
||||||
assert_eq!(
|
|
||||||
request.headers().get::<XForwardedFor>(),
|
|
||||||
Some(&XForwardedFor(vec![
|
|
||||||
Ipv4Addr::new(127, 0, 0, 1).into(),
|
|
||||||
Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1).into(),
|
|
||||||
Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8).into(),
|
|
||||||
]))
|
|
||||||
);
|
|
||||||
|
|
||||||
Response::new()
|
|
||||||
});
|
|
||||||
let service = ReverseProxy::new(client, Some(remote_ip.into()));
|
|
||||||
|
|
||||||
service.call(request).wait().unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn forwards_the_bodies() {
|
|
||||||
use futures::Stream;
|
|
||||||
|
|
||||||
let mut request = Request::new(Get, "/".parse().unwrap());
|
|
||||||
request.set_body("request");
|
|
||||||
|
|
||||||
let client = MockService(|request| {
|
|
||||||
let body = request.body().concat2().wait().unwrap();
|
|
||||||
assert_eq!(body.as_ref(), b"request");
|
|
||||||
|
|
||||||
Response::new().with_body("response")
|
|
||||||
});
|
|
||||||
let service = ReverseProxy::new(client, None);
|
|
||||||
|
|
||||||
let response = service.call(request).wait().unwrap();
|
|
||||||
let body = response.body().concat2().wait().unwrap();
|
|
||||||
assert_eq!(body.as_ref(), b"response");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn clones_headers() {
|
|
||||||
header! { (XTestHeader1, "X-Test-Header1") => [String] }
|
|
||||||
header! { (XTestHeader2, "X-Test-Header2") => [String] }
|
|
||||||
|
|
||||||
let mut request = Request::new(Get, "/".parse().unwrap());
|
|
||||||
request.headers_mut().set(XTestHeader1("Test1".to_owned()));
|
|
||||||
request.headers_mut().set(XTestHeader2("Test2".to_owned()));
|
|
||||||
|
|
||||||
let client = MockService(|request| {
|
|
||||||
let header1 = request.headers().get::<XTestHeader1>().unwrap();
|
|
||||||
let header2 = request.headers().get::<XTestHeader2>().unwrap();
|
|
||||||
assert_eq!(header1, &XTestHeader1("Test1".to_owned()));
|
|
||||||
assert_eq!(header2, &XTestHeader2("Test2".to_owned()));
|
|
||||||
Response::new()
|
|
||||||
});
|
|
||||||
let service = ReverseProxy::new(client, None);
|
|
||||||
|
|
||||||
service.call(request).wait().unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn removes_request_hop_headers() {
|
|
||||||
use hyper::header::{Connection, TransferEncoding, Upgrade};
|
|
||||||
|
|
||||||
let mut request = Request::new(Get, "/".parse().unwrap());
|
|
||||||
request.headers_mut().set(Connection(vec![]));
|
|
||||||
request.headers_mut().set_raw("Keep-Alive", "");
|
|
||||||
request.headers_mut().set_raw("Proxy-Authenticate", "");
|
|
||||||
request.headers_mut().set_raw("Proxy-Authorization", "");
|
|
||||||
request.headers_mut().set_raw("TE", "");
|
|
||||||
request.headers_mut().set_raw("Trailers", "");
|
|
||||||
request.headers_mut().set(TransferEncoding(vec![]));
|
|
||||||
request.headers_mut().set(Upgrade(vec![]));
|
|
||||||
|
|
||||||
let client = MockService(|request| {
|
|
||||||
assert_eq!(request.headers().get::<Connection>(), None);
|
|
||||||
assert_eq!(request.headers().get_raw("Keep-Alive"), None);
|
|
||||||
assert_eq!(request.headers().get_raw("Proxy-Authenticate"), None);
|
|
||||||
assert_eq!(request.headers().get_raw("Proxy-Authorization"), None);
|
|
||||||
assert_eq!(request.headers().get_raw("TE"), None);
|
|
||||||
assert_eq!(request.headers().get_raw("Trailers"), None);
|
|
||||||
assert_eq!(request.headers().get::<TransferEncoding>(), None);
|
|
||||||
assert_eq!(request.headers().get::<Upgrade>(), None);
|
|
||||||
Response::new()
|
|
||||||
});
|
|
||||||
let service = ReverseProxy::new(client, None);
|
|
||||||
|
|
||||||
service.call(request).wait().unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn removes_response_hop_headers() {
|
|
||||||
use hyper::header::{Connection, TransferEncoding, Upgrade};
|
|
||||||
|
|
||||||
let request = Request::new(Get, "/".parse().unwrap());
|
|
||||||
|
|
||||||
let client = MockService(|_| {
|
|
||||||
let mut response = Response::new();
|
|
||||||
response.headers_mut().set(Connection(vec![]));
|
|
||||||
response.headers_mut().set_raw("Keep-Alive", "");
|
|
||||||
response.headers_mut().set_raw("Proxy-Authenticate", "");
|
|
||||||
response.headers_mut().set_raw("Proxy-Authorization", "");
|
|
||||||
response.headers_mut().set_raw("TE", "");
|
|
||||||
response.headers_mut().set_raw("Trailers", "");
|
|
||||||
response.headers_mut().set(TransferEncoding(vec![]));
|
|
||||||
response.headers_mut().set(Upgrade(vec![]));
|
|
||||||
response
|
|
||||||
});
|
|
||||||
let service = ReverseProxy::new(client, None);
|
|
||||||
|
|
||||||
let response = service.call(request).wait().unwrap();
|
|
||||||
assert_eq!(response.headers().get::<Connection>(), None);
|
|
||||||
assert_eq!(response.headers().get_raw("Keep-Alive"), None);
|
|
||||||
assert_eq!(response.headers().get_raw("Proxy-Authenticate"), None);
|
|
||||||
assert_eq!(response.headers().get_raw("Proxy-Authorization"), None);
|
|
||||||
assert_eq!(response.headers().get_raw("TE"), None);
|
|
||||||
assert_eq!(response.headers().get_raw("Trailers"), None);
|
|
||||||
assert_eq!(response.headers().get::<TransferEncoding>(), None);
|
|
||||||
assert_eq!(response.headers().get::<Upgrade>(), None);
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user