From f82c7173275736fe47d958b1a926361da862591b Mon Sep 17 00:00:00 2001 From: Matthew Zipkin Date: Mon, 10 Mar 2025 13:30:52 -0400 Subject: [PATCH] http: disconnect after idle timeout (-rpcservertimeout) --- src/httpserver.cpp | 13 ++++++++++++- src/httpserver.h | 7 +++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 8a5a1912bee..87c4369f554 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -1201,8 +1202,11 @@ void HTTPServer::CloseConnectionInternal(std::shared_ptr& client) void HTTPServer::DisconnectClients() { + const auto now{Now()}; for (auto it = m_connected_clients.begin(); it != m_connected_clients.end();) { - if ((it->second->m_disconnect || m_disconnect_all_clients) && !it->second->m_prevent_disconnect) { + bool timeout{now - it->second->m_idle_since > m_rpcservertimeout}; + if (((it->second->m_disconnect || m_disconnect_all_clients) && !it->second->m_prevent_disconnect) + || timeout) { CloseConnectionInternal(it->second); it = m_connected_clients.erase(it); } else { @@ -1219,6 +1223,8 @@ bool HTTPServer::EventNewConnectionAccepted(NodeId node_id, auto client = std::make_shared(node_id, them); // Point back to the server client->m_server = this; + // Set timeout + client->m_idle_since = Now(); LogDebug(BCLog::HTTP, "HTTP Connection accepted from %s (id=%d)\n", client->m_origin, client->m_node_id); m_connected_clients.emplace(client->m_node_id, std::move(client)); m_no_clients = false; @@ -1250,6 +1256,9 @@ void HTTPServer::EventGotData(NodeId node_id, std::span data) return; } + // Reset idle timeout + client->m_idle_since = Now(); + // Prevent disconnect until all requests are completely handled. client->m_prevent_disconnect = true; @@ -1362,6 +1371,8 @@ bool InitHTTPServer(const util::SignalInterrupt& interrupt) // Create HTTPServer, using a dummy request handler just for this commit g_http_server = std::make_unique([&](std::unique_ptr req){}); + g_http_server->m_rpcservertimeout = std::chrono::seconds(gArgs.GetIntArg("-rpcservertimeout", DEFAULT_HTTP_SERVER_TIMEOUT)); + // Bind HTTP server to specified addresses std::vector> endpoints{GetBindAddresses()}; bool bind_success{false}; diff --git a/src/httpserver.h b/src/httpserver.h index 39abb12e5fb..cff1f287a7a 100644 --- a/src/httpserver.h +++ b/src/httpserver.h @@ -15,6 +15,7 @@ #include #include #include +#include namespace util { class SignalInterrupt; @@ -329,6 +330,9 @@ public: // Flag this client for disconnection on next loop bool m_disconnect{false}; + // Timestamp of last receive activity, used for -rpcservertimeout + SteadySeconds m_idle_since; + explicit HTTPClient(NodeId node_id, CService addr) : m_node_id(node_id), m_addr(addr) { m_origin = addr.ToStringAddrPort(); @@ -373,6 +377,9 @@ public: // Set by main thread and read by Sockman I/O thread std::atomic_bool m_disconnect_all_clients{false}; + // Idle timeout after which clients are disconnected + std::chrono::seconds m_rpcservertimeout; + /** * Be notified when a new connection has been accepted. * @param[in] node_id Id of the newly accepted connection.