Skip to content

Commit 18216ba

Browse files
committed
Add timeout after first relay success
1 parent db61cf2 commit 18216ba

File tree

3 files changed

+46
-16
lines changed

3 files changed

+46
-16
lines changed

relay/client/client.go

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -123,11 +123,12 @@ func (cc *connContainer) close() {
123123
// the client can be reused by calling Connect again. When the client is closed, all connections are closed too.
124124
// While the Connect is in progress, the OpenConn function will block until the connection is established with relay server.
125125
type Client struct {
126-
log *log.Entry
127-
parentCtx context.Context
128-
connectionURL string
129-
authTokenStore *auth.TokenStore
130-
hashedID []byte
126+
log *log.Entry
127+
parentCtx context.Context
128+
connectionURL string
129+
authTokenStore *auth.TokenStore
130+
hashedID []byte
131+
InitialConnectionTime time.Duration
131132

132133
bufPool *sync.Pool
133134

@@ -264,11 +265,12 @@ func (c *Client) Close() error {
264265
}
265266

266267
func (c *Client) connect() error {
267-
conn, err := ws.Dial(c.connectionURL)
268+
conn, latency, err := ws.Dial(c.connectionURL)
268269
if err != nil {
269270
return err
270271
}
271272
c.relayConn = conn
273+
c.InitialConnectionTime = latency
272274

273275
err = c.handShake()
274276
if err != nil {

relay/client/dialer/ws/ws.go

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@ import (
55
"fmt"
66
"net"
77
"net/http"
8+
"net/http/httptrace"
89
"net/url"
910
"strings"
11+
"time"
1012

1113
log "github.com/sirupsen/logrus"
1214
"nhooyr.io/websocket"
@@ -15,10 +17,10 @@ import (
1517
nbnet "github.com/netbirdio/netbird/util/net"
1618
)
1719

18-
func Dial(address string) (net.Conn, error) {
20+
func Dial(address string) (net.Conn, time.Duration, error) {
1921
wsURL, err := prepareURL(address)
2022
if err != nil {
21-
return nil, err
23+
return nil, 0, err
2224
}
2325

2426
opts := &websocket.DialOptions{
@@ -27,21 +29,26 @@ func Dial(address string) (net.Conn, error) {
2729

2830
parsedURL, err := url.Parse(wsURL)
2931
if err != nil {
30-
return nil, err
32+
return nil, 0, err
3133
}
3234
parsedURL.Path = ws.URLPath
3335

34-
wsConn, resp, err := websocket.Dial(context.Background(), parsedURL.String(), opts)
36+
var connStart, firstByte time.Time
37+
ctx := httptrace.WithClientTrace(context.Background(), &httptrace.ClientTrace{
38+
ConnectStart: func(network, addr string) { connStart = time.Now() },
39+
GotFirstResponseByte: func() { firstByte = time.Now() },
40+
})
41+
wsConn, resp, err := websocket.Dial(ctx, parsedURL.String(), opts)
3542
if err != nil {
3643
log.Errorf("failed to dial to Relay server '%s': %s", wsURL, err)
37-
return nil, err
44+
return nil, 0, err
3845
}
3946
if resp.Body != nil {
4047
_ = resp.Body.Close()
4148
}
4249

4350
conn := NewConn(wsConn, address)
44-
return conn, nil
51+
return conn, firstByte.Sub(connStart), nil
4552
}
4653

4754
func prepareURL(address string) (string, error) {

relay/client/picker.go

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ const (
1717
)
1818

1919
var (
20-
connectionTimeout = 30 * time.Second
20+
connectionTimeout = 30 * time.Second
21+
connectionSortingtimeout = 500 * time.Millisecond
2122
)
2223

2324
type connResult struct {
@@ -72,22 +73,33 @@ func (sp *ServerPicker) PickServer(parentCtx context.Context) (*Client, error) {
7273
func (sp *ServerPicker) startConnection(ctx context.Context, resultChan chan connResult, url string) {
7374
log.Infof("try to connecting to relay server: %s", url)
7475
relayClient := NewClient(ctx, url, sp.TokenStore, sp.PeerID)
75-
start := time.Now()
7676
err := relayClient.Connect()
7777
resultChan <- connResult{
7878
RelayClient: relayClient,
7979
Url: url,
8080
Err: err,
81-
Latency: time.Since(start),
81+
Latency: relayClient.InitialConnectionTime,
8282
}
8383
}
8484

8585
func (sp *ServerPicker) processConnResults(resultChan chan connResult, successChan chan connResult) {
8686
var hasSuccess bool
8787
var bestLatencyResult connResult
8888
bestLatencyResult.Latency = time.Hour
89+
processingCtx := context.Background()
90+
var processingCtxCancel context.CancelFunc
8991
for numOfResults := 0; numOfResults < cap(resultChan); numOfResults++ {
90-
cr := <-resultChan
92+
var cr connResult
93+
select {
94+
case <-processingCtx.Done():
95+
log.Tracef("terminating Relay server sorting early")
96+
successChan <- bestLatencyResult
97+
close(successChan)
98+
successChan = nil // Prevent any more sending to successChan
99+
// Continue receiving connections to terminate any more
100+
cr = <-resultChan
101+
case cr = <-resultChan:
102+
}
91103
if cr.Err != nil {
92104
log.Tracef("failed to connect to Relay server: %s: %v", cr.Url, cr.Err)
93105
continue
@@ -108,10 +120,19 @@ func (sp *ServerPicker) processConnResults(resultChan chan connResult, successCh
108120
}
109121
}
110122

123+
// First successful connection, start a timer to return the result
124+
if !hasSuccess {
125+
processingCtx, processingCtxCancel = context.WithTimeout(processingCtx, connectionSortingtimeout)
126+
}
111127
hasSuccess = true
112128
bestLatencyResult = cr
113129
}
114130

131+
processingCtxCancel()
132+
if successChan == nil {
133+
return
134+
}
135+
115136
if bestLatencyResult.RelayClient != nil {
116137
successChan <- bestLatencyResult
117138
}

0 commit comments

Comments
 (0)