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