[Get it on Codeberg][Source] Happy Eyeballs   [![Documentation]][Docs.rs] [![Latest version]][crates.io] [![Licence]][Spdx.org] ============== [Source]: https://codeberg.org/KMK/happy-eyeballs [Docs.rs]: https://docs.rs/happy-eyeballs [crates.io]: https://crates.io/crates/happy-eyeballs [Spdx.org]: https://spdx.org/licenses/0BSD.html [Licence]: https://img.shields.io/crates/l/happy-eyeballs [Get it on Codeberg]: https://get-it-on.codeberg.org/get-it-on-blue-on-white.svg [Documentation]: https://img.shields.io/docsrs/happy-eyeballs [Latest version]: https://img.shields.io/crates/v/happy-eyeballs [Happy Eyeballs](https://en.wikipedia.org/wiki/Happy_Eyeballs) is a technique used to provide a good user experience when connecting from dual-stack (IPv4 and IPv6) clients. This crate implements the Happy Eyeballs Connections setup described in [RFC 8305](https://datatracker.ietf.org/doc/html/rfc8305). It provides a drop-in replacement for [`std::net::TcpStream::connect`], and for async usage [`tokio::net::TcpStream::connect`](https://docs.rs/tokio/latest/tokio/net/struct.TcpStream.html#method.connect) and [`async_std::net::TcpStream::connect`](https://docs.rs/async-std/latest/async_std/net/struct.TcpStream.html#method.connect). Basic Usage ----------- Just call [`happy_eyeballs::connect`](connect) instead of [`TcpStream::connect`] ```no_run fn main() -> Result<(), std::io::Error> { let socket_addr= ("www.example", 80); let tcp_stream = happy_eyeballs::connect(socket_addr)?; println!("Connected: {tcp_stream:?}"); Ok(()) } ``` Feature flags ------------- * `async-std` provides [`happy_eyeballs::async_std::connect`], * `std-net` provides [`happy_eyeballs::connect`](connect). This is a default feature, * `tokio` provides [`happy_eyeballs::tokio::connect`]. Limitations ----------- ### DNS requests are made upfront The first thing that `happy_eyeballs::connect()` does is to resolve the hostnames into all the IP addresses. It does not initiate connection attempts before the resolution process has ended. Whereas the [RFC says](https://datatracker.ietf.org/doc/html/rfc8305#section-3): > Implementations SHOULD NOT wait for both families of answers to > return before attempting connection establishment. If one query > fails to return or takes significantly longer to return, waiting for > the second address family can significantly delay the connection > establishment of the first one. Therefore, the client SHOULD treat > DNS resolution as asynchronous ### address sorting is not configurable and favors IPv6. Once the resolution process is over, `happy-eyeballs` rearranges the resulting addresses to interleave IPv6 and IPv4. It would be better to use [Destination Address Selection](https://datatracker.ietf.org/doc/html/rfc6724#section-6). Though, as the underlying implementation usually calls `getaddrinfo(3)`, it is subject to some configuribility provided by the OS. But `happy_eyeballs::connect()` will force IPv6 first and the interleaving of IPv6 and IPv4. ### IPv4/IPv6 address interleaving is simple. As is, `happy-eyeballs` alternatively tries IPv6 then IPv4. The RFC suggests that this interleaving be configurable through a "First Address Family Count" ### The implementation is stateless Currently the algorithm does the same thing each time. If an early address proves to be unreachable or unresponsive, it will still be retried with the same preference during subsequent connections. It would be nice to make the client more stateful: > If the client has historical RTT data gathered from other > connections to the same host or prefix, it can use this information > to influence its [Connection Attempt] delay. and > If the client is stateful and has a history of expected round-trip > times (RTTs) for the routes to access each address, it SHOULD add a > Destination Address Selection rule between rules 8 and 9 that > prefers addresses with lower RTTs. If the client keeps track of > which addresses it used in the past, it SHOULD add another > Destination Address Selection rule between the RTT rule and rule 9, > which prefers used addresses over unused ones. See also ======== * [Bemused Eyeballs: Tailoring Dual Stack Applications for a CGN Environment](https://labs.apnic.net/index.php/2012/05/13/bemused-eyeballs-tailoring-dual-stack-applications-for-a-cgn-environment/) explains Happy Eyeballs in depth. Also [with graphics](https://www.potaroo.net/ispcol/2012-05/notquite.pdf) and the [slides Analysing Dual Stack Behaviour and IPv6 Quality](https://www.potaroo.net/presentations/2012-08-28-dual-stack-quality-apnic34.pdf) * [Measuring TCP Connection Establishment Times of Dual-Stacked Web Services](https://vaibhavbajpai.com/documents/papers/proceedings/dualstack-tcp-cnsm-2013.pdf) > we compare the IPv4 and IPv6 connectivity of a dual-stacked host > using a metric that measures the Transmission Control Protocol (TCP) > connection establishment time to a number of popular web > services. We witnessed several cases where the connection > establishment times and their variations over IPv6 are higher. * [`std::net` wishlist](https://github.com/rust-lang/rfcs/issues/957#issuecomment-103233989) and [Socket2](https://github.com/rust-lang/socket2). * The crate [happyeyeballs](https://github.com/conblem/happyeyeballs): - only works with Tokio, - limited to one IPv6 and one IPv4 connection.