diff --git a/lib/remote/httpserverconnection.cpp b/lib/remote/httpserverconnection.cpp index 81eb29a56..0345ab5f5 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_CheckLivenessTimer(io) + : m_WaitGroup(waitGroup), m_Stream(stream), m_Seen(Utility::GetTime()), m_CanRead(io, true), m_IoStrand(io), m_ShuttingDown(false), m_CheckLivenessTimer(io) { if (authenticated) { m_ApiUser = ApiUser::GetByClientCN(identity); @@ -65,6 +65,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); }); } /** @@ -418,6 +419,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; } @@ -476,6 +478,7 @@ void HttpServerConnection::ProcessMessages(boost::asio::yield_context yc) } m_Seen = std::numeric_limits::max(); + m_CanRead.Clear(); if (!ProcessRequest(request, response, m_WaitGroup, cpuBoundWorkTime, yc)) { break; @@ -516,3 +519,25 @@ 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) +{ + 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 2e3fda453..c2e3ac7c8 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" @@ -37,6 +38,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; @@ -49,6 +51,7 @@ private: void ProcessMessages(boost::asio::yield_context yc); void CheckLiveness(boost::asio::yield_context yc); + void DetectClientShutdown(boost::asio::yield_context yc); }; }