linkerd2-proxy/src/app/outbound.rs

216 lines
6.3 KiB
Rust

use std::fmt;
use control::destination::{Metadata, ProtocolHint};
use proxy::http::settings;
use svc;
use tap;
use transport::{connect, tls};
use NameAddr;
#[derive(Clone, Debug)]
pub struct Endpoint {
pub dst_name: Option<NameAddr>,
pub connect: connect::Target,
pub metadata: Metadata,
}
// === impl Endpoint ===
impl Endpoint {
pub fn can_use_orig_proto(&self) -> bool {
match self.metadata.protocol_hint() {
ProtocolHint::Unknown => false,
ProtocolHint::Http2 => true,
}
}
}
impl settings::router::HasConnect for Endpoint {
fn connect(&self) -> connect::Target {
self.connect.clone()
}
}
impl fmt::Display for Endpoint {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.connect.addr.fmt(f)
}
}
impl svc::watch::WithUpdate<tls::ConditionalClientConfig> for Endpoint {
type Updated = Self;
fn with_update(&self, client_config: &tls::ConditionalClientConfig) -> Self::Updated {
let mut ep = self.clone();
ep.connect.tls = ep.metadata.tls_identity().and_then(|identity| {
client_config.as_ref().map(|config| tls::ConnectionConfig {
server_identity: identity.clone(),
config: config.clone(),
})
});
ep
}
}
impl From<Endpoint> for tap::Endpoint {
fn from(ep: Endpoint) -> Self {
// TODO add route labels...
tap::Endpoint {
direction: tap::Direction::Out,
labels: ep.metadata.labels().clone(),
target: ep.connect.clone(),
}
}
}
pub mod discovery {
use futures::{Async, Poll};
use std::net::SocketAddr;
use super::super::dst::DstAddr;
use super::Endpoint;
use control::destination::Metadata;
use proxy::resolve;
use transport::{connect, tls};
use {Addr, Conditional, NameAddr};
#[derive(Clone, Debug)]
pub struct Resolve<R: resolve::Resolve<NameAddr>>(R);
#[derive(Debug)]
pub enum Resolution<R: resolve::Resolution> {
Name(NameAddr, R),
Addr(Option<SocketAddr>),
}
// === impl Resolve ===
impl<R> Resolve<R>
where
R: resolve::Resolve<NameAddr, Endpoint = Metadata>,
{
pub fn new(resolve: R) -> Self {
Resolve(resolve)
}
}
impl<R> resolve::Resolve<DstAddr> for Resolve<R>
where
R: resolve::Resolve<NameAddr, Endpoint = Metadata>,
{
type Endpoint = Endpoint;
type Resolution = Resolution<R::Resolution>;
fn resolve(&self, dst: &DstAddr) -> Self::Resolution {
match dst.as_ref() {
Addr::Name(ref name) => Resolution::Name(name.clone(), self.0.resolve(&name)),
Addr::Socket(ref addr) => Resolution::Addr(Some(*addr)),
}
}
}
// === impl Resolution ===
impl<R> resolve::Resolution for Resolution<R>
where
R: resolve::Resolution<Endpoint = Metadata>,
{
type Endpoint = Endpoint;
type Error = R::Error;
fn poll(&mut self) -> Poll<resolve::Update<Self::Endpoint>, Self::Error> {
match self {
Resolution::Name(ref name, ref mut res) => match try_ready!(res.poll()) {
resolve::Update::Remove(addr) => {
Ok(Async::Ready(resolve::Update::Remove(addr)))
}
resolve::Update::Add(addr, metadata) => {
// If the endpoint does not have TLS, note the reason.
// Otherwise, indicate that we don't (yet) have a TLS
// config. This value may be changed by a stack layer that
// provides TLS configuration.
let tls = match metadata.tls_identity() {
Conditional::None(reason) => reason.into(),
Conditional::Some(_) => tls::ReasonForNoTls::NoConfig,
};
let ep = Endpoint {
dst_name: Some(name.clone()),
connect: connect::Target::new(addr, Conditional::None(tls)),
metadata,
};
Ok(Async::Ready(resolve::Update::Add(addr, ep)))
}
},
Resolution::Addr(ref mut addr) => match addr.take() {
Some(addr) => {
let tls = tls::ReasonForNoIdentity::NoAuthorityInHttpRequest;
let ep = Endpoint {
dst_name: None,
connect: connect::Target::new(addr, Conditional::None(tls.into())),
metadata: Metadata::none(tls),
};
Ok(Async::Ready(resolve::Update::Add(addr, ep)))
}
None => Ok(Async::NotReady),
},
}
}
}
}
pub mod orig_proto_upgrade {
use http;
use super::Endpoint;
use proxy::http::orig_proto;
use svc;
#[derive(Debug, Clone)]
pub struct Layer;
#[derive(Clone, Debug)]
pub struct Stack<M>
where
M: svc::Stack<Endpoint>,
{
inner: M,
}
pub fn layer() -> Layer {
Layer
}
impl<M, A, B> svc::Layer<Endpoint, Endpoint, M> for Layer
where
M: svc::Stack<Endpoint>,
M::Value: svc::Service<Request = http::Request<A>, Response = http::Response<B>>,
{
type Value = <Stack<M> as svc::Stack<Endpoint>>::Value;
type Error = <Stack<M> as svc::Stack<Endpoint>>::Error;
type Stack = Stack<M>;
fn bind(&self, inner: M) -> Self::Stack {
Stack { inner }
}
}
// === impl Stack ===
impl<M, A, B> svc::Stack<Endpoint> for Stack<M>
where
M: svc::Stack<Endpoint>,
M::Value: svc::Service<Request = http::Request<A>, Response = http::Response<B>>,
{
type Value = svc::Either<orig_proto::Upgrade<M::Value>, M::Value>;
type Error = M::Error;
fn make(&self, endpoint: &Endpoint) -> Result<Self::Value, Self::Error> {
if endpoint.can_use_orig_proto() {
self.inner.make(&endpoint).map(|i| svc::Either::A(i.into()))
} else {
self.inner.make(&endpoint).map(svc::Either::B)
}
}
}
}