|
21 | 21 | #include <util/threadnames.h>
|
22 | 22 | #include <util/translation.h>
|
23 | 23 |
|
| 24 | +#include <condition_variable> |
24 | 25 | #include <cstdio>
|
25 | 26 | #include <cstdlib>
|
26 | 27 | #include <deque>
|
27 | 28 | #include <memory>
|
28 | 29 | #include <optional>
|
29 | 30 | #include <string>
|
| 31 | +#include <unordered_set> |
30 | 32 |
|
31 | 33 | #include <sys/types.h>
|
32 | 34 | #include <sys/stat.h>
|
33 | 35 |
|
34 | 36 | #include <event2/buffer.h>
|
35 | 37 | #include <event2/bufferevent.h>
|
36 | 38 | #include <event2/http.h>
|
| 39 | +#include <event2/http_struct.h> |
37 | 40 | #include <event2/keyvalq_struct.h>
|
38 | 41 | #include <event2/thread.h>
|
39 | 42 | #include <event2/util.h>
|
@@ -146,6 +149,10 @@ static GlobalMutex g_httppathhandlers_mutex;
|
146 | 149 | static std::vector<HTTPPathHandler> pathHandlers GUARDED_BY(g_httppathhandlers_mutex);
|
147 | 150 | //! Bound listening sockets
|
148 | 151 | static std::vector<evhttp_bound_socket *> boundSockets;
|
| 152 | +//! Track active requests |
| 153 | +static GlobalMutex g_requests_mutex; |
| 154 | +static std::condition_variable g_requests_cv; |
| 155 | +static std::unordered_set<evhttp_request*> g_requests GUARDED_BY(g_requests_mutex); |
149 | 156 |
|
150 | 157 | /** Check if a network address is allowed to access the HTTP server */
|
151 | 158 | static bool ClientAllowed(const CNetAddr& netaddr)
|
@@ -207,6 +214,17 @@ std::string RequestMethodString(HTTPRequest::RequestMethod m)
|
207 | 214 | /** HTTP request callback */
|
208 | 215 | static void http_request_cb(struct evhttp_request* req, void* arg)
|
209 | 216 | {
|
| 217 | + // Track requests and notify when a request is completed. |
| 218 | + { |
| 219 | + WITH_LOCK(g_requests_mutex, g_requests.insert(req)); |
| 220 | + g_requests_cv.notify_all(); |
| 221 | + evhttp_request_set_on_complete_cb(req, [](struct evhttp_request* req, void*) { |
| 222 | + auto n{WITH_LOCK(g_requests_mutex, return g_requests.erase(req))}; |
| 223 | + assert(n == 1); |
| 224 | + g_requests_cv.notify_all(); |
| 225 | + }, nullptr); |
| 226 | + } |
| 227 | + |
210 | 228 | // Disable reading to work around a libevent bug, fixed in 2.2.0.
|
211 | 229 | if (event_get_version_number() >= 0x02010600 && event_get_version_number() < 0x02020001) {
|
212 | 230 | evhttp_connection* conn = evhttp_request_get_connection(req);
|
@@ -458,15 +476,27 @@ void StopHTTPServer()
|
458 | 476 | evhttp_del_accept_socket(eventHTTP, socket);
|
459 | 477 | }
|
460 | 478 | boundSockets.clear();
|
461 |
| - if (eventBase) { |
462 |
| - LogPrint(BCLog::HTTP, "Waiting for HTTP event thread to exit\n"); |
463 |
| - if (g_thread_http.joinable()) g_thread_http.join(); |
| 479 | + { |
| 480 | + WAIT_LOCK(g_requests_mutex, lock); |
| 481 | + if (!g_requests.empty()) { |
| 482 | + LogPrint(BCLog::HTTP, "Waiting for %d requests to stop HTTP server\n", g_requests.size()); |
| 483 | + } |
| 484 | + g_requests_cv.wait(lock, []() EXCLUSIVE_LOCKS_REQUIRED(g_requests_mutex) { |
| 485 | + return g_requests.empty(); |
| 486 | + }); |
464 | 487 | }
|
465 | 488 | if (eventHTTP) {
|
466 |
| - evhttp_free(eventHTTP); |
467 |
| - eventHTTP = nullptr; |
| 489 | + // Schedule a callback to call evhttp_free in the event base thread, so |
| 490 | + // that evhttp_free does not need to be called again after the handling |
| 491 | + // of unfinished request connections that follows. |
| 492 | + event_base_once(eventBase, -1, EV_TIMEOUT, [](evutil_socket_t, short, void*) { |
| 493 | + evhttp_free(eventHTTP); |
| 494 | + eventHTTP = nullptr; |
| 495 | + }, nullptr, nullptr); |
468 | 496 | }
|
469 | 497 | if (eventBase) {
|
| 498 | + LogPrint(BCLog::HTTP, "Waiting for HTTP event thread to exit\n"); |
| 499 | + if (g_thread_http.joinable()) g_thread_http.join(); |
470 | 500 | event_base_free(eventBase);
|
471 | 501 | eventBase = nullptr;
|
472 | 502 | }
|
|
0 commit comments