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 indexmap::IndexSet;
|
||||||
use trust_dns_resolver::config::ResolverOpts;
|
use trust_dns_resolver::config::ResolverOpts;
|
||||||
|
|
||||||
use transport::{Host, HostAndPort, HostAndPortError, tls};
|
use addr;
|
||||||
use convert::TryFrom;
|
use convert::TryFrom;
|
||||||
use Conditional;
|
use transport::tls;
|
||||||
|
use {Conditional, Addr};
|
||||||
|
|
||||||
// TODO:
|
// TODO:
|
||||||
//
|
//
|
||||||
|
@ -34,7 +35,7 @@ pub struct Config {
|
||||||
pub metrics_listener: Listener,
|
pub metrics_listener: Listener,
|
||||||
|
|
||||||
/// Where to forward externally received connections.
|
/// 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.
|
/// The maximum amount of time to wait for a connection to a local peer.
|
||||||
pub inbound_connect_timeout: Duration,
|
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
|
/// This is optional to allow the proxy to work without the controller for
|
||||||
/// experimental & testing purposes.
|
/// 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
|
/// Time to wait when encountering errors talking to control plane before
|
||||||
/// a new connection.
|
/// a new connection.
|
||||||
|
@ -109,14 +110,9 @@ pub struct Namespaces {
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Listener {
|
pub struct Listener {
|
||||||
/// The address to which the listener should bind.
|
/// 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.
|
/// Errors produced when loading a `Config` struct.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
@ -145,7 +141,7 @@ pub enum UrlError {
|
||||||
MissingAuthority,
|
MissingAuthority,
|
||||||
|
|
||||||
/// The URL is missing the authority part.
|
/// 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.
|
/// The URL contains a path component that isn't "/", which isn't allowed.
|
||||||
PathNotAllowed,
|
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
|
// will log any errors so defer returning any errors until all of them
|
||||||
// have been parsed.
|
// have been parsed.
|
||||||
let outbound_listener_addr = parse_deprecated(
|
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(
|
let inbound_listener_addr = parse_deprecated(
|
||||||
strings, ENV_INBOUND_LISTENER, DEPRECATED_ENV_PUBLIC_LISTENER, str::parse);
|
strings, ENV_INBOUND_LISTENER, DEPRECATED_ENV_PUBLIC_LISTENER, parse_addr);
|
||||||
let control_listener_addr = parse(strings, ENV_CONTROL_LISTENER, str::parse);
|
let control_listener_addr = parse(strings, ENV_CONTROL_LISTENER, parse_addr);
|
||||||
let metrics_listener_addr = parse(strings, ENV_METRICS_LISTENER, str::parse);
|
let metrics_listener_addr = parse(strings, ENV_METRICS_LISTENER, parse_addr);
|
||||||
let inbound_forward = parse_deprecated(
|
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(
|
let inbound_connect_timeout = parse_deprecated(
|
||||||
strings, ENV_INBOUND_CONNECT_TIMEOUT, DEPRECATED_ENV_PRIVATE_CONNECT_TIMEOUT, parse_duration);
|
strings, ENV_INBOUND_CONNECT_TIMEOUT, DEPRECATED_ENV_PRIVATE_CONNECT_TIMEOUT, parse_duration);
|
||||||
let outbound_connect_timeout = parse_deprecated(
|
let outbound_connect_timeout = parse_deprecated(
|
||||||
|
@ -405,19 +401,19 @@ impl<'a> TryFrom<&'a Strings> for Config {
|
||||||
Ok(Config {
|
Ok(Config {
|
||||||
outbound_listener: Listener {
|
outbound_listener: Listener {
|
||||||
addr: outbound_listener_addr?
|
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 {
|
inbound_listener: Listener {
|
||||||
addr: inbound_listener_addr?
|
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 {
|
control_listener: Listener {
|
||||||
addr: control_listener_addr?
|
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 {
|
metrics_listener: Listener {
|
||||||
addr: metrics_listener_addr?
|
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?,
|
inbound_forward: inbound_forward?,
|
||||||
|
|
||||||
|
@ -472,27 +468,10 @@ fn default_disable_ports_protocol_detection() -> IndexSet<u16> {
|
||||||
|
|
||||||
// ===== impl Addr =====
|
// ===== impl Addr =====
|
||||||
|
|
||||||
impl FromStr for Addr {
|
fn parse_addr(s: &str) -> Result<SocketAddr, ParseError> {
|
||||||
type Err = ParseError;
|
match parse_url(s)? {
|
||||||
|
Addr::Socket(a) => Ok(a),
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
_ => Err(ParseError::HostIsNotAnIpAddress)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -562,7 +541,7 @@ fn parse_path(s: &str) -> Result<PathBuf, ParseError> {
|
||||||
Ok(PathBuf::from(s))
|
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))?;
|
let url = s.parse::<http::Uri>().map_err(|_| ParseError::UrlError(UrlError::SyntaxError))?;
|
||||||
if url.scheme_part().map(|s| s.as_str()) != Some("tcp") {
|
if url.scheme_part().map(|s| s.as_str()) != Some("tcp") {
|
||||||
return Err(ParseError::UrlError(UrlError::UnsupportedScheme));
|
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
|
// https://github.com/hyperium/http/issues/127. For now just ignore any
|
||||||
// fragment that is there.
|
// fragment that is there.
|
||||||
|
|
||||||
HostAndPort::normalize(authority, None)
|
Addr::from_authority_with_port(authority)
|
||||||
.map_err(|e| ParseError::UrlError(UrlError::AuthorityError(e)))
|
.map_err(|e| ParseError::UrlError(UrlError::AuthorityError(e)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,12 +3,12 @@ use std::fmt;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use svc;
|
use svc;
|
||||||
use transport::{tls, HostAndPort};
|
use transport::tls;
|
||||||
use Conditional;
|
use {Conditional, Addr};
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
host_and_port: HostAndPort,
|
host_and_port: Addr,
|
||||||
tls_server_identity: Conditional<tls::Identity, tls::ReasonForNoTls>,
|
tls_server_identity: Conditional<tls::Identity, tls::ReasonForNoTls>,
|
||||||
tls_config: tls::ConditionalClientConfig,
|
tls_config: tls::ConditionalClientConfig,
|
||||||
backoff: Duration,
|
backoff: Duration,
|
||||||
|
@ -18,7 +18,7 @@ pub struct Config {
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
host_and_port: HostAndPort,
|
host_and_port: Addr,
|
||||||
tls_server_identity: Conditional<tls::Identity, tls::ReasonForNoTls>,
|
tls_server_identity: Conditional<tls::Identity, tls::ReasonForNoTls>,
|
||||||
backoff: Duration,
|
backoff: Duration,
|
||||||
connect_timeout: Duration,
|
connect_timeout: Duration,
|
||||||
|
@ -116,7 +116,7 @@ pub mod add_origin {
|
||||||
fn make(&self, config: &super::Config) -> Result<Self::Value, Self::Error> {
|
fn make(&self, config: &super::Config) -> Result<Self::Value, Self::Error> {
|
||||||
let inner = self.inner.make(config)?;
|
let inner = self.inner.make(config)?;
|
||||||
let scheme = uri::Scheme::from_shared(Bytes::from_static(b"http")).unwrap();
|
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))
|
Ok(AddOrigin::new(inner, scheme, authority))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -254,7 +254,7 @@ pub mod resolve {
|
||||||
fn new_service(&self) -> Self::Future {
|
fn new_service(&self) -> Self::Future {
|
||||||
Init {
|
Init {
|
||||||
state: State::Resolve {
|
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(),
|
stack: self.stack.clone(),
|
||||||
config: self.config.clone(),
|
config: self.config.clone(),
|
||||||
},
|
},
|
||||||
|
@ -284,7 +284,7 @@ pub mod resolve {
|
||||||
ref stack,
|
ref stack,
|
||||||
} => {
|
} => {
|
||||||
let ip = try_ready!(future.poll().map_err(Error::Dns));
|
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| {
|
let tls = config.tls_server_identity.as_ref().and_then(|id| {
|
||||||
config
|
config
|
||||||
|
@ -333,13 +333,14 @@ pub mod client {
|
||||||
use tower_h2::{client, BoxBody};
|
use tower_h2::{client, BoxBody};
|
||||||
|
|
||||||
use svc;
|
use svc;
|
||||||
use transport::{connect, HostAndPort};
|
use transport::connect;
|
||||||
|
use Addr;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Target {
|
pub struct Target {
|
||||||
pub(super) connect: connect::Target,
|
pub(super) connect: connect::Target,
|
||||||
pub(super) builder: h2::client::Builder,
|
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)]
|
#[derive(Debug)]
|
||||||
|
@ -401,7 +402,7 @@ pub mod client {
|
||||||
{
|
{
|
||||||
type Value = client::Connect<
|
type Value = client::Connect<
|
||||||
C::Value,
|
C::Value,
|
||||||
::logging::ClientExecutor<&'static str, HostAndPort>,
|
::logging::ClientExecutor<&'static str, Addr>,
|
||||||
BoxBody,
|
BoxBody,
|
||||||
>;
|
>;
|
||||||
type Error = C::Error;
|
type Error = C::Error;
|
||||||
|
|
|
@ -2,20 +2,18 @@ use http;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
|
|
||||||
use proxy::http::{
|
use super::classify;
|
||||||
client, h1, normalize_uri::ShouldNormalizeUri, router, Settings,
|
use proxy::http::{client, normalize_uri::ShouldNormalizeUri, router, Settings};
|
||||||
};
|
|
||||||
use proxy::server::Source;
|
use proxy::server::Source;
|
||||||
use svc::stack_per_request::ShouldStackPerRequest;
|
use svc::stack_per_request::ShouldStackPerRequest;
|
||||||
use tap;
|
use tap;
|
||||||
use super::classify;
|
|
||||||
use transport::{connect, tls};
|
use transport::{connect, tls};
|
||||||
use Conditional;
|
use {Conditional, NameAddr};
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
pub struct Endpoint {
|
pub struct Endpoint {
|
||||||
pub addr: SocketAddr,
|
pub addr: SocketAddr,
|
||||||
pub authority: http::uri::Authority,
|
pub dst_name: Option<NameAddr>,
|
||||||
pub settings: Settings,
|
pub settings: Settings,
|
||||||
pub source_tls_status: tls::Status,
|
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())
|
.and_then(|s| s.orig_dst_if_not_local())
|
||||||
.or(self.default_addr)?;
|
.or(self.default_addr)?;
|
||||||
|
|
||||||
let authority = req
|
let dst_name = super::http_request_addr(req)
|
||||||
.uri()
|
.ok()
|
||||||
.authority_part()
|
.and_then(|h| h.into_name_addr());
|
||||||
.cloned()
|
let settings = Settings::from_request(req);
|
||||||
.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 ep = Endpoint {
|
let ep = Endpoint {
|
||||||
addr,
|
addr,
|
||||||
authority,
|
dst_name,
|
||||||
settings,
|
settings,
|
||||||
source_tls_status,
|
source_tls_status,
|
||||||
};
|
};
|
||||||
|
@ -184,22 +176,21 @@ mod tests {
|
||||||
|
|
||||||
use super::{Endpoint, Recognize};
|
use super::{Endpoint, Recognize};
|
||||||
use proxy::http::router::Recognize as _Recognize;
|
use proxy::http::router::Recognize as _Recognize;
|
||||||
use proxy::http::settings::{Host, Settings};
|
use proxy::http::settings::Settings;
|
||||||
use proxy::server::Source;
|
use proxy::server::Source;
|
||||||
use transport::tls;
|
use transport::tls;
|
||||||
use Conditional;
|
use Conditional;
|
||||||
|
|
||||||
fn make_h1_endpoint(addr: net::SocketAddr) -> Endpoint {
|
fn make_h1_endpoint(addr: net::SocketAddr) -> Endpoint {
|
||||||
let settings = Settings::Http1 {
|
let settings = Settings::Http1 {
|
||||||
host: Host::NoAuthority,
|
|
||||||
is_h1_upgrade: false,
|
is_h1_upgrade: false,
|
||||||
was_absolute_form: 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;
|
let source_tls_status = TLS_DISABLED;
|
||||||
Endpoint {
|
Endpoint {
|
||||||
addr,
|
addr,
|
||||||
authority,
|
dst_name: None,
|
||||||
settings,
|
settings,
|
||||||
source_tls_status,
|
source_tls_status,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
use http::uri;
|
|
||||||
use std::{
|
use std::{
|
||||||
fmt::{self, Write},
|
fmt::{self, Write},
|
||||||
net,
|
net,
|
||||||
|
@ -7,7 +6,7 @@ use std::{
|
||||||
use metrics::FmtLabels;
|
use metrics::FmtLabels;
|
||||||
|
|
||||||
use transport::tls;
|
use transport::tls;
|
||||||
use Conditional;
|
use {Conditional, NameAddr};
|
||||||
|
|
||||||
use super::{classify, inbound, outbound};
|
use super::{classify, inbound, outbound};
|
||||||
|
|
||||||
|
@ -16,7 +15,7 @@ pub struct EndpointLabels {
|
||||||
addr: net::SocketAddr,
|
addr: net::SocketAddr,
|
||||||
direction: Direction,
|
direction: Direction,
|
||||||
tls_status: tls::Status,
|
tls_status: tls::Status,
|
||||||
authority: Authority,
|
dst_name: Option<NameAddr>,
|
||||||
labels: Option<String>,
|
labels: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,7 +33,7 @@ enum Direction {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
struct Authority(Option<uri::Authority>);
|
struct Authority<'a>(&'a NameAddr);
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
struct Dst(outbound::Destination);
|
struct Dst(outbound::Destination);
|
||||||
|
@ -69,7 +68,7 @@ impl From<inbound::Endpoint> for EndpointLabels {
|
||||||
fn from(ep: inbound::Endpoint) -> Self {
|
fn from(ep: inbound::Endpoint) -> Self {
|
||||||
Self {
|
Self {
|
||||||
addr: ep.addr,
|
addr: ep.addr,
|
||||||
authority: Authority(Some(ep.authority)),
|
dst_name: ep.dst_name,
|
||||||
direction: Direction::In,
|
direction: Direction::In,
|
||||||
tls_status: ep.source_tls_status,
|
tls_status: ep.source_tls_status,
|
||||||
labels: None,
|
labels: None,
|
||||||
|
@ -92,26 +91,9 @@ where
|
||||||
|
|
||||||
impl From<outbound::Endpoint> for EndpointLabels {
|
impl From<outbound::Endpoint> for EndpointLabels {
|
||||||
fn from(ep: outbound::Endpoint) -> Self {
|
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 {
|
Self {
|
||||||
addr: ep.connect.addr,
|
addr: ep.connect.addr,
|
||||||
authority,
|
dst_name: ep.dst.addr.into_name_addr(),
|
||||||
direction: Direction::Out,
|
direction: Direction::Out,
|
||||||
tls_status: ep.connect.tls_status(),
|
tls_status: ep.connect.tls_status(),
|
||||||
labels: prefix_labels("dst", ep.metadata.labels().into_iter()),
|
labels: prefix_labels("dst", ep.metadata.labels().into_iter()),
|
||||||
|
@ -121,7 +103,8 @@ impl From<outbound::Endpoint> for EndpointLabels {
|
||||||
|
|
||||||
impl FmtLabels for EndpointLabels {
|
impl FmtLabels for EndpointLabels {
|
||||||
fn fmt_labels(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
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() {
|
if let Some(labels) = self.labels.as_ref() {
|
||||||
write!(f, ",{}", labels)?;
|
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 {
|
fn fmt_labels(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match self.0 {
|
match self.0.port() {
|
||||||
Some(ref a) => write!(f, "authority=\"{}\"", a),
|
80 => write!(f, "authority=\"{}\"", self.0.name()),
|
||||||
None => write!(f, "authority=\"\""),
|
_ => write!(f, "authority=\"{}\"", self.0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -159,11 +142,7 @@ impl FmtLabels for Dst {
|
||||||
} else {
|
} else {
|
||||||
"h1"
|
"h1"
|
||||||
};
|
};
|
||||||
write!(
|
write!(f, "dst=\"{}\",dst_protocol=\"{}\"", self.0.addr, proto)?;
|
||||||
f,
|
|
||||||
"dst=\"{}\",dst_protocol=\"{}\"",
|
|
||||||
self.0.name_or_addr, proto
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
//! Configures and runs the linkerd2 service sidecar proxy
|
//! Configures and runs the linkerd2 service sidecar proxy
|
||||||
|
|
||||||
use convert::TryFrom;
|
use http;
|
||||||
use logging;
|
|
||||||
|
|
||||||
mod classify;
|
mod classify;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
|
@ -12,11 +11,35 @@ mod metric_labels;
|
||||||
mod outbound;
|
mod outbound;
|
||||||
mod profiles;
|
mod profiles;
|
||||||
|
|
||||||
use self::config::{Config, Env};
|
|
||||||
pub use self::main::Main;
|
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();
|
logging::init();
|
||||||
let config_strings = Env;
|
config::Config::try_from(&config::Env)
|
||||||
Config::try_from(&config_strings)
|
}
|
||||||
|
|
||||||
|
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 http;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::net::SocketAddr;
|
|
||||||
|
|
||||||
use app::classify;
|
use app::classify;
|
||||||
use control::destination::{Metadata, ProtocolHint};
|
use control::destination::{Metadata, ProtocolHint};
|
||||||
use proxy::{
|
use proxy::http::{
|
||||||
http::{
|
classify::CanClassify,
|
||||||
classify::CanClassify,
|
client,
|
||||||
client, h1,
|
normalize_uri::ShouldNormalizeUri,
|
||||||
normalize_uri::ShouldNormalizeUri,
|
profiles::{self, CanGetDestination},
|
||||||
profiles::{self, CanGetDestination},
|
router, Settings,
|
||||||
router, Settings,
|
|
||||||
},
|
|
||||||
Source,
|
|
||||||
};
|
};
|
||||||
use svc::{self, stack_per_request::ShouldStackPerRequest};
|
use svc::{self, stack_per_request::ShouldStackPerRequest};
|
||||||
use tap;
|
use tap;
|
||||||
use transport::{connect, tls, DnsNameAndPort, Host, HostAndPort};
|
use transport::{connect, tls};
|
||||||
|
use {Addr, NameAddr};
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Endpoint {
|
pub struct Endpoint {
|
||||||
pub dst: Destination,
|
pub dst: Destination,
|
||||||
pub connect: connect::Target,
|
pub connect: connect::Target,
|
||||||
pub metadata: Metadata,
|
pub metadata: Metadata,
|
||||||
_p: (),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
pub struct Destination {
|
pub struct Destination {
|
||||||
pub name_or_addr: NameOrAddr,
|
pub addr: Addr,
|
||||||
pub settings: Settings,
|
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)]
|
#[derive(Clone, Debug)]
|
||||||
|
@ -50,7 +35,7 @@ pub struct Route {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct Recognize {}
|
pub struct Recognize;
|
||||||
|
|
||||||
// === impl Endpoint ===
|
// === impl Endpoint ===
|
||||||
|
|
||||||
|
@ -136,34 +121,26 @@ impl<B> router::Recognize<http::Request<B>> for Recognize {
|
||||||
type Target = Destination;
|
type Target = Destination;
|
||||||
|
|
||||||
fn recognize(&self, req: &http::Request<B>) -> Option<Self::Target> {
|
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);
|
debug!("recognize: dst={:?}", dst);
|
||||||
dst
|
Some(dst)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// === impl Destination ===
|
// === impl Destination ===
|
||||||
|
|
||||||
impl Destination {
|
impl Destination {
|
||||||
pub fn new(name_or_addr: NameOrAddr, settings: Settings) -> Self {
|
pub fn new(addr: Addr, settings: Settings) -> Self {
|
||||||
Self {
|
Self { addr, settings }
|
||||||
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))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CanGetDestination for Destination {
|
impl CanGetDestination for Destination {
|
||||||
fn get_destination(&self) -> Option<&DnsNameAndPort> {
|
fn get_destination(&self) -> Option<&NameAddr> {
|
||||||
match self.name_or_addr {
|
match self.addr {
|
||||||
NameOrAddr::Name(ref dst) => Some(dst),
|
Addr::Name(ref name) => Some(name),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -171,78 +148,7 @@ impl CanGetDestination for Destination {
|
||||||
|
|
||||||
impl fmt::Display for Destination {
|
impl fmt::Display for Destination {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
self.name_or_addr.fmt(f)
|
self.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()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -258,14 +164,14 @@ pub mod discovery {
|
||||||
use futures::{Async, Poll};
|
use futures::{Async, Poll};
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
|
|
||||||
use super::{Destination, Endpoint, NameOrAddr};
|
use super::{Destination, Endpoint};
|
||||||
use control::destination::Metadata;
|
use control::destination::Metadata;
|
||||||
use proxy::resolve;
|
use proxy::resolve;
|
||||||
use transport::{connect, tls, DnsNameAndPort};
|
use transport::{connect, tls};
|
||||||
use Conditional;
|
use {Addr, Conditional, NameAddr};
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Resolve<R: resolve::Resolve<DnsNameAndPort>>(R);
|
pub struct Resolve<R: resolve::Resolve<NameAddr>>(R);
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Resolution<R: resolve::Resolution> {
|
pub enum Resolution<R: resolve::Resolution> {
|
||||||
|
@ -277,7 +183,7 @@ pub mod discovery {
|
||||||
|
|
||||||
impl<R> Resolve<R>
|
impl<R> Resolve<R>
|
||||||
where
|
where
|
||||||
R: resolve::Resolve<DnsNameAndPort, Endpoint = Metadata>,
|
R: resolve::Resolve<NameAddr, Endpoint = Metadata>,
|
||||||
{
|
{
|
||||||
pub fn new(resolve: R) -> Self {
|
pub fn new(resolve: R) -> Self {
|
||||||
Resolve(resolve)
|
Resolve(resolve)
|
||||||
|
@ -286,15 +192,15 @@ pub mod discovery {
|
||||||
|
|
||||||
impl<R> resolve::Resolve<Destination> for Resolve<R>
|
impl<R> resolve::Resolve<Destination> for Resolve<R>
|
||||||
where
|
where
|
||||||
R: resolve::Resolve<DnsNameAndPort, Endpoint = Metadata>,
|
R: resolve::Resolve<NameAddr, Endpoint = Metadata>,
|
||||||
{
|
{
|
||||||
type Endpoint = Endpoint;
|
type Endpoint = Endpoint;
|
||||||
type Resolution = Resolution<R::Resolution>;
|
type Resolution = Resolution<R::Resolution>;
|
||||||
|
|
||||||
fn resolve(&self, dst: &Destination) -> Self::Resolution {
|
fn resolve(&self, dst: &Destination) -> Self::Resolution {
|
||||||
match dst.name_or_addr {
|
match dst.addr {
|
||||||
NameOrAddr::Name(ref name) => Resolution::Name(dst.clone(), self.0.resolve(&name)),
|
Addr::Name(ref name) => Resolution::Name(dst.clone(), self.0.resolve(&name)),
|
||||||
NameOrAddr::Addr(ref addr) => Resolution::Addr(dst.clone(), Some(*addr)),
|
Addr::Socket(ref addr) => Resolution::Addr(dst.clone(), Some(*addr)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -327,7 +233,6 @@ pub mod discovery {
|
||||||
dst: dst.clone(),
|
dst: dst.clone(),
|
||||||
connect: connect::Target::new(addr, Conditional::None(tls)),
|
connect: connect::Target::new(addr, Conditional::None(tls)),
|
||||||
metadata,
|
metadata,
|
||||||
_p: (),
|
|
||||||
};
|
};
|
||||||
Ok(Async::Ready(resolve::Update::Add(addr, ep)))
|
Ok(Async::Ready(resolve::Update::Add(addr, ep)))
|
||||||
}
|
}
|
||||||
|
@ -339,7 +244,6 @@ pub mod discovery {
|
||||||
dst: dst.clone(),
|
dst: dst.clone(),
|
||||||
connect: connect::Target::new(addr, Conditional::None(tls.into())),
|
connect: connect::Target::new(addr, Conditional::None(tls.into())),
|
||||||
metadata: Metadata::none(tls),
|
metadata: Metadata::none(tls),
|
||||||
_p: (),
|
|
||||||
};
|
};
|
||||||
let up = resolve::Update::Add(addr, ep);
|
let up = resolve::Update::Add(addr, ep);
|
||||||
Ok(Async::Ready(up))
|
Ok(Async::Ready(up))
|
||||||
|
|
|
@ -11,7 +11,7 @@ use api::destination as api;
|
||||||
|
|
||||||
use control;
|
use control;
|
||||||
use proxy::http::profiles;
|
use proxy::http::profiles;
|
||||||
use transport::DnsNameAndPort;
|
use NameAddr;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Client<T, N> {
|
pub struct Client<T, N> {
|
||||||
|
@ -61,7 +61,7 @@ where
|
||||||
{
|
{
|
||||||
type Stream = Rx<T>;
|
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)?;
|
let fqa = self.normalize_name.normalize(dst)?;
|
||||||
Some(Rx {
|
Some(Rx {
|
||||||
dst: fqa.without_trailing_dot().to_owned(),
|
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
|
/// Like `std::option::Option<C>` but `None` carries a reason why the value
|
||||||
/// isn't available.
|
/// isn't available.
|
||||||
#[derive(Clone)]
|
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||||
pub enum Conditional<C, R>
|
pub enum Conditional<C, R> {
|
||||||
where
|
|
||||||
C: Clone,
|
|
||||||
R: Clone,
|
|
||||||
{
|
|
||||||
Some(C),
|
Some(C),
|
||||||
None(R),
|
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>
|
impl<C, R> Conditional<C, R>
|
||||||
where
|
where
|
||||||
C: Clone,
|
C: Clone,
|
||||||
|
|
|
@ -25,8 +25,8 @@ use control::{
|
||||||
remote_stream::Remote,
|
remote_stream::Remote,
|
||||||
};
|
};
|
||||||
use dns::{self, IpAddrListFuture};
|
use dns::{self, IpAddrListFuture};
|
||||||
use transport::{tls, DnsNameAndPort};
|
use transport::tls;
|
||||||
use Conditional;
|
use {Conditional, NameAddr};
|
||||||
|
|
||||||
use super::{ActiveQuery, DestinationServiceQuery, UpdateRx};
|
use super::{ActiveQuery, DestinationServiceQuery, UpdateRx};
|
||||||
|
|
||||||
|
@ -50,15 +50,15 @@ where
|
||||||
&mut self,
|
&mut self,
|
||||||
dns_resolver: &dns::Resolver,
|
dns_resolver: &dns::Resolver,
|
||||||
deadline: Instant,
|
deadline: Instant,
|
||||||
authority: &DnsNameAndPort,
|
authority: &NameAddr,
|
||||||
) {
|
) {
|
||||||
trace!(
|
trace!(
|
||||||
"resetting DNS query for {} at {:?}",
|
"resetting DNS query for {} at {:?}",
|
||||||
authority.host,
|
authority.name(),
|
||||||
deadline
|
deadline
|
||||||
);
|
);
|
||||||
self.reset_on_next_modification();
|
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
|
// Processes Destination service updates from `request_rx`, returning the new query
|
||||||
|
@ -67,7 +67,7 @@ where
|
||||||
// "no change in existence" instead of "unknown".
|
// "no change in existence" instead of "unknown".
|
||||||
pub(super) fn poll_destination_service(
|
pub(super) fn poll_destination_service(
|
||||||
&mut self,
|
&mut self,
|
||||||
auth: &DnsNameAndPort,
|
auth: &NameAddr,
|
||||||
mut rx: UpdateRx<T>,
|
mut rx: UpdateRx<T>,
|
||||||
tls_controller_namespace: Option<&str>,
|
tls_controller_namespace: Option<&str>,
|
||||||
) -> (ActiveQuery<T>, Exists<()>) {
|
) -> (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
|
// Duration to wait before polling DNS again after an error
|
||||||
// (or a NXDOMAIN response with no TTL).
|
// (or a NXDOMAIN response with no TTL).
|
||||||
const DNS_ERROR_TTL: Duration = Duration::from_secs(5);
|
const DNS_ERROR_TTL: Duration = Duration::from_secs(5);
|
||||||
|
@ -147,7 +147,7 @@ where
|
||||||
authority,
|
authority,
|
||||||
ips.iter().map(|ip| {
|
ips.iter().map(|ip| {
|
||||||
(
|
(
|
||||||
SocketAddr::from((ip, authority.port)),
|
SocketAddr::from((ip, authority.port())),
|
||||||
Metadata::none(tls::ReasonForNoIdentity::NotProvidedByServiceDiscovery),
|
Metadata::none(tls::ReasonForNoIdentity::NotProvidedByServiceDiscovery),
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
|
@ -169,7 +169,7 @@ where
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
// Do nothing so that the most recent non-error response is used until a
|
// Do nothing so that the most recent non-error response is used until a
|
||||||
// non-error response is received
|
// 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.
|
// Poll again after the default wait time.
|
||||||
Instant::now() + DNS_ERROR_TTL
|
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
|
where
|
||||||
A: Iterator<Item = (SocketAddr, Metadata)>,
|
A: Iterator<Item = (SocketAddr, Metadata)>,
|
||||||
{
|
{
|
||||||
|
@ -214,7 +214,7 @@ where
|
||||||
self.addrs = Exists::Yes(cache);
|
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
|
where
|
||||||
A: Iterator<Item = SocketAddr>,
|
A: Iterator<Item = SocketAddr>,
|
||||||
{
|
{
|
||||||
|
@ -230,7 +230,7 @@ where
|
||||||
self.addrs = Exists::Yes(cache);
|
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!(
|
trace!(
|
||||||
"no endpoints for {:?} that is known to {}",
|
"no endpoints for {:?} that is known to {}",
|
||||||
authority_for_logging,
|
authority_for_logging,
|
||||||
|
@ -253,7 +253,7 @@ where
|
||||||
|
|
||||||
fn on_change(
|
fn on_change(
|
||||||
responders: &mut Vec<Responder>,
|
responders: &mut Vec<Responder>,
|
||||||
authority_for_logging: &DnsNameAndPort,
|
authority_for_logging: &NameAddr,
|
||||||
change: CacheChange<SocketAddr, Metadata>,
|
change: CacheChange<SocketAddr, Metadata>,
|
||||||
) {
|
) {
|
||||||
let (update_str, update, addr) = match change {
|
let (update_str, update, addr) = match change {
|
||||||
|
|
|
@ -29,7 +29,7 @@ use control::{
|
||||||
remote_stream::{Receiver, Remote},
|
remote_stream::{Receiver, Remote},
|
||||||
};
|
};
|
||||||
use dns;
|
use dns;
|
||||||
use transport::DnsNameAndPort;
|
use NameAddr;
|
||||||
|
|
||||||
mod destination_set;
|
mod destination_set;
|
||||||
|
|
||||||
|
@ -59,9 +59,9 @@ pub(super) struct Background<T: HttpService> {
|
||||||
/// which require reconnects.
|
/// which require reconnects.
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct DestinationCache<T: HttpService> {
|
struct DestinationCache<T: HttpService> {
|
||||||
destinations: HashMap<DnsNameAndPort, DestinationSet<T>>,
|
destinations: HashMap<NameAddr, DestinationSet<T>>,
|
||||||
/// A queue of authorities that need to be reconnected.
|
/// A queue of authorities that need to be reconnected.
|
||||||
reconnects: VecDeque<DnsNameAndPort>,
|
reconnects: VecDeque<NameAddr>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The configurationn necessary to create a new Destination service
|
/// The configurationn necessary to create a new Destination service
|
||||||
|
@ -338,7 +338,7 @@ impl NewQuery {
|
||||||
fn query_destination_service_if_relevant<T>(
|
fn query_destination_service_if_relevant<T>(
|
||||||
&self,
|
&self,
|
||||||
client: Option<&mut T>,
|
client: Option<&mut T>,
|
||||||
auth: &DnsNameAndPort,
|
auth: &NameAddr,
|
||||||
connect_or_reconnect: &str,
|
connect_or_reconnect: &str,
|
||||||
) -> DestinationServiceQuery<T>
|
) -> DestinationServiceQuery<T>
|
||||||
where
|
where
|
||||||
|
@ -415,7 +415,7 @@ where
|
||||||
/// Returns true if `auth` is currently known to need a Destination
|
/// Returns true if `auth` is currently known to need a Destination
|
||||||
/// service query, but was unable to query previously due to the query
|
/// service query, but was unable to query previously due to the query
|
||||||
/// limit being reached.
|
/// limit being reached.
|
||||||
fn needs_query_for(&self, auth: &DnsNameAndPort) -> bool {
|
fn needs_query_for(&self, auth: &NameAddr) -> bool {
|
||||||
self.destinations
|
self.destinations
|
||||||
.get(auth)
|
.get(auth)
|
||||||
.map(|dst| dst.needs_query_capacity())
|
.map(|dst| dst.needs_query_capacity())
|
||||||
|
|
|
@ -40,13 +40,12 @@ use tower_h2::{Body, BoxBody, Data, HttpService};
|
||||||
use dns;
|
use dns;
|
||||||
use transport::tls;
|
use transport::tls;
|
||||||
use proxy::resolve::{self, Resolve, Update};
|
use proxy::resolve::{self, Resolve, Update};
|
||||||
use transport::DnsNameAndPort;
|
|
||||||
|
|
||||||
pub mod background;
|
pub mod background;
|
||||||
|
|
||||||
use app::config::Namespaces;
|
use app::config::Namespaces;
|
||||||
use self::background::Background;
|
use self::background::Background;
|
||||||
use Conditional;
|
use {Conditional, NameAddr};
|
||||||
|
|
||||||
/// A handle to request resolutions from the background discovery task.
|
/// A handle to request resolutions from the background discovery task.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -57,7 +56,7 @@ pub struct Resolver {
|
||||||
/// Requests that resolution updaes for `authority` be sent on `responder`.
|
/// Requests that resolution updaes for `authority` be sent on `responder`.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct ResolveRequest {
|
struct ResolveRequest {
|
||||||
authority: DnsNameAndPort,
|
authority: NameAddr,
|
||||||
responder: Responder,
|
responder: Responder,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,12 +135,12 @@ where
|
||||||
|
|
||||||
// ==== impl Resolver =====
|
// ==== impl Resolver =====
|
||||||
|
|
||||||
impl Resolve<DnsNameAndPort> for Resolver {
|
impl Resolve<NameAddr> for Resolver {
|
||||||
type Endpoint = Metadata;
|
type Endpoint = Metadata;
|
||||||
type Resolution = Resolution;
|
type Resolution = Resolution;
|
||||||
|
|
||||||
/// Start watching for address changes for a certain authority.
|
/// 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);
|
trace!("resolve; authority={:?}", authority);
|
||||||
let (update_tx, update_rx) = mpsc::unbounded();
|
let (update_tx, update_rx) = mpsc::unbounded();
|
||||||
let active = Arc::new(());
|
let active = Arc::new(());
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use bytes::{BytesMut};
|
use bytes::{BytesMut};
|
||||||
|
|
||||||
use transport::DnsNameAndPort;
|
use NameAddr;
|
||||||
|
|
||||||
pub trait Normalize {
|
pub trait Normalize {
|
||||||
fn normalize(&self, authority: &DnsNameAndPort) -> Option<FullyQualifiedAuthority>;
|
fn normalize(&self, authority: &NameAddr) -> Option<FullyQualifiedAuthority>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
@ -24,8 +24,8 @@ impl KubernetesNormalize {
|
||||||
impl Normalize for KubernetesNormalize {
|
impl Normalize for KubernetesNormalize {
|
||||||
/// Normalizes the name according to Kubernetes service naming conventions.
|
/// Normalizes the name according to Kubernetes service naming conventions.
|
||||||
/// Case folding is not done; that is done internally inside `Authority`.
|
/// Case folding is not done; that is done internally inside `Authority`.
|
||||||
fn normalize(&self, authority: &DnsNameAndPort) -> Option<FullyQualifiedAuthority> {
|
fn normalize(&self, authority: &NameAddr) -> Option<FullyQualifiedAuthority> {
|
||||||
let name: &str = authority.host.as_ref();
|
let name: &str = authority.name().as_ref();
|
||||||
|
|
||||||
// parts should have a maximum 4 of pieces (name, namespace, svc, zone)
|
// parts should have a maximum 4 of pieces (name, namespace, svc, zone)
|
||||||
let mut parts = name.splitn(4, '.');
|
let mut parts = name.splitn(4, '.');
|
||||||
|
@ -95,7 +95,7 @@ impl Normalize for KubernetesNormalize {
|
||||||
additional_len += 1 + zone.len(); // "." + zone
|
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.
|
80 => 0, // XXX: Assumes http://, which is all we support right now.
|
||||||
p if p >= 10000 => 1 + 5,
|
p if p >= 10000 => 1 + 5,
|
||||||
p if p >= 1000 => 1 + 4,
|
p if p >= 1000 => 1 + 4,
|
||||||
|
@ -126,7 +126,7 @@ impl Normalize for KubernetesNormalize {
|
||||||
// Append the port
|
// Append the port
|
||||||
if port_str_len > 0 {
|
if port_str_len > 0 {
|
||||||
normalized.extend_from_slice(b":");
|
normalized.extend_from_slice(b":");
|
||||||
let port = authority.port.to_string();
|
let port = authority.port().to_string();
|
||||||
normalized.extend_from_slice(port.as_ref());
|
normalized.extend_from_slice(port.as_ref());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,19 +142,18 @@ impl FullyQualifiedAuthority {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use transport::{DnsNameAndPort, Host, HostAndPort};
|
|
||||||
use http::uri::Authority;
|
use http::uri::Authority;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use {Addr, NameAddr};
|
||||||
use super::Normalize;
|
use super::Normalize;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_normalized_authority() {
|
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();
|
let authority = Authority::from_str(input).unwrap();
|
||||||
match HostAndPort::normalize(&authority, Some(80)) {
|
match Addr::from_authority_and_default_port(&authority, 80) {
|
||||||
Ok(HostAndPort { host: Host::DnsName(host), port }) =>
|
Ok(Addr::Name(name)) => name,
|
||||||
DnsNameAndPort { host, port },
|
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
unreachable!("{:?} when parsing {:?}", e, input)
|
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 app::config::Config;
|
||||||
use transport::{self, tls};
|
use transport::tls;
|
||||||
|
use Addr;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Resolver {
|
pub struct Resolver {
|
||||||
|
@ -98,14 +99,15 @@ impl Resolver {
|
||||||
(resolver, background)
|
(resolver, background)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resolve_one_ip(&self, host: &transport::Host) -> IpAddrFuture {
|
pub fn resolve_one_ip(&self, host: &Addr) -> IpAddrFuture {
|
||||||
match *host {
|
match host {
|
||||||
transport::Host::DnsName(ref name) => {
|
Addr::Name(n) => {
|
||||||
|
let name = n.name();
|
||||||
let ctx = ResolveOneCtx(name.clone());
|
let ctx = ResolveOneCtx(name.clone());
|
||||||
let f = ::logging::context_future(ctx, self.lookup_ip(name));
|
let f = ::logging::context_future(ctx, self.lookup_ip(name));
|
||||||
IpAddrFuture::DNS(Box::new(f))
|
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.
|
// `linkerd2_metrics` is needed to satisfy the macro, but this is nicer to use internally.
|
||||||
use self::linkerd2_metrics as metrics;
|
use self::linkerd2_metrics as metrics;
|
||||||
|
|
||||||
|
mod addr;
|
||||||
pub mod app;
|
pub mod app;
|
||||||
mod conditional;
|
mod conditional;
|
||||||
pub mod control;
|
pub mod control;
|
||||||
|
@ -65,5 +66,6 @@ mod tap;
|
||||||
pub mod telemetry;
|
pub mod telemetry;
|
||||||
pub mod transport;
|
pub mod transport;
|
||||||
|
|
||||||
|
use self::addr::{Addr, NameAddr};
|
||||||
use self::conditional::Conditional;
|
use self::conditional::Conditional;
|
||||||
pub use self::transport::SoOriginalDst;
|
pub use self::transport::SoOriginalDst;
|
||||||
|
|
|
@ -10,7 +10,7 @@ use std::iter::FromIterator;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::{error, fmt};
|
use std::{error, fmt};
|
||||||
|
|
||||||
use transport::DnsNameAndPort;
|
use NameAddr;
|
||||||
|
|
||||||
pub type Routes = Vec<(RequestMatch, Route)>;
|
pub type Routes = Vec<(RequestMatch, Route)>;
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ pub type Routes = Vec<(RequestMatch, Route)>;
|
||||||
pub trait GetRoutes {
|
pub trait GetRoutes {
|
||||||
type Stream: Stream<Item = Routes, Error = Error>;
|
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.
|
/// 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;
|
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`.
|
/// can be discovered via `GetRoutes`.
|
||||||
pub trait CanGetDestination {
|
pub trait CanGetDestination {
|
||||||
fn get_destination(&self) -> Option<&DnsNameAndPort>;
|
fn get_destination(&self) -> Option<&NameAddr>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
use http::{self, uri};
|
use http::{self, header::HOST};
|
||||||
|
|
||||||
use super::h1;
|
|
||||||
|
|
||||||
/// Settings portion of the `Recognize` key for a request.
|
/// Settings portion of the `Recognize` key for a request.
|
||||||
///
|
///
|
||||||
|
@ -10,7 +8,8 @@ use super::h1;
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
pub enum Settings {
|
pub enum Settings {
|
||||||
Http1 {
|
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.
|
/// Whether the request wants to use HTTP/1.1's Upgrade mechanism.
|
||||||
///
|
///
|
||||||
/// Since these cannot be translated into orig-proto, it must be
|
/// 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.
|
/// used to determine what URI normalization will be necessary.
|
||||||
was_absolute_form: bool,
|
was_absolute_form: bool,
|
||||||
},
|
},
|
||||||
Http2
|
Http2,
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
|
||||||
pub enum Host {
|
|
||||||
Authority(uri::Authority),
|
|
||||||
NoAuthority,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== impl Settings =====
|
// ===== impl Settings =====
|
||||||
|
|
||||||
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 {
|
if req.version() == http::Version::HTTP_2 {
|
||||||
return Settings::Http2;
|
return Settings::Http2;
|
||||||
}
|
}
|
||||||
|
|
||||||
let was_absolute_form = super::h1::is_absolute_form(req.uri());
|
let is_missing_authority = req
|
||||||
trace!(
|
.uri()
|
||||||
"Settings::detect(); req.uri='{:?}'; was_absolute_form={:?};",
|
.authority_part()
|
||||||
req.uri(), was_absolute_form
|
.map(|_| false)
|
||||||
);
|
.or_else(|| {
|
||||||
// If the request has an authority part, use that as the host part of
|
req.headers()
|
||||||
// the key for an HTTP/1.x request.
|
.get(HOST)
|
||||||
let host = Host::detect(req);
|
.and_then(|h| h.to_str().ok())
|
||||||
|
.map(|h| h.is_empty())
|
||||||
let is_h1_upgrade = super::h1::wants_upgrade(req);
|
})
|
||||||
|
.unwrap_or(true);
|
||||||
|
|
||||||
Settings::Http1 {
|
Settings::Http1 {
|
||||||
host,
|
was_absolute_form: super::h1::is_absolute_form(req.uri()),
|
||||||
is_h1_upgrade,
|
is_h1_upgrade: super::h1::wants_upgrade(req),
|
||||||
was_absolute_form,
|
stack_per_request: is_missing_authority,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if the request was originally received in absolute form.
|
/// Returns true if the request was originally received in absolute form.
|
||||||
pub fn was_absolute_form(&self) -> bool {
|
pub fn was_absolute_form(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
&Settings::Http1 { was_absolute_form, .. } => was_absolute_form,
|
Settings::Http1 {
|
||||||
_ => false,
|
was_absolute_form, ..
|
||||||
|
} => *was_absolute_form,
|
||||||
|
Settings::Http2 => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn can_reuse_clients(&self) -> bool {
|
pub fn can_reuse_clients(&self) -> bool {
|
||||||
match *self {
|
match self {
|
||||||
Settings::Http2 | Settings::Http1 { host: Host::Authority(_), .. } => true,
|
Settings::Http1 {
|
||||||
_ => false,
|
stack_per_request, ..
|
||||||
|
} => !stack_per_request,
|
||||||
|
Settings::Http2 => true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_h1_upgrade(&self) -> bool {
|
pub fn is_h1_upgrade(&self) -> bool {
|
||||||
match *self {
|
match self {
|
||||||
Settings::Http1 { is_h1_upgrade: true, .. } => true,
|
Settings::Http1 { is_h1_upgrade, .. } => *is_h1_upgrade,
|
||||||
_ => false,
|
Settings::Http2 => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_http2(&self) -> bool {
|
pub fn is_http2(&self) -> bool {
|
||||||
match *self {
|
match self {
|
||||||
|
Settings::Http1 { .. } => false,
|
||||||
Settings::Http2 => true,
|
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;
|
pub use self::tokio_connect::Connect;
|
||||||
|
|
||||||
use http;
|
|
||||||
use std::{error, fmt, io};
|
use std::{error, fmt, io};
|
||||||
use std::net::{IpAddr, SocketAddr};
|
use std::net::SocketAddr;
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
use convert::TryFrom;
|
|
||||||
use dns;
|
|
||||||
use svc;
|
use svc;
|
||||||
use transport::{connection, tls};
|
use transport::{connection, tls};
|
||||||
|
|
||||||
|
@ -26,86 +22,6 @@ pub struct Target {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct InvalidTarget;
|
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 =====
|
||||||
|
|
||||||
impl Target {
|
impl Target {
|
||||||
|
@ -159,27 +75,3 @@ impl fmt::Display for InvalidTarget {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl error::Error 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,
|
reactor::Handle,
|
||||||
};
|
};
|
||||||
|
|
||||||
use app::config::Addr;
|
|
||||||
use Conditional;
|
use Conditional;
|
||||||
use transport::{AddrInfo, BoxedIo, GetOriginalDst, tls};
|
use transport::{AddrInfo, BoxedIo, GetOriginalDst, tls};
|
||||||
|
|
||||||
|
@ -114,10 +113,10 @@ pub struct PeekFuture<T> {
|
||||||
// ===== impl BoundPort =====
|
// ===== impl BoundPort =====
|
||||||
|
|
||||||
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>
|
-> 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()?;
|
let local_addr = inner.local_addr()?;
|
||||||
Ok(BoundPort {
|
Ok(BoundPort {
|
||||||
inner,
|
inner,
|
||||||
|
|
|
@ -14,7 +14,6 @@ use tokio::{
|
||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
|
|
||||||
use app::config::Addr;
|
|
||||||
use Conditional;
|
use Conditional;
|
||||||
|
|
||||||
use super::{
|
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
|
// tests to run at once, which wouldn't work if they all were bound on
|
||||||
// a fixed port.
|
// a fixed port.
|
||||||
let addr = "127.0.0.1:0".parse::<SocketAddr>().unwrap();
|
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();
|
.unwrap();
|
||||||
let server_addr = server_bound.local_addr();
|
let server_addr = server_bound.local_addr();
|
||||||
|
|
||||||
|
|
|
@ -15,10 +15,7 @@ pub use self::{
|
||||||
GetOriginalDst,
|
GetOriginalDst,
|
||||||
SoOriginalDst
|
SoOriginalDst
|
||||||
},
|
},
|
||||||
connect::{
|
connect::Connect,
|
||||||
Connect,
|
|
||||||
DnsNameAndPort, Host, HostAndPort, HostAndPortError,
|
|
||||||
},
|
|
||||||
connection::{
|
connection::{
|
||||||
BoundPort,
|
BoundPort,
|
||||||
Connection,
|
Connection,
|
||||||
|
|
Loading…
Reference in New Issue