http: Abort with clear error instead of hanging forever

Could happen when connection doesn't complete for some reason.

Output early warning to give clue about what's up.

Prior check+log message before even starting to wait was a bit too eager to hint at something being wrong.
This commit is contained in:
Hodlinator
2025-02-21 14:58:29 +01:00
parent 757de96db5
commit a00dfd2046

View File

@@ -197,10 +197,10 @@ public:
return WITH_LOCK(m_mutex, return m_tracker.size()); return WITH_LOCK(m_mutex, return m_tracker.size());
} }
//! Wait until there are no more connections with active requests in the tracker //! Wait until there are no more connections with active requests in the tracker
void WaitUntilEmpty() const EXCLUSIVE_LOCKS_REQUIRED(!m_mutex) void WaitUntilEmpty(std::chrono::seconds timeout) const EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
{ {
WAIT_LOCK(m_mutex, lock); WAIT_LOCK(m_mutex, lock);
m_cv.wait(lock, [this]() EXCLUSIVE_LOCKS_REQUIRED(m_mutex) { return m_tracker.empty(); }); m_cv.wait_for(lock, timeout, [this]() EXCLUSIVE_LOCKS_REQUIRED(m_mutex) { return m_tracker.empty(); });
} }
}; };
//! Track active requests //! Track active requests
@@ -536,11 +536,19 @@ void StopHTTPServer()
evhttp_del_accept_socket(eventHTTP, socket); evhttp_del_accept_socket(eventHTTP, socket);
} }
boundSockets.clear(); boundSockets.clear();
{ // Give clients time to close down TCP connections from their side before we
if (const auto n_connections{g_requests.CountActiveConnections()}; n_connections != 0) { // free eventHTTP, this avoids issues like RemoteDisconnected exceptions in
LogDebug(BCLog::HTTP, "Waiting for %d connections to stop HTTP server\n", n_connections); // the test framework.
g_requests.WaitUntilEmpty(/*timeout=*/30s);
if (auto connections{g_requests.CountActiveConnections()}) {
LogWarning("%d remaining HTTP connection(s) after 30s timeout, waiting for longer.", connections);
g_requests.WaitUntilEmpty(/*timeout=*/10min);
if ((connections = g_requests.CountActiveConnections())) {
LogError("%d remaining HTTP connection(s) after long timeout. "
"Aborting to avoid potential use-after-frees from "
"connections still running after freeing eventHTTP.", connections);
std::abort();
} }
g_requests.WaitUntilEmpty();
} }
if (eventHTTP) { if (eventHTTP) {
// Schedule a callback to call evhttp_free in the event base thread, as // Schedule a callback to call evhttp_free in the event base thread, as