Crate happy_eyeballs
source ·Expand description
Happy Eyeballs
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
provideshappy_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
-
Bemused Eyeballs: Tailoring Dual Stack Applications for a CGN Environment explains Happy Eyeballs in depth. Also with graphics and the slides Analysing Dual Stack Behaviour and IPv6 Quality
-
Measuring TCP Connection Establishment Times of Dual-Stacked Web Services
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 and Socket2. -
The crate happyeyeballs:
- only works with Tokio,
- limited to one IPv6 and one IPv4 connection.
Functions
- Opens a TCP connection to a remote host.