pkg/server: add retry logic for random port assignment
Change-Id: I70464e344a79dce8eadb9513d2a990faf3b2cca8
This commit is contained in:
parent
fff10b041c
commit
52d6852e58
@ -6,8 +6,12 @@ package server
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"net"
|
||||
"os"
|
||||
"runtime"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
quicgo "github.com/lucas-clemente/quic-go"
|
||||
"github.com/zeebo/errs"
|
||||
@ -74,14 +78,30 @@ func New(log *zap.Logger, tlsOptions *tlsopts.Options, publicAddr, privateAddr s
|
||||
Manager: rpc.NewDefaultManagerOptions(),
|
||||
}
|
||||
|
||||
publicTCPListener, err := net.Listen("tcp", publicAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var err error
|
||||
var publicTCPListener, publicQUICListener net.Listener
|
||||
for retry := 0; ; retry++ {
|
||||
publicTCPListener, err = net.Listen("tcp", publicAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
publicQUICListener, err := quic.NewListener(tlsOptions.ServerTLSConfig(), publicTCPListener.Addr().String(), &quicgo.Config{MaxIdleTimeout: defaultUserTimeout})
|
||||
if err != nil {
|
||||
return nil, errs.Combine(err, publicTCPListener.Close())
|
||||
publicQUICListener, err = quic.NewListener(tlsOptions.ServerTLSConfig(), publicTCPListener.Addr().String(), &quicgo.Config{MaxIdleTimeout: defaultUserTimeout})
|
||||
if err != nil {
|
||||
_, port, _ := net.SplitHostPort(publicAddr)
|
||||
if port == "0" && retry < 10 && isErrorAddressAlreadyInUse(err) {
|
||||
// from here, we know for sure that the tcp port chosen by the
|
||||
// os is available, but we don't know if the same port number
|
||||
// for udp is also available.
|
||||
// if a udp port is already in use, we will close the tcp port and retry
|
||||
// to find one that is available for both udp and tcp.
|
||||
_ = publicTCPListener.Close()
|
||||
continue
|
||||
}
|
||||
return nil, errs.Combine(err, publicTCPListener.Close())
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
publicMux := drpcmux.New()
|
||||
@ -223,3 +243,24 @@ func (p *Server) Run(ctx context.Context) (err error) {
|
||||
muxCancel()
|
||||
return errs.Combine(err, muxGroup.Wait())
|
||||
}
|
||||
|
||||
// isErrorAddressAlreadyInUse checks whether the error is corresponding to
|
||||
// EADDRINUSE. Taken from https://stackoverflow.com/a/65865898.
|
||||
func isErrorAddressAlreadyInUse(err error) bool {
|
||||
var eOsSyscall *os.SyscallError
|
||||
if !errors.As(err, &eOsSyscall) {
|
||||
return false
|
||||
}
|
||||
var errErrno syscall.Errno
|
||||
if !errors.As(eOsSyscall.Err, &errErrno) {
|
||||
return false
|
||||
}
|
||||
if errErrno == syscall.EADDRINUSE {
|
||||
return true
|
||||
}
|
||||
const WSAEADDRINUSE = 10048
|
||||
if runtime.GOOS == "windows" && errErrno == WSAEADDRINUSE {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user