DeterministicESPAsyncWebServer v6.28.0
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
happy_eyeballs.cpp
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file happy_eyeballs.cpp
6 * @brief Dual-stack destination selection + Happy Eyeballs fallback (see happy_eyeballs.h).
7 */
8
10
11#if DETWS_ENABLE_HAPPY_EYEBALLS
12
13namespace
14{
15// Effective family for interleave: an IPv4-mapped IPv6 address is treated as IPv4.
16bool eff_is_v6(const DetIp *ip)
17{
19}
20
21int scope_rank(const DetIp *ip)
22{
23 switch (det_ip_classify(ip))
24 {
26 return 5;
28 return 4;
30 return 3;
32 return 2;
34 return 1;
35 default:
36 return 0; // unspecified
37 }
38}
39} // namespace
40
41int detws_he_pref(const DetIp *ip)
42{
43 if (!ip || ip->family == DetIpFamily::DET_IP_NONE)
44 return -1;
45 // Scope dominates; within a scope a native IPv6 outranks IPv4 (RFC 6724 default policy).
46 return scope_rank(ip) * 2 + (eff_is_v6(ip) ? 1 : 0);
47}
48
49void detws_he_order(DetIp *list, size_t n)
50{
51 if (!list || n < 2)
52 return;
53
54 // Stable insertion sort by preference (descending).
55 for (size_t i = 1; i < n; i++)
56 {
57 DetIp key = list[i];
58 int kp = detws_he_pref(&key);
59 size_t j = i;
60 while (j > 0 && detws_he_pref(&list[j - 1]) < kp)
61 {
62 list[j] = list[j - 1];
63 j--;
64 }
65 list[j] = key;
66 }
67
68 if (n > DETWS_HE_MAX)
69 return; // too large to interleave in the fixed scratch; sorted order stands.
70
71 // RFC 8305 sec 4: interleave families so successive attempts alternate v6/v4. Preserve the
72 // preference order within each family; start with the family of the top-preference address.
73 DetIp out[DETWS_HE_MAX];
74 size_t o = 0;
75 size_t iv6 = 0;
76 size_t iv4 = 0;
77 // Collect indices per family in preference order.
78 size_t v6[DETWS_HE_MAX];
79 size_t v4[DETWS_HE_MAX];
80 size_t nv6 = 0;
81 size_t nv4 = 0;
82 for (size_t i = 0; i < n; i++)
83 {
84 if (eff_is_v6(&list[i]))
85 v6[nv6++] = i;
86 else
87 v4[nv4++] = i;
88 }
89 bool take_v6 = eff_is_v6(&list[0]); // whichever family the best address belongs to goes first
90 while (iv6 < nv6 || iv4 < nv4)
91 {
92 if (take_v6 && iv6 < nv6)
93 out[o++] = list[v6[iv6++]];
94 else if (!take_v6 && iv4 < nv4)
95 out[o++] = list[v4[iv4++]];
96 else if (iv6 < nv6) // the preferred family is exhausted; drain the other
97 out[o++] = list[v6[iv6++]];
98 else
99 out[o++] = list[v4[iv4++]];
100 take_v6 = !take_v6;
101 }
102 for (size_t i = 0; i < n; i++)
103 list[i] = out[i];
104}
105
106bool detws_he_attempt_due(uint32_t last_start_ms, uint32_t now_ms, uint32_t attempt_delay_ms)
107{
108 uint32_t elapsed = now_ms - last_start_ms; // wrap-safe modular subtraction
109 return elapsed >= attempt_delay_ms;
110}
111
112#endif // DETWS_ENABLE_HAPPY_EYEBALLS
Dual-stack destination selection + Happy Eyeballs fallback (DETWS_ENABLE_HAPPY_EYEBALLS).
DetIpScope det_ip_classify(const DetIp *ip)
Classify ip into a DetIpScope.
Definition ip.cpp:448
bool det_ip_is_v4_mapped(const DetIp *ip)
True if ip is an IPv4-mapped IPv6 address (::ffff:a.b.c.d, RFC 4291 §2.5.5.2).
Definition ip.cpp:443
@ DET_IP_V6
IPv6 (bytes[0..15])
@ DET_IP_NONE
empty / unparsed
@ DET_IP_SCOPE_GLOBAL
globally routable unicast
@ DET_IP_SCOPE_LOOPBACK
127.0.0.0/8 / ::1
@ DET_IP_SCOPE_PRIVATE
RFC1918 (10/8, 172.16/12, 192.168/16) / ULA fc00::/7.
@ DET_IP_SCOPE_MULTICAST
224.0.0.0/4 / ff00::/8
@ DET_IP_SCOPE_LINK_LOCAL
169.254.0.0/16 / fe80::/10
A v4 or v6 address in network (big-endian) byte order.
Definition ip.h:52
DetIpFamily family
address family tag
Definition ip.h:53