linkerd2-proxy/proxy/src/inbound.rs

188 lines
5.3 KiB
Rust

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<B> = bind::Bind<Arc<ctx::Proxy>, B>;
pub struct Inbound<B> {
default_addr: Option<SocketAddr>,
bind: Bind<B>,
}
const MAX_IN_FLIGHT: usize = 10_000;
// ===== impl Inbound =====
impl<B> Inbound<B> {
pub fn new(default_addr: Option<SocketAddr>, bind: Bind<B>) -> Self {
Self {
default_addr,
bind,
}
}
}
impl<B> Recognize for Inbound<B>
where
B: tower_h2::Body + 'static,
{
type Request = http::Request<B>;
type Response = bind::HttpResponse;
type Error = tower_in_flight_limit::Error<
tower_buffer::Error<
<bind::Service<B> as tower::Service>::Error
>
>;
type Key = (SocketAddr, bind::Protocol);
type RouteError = bind::BufferSpawnError;
type Service = InFlightLimit<Buffer<bind::Service<B>>>;
fn recognize(&self, req: &Self::Request) -> Option<Reuse<Self::Key>> {
let key = req.extensions()
.get::<Arc<ctx::transport::Server>>()
.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<Self::Service, Self::RouteError> {
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<net::SocketAddr>, ctx: &Arc<ctx::Proxy>) -> 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<net::SocketAddr>,
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<net::SocketAddr>) -> 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<net::SocketAddr>,
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)
)
}
}
}