From 937f21d67c4382cf8fc140eab5cae57984308b15 Mon Sep 17 00:00:00 2001 From: Johannes Schmidt Date: Wed, 2 Jul 2025 11:37:10 +0200 Subject: [PATCH] Detect client-side shutdown of TLS stream --- lib/remote/httpserverconnection.cpp | 29 ++++++++++++++++++++++++++++- lib/remote/httpserverconnection.hpp | 3 +++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/lib/remote/httpserverconnection.cpp b/lib/remote/httpserverconnection.cpp index 617515822..f7320ca21 100644 --- a/lib/remote/httpserverconnection.cpp +++ b/lib/remote/httpserverconnection.cpp @@ -41,7 +41,7 @@ HttpServerConnection::HttpServerConnection(const WaitGroup::Ptr& waitGroup, cons } HttpServerConnection::HttpServerConnection(const WaitGroup::Ptr& waitGroup, const String& identity, bool authenticated, const Shared::Ptr& stream, boost::asio::io_context& io) - : m_WaitGroup(waitGroup), m_Stream(stream), m_Seen(Utility::GetTime()), m_IoStrand(io), m_ShuttingDown(false), m_HasStartedStreaming(false), + : m_WaitGroup(waitGroup), m_Stream(stream), m_Seen(Utility::GetTime()), m_CanRead(io, true), m_IoStrand(io), m_ShuttingDown(false), m_HasStartedStreaming(false), m_CheckLivenessTimer(io) { if (authenticated) { @@ -66,6 +66,7 @@ void HttpServerConnection::Start() IoEngine::SpawnCoroutine(m_IoStrand, [this, keepAlive](asio::yield_context yc) { ProcessMessages(yc); }); IoEngine::SpawnCoroutine(m_IoStrand, [this, keepAlive](asio::yield_context yc) { CheckLiveness(yc); }); + IoEngine::SpawnCoroutine(m_IoStrand, [this, keepAlive](asio::yield_context yc) { DetectClientShutdown(yc); }); } /** @@ -446,6 +447,7 @@ void HttpServerConnection::ProcessMessages(boost::asio::yield_context yc) response.set(http::field::server, l_ServerHeader); + m_CanRead.WaitForSet(yc); if (!EnsureValidHeaders(buf, request, response, m_ShuttingDown, yc)) { break; } @@ -504,6 +506,7 @@ void HttpServerConnection::ProcessMessages(boost::asio::yield_context yc) } m_Seen = std::numeric_limits::max(); + m_CanRead.Clear(); if (!ProcessRequest(*m_Stream, request, response, *this, m_HasStartedStreaming, m_WaitGroup, cpuBoundWorkTime, yc)) { break; @@ -544,3 +547,27 @@ void HttpServerConnection::CheckLiveness(boost::asio::yield_context yc) } } } + +/** + * Detects a shutdown initiated by the client side. + * + * @param yc The yield context for the coroutine of this function + */ +void HttpServerConnection::DetectClientShutdown(boost::asio::yield_context yc) +{ + using wait_type = boost::asio::socket_base::wait_type; + + while (!m_ShuttingDown) { + m_CanRead.WaitForClear(yc); + + boost::system::error_code ec; + m_Stream->async_fill(yc[ec]); + if (ec && !m_ShuttingDown) { + Log(LogInformation, "HttpServerConnection") << "Detected shutdown from client: " << m_PeerAddress << "."; + Disconnect(yc); + break; + } + + m_CanRead.Set(); + } +} diff --git a/lib/remote/httpserverconnection.hpp b/lib/remote/httpserverconnection.hpp index e4f7d257e..4af103ca7 100644 --- a/lib/remote/httpserverconnection.hpp +++ b/lib/remote/httpserverconnection.hpp @@ -4,6 +4,7 @@ #define HTTPSERVERCONNECTION_H #include "remote/apiuser.hpp" +#include "base/io-engine.hpp" #include "base/string.hpp" #include "base/tlsstream.hpp" #include "base/wait-group.hpp" @@ -38,6 +39,7 @@ private: ApiUser::Ptr m_ApiUser; Shared::Ptr m_Stream; double m_Seen; + AsioDualEvent m_CanRead; String m_PeerAddress; boost::asio::io_context::strand m_IoStrand; bool m_ShuttingDown; @@ -51,6 +53,7 @@ private: void ProcessMessages(boost::asio::yield_context yc); void CheckLiveness(boost::asio::yield_context yc); + void DetectClientShutdown(boost::asio::yield_context yc); }; }