Skip to content

Commit

Permalink
Provide ping handler that responds when client blocked
Browse files Browse the repository at this point in the history
The websocket only handles responding to pings when reading messages.  If the client is blocked handling incoming messages and does not read additional messages for some time, websocker keep-alive pings are not responded to.  This may lead to the server dropping the websocket connection thinking that the client is dead.  This PR provides a ping handler that answers pings even when the client if not reading messages.

Addresses issue #245
  • Loading branch information
gammazero committed Jul 20, 2021
1 parent dfbb28c commit 775c9a8
Showing 1 changed file with 29 additions and 0 deletions.
29 changes: 29 additions & 0 deletions transport/websocketpeer.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ type WebsocketConnection interface {
WriteMessage(messageType int, data []byte) error
ReadMessage() (messageType int, p []byte, err error)
SetPongHandler(h func(appData string) error)
SetPingHandler(h func(appData string) error)
Subprotocol() string
}

Expand Down Expand Up @@ -255,6 +256,15 @@ func (w *websocketPeer) sendHandler() {
defer close(w.writerDone)
defer w.cancelSender()

pongs := make(chan string, 1) // capacity must be >= 1
w.conn.SetPingHandler(func(m string) error {
select {
case pongs <- m:
default:
}
return nil
})

sendLoop:
for {
select {
Expand All @@ -271,6 +281,11 @@ sendLoop:
}
return
}
case m := <-pongs:
err := w.conn.WriteMessage(websocket.PongMessage, []byte(m))
if err != nil {
w.log.Print(err)
}
case <-w.ctxSender.Done():
return
}
Expand All @@ -281,6 +296,15 @@ func (w *websocketPeer) sendHandlerKeepAlive(keepAlive time.Duration) {
defer close(w.writerDone)
defer w.cancelSender()

pongs := make(chan string, 1) // capacity must be >= 1
w.conn.SetPingHandler(func(m string) error {
select {
case pongs <- m:
default:
}
return nil
})

var pendingPongs int32
w.conn.SetPongHandler(func(msg string) error {
// Any response resets counter.
Expand Down Expand Up @@ -322,6 +346,11 @@ recvLoop:
return
}
atomic.AddInt32(&pendingPongs, 1)
case m := <-pongs:
err := w.conn.WriteMessage(websocket.PongMessage, []byte(m))
if err != nil {
w.log.Print(err)
}
case <-senderDone:
return
}
Expand Down

0 comments on commit 775c9a8

Please sign in to comment.