hyper-http-connector 0.1.0

Replacement for the default Hyper HttpConnector
Documentation
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};

mod c_ares;

pub use self::c_ares::GLOBAL_RESOLVER;

pub struct IpAddrs {
    ips: Vec<SocketAddr>,
    current: usize,
    offset: usize,
}

impl IpAddrs {
    pub fn new(port: u16, mut ips: Vec<Ipv4Addr>, offset: usize) -> IpAddrs {
        ips.sort();

        let ips = ips
            .into_iter()
            .map(|ip| SocketAddr::V4(SocketAddrV4::new(ip, port)))
            .collect::<Vec<_>>();

        IpAddrs {
            ips,
            current: 0,
            offset,
        }
    }

    pub fn try_parse(host: &str, port: u16) -> Option<IpAddrs> {
        if let Ok(addr) = host.parse::<Ipv4Addr>() {
            let addr = SocketAddrV4::new(addr, port);
            return Some(IpAddrs {
                ips: vec![SocketAddr::V4(addr)],
                current: 0,
                offset: 0,
            });
        }

        if let Ok(addr) = host.parse::<Ipv6Addr>() {
            let addr = SocketAddrV6::new(addr, port, 0, 0);
            return Some(IpAddrs {
                ips: vec![SocketAddr::V6(addr)],
                current: 0,
                offset: 0,
            });
        }

        None
    }
}

impl Iterator for IpAddrs {
    type Item = SocketAddr;

    #[inline]
    fn next(&mut self) -> Option<Self::Item> {
        if self.current == self.ips.len() {
            return None;
        }

        let index = (self.current + self.offset) % self.ips.len();
        let item = self.ips[index];
        self.current += 1;
        Some(item)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    macro_rules! assert_eq_ip {
        ($elem:expr, $expected:expr) => {
            match $elem {
                Some(SocketAddr::V4(addr)) => assert_eq!(addr.ip(), $expected),
                _ => panic!("Not a Some(SocketAddrV4)!"),
            }
        };
    }

    #[test]
    fn it_sorts_by_ip() {
        let mut a = IpAddrs::new(
            999,
            vec![
                Ipv4Addr::new(127, 0, 0, 1),
                Ipv4Addr::new(127, 0, 0, 4),
                Ipv4Addr::new(127, 0, 0, 3),
                Ipv4Addr::new(127, 0, 0, 2),
            ],
            0,
        );

        assert_eq_ip!(a.next(), &Ipv4Addr::new(127, 0, 0, 1));
        assert_eq_ip!(a.next(), &Ipv4Addr::new(127, 0, 0, 2));
        assert_eq_ip!(a.next(), &Ipv4Addr::new(127, 0, 0, 3));
        assert_eq_ip!(a.next(), &Ipv4Addr::new(127, 0, 0, 4));
        assert_eq!(a.next(), None);
    }

    #[test]
    fn shifted_offsets_work() {
        let mut a = IpAddrs::new(
            999,
            vec![
                Ipv4Addr::new(127, 0, 0, 1),
                Ipv4Addr::new(127, 0, 0, 4),
                Ipv4Addr::new(127, 0, 0, 3),
                Ipv4Addr::new(127, 0, 0, 2),
            ],
            2,
        );

        assert_eq_ip!(a.next(), &Ipv4Addr::new(127, 0, 0, 3));
        assert_eq_ip!(a.next(), &Ipv4Addr::new(127, 0, 0, 4));
        assert_eq_ip!(a.next(), &Ipv4Addr::new(127, 0, 0, 1));
        assert_eq_ip!(a.next(), &Ipv4Addr::new(127, 0, 0, 2));
        assert_eq!(a.next(), None);
    }
}