Skip to content

Commit

Permalink
libcurl/conn/how.md: How libcurl connects
Browse files Browse the repository at this point in the history
  • Loading branch information
bagder committed Jan 7, 2024
1 parent 7b32a4a commit 139b191
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 0 deletions.
1 change: 1 addition & 0 deletions SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@
* [HSTS](libcurl/callbacks/hsts.md)
* [Prereq](libcurl/callbacks/prereq.md)
* [Connection control](libcurl/conn.md)
* [How libcurl connects](libcurl/conn/how.md)
* [Connection reuse](libcurl/conn/reuse.md)
* [Name resolving](libcurl/conn/names.md)
* [Proxies](libcurl/conn/proxies.md)
Expand Down
1 change: 1 addition & 0 deletions libcurl/conn.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ involved. A connection done using an Internet transport protocol like TCP or
QUIC. Transfers are done *over* connections and libcurl offers a lot of
concepts for connections and options to control how it works with them.

* [How libcurl connects](conn/how.md)
* [Connection reuse](conn/reuse.md)
* [Name resolving](conn/names.md)
* [Proxies](conn/proxies.md)
74 changes: 74 additions & 0 deletions libcurl/conn/how.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# How libcurl connects

When libcurl is about to do an Internet transfer, it first *resolves* the host
name to get a number of IP addresses for the host. A hostname needs to have
at least one address for libcurl to be able to connect to it.

A hostname can have both IPv4 addresses and IPv6 addresses and they can have
a set of both.

If the host only returned addresses of a single IP family, libcurl iterates
over each address and tries to connect. If the connect attempt fails for an
IP, libcurl continues to try the next entry until the entire list is
exhausted.

An application can limit which IP versions libcurl uses by setting
`CURLOPT_IPRESOLVE`.

## Happy Eyeballs

When it has received both IPv4 and IPv6 addresses for a host, libcurl first
tries to connect to an IPv6 address and after a short delay it tries
connecting to the first IPv4 address - at the same time and in parallel. Once
one of the attempts succeeds, the others are discarded. This method of
attempting to connect using both families at the same time is called **Happy
Eyeballs** and is the widely accepted best practice for Internet clients.

An application can set the delay with which the second family connect attempt
starts in the Happy Eyeball procedure by using
`CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS`.

## Timeout and halving

The connection phase has a maximum allowed time (set with
`CURLOPT_CONNECTTIMEOUT_MS`), which defaults to 300 seconds. The entire
connect procedure is deemed failed if no connect has succeeded within that
time.

When libcurl has multiple addresses left to try to connect to, and there is
more than 600 millisecond left, it will at most allow half the remaining time
for this attempt. This is to avoid a single "sink-hole address" make libcurl
spend its entire timeout on that bad entry.

For example: if there are 1000 milliseconds left of the timeout and there are
two IP addresses left to try to connect to, libcurl then only allows 500
milliseconds on the next attempt.

If there instead only are 600 milliseconds left of the timeout and there are
two IP addresses left to try to connect to, libcurl allows the entire
remaining timeout period on the next attempt, in order to not make it too
short to succeed. The timeout halving approach is only done as long as there
is more than 600 milliseconds remaining.

## HTTP/3

For applications that ask libcurl to use HTTP/3, it adds another layer of
Happy Eyeballs. HTTP/3 works over QUIC and QUIC is a different transport
protocol than TCP and a mechanism that sometimes is blocked or otherwise does
not work as well as TCP. In an effort to smooth out the problems this brings,
libcurl performs QUIC connects *in parallel* with regular TCP connects in
addition to the different IP version connects described above.

When libcurl get both IPv4 and IPv6 addresses for a host, and it wants to do
HTTP/3 with the host, it proceeds like this:

1. Start an IPv6 QUIC connect attempt, iterate over the IPv6 addresses
2. After a short delay, start an IPv4 QUIC connect attempt, iterate over the IPv4 addresses
3. After a short delay, start an IPv6 TCP connect attempt, iterate over the IPv6 addresses
4. After a short delay, start an IPv4 TCP connect attempt, iterate over the IPv4 addresses

Once a connect attempt is successful, all the other ones are immediately
discarded.

The HTTP/3 happy eyeballing is done when libcurl is asked to use
`CURL_HTTP_VERSION_3` but not if set to `CURL_HTTP_VERSION_3ONLY`.

0 comments on commit 139b191

Please sign in to comment.