http: properly respond to HTTP request during shutdown
What changed, and why it matters
This change fixes a bug in Bitcoin Core's built-in web server (used by RPC and REST interfaces). During server shutdown, incoming HTTP requests could be accepted but then silently dropped because the worker thread pool had already been interrupted. The server would not send any response back to the client, causing the connection to hang until it timed out. The patch detects when a request cannot be queued and immediately replies with a '503 Service Unavailable' status, telling the client the server is shutting down.
Treat as a low-severity reliability/denial-of-service improvement. Backport to maintained release branches if shutdown-time RPC availability is a concern. No immediate emergency response is warranted; monitor for related hangs during planned shutdowns.
Security signals we found
Denial-of-service hardening: prevents shutdown-time RPC/REST requests from causing hung connections
Resource leak avoidance: unqueued requests are now explicitly finalized with an HTTP response
Thread-pool interruption race condition addressed
HTTP 503 Service Unavailable returned instead of silent connection drop
Evidence from the diff
In src/httpserver.cpp, the HTTP request callback now uses a std::shared_ptr for HTTPRequest instead of std::unique_ptr so the request object remains valid if the lambda is not queued. After submitting the handler lambda to g_threadpool_http, the code checks the returned std::optional for SubmitError::Inactive or SubmitError::Interrupted. On failure, it logs a warning and calls hreq->WriteReply(HTTP_SERVICE_UNAVAILABLE, …), ensuring libevent sends a proper response rather than leaving the connection unanswered. An Assume() assertion checks that no other references remain before the reply is written.
Changed components
src/httpserver.cppHTTP server / RPC and REST interfaceg_threadpool_http worker thread poollibevent evhttp request handlingInspect captured patch +9 / −3
diff --git a/src/httpserver.cpp b/src/httpserver.cpp
index 671e1196..587f7d21 100644
--- a/src/httpserver.cpp
+++ b/src/httpserver.cpp
@@ -211,7 +211,7 @@ static void http_request_cb(struct evhttp_request* req, void* arg)
}
}
}
- auto hreq{std::make_unique<HTTPRequest>(req, *static_cast<const util::SignalInterrupt*>(arg))};
+ auto hreq{std::make_shared<HTTPRequest>(req, *static_cast<const util::SignalInterrupt*>(arg))};
// Early address-based allow check
if (!ClientAllowed(hreq->GetPeer())) {
@@ -258,7 +258,7 @@ static void http_request_cb(struct evhttp_request* req, void* arg)
return;
}
- auto item = [req = std::move(hreq), in_path = std::move(path), fn = i->handler]() {
+ auto item = [req = hreq, in_path = std::move(path), fn = i->handler]() {
std::string err_msg;
try {
fn(req.get(), in_path);
@@ -276,7 +276,13 @@ static void http_request_cb(struct evhttp_request* req, void* arg)
req->WriteReply(HTTP_INTERNAL_SERVER_ERROR, err_msg);
};
- [[maybe_unused]] auto _{g_threadpool_http.Submit(std::move(item))};
+ if (auto res = g_threadpool_http.Submit(std::move(item)); !res.has_value()) {
+ Assume(hreq.use_count() == 1); // ensure request will be deleted
+ // Both SubmitError::Inactive and SubmitError::Interrupted mean shutdown
+ LogWarning("HTTP request rejected during server shutdown: '%s'", SubmitErrorString(res.error()));
+ hreq->WriteReply(HTTP_SERVICE_UNAVAILABLE, "Request rejected during server shutdown");
+ return;
+ }
} else {
hreq->WriteReply(HTTP_NOT_FOUND);
}
Why this scored 35/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.