happy_eyeballs/
lib.rs

1#![doc = include_str!("../README.md")]
2
3use std::time::Duration;
4
5static CONNECTION_ATTEMPT_DELAY: Duration = Duration::from_millis(250);
6
7#[cfg(feature = "std-net")]
8mod std_net;
9#[cfg(feature = "std-net")]
10pub use std_net::connect;
11
12#[cfg(feature = "tokio")]
13pub mod tokio;
14
15#[cfg(feature = "async-std")]
16pub mod async_std;
17
18#[cfg(test)]
19mod test_utils {
20    use rand::{thread_rng, Rng};
21    use std::io::ErrorKind;
22    use std::io::Write;
23    use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, TcpListener};
24    use std::thread::JoinHandle;
25
26    fn listen<A: Into<IpAddr>>(addr: A) -> (TcpListener, IpAddr, u16) {
27        let addr: IpAddr = addr.into();
28        let tar_pit_port = std::env::var("HAPPY_EYEBALLS_TAR_PIT_PORT")
29            .ok()
30            .and_then(|s| u16::from_str_radix(&s, 10).ok());
31        loop {
32            // RFC 6056, RFC 6335
33            let port: u16 = thread_rng().gen_range(49152..=65535);
34            if tar_pit_port == Some(port) {
35                continue;
36            }
37            match TcpListener::bind((addr, port)) {
38                Ok(listener) => return (listener, addr, port),
39                Err(err) if err.kind() == ErrorKind::AddrInUse => (),
40                Err(e) => panic!("TcpListener::bind({addr}, {port}) failed: {e}"),
41            }
42        }
43    }
44
45    fn serve<A: Into<IpAddr>>(addr: A) -> (JoinHandle<()>, IpAddr, u16) {
46        let (serve, addr, port) = listen(addr);
47        let data = format!("{addr}");
48        let handle = std::thread::spawn(move || {
49            for s in serve.incoming() {
50                s.unwrap().write_all(data.as_bytes()).unwrap();
51            }
52        });
53        (handle, addr, port)
54    }
55
56    pub fn serve_4() -> (JoinHandle<()>, IpAddr, u16) {
57        serve(Ipv4Addr::LOCALHOST)
58    }
59
60    pub fn serve_6() -> (JoinHandle<()>, IpAddr, u16) {
61        serve(Ipv6Addr::LOCALHOST)
62    }
63
64    pub fn tar_pit<A: Into<IpAddr>>(addr: A) -> ((), IpAddr, u16) {
65        let addr: IpAddr = addr.into();
66        let port = std::env::var("HAPPY_EYEBALLS_TAR_PIT_PORT").expect(
67            "\
68To test the tar pit, set the environment variable HAPPY_EYEBALLS_TAR_PIT_PORT \
69to the port number that is blackholed in IPv4 *and* IPv6.",
70        );
71        let port = u16::from_str_radix(&port, 10).unwrap();
72        ((), addr, port)
73    }
74}