Skip to content

Commit 5e0c221

Browse files
committed
Make HTTP server shutdown more graceful
Shutting down the HTTP server currently breaks off all current requests. This can create a race condition with RPC `stop` command, where the calling process never receives confirmation. This change removes the listening sockets on shutdown so that no new requests can come in, but no longer breaks off requests in progress. Meant to fix #6717.
1 parent ad57b31 commit 5e0c221

File tree

2 files changed

+23
-7
lines changed

2 files changed

+23
-7
lines changed

src/httpserver.cpp

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,8 @@ static std::vector<CSubNet> rpc_allow_subnets;
155155
static WorkQueue<HTTPClosure>* workQueue = 0;
156156
//! Handlers for (sub)paths
157157
std::vector<HTTPPathHandler> pathHandlers;
158+
//! Bound listening sockets
159+
std::vector<evhttp_bound_socket *> boundSockets;
158160

159161
/** Check if a network address is allowed to access the HTTP server */
160162
static bool ClientAllowed(const CNetAddr& netaddr)
@@ -264,6 +266,13 @@ static void http_request_cb(struct evhttp_request* req, void* arg)
264266
}
265267
}
266268

269+
/** Callback to reject HTTP requests after shutdown. */
270+
static void http_reject_request_cb(struct evhttp_request* req, void*)
271+
{
272+
LogPrint("http", "Rejecting request while shutting down\n");
273+
evhttp_send_error(req, HTTP_SERVUNAVAIL, NULL);
274+
}
275+
267276
/** Event dispatcher thread */
268277
static void ThreadHTTP(struct event_base* base, struct evhttp* http)
269278
{
@@ -278,7 +287,6 @@ static void ThreadHTTP(struct event_base* base, struct evhttp* http)
278287
static bool HTTPBindAddresses(struct evhttp* http)
279288
{
280289
int defaultPort = GetArg("-rpcport", BaseParams().RPCPort());
281-
int nBound = 0;
282290
std::vector<std::pair<std::string, uint16_t> > endpoints;
283291

284292
// Determine what addresses to bind to
@@ -304,13 +312,14 @@ static bool HTTPBindAddresses(struct evhttp* http)
304312
// Bind addresses
305313
for (std::vector<std::pair<std::string, uint16_t> >::iterator i = endpoints.begin(); i != endpoints.end(); ++i) {
306314
LogPrint("http", "Binding RPC on address %s port %i\n", i->first, i->second);
307-
if (evhttp_bind_socket(http, i->first.empty() ? NULL : i->first.c_str(), i->second) == 0) {
308-
nBound += 1;
315+
evhttp_bound_socket *bind_handle = evhttp_bind_socket_with_handle(http, i->first.empty() ? NULL : i->first.c_str(), i->second);
316+
if (bind_handle) {
317+
boundSockets.push_back(bind_handle);
309318
} else {
310319
LogPrintf("Binding RPC on address %s port %i failed.\n", i->first, i->second);
311320
}
312321
}
313-
return nBound > 0;
322+
return !boundSockets.empty();
314323
}
315324

316325
/** Simple wrapper to set thread name and run work queue */
@@ -410,8 +419,14 @@ bool StartHTTPServer(boost::thread_group& threadGroup)
410419
void InterruptHTTPServer()
411420
{
412421
LogPrint("http", "Interrupting HTTP server\n");
413-
if (eventBase)
414-
event_base_loopbreak(eventBase);
422+
if (eventHTTP) {
423+
// Unlisten sockets
424+
BOOST_FOREACH (evhttp_bound_socket *socket, boundSockets) {
425+
evhttp_del_accept_socket(eventHTTP, socket);
426+
}
427+
// Reject requests on current connections
428+
evhttp_set_gencb(eventHTTP, http_reject_request_cb, NULL);
429+
}
415430
if (workQueue)
416431
workQueue->Interrupt();
417432
}

src/rpcserver.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,8 @@ UniValue stop(const UniValue& params, bool fHelp)
243243
throw runtime_error(
244244
"stop\n"
245245
"\nStop Bitcoin server.");
246-
// Shutdown will take long enough that the response should get back
246+
// Event loop will exit after current HTTP requests have been handled, so
247+
// this reply will get back to the client.
247248
StartShutdown();
248249
return "Bitcoin server stopping";
249250
}

0 commit comments

Comments
 (0)