netbird/relay/client/dialer/race_dialer.go

79 lines
1.6 KiB
Go
Raw Normal View History

2024-11-28 11:53:35 +01:00
package dialer
import (
"context"
"errors"
"net"
"time"
log "github.com/sirupsen/logrus"
)
2024-11-28 18:11:15 +01:00
var (
connectionTimeout = 30 * time.Second
)
2024-11-28 17:34:09 +01:00
type DialerFn interface {
Dial(ctx context.Context, address string) (net.Conn, error)
Protocol() string
}
2024-11-28 11:53:35 +01:00
type dialResult struct {
2024-11-28 17:34:09 +01:00
Conn net.Conn
Protocol string
Err error
2024-11-28 11:53:35 +01:00
}
2024-11-28 17:34:09 +01:00
func RaceDial(log *log.Entry, serverURL string, dialerFns ...DialerFn) (net.Conn, error) {
connChan := make(chan dialResult, len(dialerFns))
2024-11-28 11:53:35 +01:00
winnerConn := make(chan net.Conn, 1)
abortCtx, abort := context.WithCancel(context.Background())
defer abort()
2024-11-28 17:34:09 +01:00
for _, d := range dialerFns {
2024-11-28 11:53:35 +01:00
go func() {
2024-11-28 18:11:15 +01:00
ctx, cancel := context.WithTimeout(abortCtx, connectionTimeout)
2024-11-28 11:53:35 +01:00
defer cancel()
2024-11-28 17:34:09 +01:00
log.Infof("dialing Relay server via %s", d.Protocol())
conn, err := d.Dial(ctx, serverURL)
connChan <- dialResult{Conn: conn, Protocol: d.Protocol(), Err: err}
2024-11-28 11:53:35 +01:00
}()
}
go func() {
var hasWinner bool
2024-11-28 17:34:09 +01:00
for i := 0; i < len(dialerFns); i++ {
2024-11-28 11:53:35 +01:00
dr := <-connChan
if dr.Err != nil {
2024-11-28 17:34:09 +01:00
if errors.Is(dr.Err, context.Canceled) {
log.Infof("connection attempt aborted via: %s", dr.Protocol)
} else {
2024-11-28 18:11:15 +01:00
log.Errorf("failed to dial via %s: %s", dr.Protocol, dr.Err)
2024-11-28 17:34:09 +01:00
}
2024-11-28 11:53:35 +01:00
continue
}
if hasWinner {
2024-11-28 17:34:09 +01:00
if cerr := dr.Conn.Close(); cerr != nil {
2024-11-28 18:11:15 +01:00
log.Warnf("failed to close connection via %s: %s", dr.Protocol, cerr)
2024-11-28 17:34:09 +01:00
}
2024-11-28 11:53:35 +01:00
continue
}
2024-11-28 17:34:09 +01:00
log.Infof("successfully dialed via: %s", dr.Protocol)
2024-11-28 18:11:15 +01:00
abort()
2024-11-28 11:53:35 +01:00
hasWinner = true
winnerConn <- dr.Conn
}
close(winnerConn)
}()
conn, ok := <-winnerConn
if !ok {
2024-11-28 17:34:09 +01:00
return nil, errors.New("failed to dial to Relay server on any protocol")
2024-11-28 11:53:35 +01:00
}
return conn, nil
}