Skip to content

Commit 97fdcb4

Browse files
authored
New feature: Happy Eyeballs (RFC 8305) (#4667)
Closes #4473
1 parent bfbccc2 commit 97fdcb4

File tree

6 files changed

+417
-99
lines changed

6 files changed

+417
-99
lines changed

infra/conf/transport_internet.go

Lines changed: 52 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -699,25 +699,50 @@ type CustomSockoptConfig struct {
699699
Type string `json:"type"`
700700
}
701701

702+
type HappyEyeballsConfig struct {
703+
PrioritizeIPv6 bool `json:"prioritizeIPv6"`
704+
TryDelayMs uint64 `json:"tryDelayMs"`
705+
Interleave uint32 `json:"interleave"`
706+
MaxConcurrentTry uint32 `json:"maxConcurrentTry"`
707+
}
708+
709+
func (h *HappyEyeballsConfig) UnmarshalJSON(data []byte) error {
710+
var innerHappyEyeballsConfig = struct {
711+
PrioritizeIPv6 bool `json:"prioritizeIPv6"`
712+
TryDelayMs uint64 `json:"tryDelayMs"`
713+
Interleave uint32 `json:"interleave"`
714+
MaxConcurrentTry uint32 `json:"maxConcurrentTry"`
715+
}{PrioritizeIPv6: false, Interleave: 1, TryDelayMs: 0, MaxConcurrentTry: 4}
716+
if err := json.Unmarshal(data, &innerHappyEyeballsConfig); err != nil {
717+
return err
718+
}
719+
h.PrioritizeIPv6 = innerHappyEyeballsConfig.PrioritizeIPv6
720+
h.TryDelayMs = innerHappyEyeballsConfig.TryDelayMs
721+
h.Interleave = innerHappyEyeballsConfig.Interleave
722+
h.MaxConcurrentTry = innerHappyEyeballsConfig.MaxConcurrentTry
723+
return nil
724+
}
725+
702726
type SocketConfig struct {
703-
Mark int32 `json:"mark"`
704-
TFO interface{} `json:"tcpFastOpen"`
705-
TProxy string `json:"tproxy"`
706-
AcceptProxyProtocol bool `json:"acceptProxyProtocol"`
707-
DomainStrategy string `json:"domainStrategy"`
708-
DialerProxy string `json:"dialerProxy"`
709-
TCPKeepAliveInterval int32 `json:"tcpKeepAliveInterval"`
710-
TCPKeepAliveIdle int32 `json:"tcpKeepAliveIdle"`
711-
TCPCongestion string `json:"tcpCongestion"`
712-
TCPWindowClamp int32 `json:"tcpWindowClamp"`
713-
TCPMaxSeg int32 `json:"tcpMaxSeg"`
714-
Penetrate bool `json:"penetrate"`
715-
TCPUserTimeout int32 `json:"tcpUserTimeout"`
716-
V6only bool `json:"v6only"`
717-
Interface string `json:"interface"`
718-
TcpMptcp bool `json:"tcpMptcp"`
719-
CustomSockopt []*CustomSockoptConfig `json:"customSockopt"`
720-
AddressPortStrategy string `json:"addressPortStrategy"`
727+
Mark int32 `json:"mark"`
728+
TFO interface{} `json:"tcpFastOpen"`
729+
TProxy string `json:"tproxy"`
730+
AcceptProxyProtocol bool `json:"acceptProxyProtocol"`
731+
DomainStrategy string `json:"domainStrategy"`
732+
DialerProxy string `json:"dialerProxy"`
733+
TCPKeepAliveInterval int32 `json:"tcpKeepAliveInterval"`
734+
TCPKeepAliveIdle int32 `json:"tcpKeepAliveIdle"`
735+
TCPCongestion string `json:"tcpCongestion"`
736+
TCPWindowClamp int32 `json:"tcpWindowClamp"`
737+
TCPMaxSeg int32 `json:"tcpMaxSeg"`
738+
Penetrate bool `json:"penetrate"`
739+
TCPUserTimeout int32 `json:"tcpUserTimeout"`
740+
V6only bool `json:"v6only"`
741+
Interface string `json:"interface"`
742+
TcpMptcp bool `json:"tcpMptcp"`
743+
CustomSockopt []*CustomSockoptConfig `json:"customSockopt"`
744+
AddressPortStrategy string `json:"addressPortStrategy"`
745+
HappyEyeballsSettings *HappyEyeballsConfig `json:"happyEyeballs"`
721746
}
722747

723748
// Build implements Buildable.
@@ -809,6 +834,14 @@ func (c *SocketConfig) Build() (*internet.SocketConfig, error) {
809834
return nil, errors.New("unsupported address and port strategy: ", c.AddressPortStrategy)
810835
}
811836

837+
var happyEyeballs = &internet.HappyEyeballsConfig{Interleave: 1, PrioritizeIpv6: false, TryDelayMs: 0, MaxConcurrentTry: 4}
838+
if c.HappyEyeballsSettings != nil {
839+
happyEyeballs.PrioritizeIpv6 = c.HappyEyeballsSettings.PrioritizeIPv6
840+
happyEyeballs.Interleave = c.HappyEyeballsSettings.Interleave
841+
happyEyeballs.TryDelayMs = c.HappyEyeballsSettings.TryDelayMs
842+
happyEyeballs.MaxConcurrentTry = c.HappyEyeballsSettings.MaxConcurrentTry
843+
}
844+
812845
return &internet.SocketConfig{
813846
Mark: c.Mark,
814847
Tfo: tfo,
@@ -828,6 +861,7 @@ func (c *SocketConfig) Build() (*internet.SocketConfig, error) {
828861
TcpMptcp: c.TcpMptcp,
829862
CustomSockopt: customSockopts,
830863
AddressPortStrategy: addressPortStrategy,
864+
HappyEyeballs: happyEyeballs,
831865
}, nil
832866
}
833867

infra/conf/transport_test.go

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ func TestSocketConfig(t *testing.T) {
2626
Tfo: 256,
2727
DomainStrategy: internet.DomainStrategy_USE_IP,
2828
DialerProxy: "tag",
29+
HappyEyeballs: &internet.HappyEyeballsConfig{Interleave: 1, TryDelayMs: 0, PrioritizeIpv6: false, MaxConcurrentTry: 4},
2930
}
3031
runMultiTestCase(t, []TestCase{
3132
{
@@ -45,8 +46,9 @@ func TestSocketConfig(t *testing.T) {
4546

4647
// test "tcpFastOpen": false, disabled TFO is expected
4748
expectedOutput = &internet.SocketConfig{
48-
Mark: 0,
49-
Tfo: -1,
49+
Mark: 0,
50+
Tfo: -1,
51+
HappyEyeballs: &internet.HappyEyeballsConfig{Interleave: 1, TryDelayMs: 0, PrioritizeIpv6: false, MaxConcurrentTry: 4},
5052
}
5153
runMultiTestCase(t, []TestCase{
5254
{
@@ -63,8 +65,9 @@ func TestSocketConfig(t *testing.T) {
6365

6466
// test "tcpFastOpen": 65535, queue length 65535 is expected
6567
expectedOutput = &internet.SocketConfig{
66-
Mark: 0,
67-
Tfo: 65535,
68+
Mark: 0,
69+
Tfo: 65535,
70+
HappyEyeballs: &internet.HappyEyeballsConfig{Interleave: 1, TryDelayMs: 0, PrioritizeIpv6: false, MaxConcurrentTry: 4},
6871
}
6972
runMultiTestCase(t, []TestCase{
7073
{
@@ -81,8 +84,9 @@ func TestSocketConfig(t *testing.T) {
8184

8285
// test "tcpFastOpen": -65535, disable TFO is expected
8386
expectedOutput = &internet.SocketConfig{
84-
Mark: 0,
85-
Tfo: -65535,
87+
Mark: 0,
88+
Tfo: -65535,
89+
HappyEyeballs: &internet.HappyEyeballsConfig{Interleave: 1, TryDelayMs: 0, PrioritizeIpv6: false, MaxConcurrentTry: 4},
8690
}
8791
runMultiTestCase(t, []TestCase{
8892
{
@@ -99,8 +103,9 @@ func TestSocketConfig(t *testing.T) {
99103

100104
// test "tcpFastOpen": 0, no operation is expected
101105
expectedOutput = &internet.SocketConfig{
102-
Mark: 0,
103-
Tfo: 0,
106+
Mark: 0,
107+
Tfo: 0,
108+
HappyEyeballs: &internet.HappyEyeballsConfig{Interleave: 1, TryDelayMs: 0, PrioritizeIpv6: false, MaxConcurrentTry: 4},
104109
}
105110
runMultiTestCase(t, []TestCase{
106111
{
@@ -117,8 +122,9 @@ func TestSocketConfig(t *testing.T) {
117122

118123
// test omit "tcpFastOpen", no operation is expected
119124
expectedOutput = &internet.SocketConfig{
120-
Mark: 0,
121-
Tfo: 0,
125+
Mark: 0,
126+
Tfo: 0,
127+
HappyEyeballs: &internet.HappyEyeballsConfig{Interleave: 1, TryDelayMs: 0, PrioritizeIpv6: false, MaxConcurrentTry: 4},
122128
}
123129
runMultiTestCase(t, []TestCase{
124130
{
@@ -133,8 +139,9 @@ func TestSocketConfig(t *testing.T) {
133139

134140
// test "tcpFastOpen": null, no operation is expected
135141
expectedOutput = &internet.SocketConfig{
136-
Mark: 0,
137-
Tfo: 0,
142+
Mark: 0,
143+
Tfo: 0,
144+
HappyEyeballs: &internet.HappyEyeballsConfig{Interleave: 1, TryDelayMs: 0, PrioritizeIpv6: false, MaxConcurrentTry: 4},
138145
}
139146
runMultiTestCase(t, []TestCase{
140147
{

0 commit comments

Comments
 (0)