@@ -22,7 +22,7 @@ import (
2222// Constants for WebSocket timeouts and limits.
2323const (
2424 pingInterval = 54 * time .Second
25- readDeadline = 60 * time .Second
25+ readTimeout = 60 * time .Second // Must be > pingInterval to avoid false timeouts
2626 writeTimeout = 10 * time .Second
2727 minTokenLength = 40 // Minimum GitHub token length
2828 maxTokenLength = 255 // Maximum GitHub token length
@@ -376,22 +376,6 @@ func (h *WebSocketHandler) validateAuth(ctx context.Context, ws *websocket.Conn,
376376 return userOrgs , nil
377377}
378378
379- // handleClientPing responds to a ping message from the client.
380- func (* WebSocketHandler ) handleClientPing (ws * websocket.Conn , msgMap map [string ]any , clientID string ) {
381- pong := map [string ]any {"type" : "pong" }
382- // Echo back any sequence number if present
383- if seq , ok := msgMap ["seq" ]; ok {
384- pong ["seq" ] = seq
385- }
386- if err := ws .SetWriteDeadline (time .Now ().Add (writeTimeout )); err != nil {
387- log .Printf ("failed to set write deadline for pong to client %s: %v" , clientID , err )
388- return
389- }
390- if err := websocket .JSON .Send (ws , pong ); err != nil {
391- log .Printf ("failed to send pong to client %s: %v" , clientID , err )
392- }
393- }
394-
395379// Handle handles a WebSocket connection.
396380//
397381//nolint:funlen,gocyclo // This function orchestrates the complete WebSocket lifecycle and cannot be split without losing clarity
@@ -495,7 +479,7 @@ func (h *WebSocketHandler) Handle(ws *websocket.Conn) {
495479 }
496480 defer h .connLimiter .Remove (ip )
497481
498- // Set read deadline for initial subscription
482+ // Set read deadline for initial subscription (shorter timeout for handshake)
499483 if err := ws .SetDeadline (time .Now ().Add (5 * time .Second )); err != nil {
500484 log .Printf ("failed to set deadline for %s: %v" , ip , err )
501485 return
@@ -676,18 +660,15 @@ func (h *WebSocketHandler) Handle(ws *websocket.Conn) {
676660 go client .Run (ctx , pingInterval , writeTimeout )
677661
678662 // Handle incoming messages with responsive shutdown
679- // Use a shorter read timeout to make shutdown more responsive
680- readTimeout := 2 * time .Second
681-
682663 // Create a ticker for periodic context checks during blocking reads
683664 contextCheckTicker := time .NewTicker (1 * time .Second )
684665 defer contextCheckTicker .Stop ()
685666
667+ // Set initial read deadline - must be longer than pingInterval to avoid false timeouts
686668 if err := ws .SetReadDeadline (time .Now ().Add (readTimeout )); err != nil {
687669 log .Printf ("failed to set read deadline for %s: %v" , ip , err )
688670 return
689671 }
690- // Read deadline set for responsive shutdown
691672
692673 // Message read loop with responsive shutdown
693674 for {
@@ -717,7 +698,7 @@ func (h *WebSocketHandler) Handle(ws *websocket.Conn) {
717698 log .Printf ("client %s connection already closed" , client .ID )
718699 case strings .Contains (err .Error (), "i/o timeout" ):
719700 log .Printf ("TIMEOUT: client %s read timeout at %s (no messages received for %v)" ,
720- client .ID , time .Now ().Format (time .RFC3339 ), readDeadline )
701+ client .ID , time .Now ().Format (time .RFC3339 ), readTimeout )
721702 default :
722703 log .Printf ("client %s read error: %v" , client .ID , err )
723704 }
@@ -739,7 +720,17 @@ func (h *WebSocketHandler) Handle(ws *websocket.Conn) {
739720 continue
740721 case "ping" :
741722 // Client sent us a ping, send pong back
742- h .handleClientPing (ws , msgMap , client .ID )
723+ pong := map [string ]any {"type" : "pong" }
724+ if seq , ok := msgMap ["seq" ]; ok {
725+ pong ["seq" ] = seq
726+ }
727+ if err := ws .SetWriteDeadline (time .Now ().Add (writeTimeout )); err != nil {
728+ log .Printf ("failed to set write deadline for pong to client %s: %v" , client .ID , err )
729+ continue
730+ }
731+ if err := websocket .JSON .Send (ws , pong ); err != nil {
732+ log .Printf ("failed to send pong to client %s: %v" , client .ID , err )
733+ }
743734 continue
744735 case "keepalive" , "heartbeat" :
745736 // Common keepalive messages - just acknowledge receipt
0 commit comments