use std::error::Error as StdError;
use std::{fmt, mem};
#[cfg(try_from)] use std::convert::TryFrom;
use bytes::{BufMut, Bytes, BytesMut};
use futures::Future;
use http::{uri, Response, Uri};
use tokio_io::{AsyncRead, AsyncWrite};
#[cfg(feature = "runtime")] pub mod dns;
#[cfg(feature = "runtime")] mod http;
#[cfg(feature = "runtime")] pub use self::http::{HttpConnector, HttpInfo};
pub trait Connect: Send + Sync {
    
    type Transport: AsyncRead + AsyncWrite + Send + 'static;
    
    type Error: Into<Box<dyn StdError + Send + Sync>>;
    
    type Future: Future<Item=(Self::Transport, Connected), Error=Self::Error> + Send;
    
    fn connect(&self, dst: Destination) -> Self::Future;
}
#[derive(Clone, Debug)]
pub struct Destination {
    pub(super) uri: Uri,
}
#[derive(Debug)]
pub struct Connected {
    pub(super) alpn: Alpn,
    pub(super) is_proxied: bool,
    pub(super) extra: Option<Extra>,
}
pub(super) struct Extra(Box<dyn ExtraInner>);
#[derive(Clone, Copy, Debug, PartialEq)]
pub(super) enum Alpn {
    H2,
    None,
}
impl Destination {
    
    
    
    
    
    
    pub fn try_from_uri(uri: Uri) -> ::Result<Self> {
        uri.authority_part().ok_or(::error::Parse::Uri)?;
        uri.scheme_part().ok_or(::error::Parse::Uri)?;
        Ok(Destination { uri })
    }
    
    #[inline]
    pub fn scheme(&self) -> &str {
        self.uri
            .scheme_str()
            .unwrap_or("")
    }
    
    #[inline]
    pub fn host(&self) -> &str {
        self.uri
            .host()
            .unwrap_or("")
    }
    
    #[inline]
    pub fn port(&self) -> Option<u16> {
        self.uri.port_u16()
    }
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    pub fn set_scheme(&mut self, scheme: &str) -> ::Result<()> {
        let scheme = scheme.parse().map_err(::error::Parse::from)?;
        self.update_uri(move |parts| {
            parts.scheme = Some(scheme);
        })
    }
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    pub fn set_host(&mut self, host: &str) -> ::Result<()> {
        
        if host.contains('@') {
            return Err(::error::Parse::Uri.into());
        }
        let auth = if let Some(port) = self.port() {
            let bytes = Bytes::from(format!("{}:{}", host, port));
            uri::Authority::from_shared(bytes)
                .map_err(::error::Parse::from)?
        } else {
            let auth = host.parse::<uri::Authority>().map_err(::error::Parse::from)?;
            if auth.port_part().is_some() { 
                return Err(::error::Parse::Uri.into());
            }
            auth
        };
        self.update_uri(move |parts| {
            parts.authority = Some(auth);
        })
    }
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    pub fn set_port<P>(&mut self, port: P)
    where
        P: Into<Option<u16>>,
    {
        self.set_port_opt(port.into());
    }
    fn set_port_opt(&mut self, port: Option<u16>) {
        use std::fmt::Write;
        let auth = if let Some(port) = port {
            let host = self.host();
            
            
            let cap = host.len() + 1 + 5;
            let mut buf = BytesMut::with_capacity(cap);
            buf.put_slice(host.as_bytes());
            buf.put_u8(b':');
            write!(buf, "{}", port)
                .expect("should have space for 5 digits");
            uri::Authority::from_shared(buf.freeze())
                .expect("valid host + :port should be valid authority")
        } else {
            self.host().parse()
                .expect("valid host without port should be valid authority")
        };
        self.update_uri(move |parts| {
            parts.authority = Some(auth);
        })
            .expect("valid uri should be valid with port");
    }
    fn update_uri<F>(&mut self, f: F) -> ::Result<()>
    where
        F: FnOnce(&mut uri::Parts)
    {
        
        let old_uri = mem::replace(&mut self.uri, Uri::default());
        
        let mut parts: uri::Parts = old_uri.clone().into();
        f(&mut parts);
        match Uri::from_parts(parts) {
            Ok(uri) => {
                self.uri = uri;
                Ok(())
            },
            Err(err) => {
                self.uri = old_uri;
                Err(::error::Parse::from(err).into())
            },
        }
    }
    
}
#[cfg(try_from)]
impl TryFrom<Uri> for Destination {
    type Error = ::error::Error;
    fn try_from(uri: Uri) -> Result<Self, Self::Error> {
        Destination::try_from_uri(uri)
    }
}
impl Connected {
    
    pub fn new() -> Connected {
        Connected {
            alpn: Alpn::None,
            is_proxied: false,
            extra: None,
        }
    }
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    pub fn proxy(mut self, is_proxied: bool) -> Connected {
        self.is_proxied = is_proxied;
        self
    }
    
    pub fn extra<T: Clone + Send + Sync + 'static>(mut self, extra: T) -> Connected {
        if let Some(prev) = self.extra {
            self.extra = Some(Extra(Box::new(ExtraChain(prev.0, extra))));
        } else {
            self.extra = Some(Extra(Box::new(ExtraEnvelope(extra))));
        }
        self
    }
    
    
    pub fn negotiated_h2(mut self) -> Connected {
        self.alpn = Alpn::H2;
        self
    }
    
    
    pub(super) fn clone(&self) -> Connected {
        Connected {
            alpn: self.alpn.clone(),
            is_proxied: self.is_proxied,
            extra: self.extra.clone(),
        }
    }
}
impl Extra {
    pub(super) fn set(&self, res: &mut Response<::Body>) {
        self.0.set(res);
    }
}
impl Clone for Extra {
    fn clone(&self) -> Extra {
        Extra(self.0.clone_box())
    }
}
impl fmt::Debug for Extra {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("Extra")
            .finish()
    }
}
trait ExtraInner: Send + Sync {
    fn clone_box(&self) -> Box<dyn ExtraInner>;
    fn set(&self, res: &mut Response<::Body>);
}
#[derive(Clone)]
struct ExtraEnvelope<T>(T);
impl<T> ExtraInner for ExtraEnvelope<T>
where
    T: Clone + Send + Sync + 'static
{
    fn clone_box(&self) -> Box<dyn ExtraInner> {
        Box::new(self.clone())
    }
    fn set(&self, res: &mut Response<::Body>) {
        res.extensions_mut().insert(self.0.clone());
    }
}
struct ExtraChain<T>(Box<dyn ExtraInner>, T);
impl<T: Clone> Clone for ExtraChain<T> {
    fn clone(&self) -> Self {
        ExtraChain(self.0.clone_box(), self.1.clone())
    }
}
impl<T> ExtraInner for ExtraChain<T>
where
    T: Clone + Send + Sync + 'static
{
    fn clone_box(&self) -> Box<dyn ExtraInner> {
        Box::new(self.clone())
    }
    fn set(&self, res: &mut Response<::Body>) {
        self.0.set(res);
        res.extensions_mut().insert(self.1.clone());
    }
}
#[cfg(test)]
mod tests {
    use super::{Connected, Destination, TryFrom};
    #[test]
    fn test_destination_set_scheme() {
        let mut dst = Destination {
            uri: "http://hyper.rs".parse().expect("initial parse"),
        };
        assert_eq!(dst.scheme(), "http");
        assert_eq!(dst.host(), "hyper.rs");
        dst.set_scheme("https").expect("set https");
        assert_eq!(dst.scheme(), "https");
        assert_eq!(dst.host(), "hyper.rs");
        dst.set_scheme("<im not a scheme//?>").unwrap_err();
        assert_eq!(dst.scheme(), "https", "error doesn't modify dst");
        assert_eq!(dst.host(), "hyper.rs", "error doesn't modify dst");
    }
    #[test]
    fn test_destination_set_host() {
        let mut dst = Destination {
            uri: "http://hyper.rs".parse().expect("initial parse"),
        };
        assert_eq!(dst.scheme(), "http");
        assert_eq!(dst.host(), "hyper.rs");
        assert_eq!(dst.port(), None);
        dst.set_host("seanmonstar.com").expect("set https");
        assert_eq!(dst.scheme(), "http");
        assert_eq!(dst.host(), "seanmonstar.com");
        assert_eq!(dst.port(), None);
        dst.set_host("/im-not a host! >:)").unwrap_err();
        assert_eq!(dst.scheme(), "http", "error doesn't modify dst");
        assert_eq!(dst.host(), "seanmonstar.com", "error doesn't modify dst");
        assert_eq!(dst.port(), None, "error doesn't modify dst");
        
        dst.set_host("seanmonstar.com:3030").expect_err("set_host sneaky port");
        assert_eq!(dst.scheme(), "http", "error doesn't modify dst");
        assert_eq!(dst.host(), "seanmonstar.com", "error doesn't modify dst");
        assert_eq!(dst.port(), None, "error doesn't modify dst");
        
        dst.set_host("sean@nope").expect_err("set_host sneaky userinfo");
        assert_eq!(dst.scheme(), "http", "error doesn't modify dst");
        assert_eq!(dst.host(), "seanmonstar.com", "error doesn't modify dst");
        assert_eq!(dst.port(), None, "error doesn't modify dst");
        
        dst.set_host("[::1]").expect("set_host with IPv6");
        assert_eq!(dst.host(), "[::1]");
        assert_eq!(dst.port(), None, "IPv6 didn't affect port");
        
        dst.set_host("[::2]:1337").expect_err("set_host with IPv6 and sneaky port");
        assert_eq!(dst.host(), "[::1]");
        assert_eq!(dst.port(), None);
        
        
        let mut dst = Destination {
            uri: "http://hyper.rs:8080".parse().expect("initial parse 2"),
        };
        assert_eq!(dst.scheme(), "http");
        assert_eq!(dst.host(), "hyper.rs");
        assert_eq!(dst.port(), Some(8080));
        dst.set_host("seanmonstar.com").expect("set host");
        assert_eq!(dst.scheme(), "http");
        assert_eq!(dst.host(), "seanmonstar.com");
        assert_eq!(dst.port(), Some(8080));
        dst.set_host("/im-not a host! >:)").unwrap_err();
        assert_eq!(dst.scheme(), "http", "error doesn't modify dst");
        assert_eq!(dst.host(), "seanmonstar.com", "error doesn't modify dst");
        assert_eq!(dst.port(), Some(8080), "error doesn't modify dst");
        
        dst.set_host("seanmonstar.com:3030").expect_err("set_host sneaky port");
        assert_eq!(dst.scheme(), "http", "error doesn't modify dst");
        assert_eq!(dst.host(), "seanmonstar.com", "error doesn't modify dst");
        assert_eq!(dst.port(), Some(8080), "error doesn't modify dst");
        
        dst.set_host("sean@nope").expect_err("set_host sneaky userinfo");
        assert_eq!(dst.scheme(), "http", "error doesn't modify dst");
        assert_eq!(dst.host(), "seanmonstar.com", "error doesn't modify dst");
        assert_eq!(dst.port(), Some(8080), "error doesn't modify dst");
        
        dst.set_host("[::1]").expect("set_host with IPv6");
        assert_eq!(dst.host(), "[::1]");
        assert_eq!(dst.port(), Some(8080), "IPv6 didn't affect port");
        
        dst.set_host("[::2]:1337").expect_err("set_host with IPv6 and sneaky port");
        assert_eq!(dst.host(), "[::1]");
        assert_eq!(dst.port(), Some(8080));
    }
    #[test]
    fn test_destination_set_port() {
        let mut dst = Destination {
            uri: "http://hyper.rs".parse().expect("initial parse"),
        };
        assert_eq!(dst.scheme(), "http");
        assert_eq!(dst.host(), "hyper.rs");
        assert_eq!(dst.port(), None);
        dst.set_port(None);
        assert_eq!(dst.scheme(), "http");
        assert_eq!(dst.host(), "hyper.rs");
        assert_eq!(dst.port(), None);
        dst.set_port(8080);
        assert_eq!(dst.scheme(), "http");
        assert_eq!(dst.host(), "hyper.rs");
        assert_eq!(dst.port(), Some(8080));
        
        let mut dst = Destination {
            uri: "http://hyper.rs:8080".parse().expect("initial parse 2"),
        };
        assert_eq!(dst.scheme(), "http");
        assert_eq!(dst.host(), "hyper.rs");
        assert_eq!(dst.port(), Some(8080));
        dst.set_port(3030);
        assert_eq!(dst.scheme(), "http");
        assert_eq!(dst.host(), "hyper.rs");
        assert_eq!(dst.port(), Some(3030));
        dst.set_port(None);
        assert_eq!(dst.scheme(), "http");
        assert_eq!(dst.host(), "hyper.rs");
        assert_eq!(dst.port(), None);
    }
    #[cfg(try_from)]
    #[test]
    fn test_try_from_destination() {
        let uri: http::Uri = "http://hyper.rs".parse().expect("initial parse");
        let result = Destination::try_from(uri);
        assert_eq!(result.is_ok(), true);
    }
    
    #[cfg(try_from)]    
    #[test]
    fn test_try_from_no_scheme() {
        let uri: http::Uri = "hyper.rs".parse().expect("initial parse error");
        let result = Destination::try_from(uri);
        assert_eq!(result.is_err(), true);
    }
    #[derive(Clone, Debug, PartialEq)]
    struct Ex1(usize);
    #[derive(Clone, Debug, PartialEq)]
    struct Ex2(&'static str);
    #[derive(Clone, Debug, PartialEq)]
    struct Ex3(&'static str);
    #[test]
    fn test_connected_extra() {
        let c1 = Connected::new()
            .extra(Ex1(41));
        let mut res1 = ::Response::new(::Body::empty());
        assert_eq!(res1.extensions().get::<Ex1>(), None);
        c1
            .extra
            .as_ref()
            .expect("c1 extra")
            .set(&mut res1);
        assert_eq!(res1.extensions().get::<Ex1>(), Some(&Ex1(41)));
    }
    #[test]
    fn test_connected_extra_chain() {
        
        
        let c1 = Connected::new()
            .extra(Ex1(45))
            .extra(Ex2("zoom"))
            .extra(Ex3("pew pew"));
        let mut res1 = ::Response::new(::Body::empty());
        assert_eq!(res1.extensions().get::<Ex1>(), None);
        assert_eq!(res1.extensions().get::<Ex2>(), None);
        assert_eq!(res1.extensions().get::<Ex3>(), None);
        c1
            .extra
            .as_ref()
            .expect("c1 extra")
            .set(&mut res1);
        assert_eq!(res1.extensions().get::<Ex1>(), Some(&Ex1(45)));
        assert_eq!(res1.extensions().get::<Ex2>(), Some(&Ex2("zoom")));
        assert_eq!(res1.extensions().get::<Ex3>(), Some(&Ex3("pew pew")));
        
        let c2 = Connected::new()
            .extra(Ex1(33))
            .extra(Ex2("hiccup"))
            .extra(Ex1(99));
        let mut res2 = ::Response::new(::Body::empty());
        c2
            .extra
            .as_ref()
            .expect("c2 extra")
            .set(&mut res2);
        assert_eq!(res2.extensions().get::<Ex1>(), Some(&Ex1(99)));
        assert_eq!(res2.extensions().get::<Ex2>(), Some(&Ex2("hiccup")));
    }
}