Crate happy_eyeballs

source ·
Expand description

Get it on Codeberg

Happy Eyeballs   Documentation Latest version Licence

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. It provides a drop-in replacement for std::net::TcpStream::connect, and for async usage tokio::net::TcpStream::connect and async_std::net::TcpStream::connect.

Basic Usage

Just call happy_eyeballs::connect instead of [TcpStream::connect]

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. 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:

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. 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

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.

Functions

  • Opens a TCP connection to a remote host.