@@ -17,18 +17,32 @@ package telemetryapi
17
17
import (
18
18
"context"
19
19
"encoding/json"
20
+ "errors"
20
21
"fmt"
21
22
"io"
23
+ "math/rand"
24
+ "net"
22
25
"net/http"
23
26
"os"
27
+ "syscall"
24
28
"time"
25
29
26
30
"github.com/golang-collections/go-datastructures/queue"
27
31
"go.uber.org/zap"
28
32
)
29
33
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
+ }
32
46
33
47
// Listener is used to listen to the Telemetry API
34
48
type Listener struct {
@@ -46,26 +60,48 @@ func NewListener(logger *zap.Logger) *Listener {
46
60
}
47
61
}
48
62
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 {
50
84
envAwsLocal , ok := os .LookupEnv ("AWS_SAM_LOCAL" )
51
85
var addr string
52
86
if ok && envAwsLocal == "true" {
53
- addr = ":" + defaultListenerPort
87
+ addr = ":" + port
54
88
} else {
55
- addr = "sandbox.localdomain:" + defaultListenerPort
89
+ addr = "sandbox.localdomain:" + port
56
90
}
57
-
58
91
return addr
59
92
}
60
93
61
94
// Start the server in a goroutine where the log events will be sent
62
95
func (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
+ }
64
100
s .logger .Info ("Listening for requests" , zap .String ("address" , address ))
65
101
s .httpServer = & http.Server {Addr : address }
66
102
http .HandleFunc ("/" , s .httpHandler )
67
103
go func () {
68
- err := s .httpServer .ListenAndServe ( )
104
+ err := s .httpServer .Serve ( listener )
69
105
if err != http .ErrServerClosed {
70
106
s .logger .Error ("Unexpected stop on HTTP Server" , zap .Error (err ))
71
107
s .Shutdown ()
0 commit comments