From 14958dd50cfde6ec9c7a0a8026d8bf79d3224d66 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Fri, 7 Jan 2022 18:46:02 -0600 Subject: [PATCH] [release-v1.7] rpcserver: Update websocket ping timeout handling. Currently, timeouts when responding to a websocket ping control message with a pong message cause the websocket client to be disconnected, but these types of failures are nearly always temporary network errors due to things such as congestion and thus should not result in disconnection. In order to make that the case, this modifies the websocket ping handler to ignore timeout network errors when attempting to respond with a pong and also takes this opportunity to increase the timeout to 5 seconds from 1 second to support multi-continent communication. --- internal/rpcserver/rpcserver.go | 15 +++++++++------ internal/rpcserver/rpcwebsocket.go | 6 +++++- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/internal/rpcserver/rpcserver.go b/internal/rpcserver/rpcserver.go index 7d166ade1f..88899ac339 100644 --- a/internal/rpcserver/rpcserver.go +++ b/internal/rpcserver/rpcserver.go @@ -1,5 +1,5 @@ // Copyright (c) 2013-2016 The btcsuite developers -// Copyright (c) 2015-2021 The Decred developers +// Copyright (c) 2015-2022 The Decred developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -6358,11 +6358,14 @@ func (s *Server) route(ctx context.Context) *http.Server { return } ws.SetPingHandler(func(payload string) error { - err := ws.WriteControl(websocket.PongMessage, []byte(payload), - time.Now().Add(time.Second)) log.Debugf("ping received: len %d", len(payload)) - log.Tracef("ping payload: %s", payload) - if err != nil { + log.Tracef("ping payload: %q", payload) + var netErr net.Error + err := ws.WriteControl(websocket.PongMessage, []byte(payload), + time.Now().Add(websocketPongTimeout)) + if err != nil && !errors.Is(err, websocket.ErrCloseSent) && + !(errors.As(err, &netErr) && netErr.Timeout()) { + log.Errorf("Failed to send pong: %v", err) return err } @@ -6370,7 +6373,7 @@ func (s *Server) route(ctx context.Context) *http.Server { }) ws.SetPongHandler(func(payload string) error { log.Debugf("pong received: len %d", len(payload)) - log.Tracef("pong payload: %s", payload) + log.Tracef("pong payload: %q", payload) return nil }) if !authenticated { diff --git a/internal/rpcserver/rpcwebsocket.go b/internal/rpcserver/rpcwebsocket.go index b172dc3469..e89ab88dfc 100644 --- a/internal/rpcserver/rpcwebsocket.go +++ b/internal/rpcserver/rpcwebsocket.go @@ -1,5 +1,5 @@ // Copyright (c) 2013-2016 The btcsuite developers -// Copyright (c) 2015-2021 The Decred developers +// Copyright (c) 2015-2022 The Decred developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -52,6 +52,10 @@ const ( // websocketReadLimitAuthenticated is the maximum number of bytes allowed // for an authenticated JSON-RPC message read from a websocket client. websocketReadLimitAuthenticated = 1 << 24 // 16 MiB + + // websocketPongTimeout is the maximum amount of time attempts to respond to + // websocket ping messages with a pong will wait before giving up. + websocketPongTimeout = time.Second * 5 ) type semaphore chan struct{}