//! Defines the `ListenEndpoint` type and related items. use std::error; use std::iter; use std::fmt; use std::io; use std::str::FromStr; use std::net; use std::net::{ToSocketAddrs, IpAddr}; use url; use void::Void; use socket_addr::SocketAddr; use protocol::Protocol; use protocol; /// Enum representing listening endpoint of supported protocols #[derive(Copy, Debug, Eq, PartialEq, Hash, Clone, RustcEncodable, RustcDecodable)] pub enum ListenEndpoint { /// A TCP listen endpoint. Tcp(SocketAddr), /// A uTP listen endpoint. Utp(SocketAddr), } impl ListenEndpoint { /// Get the protocol of this endpoint. pub fn protocol(&self) -> Protocol { match *self { ListenEndpoint::Utp(..) => Protocol::Utp, ListenEndpoint::Tcp(..) => Protocol::Tcp, } } /* pub fn is_loopback(&self) -> bool { match self.socket_addr.ip() { IpAddr::V4(ipv4_addr) => ipv4_addr.is_loopback(), IpAddr::V6(ipv6_addr) => ipv6_addr.is_loopback(), } } */ } impl fmt::Display for ListenEndpoint { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { ListenEndpoint::Tcp(addr) => write!(f, "tcp-listen://{}/", addr), ListenEndpoint::Utp(addr) => write!(f, "utp-listen://{}/", addr), } } } /// A trait for types that can be converted to an iterator of listen endpoints. This is similar to /// the `ToSocketAddrs` trait from the standard library. pub trait ToListenEndpoints { /// Errors that can occur when parsing the next endpoint. type Err: error::Error + Send + 'static; /// The iterator returned by `to_listen_endpoints`. type Iter: Iterator>; /// Returns an iterator that yields a sequence of listen endpoints (or errors). fn to_listen_endpoints(&self) -> Self::Iter; } impl ToListenEndpoints for ListenEndpoint { type Err = Void; type Iter = iter::Once>; fn to_listen_endpoints(&self) -> iter::Once> { iter::once(Ok(*self)) } } impl ToListenEndpoints for str { type Err = ParseListenEndpointError; type Iter = iter::Once>; fn to_listen_endpoints(&self) -> iter::Once> { iter::once(ListenEndpoint::from_str(self)) } } impl<'a, T: ToListenEndpoints + ?Sized> ToListenEndpoints for &'a T { type Err = T::Err; type Iter = T::Iter; fn to_listen_endpoints(&self) -> T::Iter { (*self).to_listen_endpoints() } } /// Iterator returned when `ToListenEndpoints::to_listen_endpoints` is called on a slice. pub struct SliceListenEndpointsIter<'a, E: ToListenEndpoints + 'a> { slice: &'a [E], iter: Option, } impl<'a, E: ToListenEndpoints + 'a> ToListenEndpoints for &'a [E] { type Iter = SliceListenEndpointsIter<'a, E>; type Err = E::Err; fn to_listen_endpoints(&self) -> SliceListenEndpointsIter<'a, E> { match self.len() { 0 => SliceListenEndpointsIter { slice: &[], iter: None, }, _ => SliceListenEndpointsIter { slice: &self[1..], iter: Some(self[0].to_listen_endpoints()), }, } } } impl<'a, E: ToListenEndpoints + 'a> Iterator for SliceListenEndpointsIter<'a, E> { type Item = Result; fn next(&mut self) -> Option> { loop { match self.iter { None => return None, Some(ref mut iter) => { let res = iter.next(); match res { Some(x) => return Some(x), None => (), }; }, }; match self.slice.len() { 0 => self.iter = None, _ => { self.iter = Some(self.slice[0].to_listen_endpoints()); self.slice = &self.slice[1..]; }, } } } } quick_error! { /// Errors returned by `FromStr::from_str` when parsing an `ListenEndpoint` #[derive(Debug)] pub enum ParseListenEndpointError { /// Error parsing url BadUrl(err: url::ParseError) { description("Error parsing url") display("Error parsing url: {}", err) cause(err) } /// Listen endpoint url contains unexpected query UnexpectedQuery(query: String) { description("Listen endpoint url contains unexpected query") display("Listen endpoint url contains unexpected query: {:?}", query) } /// Listen endpoint url contains unexpected fragment") UnexpectedFragment(fragment: String) { description("Listen endpoint url contains unexpected fragment") display("Listen endpoint url contains unexpected fragment: {:?}", fragment) } /// Listen endpoint url contains unexpected username UnexpectedUsername(username: String) { description("Listen endpoint url contains unexpected username") display("Listen endpoint url contains unexpected username: {:?}", username) } /// Listen endpoint url contains unexpected password UnexpectedPassword { description("Listen endpoint url contains unexpected password") } /// Listen endpoint url contains unexpected path UnexpectedPath(path: Vec) { description("Listen endpoint url contains unexpected path") display("Listen endpoint url contains unexpected path: {:?}", path) } /// Failed to parse the address of the listen endpoint url ParseAddr(err: io::Error) { description("Failed to parse the address of the listen endpoint url") display("Failed to parse the address of the listen endpoint url: {}", err) cause(err) } /// Listen endpoint url is missing the address MissingAddr { description("Listen endpoint url is missing the address") } /// Multiple addresses were parsed from the listen endpoint url MultipleAddrs(a0: SocketAddr, a1: SocketAddr) { description("Multiple addresses were parsed from the listen endpoint url") display("Multiple addresses were parsed from the listen endpoint url: {} and {}", a0, a1) } /// Unknown scheme in listen endpoint url UnknownScheme(scheme: String) { description("Unknown scheme in listen endpoint url") display("Unknown scheme in listen endpoint url: {:?}", scheme) } } } impl FromStr for ListenEndpoint { type Err = ParseListenEndpointError; fn from_str(s: &str) -> Result { let url = match url::UrlParser::new().scheme_type_mapper(protocol::scheme_type_mapper) .parse(s) { Ok(url) => url, Err(e) => return Err(ParseListenEndpointError::BadUrl(e)), }; match &url.scheme[..] { "tcp-listen" => { if let Some(q) = url.query { return Err(ParseListenEndpointError::UnexpectedQuery(q)); } if let Some(f) = url.fragment { return Err(ParseListenEndpointError::UnexpectedFragment(f)); } match url.scheme_data { url::SchemeData::NonRelative(..) => panic!("scheme_type_mapper should not have returned this."), url::SchemeData::Relative(rsd) => { if rsd.username != "" { return Err(ParseListenEndpointError::UnexpectedUsername(rsd.username)); } if rsd.password.is_some() { // Don't return the password in the error. return Err(ParseListenEndpointError::UnexpectedPassword); } if !(rsd.path.len() == 1 && &rsd.path[0] == "") { return Err(ParseListenEndpointError::UnexpectedPath(rsd.path)); } let port = rsd.port.unwrap_or(0); match rsd.host { url::Host::Domain(s) => { let mut iter = match (&s[..], port).to_socket_addrs() { Ok(iter) => iter, Err(e) => return Err(ParseListenEndpointError::ParseAddr(e)), }; match (iter.next(), iter.next()) { (Some(a0), Some(a1)) => return Err(ParseListenEndpointError::MultipleAddrs(SocketAddr(a0), SocketAddr(a1))), (Some(a), None) => Ok(ListenEndpoint::Tcp(SocketAddr(a))), _ => return Err(ParseListenEndpointError::MissingAddr), } }, url::Host::Ipv4(ipv4_addr) => Ok(ListenEndpoint::Tcp(SocketAddr( net::SocketAddr::new(IpAddr::V4(ipv4_addr), port) ))), url::Host::Ipv6(ipv6_addr) => Ok(ListenEndpoint::Tcp(SocketAddr( net::SocketAddr::new(IpAddr::V6(ipv6_addr), port) ))), } }, } }, _ => return Err(ParseListenEndpointError::UnknownScheme(url.scheme)), } } } #[cfg(test)] mod tests { use std::net::{IpAddr, Ipv4Addr}; use std::net; use socket_addr::SocketAddr; use listen_endpoint::{ListenEndpoint, ToListenEndpoints}; #[test] fn parse_listen_endpoint() { let s = &["tcp-listen://1.2.3.4:45666"][..]; let mut listen_endpoints = s.to_listen_endpoints(); let listen_endpoint = unwrap_result!(unwrap_option!(listen_endpoints.next(), "Expected at least one listen endpoint")); assert_eq!(listen_endpoint, ListenEndpoint::Tcp(SocketAddr( net::SocketAddr::new(IpAddr::V4(Ipv4Addr::new(1, 2, 3, 4)), 45666) ))); if let Some(res) = listen_endpoints.next() { panic!("Did not expect a second listen endpoint: {:?}", res); } } }