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:
Oliver Gould 2018-11-08 14:49:42 -08:00 committed by GitHub
parent 5e0a15b8a7
commit c4b3765574
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 379 additions and 511 deletions

177
src/addr.rs Normal file
View File

@ -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)
}
}
}

View File

@ -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)))
}

View File

@ -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;

View File

@ -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,
}

View File

@ -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(())
}

View File

@ -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)
})
}

View File

@ -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))

View File

@ -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(),

View File

@ -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,

View File

@ -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 {

View File

@ -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())

View File

@ -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(());

View File

@ -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)
},

View File

@ -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()),
}
}

View File

@ -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;

View File

@ -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)]

View File

@ -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)
}
}

View File

@ -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)
}
}
}

View File

@ -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,

View File

@ -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();

View File

@ -15,10 +15,7 @@ pub use self::{
GetOriginalDst,
SoOriginalDst
},
connect::{
Connect,
DnsNameAndPort, Host, HostAndPort, HostAndPortError,
},
connect::Connect,
connection::{
BoundPort,
Connection,