Unify Name/Host/Addr types under Addr (#120)
Currently, the proxy uses a variety of types to represent the logical destination of a request. Outbound destinations use a `NameAddr` type which may be either a `DnsNameAndPort` or a `SocketAddr`. Other parts of the code used a `HostAndPort` enum that always contained a port and also contained a `Host` which could either be a `dns::Name` or a `IpAddr`. Furthermore, we coerce these types into a `http::uri::Authority` in many cases. All of these types represent the same thing; and it's not clear when/why it's appropriate to use a given variant. In order to simplify the situtation, a new `addr` module has been introduced with `Addr` and `NameAddr` types. A `Addr` may contain either a `NameAddr` or a `SocketAddr`. The `Host` value has been removed from the `Settings::Http1` type, replaced by a boolean, as it's redundant information stored elsewhere in the route key. There is one small change in behavior: The `authority` metrics label is now omitted only for requests that include an `:authority` or `Host` with a _name_ (i.e. and not an IP address).
This commit is contained in:
parent
5e0a15b8a7
commit
c4b3765574
|
@ -0,0 +1,177 @@
|
|||
use http;
|
||||
use std::fmt;
|
||||
use std::net::{IpAddr, SocketAddr};
|
||||
use std::str::FromStr;
|
||||
|
||||
use convert::TryFrom;
|
||||
pub use dns::Name;
|
||||
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
||||
pub enum Addr {
|
||||
Name(NameAddr),
|
||||
Socket(SocketAddr),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
||||
pub struct NameAddr {
|
||||
name: Name,
|
||||
port: u16,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum Error {
|
||||
/// The host is not a valid DNS name or IP address.
|
||||
InvalidHost,
|
||||
|
||||
/// The port is missing.
|
||||
MissingPort,
|
||||
}
|
||||
|
||||
// === impl Addr ===
|
||||
|
||||
impl Addr {
|
||||
pub fn new(host: &str, port: u16) -> Result<Self, Error> {
|
||||
IpAddr::from_str(host)
|
||||
.map(|ip| Addr::Socket((ip, port).into()))
|
||||
.or_else(|_| NameAddr::new(host, port).map(Addr::Name))
|
||||
}
|
||||
|
||||
pub fn from_authority_and_default_port(
|
||||
a: &http::uri::Authority,
|
||||
default_port: u16,
|
||||
) -> Result<Self, Error> {
|
||||
Self::new(a.host(), a.port().unwrap_or(default_port))
|
||||
}
|
||||
|
||||
pub fn from_authority_with_port(a: &http::uri::Authority) -> Result<Self, Error> {
|
||||
a.port()
|
||||
.ok_or(Error::MissingPort)
|
||||
.and_then(|p| Self::new(a.host(), p))
|
||||
}
|
||||
|
||||
pub fn port(&self) -> u16 {
|
||||
match self {
|
||||
Addr::Name(n) => n.port(),
|
||||
Addr::Socket(a) => a.port(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_loopback(&self) -> bool {
|
||||
match self {
|
||||
Addr::Name(n) => n.is_localhost(),
|
||||
Addr::Socket(a) => a.ip().is_loopback(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_authority(&self) -> http::uri::Authority {
|
||||
match self {
|
||||
Addr::Name(n) => n.as_authority(),
|
||||
Addr::Socket(a) => http::uri::Authority::from_str(&format!("{}", a))
|
||||
.expect("SocketAddr must be valid authority"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn socket_addr(&self) -> Option<SocketAddr> {
|
||||
match self {
|
||||
Addr::Socket(a) => Some(*a),
|
||||
Addr::Name(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name_addr(&self) -> Option<&NameAddr> {
|
||||
match self {
|
||||
Addr::Name(ref n) => Some(n),
|
||||
Addr::Socket(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_name_addr(self) -> Option<NameAddr> {
|
||||
match self {
|
||||
Addr::Name(n) => Some(n),
|
||||
Addr::Socket(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Addr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Addr::Name(NameAddr { ref name, port }) => write!(f, "{}:{}", name, port),
|
||||
Addr::Socket(addr) => write!(f, "{}", addr),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// === impl NameAddr ===
|
||||
|
||||
impl NameAddr {
|
||||
pub fn new(host: &str, port: u16) -> Result<Self, Error> {
|
||||
if host.is_empty() {
|
||||
return Err(Error::InvalidHost);
|
||||
}
|
||||
|
||||
Name::try_from(host.as_bytes())
|
||||
.map(|name| NameAddr { name, port })
|
||||
.map_err(|_| Error::InvalidHost)
|
||||
}
|
||||
|
||||
pub fn from_authority_with_default_port(
|
||||
a: &http::uri::Authority,
|
||||
default_port: u16,
|
||||
) -> Result<Self, Error> {
|
||||
Self::new(a.host(), a.port().unwrap_or(default_port))
|
||||
}
|
||||
|
||||
pub fn from_authority_with_port(a: &http::uri::Authority) -> Result<Self, Error> {
|
||||
a.port()
|
||||
.ok_or(Error::MissingPort)
|
||||
.and_then(|p| Self::new(a.host(), p))
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &Name {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn port(&self) -> u16 {
|
||||
self.port
|
||||
}
|
||||
|
||||
pub fn is_localhost(&self) -> bool {
|
||||
self.name.is_localhost()
|
||||
}
|
||||
|
||||
pub fn as_authority(&self) -> http::uri::Authority {
|
||||
http::uri::Authority::from_str(self.name.as_ref())
|
||||
.expect("NameAddr must be valid authority")
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for NameAddr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}:{}", self.name, self.port)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use http::uri::Authority;
|
||||
|
||||
#[test]
|
||||
fn test_is_loopback() {
|
||||
let cases = &[
|
||||
("localhost", false), // Not absolute
|
||||
("localhost.", true),
|
||||
("LocalhOsT.", true), // Case-insensitive
|
||||
("mlocalhost.", false), // prefixed
|
||||
("localhost1.", false), // suffixed
|
||||
("127.0.0.1", true), // IPv4
|
||||
("[::1]", true), // IPv6
|
||||
];
|
||||
for (host, expected_result) in cases {
|
||||
let authority = Authority::from_static(host);
|
||||
let hp = Addr::from_authority_and_default_port(&authority, 80).unwrap();
|
||||
assert_eq!(hp.is_loopback(), *expected_result, "{:?}", host)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,9 +10,10 @@ use http;
|
|||
use indexmap::IndexSet;
|
||||
use trust_dns_resolver::config::ResolverOpts;
|
||||
|
||||
use transport::{Host, HostAndPort, HostAndPortError, tls};
|
||||
use addr;
|
||||
use convert::TryFrom;
|
||||
use Conditional;
|
||||
use transport::tls;
|
||||
use {Conditional, Addr};
|
||||
|
||||
// TODO:
|
||||
//
|
||||
|
@ -34,7 +35,7 @@ pub struct Config {
|
|||
pub metrics_listener: Listener,
|
||||
|
||||
/// Where to forward externally received connections.
|
||||
pub inbound_forward: Option<Addr>,
|
||||
pub inbound_forward: Option<SocketAddr>,
|
||||
|
||||
/// The maximum amount of time to wait for a connection to a local peer.
|
||||
pub inbound_connect_timeout: Duration,
|
||||
|
@ -71,7 +72,7 @@ pub struct Config {
|
|||
///
|
||||
/// This is optional to allow the proxy to work without the controller for
|
||||
/// experimental & testing purposes.
|
||||
pub control_host_and_port: Option<HostAndPort>,
|
||||
pub control_host_and_port: Option<Addr>,
|
||||
|
||||
/// Time to wait when encountering errors talking to control plane before
|
||||
/// a new connection.
|
||||
|
@ -109,14 +110,9 @@ pub struct Namespaces {
|
|||
#[derive(Clone, Debug)]
|
||||
pub struct Listener {
|
||||
/// The address to which the listener should bind.
|
||||
pub addr: Addr,
|
||||
pub addr: SocketAddr,
|
||||
}
|
||||
|
||||
/// A logical address. This abstracts over the various strategies for cross
|
||||
/// process communication.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Addr(SocketAddr);
|
||||
|
||||
/// Errors produced when loading a `Config` struct.
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Error {
|
||||
|
@ -145,7 +141,7 @@ pub enum UrlError {
|
|||
MissingAuthority,
|
||||
|
||||
/// The URL is missing the authority part.
|
||||
AuthorityError(HostAndPortError),
|
||||
AuthorityError(addr::Error),
|
||||
|
||||
/// The URL contains a path component that isn't "/", which isn't allowed.
|
||||
PathNotAllowed,
|
||||
|
@ -285,13 +281,13 @@ impl<'a> TryFrom<&'a Strings> for Config {
|
|||
// will log any errors so defer returning any errors until all of them
|
||||
// have been parsed.
|
||||
let outbound_listener_addr = parse_deprecated(
|
||||
strings, ENV_OUTBOUND_LISTENER, DEPRECATED_ENV_PRIVATE_LISTENER, str::parse);
|
||||
strings, ENV_OUTBOUND_LISTENER, DEPRECATED_ENV_PRIVATE_LISTENER, parse_addr);
|
||||
let inbound_listener_addr = parse_deprecated(
|
||||
strings, ENV_INBOUND_LISTENER, DEPRECATED_ENV_PUBLIC_LISTENER, str::parse);
|
||||
let control_listener_addr = parse(strings, ENV_CONTROL_LISTENER, str::parse);
|
||||
let metrics_listener_addr = parse(strings, ENV_METRICS_LISTENER, str::parse);
|
||||
strings, ENV_INBOUND_LISTENER, DEPRECATED_ENV_PUBLIC_LISTENER, parse_addr);
|
||||
let control_listener_addr = parse(strings, ENV_CONTROL_LISTENER, parse_addr);
|
||||
let metrics_listener_addr = parse(strings, ENV_METRICS_LISTENER, parse_addr);
|
||||
let inbound_forward = parse_deprecated(
|
||||
strings, ENV_INBOUND_FORWARD, DEPRECATED_ENV_PRIVATE_FORWARD, str::parse);
|
||||
strings, ENV_INBOUND_FORWARD, DEPRECATED_ENV_PRIVATE_FORWARD, parse_addr);
|
||||
let inbound_connect_timeout = parse_deprecated(
|
||||
strings, ENV_INBOUND_CONNECT_TIMEOUT, DEPRECATED_ENV_PRIVATE_CONNECT_TIMEOUT, parse_duration);
|
||||
let outbound_connect_timeout = parse_deprecated(
|
||||
|
@ -405,19 +401,19 @@ impl<'a> TryFrom<&'a Strings> for Config {
|
|||
Ok(Config {
|
||||
outbound_listener: Listener {
|
||||
addr: outbound_listener_addr?
|
||||
.unwrap_or_else(|| Addr::from_str(DEFAULT_OUTBOUND_LISTENER).unwrap()),
|
||||
.unwrap_or_else(|| parse_addr(DEFAULT_OUTBOUND_LISTENER).unwrap()),
|
||||
},
|
||||
inbound_listener: Listener {
|
||||
addr: inbound_listener_addr?
|
||||
.unwrap_or_else(|| Addr::from_str(DEFAULT_INBOUND_LISTENER).unwrap()),
|
||||
.unwrap_or_else(|| parse_addr(DEFAULT_INBOUND_LISTENER).unwrap()),
|
||||
},
|
||||
control_listener: Listener {
|
||||
addr: control_listener_addr?
|
||||
.unwrap_or_else(|| Addr::from_str(DEFAULT_CONTROL_LISTENER).unwrap()),
|
||||
.unwrap_or_else(|| parse_addr(DEFAULT_CONTROL_LISTENER).unwrap()),
|
||||
},
|
||||
metrics_listener: Listener {
|
||||
addr: metrics_listener_addr?
|
||||
.unwrap_or_else(|| Addr::from_str(DEFAULT_METRICS_LISTENER).unwrap()),
|
||||
.unwrap_or_else(|| parse_addr(DEFAULT_METRICS_LISTENER).unwrap()),
|
||||
},
|
||||
inbound_forward: inbound_forward?,
|
||||
|
||||
|
@ -472,27 +468,10 @@ fn default_disable_ports_protocol_detection() -> IndexSet<u16> {
|
|||
|
||||
// ===== impl Addr =====
|
||||
|
||||
impl FromStr for Addr {
|
||||
type Err = ParseError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let a = parse_url(s)?;
|
||||
if let Host::Ip(ip) = a.host {
|
||||
return Ok(Addr(SocketAddr::from((ip, a.port))));
|
||||
}
|
||||
Err(ParseError::HostIsNotAnIpAddress)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Addr> for SocketAddr {
|
||||
fn from(addr: Addr) -> SocketAddr {
|
||||
addr.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SocketAddr> for Addr {
|
||||
fn from(addr: SocketAddr) -> Self {
|
||||
Addr(addr)
|
||||
fn parse_addr(s: &str) -> Result<SocketAddr, ParseError> {
|
||||
match parse_url(s)? {
|
||||
Addr::Socket(a) => Ok(a),
|
||||
_ => Err(ParseError::HostIsNotAnIpAddress)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -562,7 +541,7 @@ fn parse_path(s: &str) -> Result<PathBuf, ParseError> {
|
|||
Ok(PathBuf::from(s))
|
||||
}
|
||||
|
||||
fn parse_url(s: &str) -> Result<HostAndPort, ParseError> {
|
||||
fn parse_url(s: &str) -> Result<Addr, ParseError> {
|
||||
let url = s.parse::<http::Uri>().map_err(|_| ParseError::UrlError(UrlError::SyntaxError))?;
|
||||
if url.scheme_part().map(|s| s.as_str()) != Some("tcp") {
|
||||
return Err(ParseError::UrlError(UrlError::UnsupportedScheme));
|
||||
|
@ -577,7 +556,7 @@ fn parse_url(s: &str) -> Result<HostAndPort, ParseError> {
|
|||
// https://github.com/hyperium/http/issues/127. For now just ignore any
|
||||
// fragment that is there.
|
||||
|
||||
HostAndPort::normalize(authority, None)
|
||||
Addr::from_authority_with_port(authority)
|
||||
.map_err(|e| ParseError::UrlError(UrlError::AuthorityError(e)))
|
||||
}
|
||||
|
||||
|
|
|
@ -3,12 +3,12 @@ use std::fmt;
|
|||
use std::time::Duration;
|
||||
|
||||
use svc;
|
||||
use transport::{tls, HostAndPort};
|
||||
use Conditional;
|
||||
use transport::tls;
|
||||
use {Conditional, Addr};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Config {
|
||||
host_and_port: HostAndPort,
|
||||
host_and_port: Addr,
|
||||
tls_server_identity: Conditional<tls::Identity, tls::ReasonForNoTls>,
|
||||
tls_config: tls::ConditionalClientConfig,
|
||||
backoff: Duration,
|
||||
|
@ -18,7 +18,7 @@ pub struct Config {
|
|||
|
||||
impl Config {
|
||||
pub fn new(
|
||||
host_and_port: HostAndPort,
|
||||
host_and_port: Addr,
|
||||
tls_server_identity: Conditional<tls::Identity, tls::ReasonForNoTls>,
|
||||
backoff: Duration,
|
||||
connect_timeout: Duration,
|
||||
|
@ -116,7 +116,7 @@ pub mod add_origin {
|
|||
fn make(&self, config: &super::Config) -> Result<Self::Value, Self::Error> {
|
||||
let inner = self.inner.make(config)?;
|
||||
let scheme = uri::Scheme::from_shared(Bytes::from_static(b"http")).unwrap();
|
||||
let authority = uri::Authority::from(&config.host_and_port);
|
||||
let authority = config.host_and_port.as_authority();
|
||||
Ok(AddOrigin::new(inner, scheme, authority))
|
||||
}
|
||||
}
|
||||
|
@ -254,7 +254,7 @@ pub mod resolve {
|
|||
fn new_service(&self) -> Self::Future {
|
||||
Init {
|
||||
state: State::Resolve {
|
||||
future: self.dns.resolve_one_ip(&self.config.host_and_port.host),
|
||||
future: self.dns.resolve_one_ip(&self.config.host_and_port),
|
||||
stack: self.stack.clone(),
|
||||
config: self.config.clone(),
|
||||
},
|
||||
|
@ -284,7 +284,7 @@ pub mod resolve {
|
|||
ref stack,
|
||||
} => {
|
||||
let ip = try_ready!(future.poll().map_err(Error::Dns));
|
||||
let sa = SocketAddr::from((ip, config.host_and_port.port));
|
||||
let sa = SocketAddr::from((ip, config.host_and_port.port()));
|
||||
|
||||
let tls = config.tls_server_identity.as_ref().and_then(|id| {
|
||||
config
|
||||
|
@ -333,13 +333,14 @@ pub mod client {
|
|||
use tower_h2::{client, BoxBody};
|
||||
|
||||
use svc;
|
||||
use transport::{connect, HostAndPort};
|
||||
use transport::connect;
|
||||
use Addr;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Target {
|
||||
pub(super) connect: connect::Target,
|
||||
pub(super) builder: h2::client::Builder,
|
||||
pub(super) log_ctx: ::logging::Client<&'static str, HostAndPort>,
|
||||
pub(super) log_ctx: ::logging::Client<&'static str, Addr>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -401,7 +402,7 @@ pub mod client {
|
|||
{
|
||||
type Value = client::Connect<
|
||||
C::Value,
|
||||
::logging::ClientExecutor<&'static str, HostAndPort>,
|
||||
::logging::ClientExecutor<&'static str, Addr>,
|
||||
BoxBody,
|
||||
>;
|
||||
type Error = C::Error;
|
||||
|
|
|
@ -2,20 +2,18 @@ use http;
|
|||
use std::fmt;
|
||||
use std::net::SocketAddr;
|
||||
|
||||
use proxy::http::{
|
||||
client, h1, normalize_uri::ShouldNormalizeUri, router, Settings,
|
||||
};
|
||||
use super::classify;
|
||||
use proxy::http::{client, normalize_uri::ShouldNormalizeUri, router, Settings};
|
||||
use proxy::server::Source;
|
||||
use svc::stack_per_request::ShouldStackPerRequest;
|
||||
use tap;
|
||||
use super::classify;
|
||||
use transport::{connect, tls};
|
||||
use Conditional;
|
||||
use {Conditional, NameAddr};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct Endpoint {
|
||||
pub addr: SocketAddr,
|
||||
pub authority: http::uri::Authority,
|
||||
pub dst_name: Option<NameAddr>,
|
||||
pub settings: Settings,
|
||||
pub source_tls_status: tls::Status,
|
||||
}
|
||||
|
@ -93,20 +91,14 @@ impl<A> router::Recognize<http::Request<A>> for Recognize {
|
|||
.and_then(|s| s.orig_dst_if_not_local())
|
||||
.or(self.default_addr)?;
|
||||
|
||||
let authority = req
|
||||
.uri()
|
||||
.authority_part()
|
||||
.cloned()
|
||||
.or_else(|| h1::authority_from_host(req))
|
||||
.or_else(|| {
|
||||
let a = format!("{}", addr);
|
||||
http::uri::Authority::from_shared(a.into()).ok()
|
||||
})?;
|
||||
let settings = Settings::detect(req);
|
||||
let dst_name = super::http_request_addr(req)
|
||||
.ok()
|
||||
.and_then(|h| h.into_name_addr());
|
||||
let settings = Settings::from_request(req);
|
||||
|
||||
let ep = Endpoint {
|
||||
addr,
|
||||
authority,
|
||||
dst_name,
|
||||
settings,
|
||||
source_tls_status,
|
||||
};
|
||||
|
@ -184,22 +176,21 @@ mod tests {
|
|||
|
||||
use super::{Endpoint, Recognize};
|
||||
use proxy::http::router::Recognize as _Recognize;
|
||||
use proxy::http::settings::{Host, Settings};
|
||||
use proxy::http::settings::Settings;
|
||||
use proxy::server::Source;
|
||||
use transport::tls;
|
||||
use Conditional;
|
||||
|
||||
fn make_h1_endpoint(addr: net::SocketAddr) -> Endpoint {
|
||||
let settings = Settings::Http1 {
|
||||
host: Host::NoAuthority,
|
||||
is_h1_upgrade: false,
|
||||
was_absolute_form: false,
|
||||
stack_per_request: true,
|
||||
};
|
||||
let authority = http::uri::Authority::from_shared(format!("{}", addr).into()).unwrap();
|
||||
let source_tls_status = TLS_DISABLED;
|
||||
Endpoint {
|
||||
addr,
|
||||
authority,
|
||||
dst_name: None,
|
||||
settings,
|
||||
source_tls_status,
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
use http::uri;
|
||||
use std::{
|
||||
fmt::{self, Write},
|
||||
net,
|
||||
|
@ -7,7 +6,7 @@ use std::{
|
|||
use metrics::FmtLabels;
|
||||
|
||||
use transport::tls;
|
||||
use Conditional;
|
||||
use {Conditional, NameAddr};
|
||||
|
||||
use super::{classify, inbound, outbound};
|
||||
|
||||
|
@ -16,7 +15,7 @@ pub struct EndpointLabels {
|
|||
addr: net::SocketAddr,
|
||||
direction: Direction,
|
||||
tls_status: tls::Status,
|
||||
authority: Authority,
|
||||
dst_name: Option<NameAddr>,
|
||||
labels: Option<String>,
|
||||
}
|
||||
|
||||
|
@ -34,7 +33,7 @@ enum Direction {
|
|||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
struct Authority(Option<uri::Authority>);
|
||||
struct Authority<'a>(&'a NameAddr);
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
struct Dst(outbound::Destination);
|
||||
|
@ -69,7 +68,7 @@ impl From<inbound::Endpoint> for EndpointLabels {
|
|||
fn from(ep: inbound::Endpoint) -> Self {
|
||||
Self {
|
||||
addr: ep.addr,
|
||||
authority: Authority(Some(ep.authority)),
|
||||
dst_name: ep.dst_name,
|
||||
direction: Direction::In,
|
||||
tls_status: ep.source_tls_status,
|
||||
labels: None,
|
||||
|
@ -92,26 +91,9 @@ where
|
|||
|
||||
impl From<outbound::Endpoint> for EndpointLabels {
|
||||
fn from(ep: outbound::Endpoint) -> Self {
|
||||
use self::outbound::NameOrAddr;
|
||||
use transport::DnsNameAndPort;
|
||||
|
||||
let authority = {
|
||||
let a = match ep.dst.name_or_addr {
|
||||
NameOrAddr::Name(DnsNameAndPort { ref host, ref port }) => {
|
||||
if *port == 80 {
|
||||
format!("{}", host)
|
||||
} else {
|
||||
format!("{}:{}", host, port)
|
||||
}
|
||||
}
|
||||
NameOrAddr::Addr(addr) => format!("{}", addr),
|
||||
};
|
||||
Authority(uri::Authority::from_shared(a.into()).ok())
|
||||
};
|
||||
|
||||
Self {
|
||||
addr: ep.connect.addr,
|
||||
authority,
|
||||
dst_name: ep.dst.addr.into_name_addr(),
|
||||
direction: Direction::Out,
|
||||
tls_status: ep.connect.tls_status(),
|
||||
labels: prefix_labels("dst", ep.metadata.labels().into_iter()),
|
||||
|
@ -121,7 +103,8 @@ impl From<outbound::Endpoint> for EndpointLabels {
|
|||
|
||||
impl FmtLabels for EndpointLabels {
|
||||
fn fmt_labels(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
(&self.authority, &self.direction).fmt_labels(f)?;
|
||||
let authority = self.dst_name.as_ref().map(Authority);
|
||||
(authority, &self.direction).fmt_labels(f)?;
|
||||
|
||||
if let Some(labels) = self.labels.as_ref() {
|
||||
write!(f, ",{}", labels)?;
|
||||
|
@ -143,11 +126,11 @@ impl FmtLabels for Direction {
|
|||
}
|
||||
}
|
||||
|
||||
impl FmtLabels for Authority {
|
||||
impl<'a> FmtLabels for Authority<'a> {
|
||||
fn fmt_labels(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self.0 {
|
||||
Some(ref a) => write!(f, "authority=\"{}\"", a),
|
||||
None => write!(f, "authority=\"\""),
|
||||
match self.0.port() {
|
||||
80 => write!(f, "authority=\"{}\"", self.0.name()),
|
||||
_ => write!(f, "authority=\"{}\"", self.0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -159,11 +142,7 @@ impl FmtLabels for Dst {
|
|||
} else {
|
||||
"h1"
|
||||
};
|
||||
write!(
|
||||
f,
|
||||
"dst=\"{}\",dst_protocol=\"{}\"",
|
||||
self.0.name_or_addr, proto
|
||||
)?;
|
||||
write!(f, "dst=\"{}\",dst_protocol=\"{}\"", self.0.addr, proto)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
//! Configures and runs the linkerd2 service sidecar proxy
|
||||
|
||||
use convert::TryFrom;
|
||||
use logging;
|
||||
use http;
|
||||
|
||||
mod classify;
|
||||
pub mod config;
|
||||
|
@ -12,11 +11,35 @@ mod metric_labels;
|
|||
mod outbound;
|
||||
mod profiles;
|
||||
|
||||
use self::config::{Config, Env};
|
||||
pub use self::main::Main;
|
||||
use addr::{self, Addr};
|
||||
|
||||
pub fn init() -> Result<config::Config, config::Error> {
|
||||
use convert::TryFrom;
|
||||
use logging;
|
||||
|
||||
pub fn init() -> Result<Config, config::Error> {
|
||||
logging::init();
|
||||
let config_strings = Env;
|
||||
Config::try_from(&config_strings)
|
||||
config::Config::try_from(&config::Env)
|
||||
}
|
||||
|
||||
fn http_request_addr<B>(req: &http::Request<B>) -> Result<Addr, addr::Error> {
|
||||
use proxy::{http::h1, Source};
|
||||
const DEFAULT_PORT: u16 = 80;
|
||||
|
||||
req.uri()
|
||||
.authority_part()
|
||||
.ok_or(addr::Error::InvalidHost)
|
||||
.and_then(|a| Addr::from_authority_and_default_port(a, DEFAULT_PORT))
|
||||
.or_else(|_| {
|
||||
h1::authority_from_host(req)
|
||||
.ok_or(addr::Error::InvalidHost)
|
||||
.and_then(|a| Addr::from_authority_and_default_port(&a, DEFAULT_PORT))
|
||||
})
|
||||
.or_else(|e| {
|
||||
req.extensions()
|
||||
.get::<Source>()
|
||||
.and_then(|src| src.orig_dst_if_not_local())
|
||||
.map(Addr::Socket)
|
||||
.ok_or(e)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,46 +1,31 @@
|
|||
use http;
|
||||
use std::fmt;
|
||||
use std::net::SocketAddr;
|
||||
|
||||
use app::classify;
|
||||
use control::destination::{Metadata, ProtocolHint};
|
||||
use proxy::{
|
||||
http::{
|
||||
classify::CanClassify,
|
||||
client, h1,
|
||||
normalize_uri::ShouldNormalizeUri,
|
||||
profiles::{self, CanGetDestination},
|
||||
router, Settings,
|
||||
},
|
||||
Source,
|
||||
use proxy::http::{
|
||||
classify::CanClassify,
|
||||
client,
|
||||
normalize_uri::ShouldNormalizeUri,
|
||||
profiles::{self, CanGetDestination},
|
||||
router, Settings,
|
||||
};
|
||||
use svc::{self, stack_per_request::ShouldStackPerRequest};
|
||||
use tap;
|
||||
use transport::{connect, tls, DnsNameAndPort, Host, HostAndPort};
|
||||
use transport::{connect, tls};
|
||||
use {Addr, NameAddr};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Endpoint {
|
||||
pub dst: Destination,
|
||||
pub connect: connect::Target,
|
||||
pub metadata: Metadata,
|
||||
_p: (),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct Destination {
|
||||
pub name_or_addr: NameOrAddr,
|
||||
pub addr: Addr,
|
||||
pub settings: Settings,
|
||||
_p: (),
|
||||
}
|
||||
|
||||
/// Describes a destination for HTTP requests.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum NameOrAddr {
|
||||
/// A logical, lazily-bound endpoint.
|
||||
Name(DnsNameAndPort),
|
||||
|
||||
/// A single, bound endpoint.
|
||||
Addr(SocketAddr),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
@ -50,7 +35,7 @@ pub struct Route {
|
|||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct Recognize {}
|
||||
pub struct Recognize;
|
||||
|
||||
// === impl Endpoint ===
|
||||
|
||||
|
@ -136,34 +121,26 @@ impl<B> router::Recognize<http::Request<B>> for Recognize {
|
|||
type Target = Destination;
|
||||
|
||||
fn recognize(&self, req: &http::Request<B>) -> Option<Self::Target> {
|
||||
let dst = Destination::from_request(req);
|
||||
let addr = super::http_request_addr(req).ok()?;
|
||||
let settings = Settings::from_request(req);
|
||||
let dst = Destination::new(addr, settings);
|
||||
debug!("recognize: dst={:?}", dst);
|
||||
dst
|
||||
Some(dst)
|
||||
}
|
||||
}
|
||||
|
||||
// === impl Destination ===
|
||||
|
||||
impl Destination {
|
||||
pub fn new(name_or_addr: NameOrAddr, settings: Settings) -> Self {
|
||||
Self {
|
||||
name_or_addr,
|
||||
settings,
|
||||
_p: (),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_request<A>(req: &http::Request<A>) -> Option<Self> {
|
||||
let name_or_addr = NameOrAddr::from_request(req)?;
|
||||
let settings = Settings::detect(req);
|
||||
Some(Self::new(name_or_addr, settings))
|
||||
pub fn new(addr: Addr, settings: Settings) -> Self {
|
||||
Self { addr, settings }
|
||||
}
|
||||
}
|
||||
|
||||
impl CanGetDestination for Destination {
|
||||
fn get_destination(&self) -> Option<&DnsNameAndPort> {
|
||||
match self.name_or_addr {
|
||||
NameOrAddr::Name(ref dst) => Some(dst),
|
||||
fn get_destination(&self) -> Option<&NameAddr> {
|
||||
match self.addr {
|
||||
Addr::Name(ref name) => Some(name),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -171,78 +148,7 @@ impl CanGetDestination for Destination {
|
|||
|
||||
impl fmt::Display for Destination {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.name_or_addr.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for NameOrAddr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
NameOrAddr::Name(ref name) => write!(f, "{}:{}", name.host, name.port),
|
||||
NameOrAddr::Addr(ref addr) => addr.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NameOrAddr {
|
||||
/// Determines the destination for a request.
|
||||
///
|
||||
/// Typically, a request's authority is used to produce a `NameOrAddr`. If the
|
||||
/// authority addresses a DNS name, a `NameOrAddr::Name` is returned; and, otherwise,
|
||||
/// it addresses a fixed IP address and a `NameOrAddr::Addr` is returned. The port is
|
||||
/// inferred if not specified in the authority.
|
||||
///
|
||||
/// If no authority is available, the `SO_ORIGINAL_DST` socket option is checked. If
|
||||
/// it's available, it is used to return a `NameOrAddr::Addr`. This socket option is
|
||||
/// typically set by `iptables(8)` in containerized environments like Kubernetes (as
|
||||
/// configured by the `proxy-init` program).
|
||||
///
|
||||
/// If none of this information is available, no `NameOrAddr` is returned.
|
||||
pub fn from_request<B>(req: &http::Request<B>) -> Option<NameOrAddr> {
|
||||
match Self::host_port(req) {
|
||||
Some(HostAndPort {
|
||||
host: Host::DnsName(host),
|
||||
port,
|
||||
}) => {
|
||||
let name_or_addr = DnsNameAndPort { host, port };
|
||||
Some(NameOrAddr::Name(name_or_addr))
|
||||
}
|
||||
|
||||
Some(HostAndPort {
|
||||
host: Host::Ip(ip),
|
||||
port,
|
||||
}) => {
|
||||
let name_or_addr = SocketAddr::from((ip, port));
|
||||
Some(NameOrAddr::Addr(name_or_addr))
|
||||
}
|
||||
|
||||
None => req
|
||||
.extensions()
|
||||
.get::<Source>()
|
||||
.and_then(|src| src.orig_dst_if_not_local())
|
||||
.map(NameOrAddr::Addr),
|
||||
}
|
||||
}
|
||||
|
||||
/// Determines the logical host:port of the request.
|
||||
///
|
||||
/// If the parsed URI includes an authority, use that. Otherwise, try to load the
|
||||
/// authority from the `Host` header.
|
||||
///
|
||||
/// The port is either parsed from the authority or a default of 80 is used.
|
||||
fn host_port<B>(req: &http::Request<B>) -> Option<HostAndPort> {
|
||||
// Note: Calls to `normalize` cannot be deduped without cloning `authority`.
|
||||
req.uri()
|
||||
.authority_part()
|
||||
.and_then(Self::normalize)
|
||||
.or_else(|| h1::authority_from_host(req).and_then(|h| Self::normalize(&h)))
|
||||
}
|
||||
|
||||
/// TODO: Return error when `HostAndPort::normalize()` fails.
|
||||
/// TODO: Use scheme-appropriate default port.
|
||||
fn normalize(authority: &http::uri::Authority) -> Option<HostAndPort> {
|
||||
const DEFAULT_PORT: Option<u16> = Some(80);
|
||||
HostAndPort::normalize(authority, DEFAULT_PORT).ok()
|
||||
self.addr.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -258,14 +164,14 @@ pub mod discovery {
|
|||
use futures::{Async, Poll};
|
||||
use std::net::SocketAddr;
|
||||
|
||||
use super::{Destination, Endpoint, NameOrAddr};
|
||||
use super::{Destination, Endpoint};
|
||||
use control::destination::Metadata;
|
||||
use proxy::resolve;
|
||||
use transport::{connect, tls, DnsNameAndPort};
|
||||
use Conditional;
|
||||
use transport::{connect, tls};
|
||||
use {Addr, Conditional, NameAddr};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Resolve<R: resolve::Resolve<DnsNameAndPort>>(R);
|
||||
pub struct Resolve<R: resolve::Resolve<NameAddr>>(R);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Resolution<R: resolve::Resolution> {
|
||||
|
@ -277,7 +183,7 @@ pub mod discovery {
|
|||
|
||||
impl<R> Resolve<R>
|
||||
where
|
||||
R: resolve::Resolve<DnsNameAndPort, Endpoint = Metadata>,
|
||||
R: resolve::Resolve<NameAddr, Endpoint = Metadata>,
|
||||
{
|
||||
pub fn new(resolve: R) -> Self {
|
||||
Resolve(resolve)
|
||||
|
@ -286,15 +192,15 @@ pub mod discovery {
|
|||
|
||||
impl<R> resolve::Resolve<Destination> for Resolve<R>
|
||||
where
|
||||
R: resolve::Resolve<DnsNameAndPort, Endpoint = Metadata>,
|
||||
R: resolve::Resolve<NameAddr, Endpoint = Metadata>,
|
||||
{
|
||||
type Endpoint = Endpoint;
|
||||
type Resolution = Resolution<R::Resolution>;
|
||||
|
||||
fn resolve(&self, dst: &Destination) -> Self::Resolution {
|
||||
match dst.name_or_addr {
|
||||
NameOrAddr::Name(ref name) => Resolution::Name(dst.clone(), self.0.resolve(&name)),
|
||||
NameOrAddr::Addr(ref addr) => Resolution::Addr(dst.clone(), Some(*addr)),
|
||||
match dst.addr {
|
||||
Addr::Name(ref name) => Resolution::Name(dst.clone(), self.0.resolve(&name)),
|
||||
Addr::Socket(ref addr) => Resolution::Addr(dst.clone(), Some(*addr)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -327,7 +233,6 @@ pub mod discovery {
|
|||
dst: dst.clone(),
|
||||
connect: connect::Target::new(addr, Conditional::None(tls)),
|
||||
metadata,
|
||||
_p: (),
|
||||
};
|
||||
Ok(Async::Ready(resolve::Update::Add(addr, ep)))
|
||||
}
|
||||
|
@ -339,7 +244,6 @@ pub mod discovery {
|
|||
dst: dst.clone(),
|
||||
connect: connect::Target::new(addr, Conditional::None(tls.into())),
|
||||
metadata: Metadata::none(tls),
|
||||
_p: (),
|
||||
};
|
||||
let up = resolve::Update::Add(addr, ep);
|
||||
Ok(Async::Ready(up))
|
||||
|
|
|
@ -11,7 +11,7 @@ use api::destination as api;
|
|||
|
||||
use control;
|
||||
use proxy::http::profiles;
|
||||
use transport::DnsNameAndPort;
|
||||
use NameAddr;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Client<T, N> {
|
||||
|
@ -61,7 +61,7 @@ where
|
|||
{
|
||||
type Stream = Rx<T>;
|
||||
|
||||
fn get_routes(&self, dst: &DnsNameAndPort) -> Option<Self::Stream> {
|
||||
fn get_routes(&self, dst: &NameAddr) -> Option<Self::Stream> {
|
||||
let fqa = self.normalize_name.normalize(dst)?;
|
||||
Some(Rx {
|
||||
dst: fqa.without_trailing_dot().to_owned(),
|
||||
|
|
|
@ -1,72 +1,11 @@
|
|||
use std;
|
||||
|
||||
/// Like `std::option::Option<C>` but `None` carries a reason why the value
|
||||
/// isn't available.
|
||||
#[derive(Clone)]
|
||||
pub enum Conditional<C, R>
|
||||
where
|
||||
C: Clone,
|
||||
R: Clone,
|
||||
{
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
pub enum Conditional<C, R> {
|
||||
Some(C),
|
||||
None(R),
|
||||
}
|
||||
|
||||
impl<C, R> Copy for Conditional<C, R>
|
||||
where
|
||||
C: Copy + Clone + std::fmt::Debug,
|
||||
R: Copy + Clone + std::fmt::Debug,
|
||||
{
|
||||
}
|
||||
|
||||
impl<C, R> std::fmt::Debug for Conditional<C, R>
|
||||
where
|
||||
C: Clone + std::fmt::Debug,
|
||||
R: Clone + std::fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
|
||||
match self {
|
||||
Conditional::Some(s) => f.debug_tuple("Some").field(s).finish(),
|
||||
Conditional::None(r) => f.debug_tuple("None").field(r).finish(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<C, R> Eq for Conditional<C, R>
|
||||
where
|
||||
C: Eq + Clone,
|
||||
R: Eq + Clone,
|
||||
{
|
||||
}
|
||||
|
||||
impl<C, R> PartialEq for Conditional<C, R>
|
||||
where
|
||||
C: PartialEq + Clone,
|
||||
R: PartialEq + Clone,
|
||||
{
|
||||
fn eq(&self, other: &Conditional<C, R>) -> bool {
|
||||
use self::Conditional::*;
|
||||
match (self, other) {
|
||||
(Some(a), Some(b)) => a.eq(b),
|
||||
(None(a), None(b)) => a.eq(b),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<C, R> std::hash::Hash for Conditional<C, R>
|
||||
where
|
||||
C: std::hash::Hash + Clone,
|
||||
R: std::hash::Hash + Clone,
|
||||
{
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
match self {
|
||||
Conditional::Some(c) => c.hash(state),
|
||||
Conditional::None(r) => r.hash(state),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<C, R> Conditional<C, R>
|
||||
where
|
||||
C: Clone,
|
||||
|
|
|
@ -25,8 +25,8 @@ use control::{
|
|||
remote_stream::Remote,
|
||||
};
|
||||
use dns::{self, IpAddrListFuture};
|
||||
use transport::{tls, DnsNameAndPort};
|
||||
use Conditional;
|
||||
use transport::tls;
|
||||
use {Conditional, NameAddr};
|
||||
|
||||
use super::{ActiveQuery, DestinationServiceQuery, UpdateRx};
|
||||
|
||||
|
@ -50,15 +50,15 @@ where
|
|||
&mut self,
|
||||
dns_resolver: &dns::Resolver,
|
||||
deadline: Instant,
|
||||
authority: &DnsNameAndPort,
|
||||
authority: &NameAddr,
|
||||
) {
|
||||
trace!(
|
||||
"resetting DNS query for {} at {:?}",
|
||||
authority.host,
|
||||
authority.name(),
|
||||
deadline
|
||||
);
|
||||
self.reset_on_next_modification();
|
||||
self.dns_query = Some(dns_resolver.resolve_all_ips(deadline, &authority.host));
|
||||
self.dns_query = Some(dns_resolver.resolve_all_ips(deadline, authority.name()));
|
||||
}
|
||||
|
||||
// Processes Destination service updates from `request_rx`, returning the new query
|
||||
|
@ -67,7 +67,7 @@ where
|
|||
// "no change in existence" instead of "unknown".
|
||||
pub(super) fn poll_destination_service(
|
||||
&mut self,
|
||||
auth: &DnsNameAndPort,
|
||||
auth: &NameAddr,
|
||||
mut rx: UpdateRx<T>,
|
||||
tls_controller_namespace: Option<&str>,
|
||||
) -> (ActiveQuery<T>, Exists<()>) {
|
||||
|
@ -123,7 +123,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
pub(super) fn poll_dns(&mut self, dns_resolver: &dns::Resolver, authority: &DnsNameAndPort) {
|
||||
pub(super) fn poll_dns(&mut self, dns_resolver: &dns::Resolver, authority: &NameAddr) {
|
||||
// Duration to wait before polling DNS again after an error
|
||||
// (or a NXDOMAIN response with no TTL).
|
||||
const DNS_ERROR_TTL: Duration = Duration::from_secs(5);
|
||||
|
@ -147,7 +147,7 @@ where
|
|||
authority,
|
||||
ips.iter().map(|ip| {
|
||||
(
|
||||
SocketAddr::from((ip, authority.port)),
|
||||
SocketAddr::from((ip, authority.port())),
|
||||
Metadata::none(tls::ReasonForNoIdentity::NotProvidedByServiceDiscovery),
|
||||
)
|
||||
}),
|
||||
|
@ -169,7 +169,7 @@ where
|
|||
Err(e) => {
|
||||
// Do nothing so that the most recent non-error response is used until a
|
||||
// non-error response is received
|
||||
trace!("DNS resolution failed for {}: {}", &authority.host, e);
|
||||
trace!("DNS resolution failed for {}: {}", authority.name(), e);
|
||||
|
||||
// Poll again after the default wait time.
|
||||
Instant::now() + DNS_ERROR_TTL
|
||||
|
@ -200,7 +200,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
fn add<A>(&mut self, authority_for_logging: &DnsNameAndPort, addrs_to_add: A)
|
||||
fn add<A>(&mut self, authority_for_logging: &NameAddr, addrs_to_add: A)
|
||||
where
|
||||
A: Iterator<Item = (SocketAddr, Metadata)>,
|
||||
{
|
||||
|
@ -214,7 +214,7 @@ where
|
|||
self.addrs = Exists::Yes(cache);
|
||||
}
|
||||
|
||||
fn remove<A>(&mut self, authority_for_logging: &DnsNameAndPort, addrs_to_remove: A)
|
||||
fn remove<A>(&mut self, authority_for_logging: &NameAddr, addrs_to_remove: A)
|
||||
where
|
||||
A: Iterator<Item = SocketAddr>,
|
||||
{
|
||||
|
@ -230,7 +230,7 @@ where
|
|||
self.addrs = Exists::Yes(cache);
|
||||
}
|
||||
|
||||
fn no_endpoints(&mut self, authority_for_logging: &DnsNameAndPort, exists: bool) {
|
||||
fn no_endpoints(&mut self, authority_for_logging: &NameAddr, exists: bool) {
|
||||
trace!(
|
||||
"no endpoints for {:?} that is known to {}",
|
||||
authority_for_logging,
|
||||
|
@ -253,7 +253,7 @@ where
|
|||
|
||||
fn on_change(
|
||||
responders: &mut Vec<Responder>,
|
||||
authority_for_logging: &DnsNameAndPort,
|
||||
authority_for_logging: &NameAddr,
|
||||
change: CacheChange<SocketAddr, Metadata>,
|
||||
) {
|
||||
let (update_str, update, addr) = match change {
|
||||
|
|
|
@ -29,7 +29,7 @@ use control::{
|
|||
remote_stream::{Receiver, Remote},
|
||||
};
|
||||
use dns;
|
||||
use transport::DnsNameAndPort;
|
||||
use NameAddr;
|
||||
|
||||
mod destination_set;
|
||||
|
||||
|
@ -59,9 +59,9 @@ pub(super) struct Background<T: HttpService> {
|
|||
/// which require reconnects.
|
||||
#[derive(Default)]
|
||||
struct DestinationCache<T: HttpService> {
|
||||
destinations: HashMap<DnsNameAndPort, DestinationSet<T>>,
|
||||
destinations: HashMap<NameAddr, DestinationSet<T>>,
|
||||
/// A queue of authorities that need to be reconnected.
|
||||
reconnects: VecDeque<DnsNameAndPort>,
|
||||
reconnects: VecDeque<NameAddr>,
|
||||
}
|
||||
|
||||
/// The configurationn necessary to create a new Destination service
|
||||
|
@ -338,7 +338,7 @@ impl NewQuery {
|
|||
fn query_destination_service_if_relevant<T>(
|
||||
&self,
|
||||
client: Option<&mut T>,
|
||||
auth: &DnsNameAndPort,
|
||||
auth: &NameAddr,
|
||||
connect_or_reconnect: &str,
|
||||
) -> DestinationServiceQuery<T>
|
||||
where
|
||||
|
@ -415,7 +415,7 @@ where
|
|||
/// Returns true if `auth` is currently known to need a Destination
|
||||
/// service query, but was unable to query previously due to the query
|
||||
/// limit being reached.
|
||||
fn needs_query_for(&self, auth: &DnsNameAndPort) -> bool {
|
||||
fn needs_query_for(&self, auth: &NameAddr) -> bool {
|
||||
self.destinations
|
||||
.get(auth)
|
||||
.map(|dst| dst.needs_query_capacity())
|
||||
|
|
|
@ -40,13 +40,12 @@ use tower_h2::{Body, BoxBody, Data, HttpService};
|
|||
use dns;
|
||||
use transport::tls;
|
||||
use proxy::resolve::{self, Resolve, Update};
|
||||
use transport::DnsNameAndPort;
|
||||
|
||||
pub mod background;
|
||||
|
||||
use app::config::Namespaces;
|
||||
use self::background::Background;
|
||||
use Conditional;
|
||||
use {Conditional, NameAddr};
|
||||
|
||||
/// A handle to request resolutions from the background discovery task.
|
||||
#[derive(Clone)]
|
||||
|
@ -57,7 +56,7 @@ pub struct Resolver {
|
|||
/// Requests that resolution updaes for `authority` be sent on `responder`.
|
||||
#[derive(Debug)]
|
||||
struct ResolveRequest {
|
||||
authority: DnsNameAndPort,
|
||||
authority: NameAddr,
|
||||
responder: Responder,
|
||||
}
|
||||
|
||||
|
@ -136,12 +135,12 @@ where
|
|||
|
||||
// ==== impl Resolver =====
|
||||
|
||||
impl Resolve<DnsNameAndPort> for Resolver {
|
||||
impl Resolve<NameAddr> for Resolver {
|
||||
type Endpoint = Metadata;
|
||||
type Resolution = Resolution;
|
||||
|
||||
/// Start watching for address changes for a certain authority.
|
||||
fn resolve(&self, authority: &DnsNameAndPort) -> Resolution {
|
||||
fn resolve(&self, authority: &NameAddr) -> Resolution {
|
||||
trace!("resolve; authority={:?}", authority);
|
||||
let (update_tx, update_rx) = mpsc::unbounded();
|
||||
let active = Arc::new(());
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use bytes::{BytesMut};
|
||||
|
||||
use transport::DnsNameAndPort;
|
||||
use NameAddr;
|
||||
|
||||
pub trait Normalize {
|
||||
fn normalize(&self, authority: &DnsNameAndPort) -> Option<FullyQualifiedAuthority>;
|
||||
fn normalize(&self, authority: &NameAddr) -> Option<FullyQualifiedAuthority>;
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
@ -24,8 +24,8 @@ impl KubernetesNormalize {
|
|||
impl Normalize for KubernetesNormalize {
|
||||
/// Normalizes the name according to Kubernetes service naming conventions.
|
||||
/// Case folding is not done; that is done internally inside `Authority`.
|
||||
fn normalize(&self, authority: &DnsNameAndPort) -> Option<FullyQualifiedAuthority> {
|
||||
let name: &str = authority.host.as_ref();
|
||||
fn normalize(&self, authority: &NameAddr) -> Option<FullyQualifiedAuthority> {
|
||||
let name: &str = authority.name().as_ref();
|
||||
|
||||
// parts should have a maximum 4 of pieces (name, namespace, svc, zone)
|
||||
let mut parts = name.splitn(4, '.');
|
||||
|
@ -95,7 +95,7 @@ impl Normalize for KubernetesNormalize {
|
|||
additional_len += 1 + zone.len(); // "." + zone
|
||||
}
|
||||
|
||||
let port_str_len = match authority.port {
|
||||
let port_str_len = match authority.port() {
|
||||
80 => 0, // XXX: Assumes http://, which is all we support right now.
|
||||
p if p >= 10000 => 1 + 5,
|
||||
p if p >= 1000 => 1 + 4,
|
||||
|
@ -126,7 +126,7 @@ impl Normalize for KubernetesNormalize {
|
|||
// Append the port
|
||||
if port_str_len > 0 {
|
||||
normalized.extend_from_slice(b":");
|
||||
let port = authority.port.to_string();
|
||||
let port = authority.port().to_string();
|
||||
normalized.extend_from_slice(port.as_ref());
|
||||
}
|
||||
|
||||
|
@ -142,19 +142,18 @@ impl FullyQualifiedAuthority {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use transport::{DnsNameAndPort, Host, HostAndPort};
|
||||
use http::uri::Authority;
|
||||
use std::str::FromStr;
|
||||
|
||||
use {Addr, NameAddr};
|
||||
use super::Normalize;
|
||||
|
||||
#[test]
|
||||
fn test_normalized_authority() {
|
||||
fn dns_name_and_port_from_str(input: &str) -> DnsNameAndPort {
|
||||
fn dns_name_and_port_from_str(input: &str) -> NameAddr {
|
||||
let authority = Authority::from_str(input).unwrap();
|
||||
match HostAndPort::normalize(&authority, Some(80)) {
|
||||
Ok(HostAndPort { host: Host::DnsName(host), port }) =>
|
||||
DnsNameAndPort { host, port },
|
||||
match Addr::from_authority_and_default_port(&authority, 80) {
|
||||
Ok(Addr::Name(name)) => name,
|
||||
Err(e) => {
|
||||
unreachable!("{:?} when parsing {:?}", e, input)
|
||||
},
|
||||
|
|
12
src/dns.rs
12
src/dns.rs
|
@ -12,7 +12,8 @@ use trust_dns_resolver::{
|
|||
};
|
||||
|
||||
use app::config::Config;
|
||||
use transport::{self, tls};
|
||||
use transport::tls;
|
||||
use Addr;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Resolver {
|
||||
|
@ -98,14 +99,15 @@ impl Resolver {
|
|||
(resolver, background)
|
||||
}
|
||||
|
||||
pub fn resolve_one_ip(&self, host: &transport::Host) -> IpAddrFuture {
|
||||
match *host {
|
||||
transport::Host::DnsName(ref name) => {
|
||||
pub fn resolve_one_ip(&self, host: &Addr) -> IpAddrFuture {
|
||||
match host {
|
||||
Addr::Name(n) => {
|
||||
let name = n.name();
|
||||
let ctx = ResolveOneCtx(name.clone());
|
||||
let f = ::logging::context_future(ctx, self.lookup_ip(name));
|
||||
IpAddrFuture::DNS(Box::new(f))
|
||||
}
|
||||
transport::Host::Ip(addr) => IpAddrFuture::Fixed(addr),
|
||||
Addr::Socket(addr) => IpAddrFuture::Fixed(addr.ip()),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -51,6 +51,7 @@ extern crate linkerd2_timeout as timeout;
|
|||
// `linkerd2_metrics` is needed to satisfy the macro, but this is nicer to use internally.
|
||||
use self::linkerd2_metrics as metrics;
|
||||
|
||||
mod addr;
|
||||
pub mod app;
|
||||
mod conditional;
|
||||
pub mod control;
|
||||
|
@ -65,5 +66,6 @@ mod tap;
|
|||
pub mod telemetry;
|
||||
pub mod transport;
|
||||
|
||||
use self::addr::{Addr, NameAddr};
|
||||
use self::conditional::Conditional;
|
||||
pub use self::transport::SoOriginalDst;
|
||||
|
|
|
@ -10,7 +10,7 @@ use std::iter::FromIterator;
|
|||
use std::sync::Arc;
|
||||
use std::{error, fmt};
|
||||
|
||||
use transport::DnsNameAndPort;
|
||||
use NameAddr;
|
||||
|
||||
pub type Routes = Vec<(RequestMatch, Route)>;
|
||||
|
||||
|
@ -21,7 +21,7 @@ pub type Routes = Vec<(RequestMatch, Route)>;
|
|||
pub trait GetRoutes {
|
||||
type Stream: Stream<Item = Routes, Error = Error>;
|
||||
|
||||
fn get_routes(&self, dst: &DnsNameAndPort) -> Option<Self::Stream>;
|
||||
fn get_routes(&self, dst: &NameAddr) -> Option<Self::Stream>;
|
||||
}
|
||||
|
||||
/// Implemented by target types that may be combined with a Route.
|
||||
|
@ -31,10 +31,10 @@ pub trait WithRoute {
|
|||
fn with_route(self, route: Route) -> Self::Output;
|
||||
}
|
||||
|
||||
/// Implemented by target types that may have a `DnsNameAndPort` destination that
|
||||
/// Implemented by target types that may have a `NameAddr` destination that
|
||||
/// can be discovered via `GetRoutes`.
|
||||
pub trait CanGetDestination {
|
||||
fn get_destination(&self) -> Option<&DnsNameAndPort>;
|
||||
fn get_destination(&self) -> Option<&NameAddr>;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
use http::{self, uri};
|
||||
|
||||
use super::h1;
|
||||
use http::{self, header::HOST};
|
||||
|
||||
/// Settings portion of the `Recognize` key for a request.
|
||||
///
|
||||
|
@ -10,7 +8,8 @@ use super::h1;
|
|||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum Settings {
|
||||
Http1 {
|
||||
host: Host,
|
||||
/// Indicates whether a new service must be created for each request.
|
||||
stack_per_request: bool,
|
||||
/// Whether the request wants to use HTTP/1.1's Upgrade mechanism.
|
||||
///
|
||||
/// Since these cannot be translated into orig-proto, it must be
|
||||
|
@ -26,79 +25,66 @@ pub enum Settings {
|
|||
/// used to determine what URI normalization will be necessary.
|
||||
was_absolute_form: bool,
|
||||
},
|
||||
Http2
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum Host {
|
||||
Authority(uri::Authority),
|
||||
NoAuthority,
|
||||
Http2,
|
||||
}
|
||||
|
||||
// ===== impl Settings =====
|
||||
|
||||
impl Settings {
|
||||
pub fn detect<B>(req: &http::Request<B>) -> Self {
|
||||
pub fn from_request<B>(req: &http::Request<B>) -> Self {
|
||||
if req.version() == http::Version::HTTP_2 {
|
||||
return Settings::Http2;
|
||||
}
|
||||
|
||||
let was_absolute_form = super::h1::is_absolute_form(req.uri());
|
||||
trace!(
|
||||
"Settings::detect(); req.uri='{:?}'; was_absolute_form={:?};",
|
||||
req.uri(), was_absolute_form
|
||||
);
|
||||
// If the request has an authority part, use that as the host part of
|
||||
// the key for an HTTP/1.x request.
|
||||
let host = Host::detect(req);
|
||||
|
||||
let is_h1_upgrade = super::h1::wants_upgrade(req);
|
||||
let is_missing_authority = req
|
||||
.uri()
|
||||
.authority_part()
|
||||
.map(|_| false)
|
||||
.or_else(|| {
|
||||
req.headers()
|
||||
.get(HOST)
|
||||
.and_then(|h| h.to_str().ok())
|
||||
.map(|h| h.is_empty())
|
||||
})
|
||||
.unwrap_or(true);
|
||||
|
||||
Settings::Http1 {
|
||||
host,
|
||||
is_h1_upgrade,
|
||||
was_absolute_form,
|
||||
was_absolute_form: super::h1::is_absolute_form(req.uri()),
|
||||
is_h1_upgrade: super::h1::wants_upgrade(req),
|
||||
stack_per_request: is_missing_authority,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if the request was originally received in absolute form.
|
||||
pub fn was_absolute_form(&self) -> bool {
|
||||
match self {
|
||||
&Settings::Http1 { was_absolute_form, .. } => was_absolute_form,
|
||||
_ => false,
|
||||
Settings::Http1 {
|
||||
was_absolute_form, ..
|
||||
} => *was_absolute_form,
|
||||
Settings::Http2 => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn can_reuse_clients(&self) -> bool {
|
||||
match *self {
|
||||
Settings::Http2 | Settings::Http1 { host: Host::Authority(_), .. } => true,
|
||||
_ => false,
|
||||
match self {
|
||||
Settings::Http1 {
|
||||
stack_per_request, ..
|
||||
} => !stack_per_request,
|
||||
Settings::Http2 => true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_h1_upgrade(&self) -> bool {
|
||||
match *self {
|
||||
Settings::Http1 { is_h1_upgrade: true, .. } => true,
|
||||
_ => false,
|
||||
match self {
|
||||
Settings::Http1 { is_h1_upgrade, .. } => *is_h1_upgrade,
|
||||
Settings::Http2 => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_http2(&self) -> bool {
|
||||
match *self {
|
||||
match self {
|
||||
Settings::Http1 { .. } => false,
|
||||
Settings::Http2 => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Host {
|
||||
pub fn detect<B>(req: &http::Request<B>) -> Host {
|
||||
req
|
||||
.uri()
|
||||
.authority_part()
|
||||
.cloned()
|
||||
.or_else(|| h1::authority_from_host(req))
|
||||
.map(Host::Authority)
|
||||
.unwrap_or_else(|| Host::NoAuthority)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,13 +2,9 @@ extern crate tokio_connect;
|
|||
|
||||
pub use self::tokio_connect::Connect;
|
||||
|
||||
use http;
|
||||
use std::{error, fmt, io};
|
||||
use std::net::{IpAddr, SocketAddr};
|
||||
use std::str::FromStr;
|
||||
use std::net::SocketAddr;
|
||||
|
||||
use convert::TryFrom;
|
||||
use dns;
|
||||
use svc;
|
||||
use transport::{connection, tls};
|
||||
|
||||
|
@ -26,86 +22,6 @@ pub struct Target {
|
|||
#[derive(Debug)]
|
||||
pub struct InvalidTarget;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct HostAndPort {
|
||||
pub host: Host,
|
||||
pub port: u16,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
||||
pub struct DnsNameAndPort {
|
||||
pub host: dns::Name,
|
||||
pub port: u16,
|
||||
}
|
||||
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Host {
|
||||
DnsName(dns::Name),
|
||||
Ip(IpAddr),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum HostAndPortError {
|
||||
/// The host is not a valid DNS name or IP address.
|
||||
InvalidHost,
|
||||
|
||||
/// The port is missing.
|
||||
MissingPort,
|
||||
}
|
||||
|
||||
// ===== impl HostAndPort =====
|
||||
|
||||
impl HostAndPort {
|
||||
pub fn normalize(a: &http::uri::Authority, default_port: Option<u16>)
|
||||
-> Result<Self, HostAndPortError>
|
||||
{
|
||||
let host = IpAddr::from_str(a.host())
|
||||
.map(Host::Ip)
|
||||
.or_else(|_|
|
||||
dns::Name::try_from(a.host().as_bytes())
|
||||
.map(Host::DnsName)
|
||||
.map_err(|_| HostAndPortError::InvalidHost))?;
|
||||
let port = a.port()
|
||||
.or(default_port)
|
||||
.ok_or_else(|| HostAndPortError::MissingPort)?;
|
||||
Ok(HostAndPort {
|
||||
host,
|
||||
port
|
||||
})
|
||||
}
|
||||
|
||||
pub fn is_loopback(&self) -> bool {
|
||||
match &self.host {
|
||||
Host::DnsName(dns_name) => dns_name.is_localhost(),
|
||||
Host::Ip(ip) => ip.is_loopback(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a HostAndPort> for http::uri::Authority {
|
||||
fn from(a: &HostAndPort) -> Self {
|
||||
let s = match a.host {
|
||||
Host::DnsName(ref n) => format!("{}:{}", n, a.port),
|
||||
Host::Ip(ref ip) => format!("{}:{}", ip, a.port),
|
||||
};
|
||||
http::uri::Authority::from_str(&s).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for HostAndPort {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self.host {
|
||||
Host::DnsName(ref dns) => {
|
||||
write!(f, "{}:{}", dns, self.port)
|
||||
}
|
||||
Host::Ip(ref ip) => {
|
||||
write!(f, "{}:{}", ip, self.port)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl Target =====
|
||||
|
||||
impl Target {
|
||||
|
@ -159,27 +75,3 @@ impl fmt::Display for InvalidTarget {
|
|||
}
|
||||
|
||||
impl error::Error for InvalidTarget {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use http::uri::Authority;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_is_loopback() {
|
||||
let cases = &[
|
||||
("localhost", false), // Not absolute
|
||||
("localhost.", true),
|
||||
("LocalhOsT.", true), // Case-insensitive
|
||||
("mlocalhost.", false), // prefixed
|
||||
("localhost1.", false), // suffixed
|
||||
("127.0.0.1", true), // IPv4
|
||||
("[::1]", true), // IPv6
|
||||
];
|
||||
for (host, expected_result) in cases {
|
||||
let authority = Authority::from_static(host);
|
||||
let hp = HostAndPort::normalize(&authority, Some(80)).unwrap();
|
||||
assert_eq!(hp.is_loopback(), *expected_result, "{:?}", host)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,6 @@ use tokio::{
|
|||
reactor::Handle,
|
||||
};
|
||||
|
||||
use app::config::Addr;
|
||||
use Conditional;
|
||||
use transport::{AddrInfo, BoxedIo, GetOriginalDst, tls};
|
||||
|
||||
|
@ -114,10 +113,10 @@ pub struct PeekFuture<T> {
|
|||
// ===== impl BoundPort =====
|
||||
|
||||
impl BoundPort {
|
||||
pub fn new(addr: Addr, tls: tls::ConditionalConnectionConfig<tls::ServerConfigWatch>)
|
||||
pub fn new(addr: SocketAddr, tls: tls::ConditionalConnectionConfig<tls::ServerConfigWatch>)
|
||||
-> Result<Self, io::Error>
|
||||
{
|
||||
let inner = std::net::TcpListener::bind(SocketAddr::from(addr))?;
|
||||
let inner = std::net::TcpListener::bind(addr)?;
|
||||
let local_addr = inner.local_addr()?;
|
||||
Ok(BoundPort {
|
||||
inner,
|
||||
|
|
|
@ -14,7 +14,6 @@ use tokio::{
|
|||
prelude::*,
|
||||
};
|
||||
|
||||
use app::config::Addr;
|
||||
use Conditional;
|
||||
|
||||
use super::{
|
||||
|
@ -119,7 +118,7 @@ fn run_test<C, CF, CR, S, SF, SR>(
|
|||
// tests to run at once, which wouldn't work if they all were bound on
|
||||
// a fixed port.
|
||||
let addr = "127.0.0.1:0".parse::<SocketAddr>().unwrap();
|
||||
let server_bound = connection::BoundPort::new(Addr::from(addr), server_tls)
|
||||
let server_bound = connection::BoundPort::new(addr, server_tls)
|
||||
.unwrap();
|
||||
let server_addr = server_bound.local_addr();
|
||||
|
||||
|
|
|
@ -15,10 +15,7 @@ pub use self::{
|
|||
GetOriginalDst,
|
||||
SoOriginalDst
|
||||
},
|
||||
connect::{
|
||||
Connect,
|
||||
DnsNameAndPort, Host, HostAndPort, HostAndPortError,
|
||||
},
|
||||
connect::Connect,
|
||||
connection::{
|
||||
BoundPort,
|
||||
Connection,
|
||||
|
|
Loading…
Reference in New Issue