diff --git a/collector/internal/telemetryapi/listener.go b/collector/internal/telemetryapi/listener.go index 8499d4e402..62a6461cec 100644 --- a/collector/internal/telemetryapi/listener.go +++ b/collector/internal/telemetryapi/listener.go @@ -17,18 +17,32 @@ package telemetryapi import ( "context" "encoding/json" + "errors" "fmt" "io" + "math/rand" + "net" "net/http" "os" + "syscall" "time" "github.com/golang-collections/go-datastructures/queue" "go.uber.org/zap" ) -const defaultListenerPort = "53612" -const initialQueueSize = 5 +const ( + initialQueueSize = 5 + maxRetries = 5 + // Define ephemeral port range (typical range is 49152-65535) + minPort = 49152 + maxPort = 65535 +) + +// getRandomPort returns a random port number within the ephemeral range +func getRandomPort() string { + return fmt.Sprintf("%d", rand.Intn(maxPort-minPort)+minPort) +} // Listener is used to listen to the Telemetry API type Listener struct { @@ -46,26 +60,48 @@ func NewListener(logger *zap.Logger) *Listener { } } -func listenOnAddress() string { +func (s *Listener) tryBindPort() (net.Listener, string, error) { + for i := 0; i < maxRetries; i++ { + port := getRandomPort() + address := listenOnAddress(port) + + l, err := net.Listen("tcp", address) + if err != nil { + if errors.Is(err, syscall.EADDRINUSE) { + s.logger.Debug("Port in use, trying another", + zap.String("address", address)) + continue + } + return nil, "", err + } + return l, address, nil + } + + return nil, "", fmt.Errorf("failed to find available port after %d attempts", maxRetries) +} + +func listenOnAddress(port string) string { envAwsLocal, ok := os.LookupEnv("AWS_SAM_LOCAL") var addr string if ok && envAwsLocal == "true" { - addr = ":" + defaultListenerPort + addr = ":" + port } else { - addr = "sandbox.localdomain:" + defaultListenerPort + addr = "sandbox.localdomain:" + port } - return addr } // Start the server in a goroutine where the log events will be sent func (s *Listener) Start() (string, error) { - address := listenOnAddress() + listener, address, err := s.tryBindPort() + if err != nil { + return "", fmt.Errorf("failed to find available port: %w", err) + } s.logger.Info("Listening for requests", zap.String("address", address)) s.httpServer = &http.Server{Addr: address} http.HandleFunc("/", s.httpHandler) go func() { - err := s.httpServer.ListenAndServe() + err := s.httpServer.Serve(listener) if err != http.ErrServerClosed { s.logger.Error("Unexpected stop on HTTP Server", zap.Error(err)) s.Shutdown()