use std::net::{SocketAddr}; use std::sync::Arc; use http; use tower_service as tower; use tower_buffer::{self, Buffer}; use tower_in_flight_limit::{self, InFlightLimit}; use tower_h2; use conduit_proxy_router::{Reuse, Recognize}; use bind; use ctx; type Bind = bind::Bind, B>; pub struct Inbound { default_addr: Option, bind: Bind, } const MAX_IN_FLIGHT: usize = 10_000; // ===== impl Inbound ===== impl Inbound { pub fn new(default_addr: Option, bind: Bind) -> Self { Self { default_addr, bind, } } } impl Recognize for Inbound where B: tower_h2::Body + 'static, { type Request = http::Request; type Response = bind::HttpResponse; type Error = tower_in_flight_limit::Error< tower_buffer::Error< as tower::Service>::Error > >; type Key = (SocketAddr, bind::Protocol); type RouteError = bind::BufferSpawnError; type Service = InFlightLimit>>; fn recognize(&self, req: &Self::Request) -> Option> { let key = req.extensions() .get::>() .and_then(|ctx| { trace!("recognize local={} orig={:?}", ctx.local, ctx.orig_dst); ctx.orig_dst_if_not_local() }) .or_else(|| self.default_addr); let proto = bind::Protocol::detect(req); let key = key.map(move|addr| proto.into_key(addr)); trace!("recognize key={:?}", key); key } /// Builds a static service to a single endpoint. /// /// # TODO /// /// Buffering is currently unbounded and does not apply timeouts. This must be /// changed. fn bind_service(&mut self, key: &Self::Key) -> Result { let &(ref addr, ref proto) = key; debug!("building inbound {:?} client to {}", proto, addr); let endpoint = (*addr).into(); let bind = self.bind.bind_service(&endpoint, proto); Buffer::new(bind, self.bind.executor()) .map(|buffer| { InFlightLimit::new(buffer, MAX_IN_FLIGHT) }) .map_err(|_| bind::BufferSpawnError::Inbound) } } #[cfg(test)] mod tests { use std::net; use std::sync::Arc; use http; use tokio_core::reactor::Core; use conduit_proxy_router::Recognize; use super::Inbound; use bind::{self, Bind, Host}; use ctx; fn new_inbound(default: Option, ctx: &Arc) -> Inbound<()> { let core = Core::new().unwrap(); let bind = Bind::new(core.handle()).with_ctx(ctx.clone()); Inbound::new(default, bind) } quickcheck! { fn recognize_orig_dst( orig_dst: net::SocketAddr, local: net::SocketAddr, remote: net::SocketAddr ) -> bool { let ctx = ctx::Proxy::inbound(&ctx::Process::test("test")); let inbound = new_inbound(None, &ctx); let srv_ctx = ctx::transport::Server::new(&ctx, &local, &remote, &Some(orig_dst)); let rec = srv_ctx.orig_dst_if_not_local().map(|addr| bind::Protocol::Http1(Host::NoAuthority).into_key(addr) ); let mut req = http::Request::new(()); req.extensions_mut() .insert(srv_ctx); inbound.recognize(&req) == rec } fn recognize_default_no_orig_dst( default: Option, local: net::SocketAddr, remote: net::SocketAddr ) -> bool { let ctx = ctx::Proxy::inbound(&ctx::Process::test("test")); let inbound = new_inbound(default, &ctx); let mut req = http::Request::new(()); req.extensions_mut() .insert(ctx::transport::Server::new( &ctx, &local, &remote, &None, )); inbound.recognize(&req) == default.map(|addr| bind::Protocol::Http1(Host::NoAuthority).into_key(addr) ) } fn recognize_default_no_ctx(default: Option) -> bool { let ctx = ctx::Proxy::inbound(&ctx::Process::test("test")); let inbound = new_inbound(default, &ctx); let req = http::Request::new(()); inbound.recognize(&req) == default.map(|addr| bind::Protocol::Http1(Host::NoAuthority).into_key(addr) ) } fn recognize_default_no_loop( default: Option, local: net::SocketAddr, remote: net::SocketAddr ) -> bool { let ctx = ctx::Proxy::inbound(&ctx::Process::test("test")); let inbound = new_inbound(default, &ctx); let mut req = http::Request::new(()); req.extensions_mut() .insert(ctx::transport::Server::new( &ctx, &local, &remote, &Some(local), )); inbound.recognize(&req) == default.map(|addr| bind::Protocol::Http1(Host::NoAuthority).into_key(addr) ) } } }