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