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