@@ -102,16 +102,31 @@ func (c *Client) Connect() error {
102102 return nil
103103}
104104
105- // Close closes the WebSocket connection
105+ // Close closes the WebSocket connection gracefully
106106func (c * Client ) Close () error {
107- close (c .done )
108- if c .conn != nil {
109- return c .conn .Close ()
107+ // Signal shutdown to all goroutines first
108+ select {
109+ case <- c .done :
110+ // Already closed
111+ return nil
112+ default :
113+ close (c .done )
110114 }
111115
112- // stop the ping monitor
116+ // Set connection status to false
113117 c .setConnected (false )
114118
119+ // Close the WebSocket connection gracefully
120+ if c .conn != nil {
121+ // Send close message
122+ c .writeMux .Lock ()
123+ c .conn .WriteMessage (websocket .CloseMessage , websocket .FormatCloseMessage (websocket .CloseNormalClosure , "" ))
124+ c .writeMux .Unlock ()
125+
126+ // Close the connection
127+ return c .conn .Close ()
128+ }
129+
115130 return nil
116131}
117132
@@ -351,9 +366,16 @@ func (c *Client) pingMonitor() {
351366 err := c .conn .WriteControl (websocket .PingMessage , []byte {}, time .Now ().Add (c .pingTimeout ))
352367 c .writeMux .Unlock ()
353368 if err != nil {
354- logger .Error ("Ping failed: %v" , err )
355- c .reconnect ()
356- return
369+ // Check if we're shutting down before logging error and reconnecting
370+ select {
371+ case <- c .done :
372+ // Expected during shutdown
373+ return
374+ default :
375+ logger .Error ("Ping failed: %v" , err )
376+ c .reconnect ()
377+ return
378+ }
357379 }
358380 }
359381 }
@@ -365,7 +387,14 @@ func (c *Client) readPumpWithDisconnectDetection() {
365387 if c .conn != nil {
366388 c .conn .Close ()
367389 }
368- c .reconnect ()
390+ // Only attempt reconnect if we're not shutting down
391+ select {
392+ case <- c .done :
393+ // Shutting down, don't reconnect
394+ return
395+ default :
396+ c .reconnect ()
397+ }
369398 }()
370399
371400 for {
@@ -376,8 +405,21 @@ func (c *Client) readPumpWithDisconnectDetection() {
376405 var msg WSMessage
377406 err := c .conn .ReadJSON (& msg )
378407 if err != nil {
379- logger .Error ("WebSocket read error: %v" , err )
380- return // triggers reconnect via defer
408+ // Check if we're shutting down before logging error
409+ select {
410+ case <- c .done :
411+ // Expected during shutdown, don't log as error
412+ logger .Debug ("WebSocket connection closed during shutdown" )
413+ return
414+ default :
415+ // Unexpected error during normal operation
416+ if websocket .IsUnexpectedCloseError (err , websocket .CloseGoingAway , websocket .CloseAbnormalClosure , websocket .CloseNormalClosure ) {
417+ logger .Error ("WebSocket read error: %v" , err )
418+ } else {
419+ logger .Debug ("WebSocket connection closed: %v" , err )
420+ }
421+ return // triggers reconnect via defer
422+ }
381423 }
382424
383425 c .handlersMux .RLock ()
@@ -396,7 +438,13 @@ func (c *Client) reconnect() {
396438 c .conn = nil
397439 }
398440
399- go c .connectWithRetry ()
441+ // Only reconnect if we're not shutting down
442+ select {
443+ case <- c .done :
444+ return
445+ default :
446+ go c .connectWithRetry ()
447+ }
400448}
401449
402450func (c * Client ) setConnected (status bool ) {
0 commit comments