Branch data Line data Source code
# 1 : : // Copyright (c) 2015-2020 The Bitcoin Core developers
# 2 : : // Distributed under the MIT software license, see the accompanying
# 3 : : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
# 4 : :
# 5 : : #include <httpserver.h>
# 6 : :
# 7 : : #include <chainparamsbase.h>
# 8 : : #include <compat.h>
# 9 : : #include <netbase.h>
# 10 : : #include <node/ui_interface.h>
# 11 : : #include <rpc/protocol.h> // For HTTP status codes
# 12 : : #include <shutdown.h>
# 13 : : #include <sync.h>
# 14 : : #include <util/strencodings.h>
# 15 : : #include <util/system.h>
# 16 : : #include <util/threadnames.h>
# 17 : : #include <util/translation.h>
# 18 : :
# 19 : : #include <deque>
# 20 : : #include <memory>
# 21 : : #include <stdio.h>
# 22 : : #include <stdlib.h>
# 23 : : #include <string>
# 24 : :
# 25 : : #include <sys/types.h>
# 26 : : #include <sys/stat.h>
# 27 : :
# 28 : : #include <event2/thread.h>
# 29 : : #include <event2/buffer.h>
# 30 : : #include <event2/bufferevent.h>
# 31 : : #include <event2/util.h>
# 32 : : #include <event2/keyvalq_struct.h>
# 33 : :
# 34 : : #include <support/events.h>
# 35 : :
# 36 : : /** Maximum size of http request (request line + headers) */
# 37 : : static const size_t MAX_HEADERS_SIZE = 8192;
# 38 : :
# 39 : : /** HTTP request work item */
# 40 : : class HTTPWorkItem final : public HTTPClosure
# 41 : : {
# 42 : : public:
# 43 : : HTTPWorkItem(std::unique_ptr<HTTPRequest> _req, const std::string &_path, const HTTPRequestHandler& _func):
# 44 : : req(std::move(_req)), path(_path), func(_func)
# 45 : 111282 : {
# 46 : 111282 : }
# 47 : : void operator()() override
# 48 : 111281 : {
# 49 : 111281 : func(req.get(), path);
# 50 : 111281 : }
# 51 : :
# 52 : : std::unique_ptr<HTTPRequest> req;
# 53 : :
# 54 : : private:
# 55 : : std::string path;
# 56 : : HTTPRequestHandler func;
# 57 : : };
# 58 : :
# 59 : : /** Simple work queue for distributing work over multiple threads.
# 60 : : * Work items are simply callable objects.
# 61 : : */
# 62 : : template <typename WorkItem>
# 63 : : class WorkQueue
# 64 : : {
# 65 : : private:
# 66 : : Mutex cs;
# 67 : : std::condition_variable cond GUARDED_BY(cs);
# 68 : : std::deque<std::unique_ptr<WorkItem>> queue GUARDED_BY(cs);
# 69 : : bool running GUARDED_BY(cs);
# 70 : : const size_t maxDepth;
# 71 : :
# 72 : : public:
# 73 : : explicit WorkQueue(size_t _maxDepth) : running(true),
# 74 : : maxDepth(_maxDepth)
# 75 : 659 : {
# 76 : 659 : }
# 77 : : /** Precondition: worker threads have all stopped (they have been joined).
# 78 : : */
# 79 : : ~WorkQueue()
# 80 : 659 : {
# 81 : 659 : }
# 82 : : /** Enqueue a work item */
# 83 : : bool Enqueue(WorkItem* item)
# 84 : 111282 : {
# 85 : 111282 : LOCK(cs);
# 86 [ + + ]: 111282 : if (queue.size() >= maxDepth) {
# 87 : 1 : return false;
# 88 : 1 : }
# 89 : 111281 : queue.emplace_back(std::unique_ptr<WorkItem>(item));
# 90 : 111281 : cond.notify_one();
# 91 : 111281 : return true;
# 92 : 111281 : }
# 93 : : /** Thread function */
# 94 : : void Run()
# 95 : 2621 : {
# 96 : 113902 : while (true) {
# 97 : 113902 : std::unique_ptr<WorkItem> i;
# 98 : 113902 : {
# 99 : 113902 : WAIT_LOCK(cs, lock);
# 100 [ + + ][ + + ]: 227343 : while (running && queue.empty())
# 101 : 113441 : cond.wait(lock);
# 102 [ + + ]: 113902 : if (!running)
# 103 : 2621 : break;
# 104 : 111281 : i = std::move(queue.front());
# 105 : 111281 : queue.pop_front();
# 106 : 111281 : }
# 107 : 111281 : (*i)();
# 108 : 111281 : }
# 109 : 2621 : }
# 110 : : /** Interrupt and exit loops */
# 111 : : void Interrupt()
# 112 : 659 : {
# 113 : 659 : LOCK(cs);
# 114 : 659 : running = false;
# 115 : 659 : cond.notify_all();
# 116 : 659 : }
# 117 : : };
# 118 : :
# 119 : : struct HTTPPathHandler
# 120 : : {
# 121 : : HTTPPathHandler(std::string _prefix, bool _exactMatch, HTTPRequestHandler _handler):
# 122 : : prefix(_prefix), exactMatch(_exactMatch), handler(_handler)
# 123 : 1321 : {
# 124 : 1321 : }
# 125 : : std::string prefix;
# 126 : : bool exactMatch;
# 127 : : HTTPRequestHandler handler;
# 128 : : };
# 129 : :
# 130 : : /** HTTP module state */
# 131 : :
# 132 : : //! libevent event loop
# 133 : : static struct event_base* eventBase = nullptr;
# 134 : : //! HTTP server
# 135 : : static struct evhttp* eventHTTP = nullptr;
# 136 : : //! List of subnets to allow RPC connections from
# 137 : : static std::vector<CSubNet> rpc_allow_subnets;
# 138 : : //! Work queue for handling longer requests off the event loop thread
# 139 : : static WorkQueue<HTTPClosure>* workQueue = nullptr;
# 140 : : //! Handlers for (sub)paths
# 141 : : static std::vector<HTTPPathHandler> pathHandlers;
# 142 : : //! Bound listening sockets
# 143 : : static std::vector<evhttp_bound_socket *> boundSockets;
# 144 : :
# 145 : : /** Check if a network address is allowed to access the HTTP server */
# 146 : : static bool ClientAllowed(const CNetAddr& netaddr)
# 147 : 111283 : {
# 148 [ - + ]: 111283 : if (!netaddr.IsValid())
# 149 : 0 : return false;
# 150 [ + - ]: 111283 : for(const CSubNet& subnet : rpc_allow_subnets)
# 151 [ + - ]: 111283 : if (subnet.Match(netaddr))
# 152 : 111283 : return true;
# 153 : 111283 : return false;
# 154 : 111283 : }
# 155 : :
# 156 : : /** Initialize ACL list for HTTP server */
# 157 : : static bool InitHTTPAllowList()
# 158 : 659 : {
# 159 : 659 : rpc_allow_subnets.clear();
# 160 : 659 : CNetAddr localv4;
# 161 : 659 : CNetAddr localv6;
# 162 : 659 : LookupHost("127.0.0.1", localv4, false);
# 163 : 659 : LookupHost("::1", localv6, false);
# 164 : 659 : rpc_allow_subnets.push_back(CSubNet(localv4, 8)); // always allow IPv4 local subnet
# 165 : 659 : rpc_allow_subnets.push_back(CSubNet(localv6)); // always allow IPv6 localhost
# 166 [ - + ]: 659 : for (const std::string& strAllow : gArgs.GetArgs("-rpcallowip")) {
# 167 : 0 : CSubNet subnet;
# 168 : 0 : LookupSubNet(strAllow, subnet);
# 169 [ # # ]: 0 : if (!subnet.IsValid()) {
# 170 : 0 : uiInterface.ThreadSafeMessageBox(
# 171 : 0 : strprintf(Untranslated("Invalid -rpcallowip subnet specification: %s. Valid are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24)."), strAllow),
# 172 : 0 : "", CClientUIInterface::MSG_ERROR);
# 173 : 0 : return false;
# 174 : 0 : }
# 175 : 0 : rpc_allow_subnets.push_back(subnet);
# 176 : 0 : }
# 177 : 659 : std::string strAllowed;
# 178 [ + + ]: 659 : for (const CSubNet& subnet : rpc_allow_subnets)
# 179 : 1318 : strAllowed += subnet.ToString() + " ";
# 180 [ + - ]: 659 : LogPrint(BCLog::HTTP, "Allowing HTTP connections from: %s\n", strAllowed);
# 181 : 659 : return true;
# 182 : 659 : }
# 183 : :
# 184 : : /** HTTP request method as string - use for logging only */
# 185 : : std::string RequestMethodString(HTTPRequest::RequestMethod m)
# 186 : 111283 : {
# 187 : 111283 : switch (m) {
# 188 [ + + ]: 36 : case HTTPRequest::GET:
# 189 : 36 : return "GET";
# 190 : 0 : break;
# 191 [ + + ]: 111247 : case HTTPRequest::POST:
# 192 : 111247 : return "POST";
# 193 : 0 : break;
# 194 [ - + ]: 0 : case HTTPRequest::HEAD:
# 195 : 0 : return "HEAD";
# 196 : 0 : break;
# 197 [ - + ]: 0 : case HTTPRequest::PUT:
# 198 : 0 : return "PUT";
# 199 : 0 : break;
# 200 [ - + ]: 0 : default:
# 201 : 0 : return "unknown";
# 202 : 111283 : }
# 203 : 111283 : }
# 204 : :
# 205 : : /** HTTP request callback */
# 206 : : static void http_request_cb(struct evhttp_request* req, void* arg)
# 207 : 111283 : {
# 208 : : // Disable reading to work around a libevent bug, fixed in 2.2.0.
# 209 [ + - ][ + - ]: 111283 : if (event_get_version_number() >= 0x02010600 && event_get_version_number() < 0x02020001) {
# 210 : 111283 : evhttp_connection* conn = evhttp_request_get_connection(req);
# 211 [ + - ]: 111283 : if (conn) {
# 212 : 111283 : bufferevent* bev = evhttp_connection_get_bufferevent(conn);
# 213 [ + - ]: 111283 : if (bev) {
# 214 : 111283 : bufferevent_disable(bev, EV_READ);
# 215 : 111283 : }
# 216 : 111283 : }
# 217 : 111283 : }
# 218 : 111283 : std::unique_ptr<HTTPRequest> hreq(new HTTPRequest(req));
# 219 : :
# 220 : : // Early address-based allow check
# 221 [ - + ]: 111283 : if (!ClientAllowed(hreq->GetPeer())) {
# 222 [ # # ]: 0 : LogPrint(BCLog::HTTP, "HTTP request from %s rejected: Client network is not allowed RPC access\n",
# 223 : 0 : hreq->GetPeer().ToString());
# 224 : 0 : hreq->WriteReply(HTTP_FORBIDDEN);
# 225 : 0 : return;
# 226 : 0 : }
# 227 : :
# 228 : : // Early reject unknown HTTP methods
# 229 [ - + ]: 111283 : if (hreq->GetRequestMethod() == HTTPRequest::UNKNOWN) {
# 230 [ # # ]: 0 : LogPrint(BCLog::HTTP, "HTTP request from %s rejected: Unknown HTTP request method\n",
# 231 : 0 : hreq->GetPeer().ToString());
# 232 : 0 : hreq->WriteReply(HTTP_BAD_METHOD);
# 233 : 0 : return;
# 234 : 0 : }
# 235 : :
# 236 [ + - ]: 111283 : LogPrint(BCLog::HTTP, "Received a %s request for %s from %s\n",
# 237 : 111283 : RequestMethodString(hreq->GetRequestMethod()), SanitizeString(hreq->GetURI(), SAFE_CHARS_URI).substr(0, 100), hreq->GetPeer().ToString());
# 238 : :
# 239 : : // Find registered handler for prefix
# 240 : 111283 : std::string strURI = hreq->GetURI();
# 241 : 111283 : std::string path;
# 242 : 111283 : std::vector<HTTPPathHandler>::const_iterator i = pathHandlers.begin();
# 243 : 111283 : std::vector<HTTPPathHandler>::const_iterator iend = pathHandlers.end();
# 244 [ + + ]: 118065 : for (; i != iend; ++i) {
# 245 : 118064 : bool match = false;
# 246 [ + + ]: 118064 : if (i->exactMatch)
# 247 : 111283 : match = (strURI == i->prefix);
# 248 : 6781 : else
# 249 : 6781 : match = (strURI.substr(0, i->prefix.size()) == i->prefix);
# 250 [ + + ]: 118064 : if (match) {
# 251 : 111282 : path = strURI.substr(i->prefix.size());
# 252 : 111282 : break;
# 253 : 111282 : }
# 254 : 118064 : }
# 255 : :
# 256 : : // Dispatch to worker thread
# 257 [ + + ]: 111283 : if (i != iend) {
# 258 : 111282 : std::unique_ptr<HTTPWorkItem> item(new HTTPWorkItem(std::move(hreq), path, i->handler));
# 259 : 111282 : assert(workQueue);
# 260 [ + + ]: 111282 : if (workQueue->Enqueue(item.get()))
# 261 : 111281 : item.release(); /* if true, queue took ownership */
# 262 : 1 : else {
# 263 : 1 : LogPrintf("WARNING: request rejected because http work queue depth exceeded, it can be increased with the -rpcworkqueue= setting\n");
# 264 : 1 : item->req->WriteReply(HTTP_SERVICE_UNAVAILABLE, "Work queue depth exceeded");
# 265 : 1 : }
# 266 : 111282 : } else {
# 267 : 1 : hreq->WriteReply(HTTP_NOT_FOUND);
# 268 : 1 : }
# 269 : 111283 : }
# 270 : :
# 271 : : /** Callback to reject HTTP requests after shutdown. */
# 272 : : static void http_reject_request_cb(struct evhttp_request* req, void*)
# 273 : 0 : {
# 274 [ # # ]: 0 : LogPrint(BCLog::HTTP, "Rejecting request while shutting down\n");
# 275 : 0 : evhttp_send_error(req, HTTP_SERVUNAVAIL, nullptr);
# 276 : 0 : }
# 277 : :
# 278 : : /** Event dispatcher thread */
# 279 : : static bool ThreadHTTP(struct event_base* base)
# 280 : 656 : {
# 281 : 656 : util::ThreadRename("http");
# 282 [ + - ]: 656 : LogPrint(BCLog::HTTP, "Entering http event loop\n");
# 283 : 656 : event_base_dispatch(base);
# 284 : : // Event loop will be interrupted by InterruptHTTPServer()
# 285 [ + - ]: 656 : LogPrint(BCLog::HTTP, "Exited http event loop\n");
# 286 : 656 : return event_base_got_break(base) == 0;
# 287 : 656 : }
# 288 : :
# 289 : : /** Bind HTTP server to specified addresses */
# 290 : : static bool HTTPBindAddresses(struct evhttp* http)
# 291 : 659 : {
# 292 : 659 : uint16_t http_port{static_cast<uint16_t>(gArgs.GetArg("-rpcport", BaseParams().RPCPort()))};
# 293 : 659 : std::vector<std::pair<std::string, uint16_t>> endpoints;
# 294 : :
# 295 : : // Determine what addresses to bind to
# 296 [ + - ][ - + ]: 659 : if (!(gArgs.IsArgSet("-rpcallowip") && gArgs.IsArgSet("-rpcbind"))) { // Default to loopback if not allowing external IPs
# [ # # ]
# 297 : 659 : endpoints.push_back(std::make_pair("::1", http_port));
# 298 : 659 : endpoints.push_back(std::make_pair("127.0.0.1", http_port));
# 299 [ - + ]: 659 : if (gArgs.IsArgSet("-rpcallowip")) {
# 300 : 0 : LogPrintf("WARNING: option -rpcallowip was specified without -rpcbind; this doesn't usually make sense\n");
# 301 : 0 : }
# 302 [ + + ]: 659 : if (gArgs.IsArgSet("-rpcbind")) {
# 303 : 1 : LogPrintf("WARNING: option -rpcbind was ignored because -rpcallowip was not specified, refusing to allow everyone to connect\n");
# 304 : 1 : }
# 305 [ # # ]: 659 : } else if (gArgs.IsArgSet("-rpcbind")) { // Specific bind address
# 306 [ # # ]: 0 : for (const std::string& strRPCBind : gArgs.GetArgs("-rpcbind")) {
# 307 : 0 : uint16_t port{http_port};
# 308 : 0 : std::string host;
# 309 : 0 : SplitHostPort(strRPCBind, port, host);
# 310 : 0 : endpoints.push_back(std::make_pair(host, port));
# 311 : 0 : }
# 312 : 0 : }
# 313 : :
# 314 : : // Bind addresses
# 315 [ + + ]: 1977 : for (std::vector<std::pair<std::string, uint16_t> >::iterator i = endpoints.begin(); i != endpoints.end(); ++i) {
# 316 [ + - ]: 1318 : LogPrint(BCLog::HTTP, "Binding RPC on address %s port %i\n", i->first, i->second);
# 317 [ - + ]: 1318 : evhttp_bound_socket *bind_handle = evhttp_bind_socket_with_handle(http, i->first.empty() ? nullptr : i->first.c_str(), i->second);
# 318 [ + - ]: 1318 : if (bind_handle) {
# 319 : 1318 : CNetAddr addr;
# 320 [ - + ][ - + ]: 1318 : if (i->first.empty() || (LookupHost(i->first, addr, false) && addr.IsBindAny())) {
# [ + - ][ - + ]
# 321 : 0 : LogPrintf("WARNING: the RPC server is not safe to expose to untrusted networks such as the public internet\n");
# 322 : 0 : }
# 323 : 1318 : boundSockets.push_back(bind_handle);
# 324 : 1318 : } else {
# 325 : 0 : LogPrintf("Binding RPC on address %s port %i failed.\n", i->first, i->second);
# 326 : 0 : }
# 327 : 1318 : }
# 328 : 659 : return !boundSockets.empty();
# 329 : 659 : }
# 330 : :
# 331 : : /** Simple wrapper to set thread name and run work queue */
# 332 : : static void HTTPWorkQueueRun(WorkQueue<HTTPClosure>* queue, int worker_num)
# 333 : 2621 : {
# 334 : 2621 : util::ThreadRename(strprintf("httpworker.%i", worker_num));
# 335 : 2621 : queue->Run();
# 336 : 2621 : }
# 337 : :
# 338 : : /** libevent event log callback */
# 339 : : static void libevent_log_cb(int severity, const char *msg)
# 340 : 0 : {
# 341 : : #ifndef EVENT_LOG_WARN
# 342 : : // EVENT_LOG_WARN was added in 2.0.19; but before then _EVENT_LOG_WARN existed.
# 343 : : # define EVENT_LOG_WARN _EVENT_LOG_WARN
# 344 : : #endif
# 345 [ # # ]: 0 : if (severity >= EVENT_LOG_WARN) // Log warn messages and higher without debug category
# 346 : 0 : LogPrintf("libevent: %s\n", msg);
# 347 : 0 : else
# 348 [ # # ]: 0 : LogPrint(BCLog::LIBEVENT, "libevent: %s\n", msg);
# 349 : 0 : }
# 350 : :
# 351 : : bool InitHTTPServer()
# 352 : 659 : {
# 353 [ - + ]: 659 : if (!InitHTTPAllowList())
# 354 : 0 : return false;
# 355 : :
# 356 : : // Redirect libevent's logging to our own log
# 357 : 659 : event_set_log_callback(&libevent_log_cb);
# 358 : : // Update libevent's log handling. Returns false if our version of
# 359 : : // libevent doesn't support debug logging, in which case we should
# 360 : : // clear the BCLog::LIBEVENT flag.
# 361 [ - + ]: 659 : if (!UpdateHTTPServerLogging(LogInstance().WillLogCategory(BCLog::LIBEVENT))) {
# 362 : 0 : LogInstance().DisableCategory(BCLog::LIBEVENT);
# 363 : 0 : }
# 364 : :
# 365 : : #ifdef WIN32
# 366 : : evthread_use_windows_threads();
# 367 : : #else
# 368 : 659 : evthread_use_pthreads();
# 369 : 659 : #endif
# 370 : :
# 371 : 659 : raii_event_base base_ctr = obtain_event_base();
# 372 : :
# 373 : : /* Create a new evhttp object to handle requests. */
# 374 : 659 : raii_evhttp http_ctr = obtain_evhttp(base_ctr.get());
# 375 : 659 : struct evhttp* http = http_ctr.get();
# 376 [ - + ]: 659 : if (!http) {
# 377 : 0 : LogPrintf("couldn't create evhttp. Exiting.\n");
# 378 : 0 : return false;
# 379 : 0 : }
# 380 : :
# 381 : 659 : evhttp_set_timeout(http, gArgs.GetArg("-rpcservertimeout", DEFAULT_HTTP_SERVER_TIMEOUT));
# 382 : 659 : evhttp_set_max_headers_size(http, MAX_HEADERS_SIZE);
# 383 : 659 : evhttp_set_max_body_size(http, MAX_SIZE);
# 384 : 659 : evhttp_set_gencb(http, http_request_cb, nullptr);
# 385 : :
# 386 [ - + ]: 659 : if (!HTTPBindAddresses(http)) {
# 387 : 0 : LogPrintf("Unable to bind any endpoint for RPC server\n");
# 388 : 0 : return false;
# 389 : 0 : }
# 390 : :
# 391 [ + - ]: 659 : LogPrint(BCLog::HTTP, "Initialized HTTP server\n");
# 392 : 659 : int workQueueDepth = std::max((long)gArgs.GetArg("-rpcworkqueue", DEFAULT_HTTP_WORKQUEUE), 1L);
# 393 : 659 : LogPrintf("HTTP: creating work queue of depth %d\n", workQueueDepth);
# 394 : :
# 395 : 659 : workQueue = new WorkQueue<HTTPClosure>(workQueueDepth);
# 396 : : // transfer ownership to eventBase/HTTP via .release()
# 397 : 659 : eventBase = base_ctr.release();
# 398 : 659 : eventHTTP = http_ctr.release();
# 399 : 659 : return true;
# 400 : 659 : }
# 401 : :
# 402 : 659 : bool UpdateHTTPServerLogging(bool enable) {
# 403 : 659 : #if LIBEVENT_VERSION_NUMBER >= 0x02010100
# 404 [ - + ]: 659 : if (enable) {
# 405 : 0 : event_enable_debug_logging(EVENT_DBG_ALL);
# 406 : 659 : } else {
# 407 : 659 : event_enable_debug_logging(EVENT_DBG_NONE);
# 408 : 659 : }
# 409 : 659 : return true;
# 410 : : #else
# 411 : : // Can't update libevent logging if version < 02010100
# 412 : : return false;
# 413 : : #endif
# 414 : 659 : }
# 415 : :
# 416 : : static std::thread g_thread_http;
# 417 : : static std::vector<std::thread> g_thread_http_workers;
# 418 : :
# 419 : : void StartHTTPServer()
# 420 : 656 : {
# 421 [ + - ]: 656 : LogPrint(BCLog::HTTP, "Starting HTTP server\n");
# 422 : 656 : int rpcThreads = std::max((long)gArgs.GetArg("-rpcthreads", DEFAULT_HTTP_THREADS), 1L);
# 423 : 656 : LogPrintf("HTTP: starting %d worker threads\n", rpcThreads);
# 424 : 656 : g_thread_http = std::thread(ThreadHTTP, eventBase);
# 425 : :
# 426 [ + + ]: 3277 : for (int i = 0; i < rpcThreads; i++) {
# 427 : 2621 : g_thread_http_workers.emplace_back(HTTPWorkQueueRun, workQueue, i);
# 428 : 2621 : }
# 429 : 656 : }
# 430 : :
# 431 : : void InterruptHTTPServer()
# 432 : 663 : {
# 433 [ + - ]: 663 : LogPrint(BCLog::HTTP, "Interrupting HTTP server\n");
# 434 [ + + ]: 663 : if (eventHTTP) {
# 435 : : // Reject requests on current connections
# 436 : 659 : evhttp_set_gencb(eventHTTP, http_reject_request_cb, nullptr);
# 437 : 659 : }
# 438 [ + + ]: 663 : if (workQueue)
# 439 : 659 : workQueue->Interrupt();
# 440 : 663 : }
# 441 : :
# 442 : : void StopHTTPServer()
# 443 : 663 : {
# 444 [ + - ]: 663 : LogPrint(BCLog::HTTP, "Stopping HTTP server\n");
# 445 [ + + ]: 663 : if (workQueue) {
# 446 [ + - ]: 659 : LogPrint(BCLog::HTTP, "Waiting for HTTP worker threads to exit\n");
# 447 [ + + ]: 2621 : for (auto& thread: g_thread_http_workers) {
# 448 : 2621 : thread.join();
# 449 : 2621 : }
# 450 : 659 : g_thread_http_workers.clear();
# 451 : 659 : delete workQueue;
# 452 : 659 : workQueue = nullptr;
# 453 : 659 : }
# 454 : : // Unlisten sockets, these are what make the event loop running, which means
# 455 : : // that after this and all connections are closed the event loop will quit.
# 456 [ + + ]: 1318 : for (evhttp_bound_socket *socket : boundSockets) {
# 457 : 1318 : evhttp_del_accept_socket(eventHTTP, socket);
# 458 : 1318 : }
# 459 : 663 : boundSockets.clear();
# 460 [ + + ]: 663 : if (eventBase) {
# 461 [ + - ]: 659 : LogPrint(BCLog::HTTP, "Waiting for HTTP event thread to exit\n");
# 462 [ + + ]: 659 : if (g_thread_http.joinable()) g_thread_http.join();
# 463 : 659 : }
# 464 [ + + ]: 663 : if (eventHTTP) {
# 465 : 659 : evhttp_free(eventHTTP);
# 466 : 659 : eventHTTP = nullptr;
# 467 : 659 : }
# 468 [ + + ]: 663 : if (eventBase) {
# 469 : 659 : event_base_free(eventBase);
# 470 : 659 : eventBase = nullptr;
# 471 : 659 : }
# 472 [ + - ]: 663 : LogPrint(BCLog::HTTP, "Stopped HTTP server\n");
# 473 : 663 : }
# 474 : :
# 475 : : struct event_base* EventBase()
# 476 : 656 : {
# 477 : 656 : return eventBase;
# 478 : 656 : }
# 479 : :
# 480 : : static void httpevent_callback_fn(evutil_socket_t, short, void* data)
# 481 : 111290 : {
# 482 : : // Static handler: simply call inner handler
# 483 : 111290 : HTTPEvent *self = static_cast<HTTPEvent*>(data);
# 484 : 111290 : self->handler();
# 485 [ + + ]: 111290 : if (self->deleteWhenTriggered)
# 486 : 111283 : delete self;
# 487 : 111290 : }
# 488 : :
# 489 : : HTTPEvent::HTTPEvent(struct event_base* base, bool _deleteWhenTriggered, const std::function<void()>& _handler):
# 490 : : deleteWhenTriggered(_deleteWhenTriggered), handler(_handler)
# 491 : 111340 : {
# 492 : 111340 : ev = event_new(base, -1, 0, httpevent_callback_fn, this);
# 493 : 111340 : assert(ev);
# 494 : 111340 : }
# 495 : : HTTPEvent::~HTTPEvent()
# 496 : 111340 : {
# 497 : 111340 : event_free(ev);
# 498 : 111340 : }
# 499 : : void HTTPEvent::trigger(struct timeval* tv)
# 500 : 111340 : {
# 501 [ + + ]: 111340 : if (tv == nullptr)
# 502 : 111283 : event_active(ev, 0, 0); // immediately trigger event in main thread
# 503 : 111340 : else
# 504 : 111340 : evtimer_add(ev, tv); // trigger after timeval passed
# 505 : 111340 : }
# 506 : : HTTPRequest::HTTPRequest(struct evhttp_request* _req, bool _replySent) : req(_req), replySent(_replySent)
# 507 : 111283 : {
# 508 : 111283 : }
# 509 : :
# 510 : : HTTPRequest::~HTTPRequest()
# 511 : 111283 : {
# 512 [ - + ]: 111283 : if (!replySent) {
# 513 : : // Keep track of whether reply was sent to avoid request leaks
# 514 : 0 : LogPrintf("%s: Unhandled request\n", __func__);
# 515 : 0 : WriteReply(HTTP_INTERNAL_SERVER_ERROR, "Unhandled request");
# 516 : 0 : }
# 517 : : // evhttpd cleans up the request, as long as a reply was sent.
# 518 : 111283 : }
# 519 : :
# 520 : : std::pair<bool, std::string> HTTPRequest::GetHeader(const std::string& hdr) const
# 521 : 111240 : {
# 522 : 111240 : const struct evkeyvalq* headers = evhttp_request_get_input_headers(req);
# 523 : 111240 : assert(headers);
# 524 : 111240 : const char* val = evhttp_find_header(headers, hdr.c_str());
# 525 [ + - ]: 111240 : if (val)
# 526 : 111240 : return std::make_pair(true, val);
# 527 : 0 : else
# 528 : 0 : return std::make_pair(false, "");
# 529 : 111240 : }
# 530 : :
# 531 : : std::string HTTPRequest::ReadBody()
# 532 : 111237 : {
# 533 : 111237 : struct evbuffer* buf = evhttp_request_get_input_buffer(req);
# 534 [ - + ]: 111237 : if (!buf)
# 535 : 0 : return "";
# 536 : 111237 : size_t size = evbuffer_get_length(buf);
# 537 : : /** Trivial implementation: if this is ever a performance bottleneck,
# 538 : : * internal copying can be avoided in multi-segment buffers by using
# 539 : : * evbuffer_peek and an awkward loop. Though in that case, it'd be even
# 540 : : * better to not copy into an intermediate string but use a stream
# 541 : : * abstraction to consume the evbuffer on the fly in the parsing algorithm.
# 542 : : */
# 543 : 111237 : const char* data = (const char*)evbuffer_pullup(buf, size);
# 544 [ + + ]: 111237 : if (!data) // returns nullptr in case of empty buffer
# 545 : 12 : return "";
# 546 : 111225 : std::string rv(data, size);
# 547 : 111225 : evbuffer_drain(buf, size);
# 548 : 111225 : return rv;
# 549 : 111225 : }
# 550 : :
# 551 : : void HTTPRequest::WriteHeader(const std::string& hdr, const std::string& value)
# 552 : 111894 : {
# 553 : 111894 : struct evkeyvalq* headers = evhttp_request_get_output_headers(req);
# 554 : 111894 : assert(headers);
# 555 : 111894 : evhttp_add_header(headers, hdr.c_str(), value.c_str());
# 556 : 111894 : }
# 557 : :
# 558 : : /** Closure sent to main thread to request a reply to be sent to
# 559 : : * a HTTP request.
# 560 : : * Replies must be sent in the main loop in the main http thread,
# 561 : : * this cannot be done from worker threads.
# 562 : : */
# 563 : : void HTTPRequest::WriteReply(int nStatus, const std::string& strReply)
# 564 : 111283 : {
# 565 : 111283 : assert(!replySent && req);
# 566 [ + + ]: 111283 : if (ShutdownRequested()) {
# 567 : 618 : WriteHeader("Connection", "close");
# 568 : 618 : }
# 569 : : // Send event to main http thread to send reply message
# 570 : 111283 : struct evbuffer* evb = evhttp_request_get_output_buffer(req);
# 571 : 111283 : assert(evb);
# 572 : 111283 : evbuffer_add(evb, strReply.data(), strReply.size());
# 573 : 111283 : auto req_copy = req;
# 574 : 111283 : HTTPEvent* ev = new HTTPEvent(eventBase, true, [req_copy, nStatus]{
# 575 : 111283 : evhttp_send_reply(req_copy, nStatus, nullptr, nullptr);
# 576 : : // Re-enable reading from the socket. This is the second part of the libevent
# 577 : : // workaround above.
# 578 [ + - ][ + - ]: 111283 : if (event_get_version_number() >= 0x02010600 && event_get_version_number() < 0x02020001) {
# 579 : 111283 : evhttp_connection* conn = evhttp_request_get_connection(req_copy);
# 580 [ + - ]: 111283 : if (conn) {
# 581 : 111283 : bufferevent* bev = evhttp_connection_get_bufferevent(conn);
# 582 [ + - ]: 111283 : if (bev) {
# 583 : 111283 : bufferevent_enable(bev, EV_READ | EV_WRITE);
# 584 : 111283 : }
# 585 : 111283 : }
# 586 : 111283 : }
# 587 : 111283 : });
# 588 : 111283 : ev->trigger(nullptr);
# 589 : 111283 : replySent = true;
# 590 : 111283 : req = nullptr; // transferred back to main thread
# 591 : 111283 : }
# 592 : :
# 593 : : CService HTTPRequest::GetPeer() const
# 594 : 333806 : {
# 595 : 333806 : evhttp_connection* con = evhttp_request_get_connection(req);
# 596 : 333806 : CService peer;
# 597 [ + - ]: 333806 : if (con) {
# 598 : : // evhttp retains ownership over returned address string
# 599 : 333806 : const char* address = "";
# 600 : 333806 : uint16_t port = 0;
# 601 : 333806 : evhttp_connection_get_peer(con, (char**)&address, &port);
# 602 : 333806 : peer = LookupNumeric(address, port);
# 603 : 333806 : }
# 604 : 333806 : return peer;
# 605 : 333806 : }
# 606 : :
# 607 : : std::string HTTPRequest::GetURI() const
# 608 : 333788 : {
# 609 : 333788 : return evhttp_request_get_uri(req);
# 610 : 333788 : }
# 611 : :
# 612 : : HTTPRequest::RequestMethod HTTPRequest::GetRequestMethod() const
# 613 : 333806 : {
# 614 : 333806 : switch (evhttp_request_get_command(req)) {
# 615 [ + + ]: 72 : case EVHTTP_REQ_GET:
# 616 : 72 : return GET;
# 617 : 0 : break;
# 618 [ + + ]: 333734 : case EVHTTP_REQ_POST:
# 619 : 333734 : return POST;
# 620 : 0 : break;
# 621 [ - + ]: 0 : case EVHTTP_REQ_HEAD:
# 622 : 0 : return HEAD;
# 623 : 0 : break;
# 624 [ - + ]: 0 : case EVHTTP_REQ_PUT:
# 625 : 0 : return PUT;
# 626 : 0 : break;
# 627 [ - + ]: 0 : default:
# 628 : 0 : return UNKNOWN;
# 629 : 0 : break;
# 630 : 333806 : }
# 631 : 333806 : }
# 632 : :
# 633 : : void RegisterHTTPHandler(const std::string &prefix, bool exactMatch, const HTTPRequestHandler &handler)
# 634 : 1321 : {
# 635 [ + - ]: 1321 : LogPrint(BCLog::HTTP, "Registering HTTP handler for %s (exactmatch %d)\n", prefix, exactMatch);
# 636 : 1321 : pathHandlers.push_back(HTTPPathHandler(prefix, exactMatch, handler));
# 637 : 1321 : }
# 638 : :
# 639 : : void UnregisterHTTPHandler(const std::string &prefix, bool exactMatch)
# 640 : 7293 : {
# 641 : 7293 : std::vector<HTTPPathHandler>::iterator i = pathHandlers.begin();
# 642 : 7293 : std::vector<HTTPPathHandler>::iterator iend = pathHandlers.end();
# 643 [ + + ]: 7293 : for (; i != iend; ++i)
# 644 [ + - ][ + - ]: 1321 : if (i->prefix == prefix && i->exactMatch == exactMatch)
# 645 : 1321 : break;
# 646 [ + + ]: 7293 : if (i != iend)
# 647 : 1321 : {
# 648 [ + - ]: 1321 : LogPrint(BCLog::HTTP, "Unregistering HTTP handler for %s (exactmatch %d)\n", prefix, exactMatch);
# 649 : 1321 : pathHandlers.erase(i);
# 650 : 1321 : }
# 651 : 7293 : }
|