linkerd2-proxy/proxy/src/bind.rs

345 lines
8.5 KiB
Rust

use std::error::Error;
use std::fmt;
use std::default::Default;
use std::marker::PhantomData;
use std::sync::Arc;
use std::sync::atomic::AtomicUsize;
use futures::{Future, Poll};
use futures::future::Map;
use http::{self, uri};
use tokio_core::reactor::Handle;
use tower_service as tower;
use tower_h2;
use tower_reconnect::Reconnect;
use conduit_proxy_router::Reuse;
use control;
use control::discovery::Endpoint;
use ctx;
use telemetry::{self, sensor};
use transparency::{self, HttpBody, h1};
use transport;
/// Binds a `Service` from a `SocketAddr`.
///
/// The returned `Service` buffers request until a connection is established.
///
/// # TODO
///
/// Buffering is not bounded and no timeouts are applied.
pub struct Bind<C, B> {
ctx: C,
sensors: telemetry::Sensors,
executor: Handle,
req_ids: Arc<AtomicUsize>,
_p: PhantomData<B>,
}
/// Binds a `Service` from a `SocketAddr` for a pre-determined protocol.
pub struct BindProtocol<C, B> {
bind: Bind<C, B>,
protocol: Protocol,
}
/// Protocol portion of the `Recognize` key for a request.
///
/// This marks whether to use HTTP/2 or HTTP/1.x for a request. In
/// the case of HTTP/1.x requests, it also stores a "host" key to ensure
/// that each host receives its own connection.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum Protocol {
Http1(Host),
Http2
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum Host {
Authority(uri::Authority),
NoAuthority,
}
/// Rewrites HTTP/1.x requests so that their URIs are in a canonical form.
///
/// The following transformations are applied:
/// - If an absolute-form URI is received, it must replace
/// the host header (in accordance with RFC7230#section-5.4)
/// - If the request URI is not in absolute form, it is rewritten to contain
/// the authority given in the `Host:` header, or, failing that, from the
/// request's original destination according to `SO_ORIGINAL_DST`.
#[derive(Copy, Clone, Debug)]
pub struct NormalizeUri<S> {
inner: S
}
pub type Service<B> = Reconnect<NormalizeUri<NewHttp<B>>>;
pub type NewHttp<B> = sensor::NewHttp<Client<B>, B, HttpBody>;
pub type HttpResponse = http::Response<sensor::http::ResponseBody<HttpBody>>;
pub type HttpRequest<B> = http::Request<sensor::http::RequestBody<B>>;
pub type Client<B> = transparency::Client<
sensor::Connect<transport::Connect>,
B,
>;
#[derive(Copy, Clone, Debug)]
pub enum BufferSpawnError {
Inbound,
Outbound,
}
impl fmt::Display for BufferSpawnError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.pad(self.description())
}
}
impl Error for BufferSpawnError {
fn description(&self) -> &str {
match *self {
BufferSpawnError::Inbound =>
"error spawning inbound buffer task",
BufferSpawnError::Outbound =>
"error spawning outbound buffer task",
}
}
fn cause(&self) -> Option<&Error> { None }
}
impl<B> Bind<(), B> {
pub fn new(executor: Handle) -> Self {
Self {
executor,
ctx: (),
sensors: telemetry::Sensors::null(),
req_ids: Default::default(),
_p: PhantomData,
}
}
pub fn with_sensors(self, sensors: telemetry::Sensors) -> Self {
Self {
sensors,
..self
}
}
pub fn with_ctx<C>(self, ctx: C) -> Bind<C, B> {
Bind {
ctx,
sensors: self.sensors,
executor: self.executor,
req_ids: self.req_ids,
_p: PhantomData,
}
}
}
impl<C: Clone, B> Clone for Bind<C, B> {
fn clone(&self) -> Self {
Self {
ctx: self.ctx.clone(),
sensors: self.sensors.clone(),
executor: self.executor.clone(),
req_ids: self.req_ids.clone(),
_p: PhantomData,
}
}
}
impl<C, B> Bind<C, B> {
// pub fn ctx(&self) -> &C {
// &self.ctx
// }
pub fn executor(&self) -> &Handle {
&self.executor
}
// pub fn req_ids(&self) -> &Arc<AtomicUsize> {
// &self.req_ids
// }
// pub fn sensors(&self) -> &telemetry::Sensors {
// &self.sensors
// }
}
impl<B> Bind<Arc<ctx::Proxy>, B>
where
B: tower_h2::Body + 'static,
{
pub fn bind_service(&self, ep: &Endpoint, protocol: &Protocol) -> Service<B> {
trace!("bind_service endpoint={:?}, protocol={:?}", ep, protocol);
let addr = ep.address();
let client_ctx = ctx::transport::Client::new(
&self.ctx,
&addr,
ep.dst_labels().cloned(),
);
// Map a socket address to a connection.
let connect = self.sensors.connect(
transport::Connect::new(addr, &self.executor),
&client_ctx,
);
let client = transparency::Client::new(
protocol,
connect,
self.executor.clone(),
);
let sensors = self.sensors.http(
self.req_ids.clone(),
client,
&client_ctx
);
// Rewrite the HTTP/1 URI, if the authorities in the Host header
// and request URI are not in agreement, or are not present.
let proxy = NormalizeUri::new(sensors);
// Automatically perform reconnects if the connection fails.
//
// TODO: Add some sort of backoff logic.
Reconnect::new(proxy)
}
}
// ===== impl BindProtocol =====
impl<C, B> Bind<C, B> {
pub fn with_protocol(self, protocol: Protocol) -> BindProtocol<C, B> {
BindProtocol {
bind: self,
protocol,
}
}
}
impl<B> control::discovery::Bind for BindProtocol<Arc<ctx::Proxy>, B>
where
B: tower_h2::Body + 'static,
{
type Endpoint = Endpoint;
type Request = http::Request<B>;
type Response = HttpResponse;
type Error = <Service<B> as tower::Service>::Error;
type Service = Service<B>;
type BindError = ();
fn bind(&self, ep: &Endpoint) -> Result<Self::Service, Self::BindError> {
Ok(self.bind.bind_service(ep, &self.protocol))
}
}
// ===== impl NormalizeUri =====
impl<S> NormalizeUri<S> {
fn new (inner: S) -> Self {
Self { inner }
}
}
impl<S, B> tower::NewService for NormalizeUri<S>
where
S: tower::NewService<
Request=http::Request<B>,
Response=HttpResponse,
>,
S::Service: tower::Service<
Request=http::Request<B>,
Response=HttpResponse,
>,
NormalizeUri<S::Service>: tower::Service,
B: tower_h2::Body,
{
type Request = <Self::Service as tower::Service>::Request;
type Response = <Self::Service as tower::Service>::Response;
type Error = <Self::Service as tower::Service>::Error;
type Service = NormalizeUri<S::Service>;
type InitError = S::InitError;
type Future = Map<
S::Future,
fn(S::Service) -> NormalizeUri<S::Service>
>;
fn new_service(&self) -> Self::Future {
self.inner.new_service().map(NormalizeUri::new)
}
}
impl<S, B> tower::Service for NormalizeUri<S>
where
S: tower::Service<
Request=http::Request<B>,
Response=HttpResponse,
>,
B: tower_h2::Body,
{
type Request = S::Request;
type Response = HttpResponse;
type Error = S::Error;
type Future = S::Future;
fn poll_ready(&mut self) -> Poll<(), S::Error> {
self.inner.poll_ready()
}
fn call(&mut self, mut request: S::Request) -> Self::Future {
if request.version() != http::Version::HTTP_2 {
h1::normalize_our_view_of_uri(&mut request);
}
self.inner.call(request)
}
}
// ===== impl Protocol =====
impl Protocol {
pub fn detect<B>(req: &http::Request<B>) -> Self {
if req.version() == http::Version::HTTP_2 {
return Protocol::Http2
}
// If the request has an authority part, use that as the host part of
// the key for an HTTP/1.x request.
let host = req.uri().authority_part()
.cloned()
.or_else(|| h1::authority_from_host(req))
.map(Host::Authority)
.unwrap_or_else(|| Host::NoAuthority);
Protocol::Http1(host)
}
pub fn is_cachable(&self) -> bool {
match *self {
Protocol::Http2 | Protocol::Http1(Host::Authority(_)) => true,
_ => false,
}
}
pub fn into_key<T>(self, key: T) -> Reuse<(T, Protocol)> {
if self.is_cachable() {
Reuse::Reusable((key, self))
} else {
Reuse::SingleUse((key, self))
}
}
}