@@ -17,18 +17,32 @@ package telemetryapi
1717import (
1818 "context"
1919 "encoding/json"
20+ "errors"
2021 "fmt"
2122 "io"
23+ "math/rand"
24+ "net"
2225 "net/http"
2326 "os"
27+ "syscall"
2428 "time"
2529
2630 "github.com/golang-collections/go-datastructures/queue"
2731 "go.uber.org/zap"
2832)
2933
30- const defaultListenerPort = "53612"
31- const initialQueueSize = 5
34+ const (
35+ initialQueueSize = 5
36+ maxRetries = 5
37+ // Define ephemeral port range (typical range is 49152-65535)
38+ minPort = 49152
39+ maxPort = 65535
40+ )
41+
42+ // getRandomPort returns a random port number within the ephemeral range
43+ func getRandomPort () string {
44+ return fmt .Sprintf ("%d" , rand .Intn (maxPort - minPort )+ minPort )
45+ }
3246
3347// Listener is used to listen to the Telemetry API
3448type Listener struct {
@@ -46,26 +60,48 @@ func NewListener(logger *zap.Logger) *Listener {
4660 }
4761}
4862
49- func listenOnAddress () string {
63+ func (s * Listener ) tryBindPort () (net.Listener , string , error ) {
64+ for i := 0 ; i < maxRetries ; i ++ {
65+ port := getRandomPort ()
66+ address := listenOnAddress (port )
67+
68+ l , err := net .Listen ("tcp" , address )
69+ if err != nil {
70+ if errors .Is (err , syscall .EADDRINUSE ) {
71+ s .logger .Debug ("Port in use, trying another" ,
72+ zap .String ("address" , address ))
73+ continue
74+ }
75+ return nil , "" , err
76+ }
77+ return l , address , nil
78+ }
79+
80+ return nil , "" , fmt .Errorf ("failed to find available port after %d attempts" , maxRetries )
81+ }
82+
83+ func listenOnAddress (port string ) string {
5084 envAwsLocal , ok := os .LookupEnv ("AWS_SAM_LOCAL" )
5185 var addr string
5286 if ok && envAwsLocal == "true" {
53- addr = ":" + defaultListenerPort
87+ addr = ":" + port
5488 } else {
55- addr = "sandbox.localdomain:" + defaultListenerPort
89+ addr = "sandbox.localdomain:" + port
5690 }
57-
5891 return addr
5992}
6093
6194// Start the server in a goroutine where the log events will be sent
6295func (s * Listener ) Start () (string , error ) {
63- address := listenOnAddress ()
96+ listener , address , err := s .tryBindPort ()
97+ if err != nil {
98+ return "" , fmt .Errorf ("failed to find available port: %w" , err )
99+ }
64100 s .logger .Info ("Listening for requests" , zap .String ("address" , address ))
65101 s .httpServer = & http.Server {Addr : address }
66102 http .HandleFunc ("/" , s .httpHandler )
67103 go func () {
68- err := s .httpServer .ListenAndServe ( )
104+ err := s .httpServer .Serve ( listener )
69105 if err != http .ErrServerClosed {
70106 s .logger .Error ("Unexpected stop on HTTP Server" , zap .Error (err ))
71107 s .Shutdown ()
0 commit comments