From 4308407dc14595c9581938dae2220b1f1d9f118a Mon Sep 17 00:00:00 2001 From: Ed Hennis Date: Thu, 11 Jan 2024 20:11:09 -0500 Subject: [PATCH] WebSocket should only call async_close once (#4848) Prevent WebSocket connections from trying to close twice. The issue only occurs in debug builds (assertions are disabled in release builds, including published packages), and when the WebSocket connections are unprivileged. The assert (and WRN log) occurs when a client drives up the resource balance enough to be forcibly disconnected while there are still messages pending to be sent. Thanks to @lathanbritz for discovering this issue in #4822. --- src/ripple/server/impl/BaseWSPeer.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/ripple/server/impl/BaseWSPeer.h b/src/ripple/server/impl/BaseWSPeer.h index b2ba7dd657..fd1722df00 100644 --- a/src/ripple/server/impl/BaseWSPeer.h +++ b/src/ripple/server/impl/BaseWSPeer.h @@ -29,6 +29,7 @@ #include #include #include + #include #include @@ -52,6 +53,9 @@ private: boost::beast::multi_buffer rb_; boost::beast::multi_buffer wb_; std::list> wq_; + /// The socket has been closed, or will close after the next write + /// finishes. Do not do any more writes, and don't try to close + /// again. bool do_close_ = false; boost::beast::websocket::close_reason cr_; waitable_timer timer_; @@ -256,6 +260,8 @@ BaseWSPeer::close( return post(strand_, [self = impl().shared_from_this(), reason] { self->close(reason); }); + if (do_close_) + return; do_close_ = true; if (wq_.empty()) { @@ -348,6 +354,7 @@ BaseWSPeer::on_write_fin(error_code const& ec) return fail(ec, "write_fin"); wq_.pop_front(); if (do_close_) + { impl().ws_.async_close( cr_, bind_executor( @@ -356,6 +363,7 @@ BaseWSPeer::on_write_fin(error_code const& ec) &BaseWSPeer::on_close, impl().shared_from_this(), std::placeholders::_1))); + } else if (!wq_.empty()) on_write({}); }