Feature: use Happy Eyeballs to make network connections (TCP-only) (#9199)

Hostnames like "content.openttd.org" resolve into multiple IPv4 and IPv6.
It is possible that either of the IPs is not working, either due to
a poorly configured OS (having IPv6 but no valid route), broken network
paths, or a service that is temporary unavailable.

Instead of trying the IPs one by one, waiting for a 3s timeout between
each, be a bit more like browsers, and stack attempts on top of each
other with slight delays. This is called Happy Eyebells.

Initially, try the first IPv6 address. If within 250ms there is no
connection yet, try the first IPv4 address. 250ms later, try the
second IPv6 address, etc, till all addresses are tried.

If any connection is created, abort all the other (pending) connections
and use the one that is created. If all fail 3s after the last connect(),
trigger a timeout for all.
This commit is contained in:
Patric Stout
2021-05-06 23:13:35 +02:00
committed by GitHub
parent f1dfa661a1
commit f7e390bdc0
8 changed files with 334 additions and 146 deletions

View File

@@ -15,6 +15,7 @@
#include "address.h"
#include "packet.h"
#include <chrono>
#include <atomic>
/** The states of sending the packets. */
@@ -63,23 +64,28 @@ public:
*/
class TCPConnecter {
private:
std::atomic<bool> connected;///< Whether we succeeded in making the connection
std::atomic<bool> aborted; ///< Whether we bailed out (i.e. connection making failed)
bool killed; ///< Whether we got killed
SOCKET sock; ///< The socket we're connecting with
addrinfo *ai = nullptr; ///< getaddrinfo() allocated linked-list of resolved addresses.
std::vector<addrinfo *> addresses; ///< Addresses we can connect to.
size_t current_address = 0; ///< Current index in addresses we are trying.
void Connect();
std::vector<SOCKET> sockets; ///< Pending connect() attempts.
std::chrono::steady_clock::time_point last_attempt; ///< Time we last tried to connect.
static void ThreadEntry(TCPConnecter *param);
std::atomic<bool> is_resolved = false; ///< Whether resolving is done.
protected:
/** Address we're connecting to */
NetworkAddress address;
void Resolve();
void OnResolved(addrinfo *ai);
bool TryNextAddress();
void Connect(addrinfo *address);
bool CheckActivity();
static void ResolveThunk(TCPConnecter *connecter);
public:
std::string connection_string; ///< Current address we are connecting to (before resolving).
TCPConnecter(const std::string &connection_string, uint16 default_port);
/** Silence the warnings */
virtual ~TCPConnecter() {}
virtual ~TCPConnecter();
/**
* Callback when the connection succeeded.