LCOV - code coverage report
Current view: top level - src - httpserver.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 398 475 83.8 %
Date: 2026-06-25 07:23:43 Functions: 78 82 95.1 %

          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 <netbase.h>
      13             : #include <node/interface_ui.h>
      14             : #include <rpc/protocol.h> // For HTTP status codes
      15             : #include <shutdown.h>
      16             : #include <sync.h>
      17             : #include <util/check.h>
      18             : #include <util/strencodings.h>
      19             : #include <util/system.h>
      20             : #include <util/threadnames.h>
      21             : #include <util/translation.h>
      22             : 
      23             : #include <cstdio>
      24             : #include <deque>
      25             : #include <optional>
      26             : #include <string>
      27             : #include <unordered_map>
      28             : 
      29             : #include <sys/types.h>
      30             : 
      31             : #include <event2/buffer.h>
      32             : #include <event2/bufferevent.h>
      33             : #include <event2/http.h>
      34             : #include <event2/http_struct.h>
      35             : #include <event2/keyvalq_struct.h>
      36             : #include <event2/thread.h>
      37             : #include <event2/util.h>
      38             : 
      39             : #include <support/events.h>
      40             : 
      41             : #include <thread>
      42             : #include <condition_variable>
      43             : 
      44             : /** Maximum size of http request (request line + headers) */
      45             : static const size_t MAX_HEADERS_SIZE = 8192;
      46             : 
      47             : /** HTTP request work item */
      48             : class HTTPWorkItem final : public HTTPClosure
      49             : {
      50             : public:
      51     1000424 :     HTTPWorkItem(std::unique_ptr<HTTPRequest> _req, const std::string &_path, const HTTPRequestHandler& _func):
      52      500212 :         req(std::move(_req)), path(_path), func(_func)
      53     1000424 :     {
      54     1000424 :     }
      55      500209 :     void operator()() override
      56             :     {
      57      500209 :         func(req.get(), path);
      58      500209 :     }
      59             : 
      60             :     std::unique_ptr<HTTPRequest> req;
      61             : 
      62             : private:
      63             :     std::string path;
      64             :     HTTPRequestHandler func;
      65             : };
      66             : 
      67             : /** Simple work queue for distributing work over multiple threads.
      68             :  * Work items are simply callable objects.
      69             :  */
      70             : template <typename WorkItem>
      71             : class WorkQueue
      72             : {
      73             : private:
      74             :     Mutex cs;
      75             :     std::condition_variable cond GUARDED_BY(cs);
      76             :     std::deque<std::unique_ptr<WorkItem>> queue GUARDED_BY(cs);
      77             :     std::deque<std::unique_ptr<WorkItem>> external_queue GUARDED_BY(cs);
      78        3019 :     bool running GUARDED_BY(cs){true};
      79             :     const size_t maxDepth;
      80             :     const size_t m_external_depth;
      81             : 
      82             : public:
      83        6038 :     explicit WorkQueue(size_t _maxDepth, size_t external_depth) :
      84        3019 :                                  maxDepth(_maxDepth), m_external_depth(external_depth)
      85        3019 :     {
      86        6038 :     }
      87             :     /** Precondition: worker threads have all stopped (they have been joined).
      88             :      */
      89        6038 :     ~WorkQueue() = default;
      90             :     /** Enqueue a work item */
      91      500212 :     bool Enqueue(WorkItem* item, bool is_external) EXCLUSIVE_LOCKS_REQUIRED(!cs)
      92             :     {
      93      500212 :         LOCK(cs);
      94      500212 :         if (!running) {
      95           0 :             return false;
      96             :         }
      97      500212 :         if (is_external) {
      98           6 :             if (external_queue.size() >= m_external_depth) return false;
      99           6 :             external_queue.emplace_back(std::unique_ptr<WorkItem>(item));
     100           6 :         } else {
     101      500206 :             if (queue.size() >= maxDepth) return false;
     102      500203 :             queue.emplace_back(std::unique_ptr<WorkItem>(item));
     103             :         }
     104      500209 :         cond.notify_one();
     105      500209 :         return true;
     106      500212 :     }
     107             :     /** Thread function */
     108       11992 :     void Run() EXCLUSIVE_LOCKS_REQUIRED(!cs)
     109             :     {
     110      512231 :         while (true) {
     111      512231 :             std::unique_ptr<WorkItem> i;
     112             :             {
     113      512231 :                 WAIT_LOCK(cs, lock);
     114     1022274 :                 while (running && external_queue.empty() && queue.empty())
     115      510043 :                     cond.wait(lock);
     116      512231 :                 if (!running && external_queue.empty() && queue.empty())
     117       12022 :                     break;
     118      500209 :                 if (!queue.empty()) {
     119      500203 :                     i = std::move(queue.front());
     120      500203 :                     queue.pop_front();
     121      500203 :                 } else {
     122           6 :                     i = std::move(external_queue.front());
     123           6 :                     external_queue.pop_front();
     124           6 :                     LogPrintf("HTTP: Calling handler for external user...\n");
     125             :                 }
     126      512231 :             }
     127      500209 :             (*i)();
     128      512231 :         }
     129       11992 :     }
     130             :     /** Interrupt and exit loops */
     131        3019 :     void Interrupt() EXCLUSIVE_LOCKS_REQUIRED(!cs)
     132             :     {
     133        3019 :         LOCK(cs);
     134        3019 :         running = false;
     135        3019 :         cond.notify_all();
     136        3019 :     }
     137             : };
     138             : 
     139             : struct HTTPPathHandler
     140             : {
     141       12068 :     HTTPPathHandler(std::string _prefix, bool _exactMatch, HTTPRequestHandler _handler):
     142        6034 :         prefix(_prefix), exactMatch(_exactMatch), handler(_handler)
     143        6034 :     {
     144       12068 :     }
     145             :     std::string prefix;
     146             :     bool exactMatch;
     147             :     HTTPRequestHandler handler;
     148             : };
     149             : 
     150             : /** HTTP module state */
     151             : 
     152             : //! libevent event loop
     153             : static struct event_base* eventBase = nullptr;
     154             : //! HTTP server
     155             : static struct evhttp* eventHTTP = nullptr;
     156             : //! List of subnets to allow RPC connections from
     157             : static std::vector<CSubNet> rpc_allow_subnets;
     158             : //! Work queue for handling longer requests off the event loop thread
     159             : static std::unique_ptr<WorkQueue<HTTPClosure>> g_work_queue{nullptr};
     160             : //! List of 'external' RPC users (global variable, used by httprpc)
     161             : std::vector<std::string> g_external_usernames;
     162             : //! Handlers for (sub)paths
     163             : static GlobalMutex g_httppathhandlers_mutex;
     164             : static std::vector<HTTPPathHandler> pathHandlers GUARDED_BY(g_httppathhandlers_mutex);
     165             : //! Bound listening sockets
     166             : static std::vector<evhttp_bound_socket *> boundSockets;
     167             : 
     168             : /**
     169             :  * @brief Helps keep track of open `evhttp_connection`s with active `evhttp_requests`
     170             :  *
     171             :  */
     172             : class HTTPRequestTracker
     173             : {
     174             : private:
     175             :     mutable Mutex m_mutex;
     176             :     mutable std::condition_variable m_cv;
     177             :     //! For each connection, keep a counter of how many requests are open
     178             :     std::unordered_map<const evhttp_connection*, size_t> m_tracker GUARDED_BY(m_mutex);
     179             : 
     180      500214 :     void RemoveConnectionInternal(const decltype(m_tracker)::iterator it) EXCLUSIVE_LOCKS_REQUIRED(m_mutex)
     181             :     {
     182      500214 :         m_tracker.erase(it);
     183      500214 :         if (m_tracker.empty()) m_cv.notify_all();
     184      500214 :     }
     185             : public:
     186             :     //! Increase request counter for the associated connection by 1
     187      500214 :     void AddRequest(evhttp_request* req) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
     188             :     {
     189      500214 :         const evhttp_connection* conn{Assert(evhttp_request_get_connection(Assert(req)))};
     190     1000428 :         WITH_LOCK(m_mutex, ++m_tracker[conn]);
     191      500214 :     }
     192             :     //! Decrease request counter for the associated connection by 1, remove connection if counter is 0
     193      500214 :     void RemoveRequest(evhttp_request* req) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
     194             :     {
     195      500214 :         const evhttp_connection* conn{Assert(evhttp_request_get_connection(Assert(req)))};
     196      500214 :         LOCK(m_mutex);
     197      500214 :         auto it{m_tracker.find(conn)};
     198      500214 :         if (it != m_tracker.end() && it->second > 0) {
     199      500214 :             if (--(it->second) == 0) RemoveConnectionInternal(it);
     200      500214 :         }
     201      500214 :     }
     202             :     //! Remove a connection entirely
     203        9104 :     void RemoveConnection(const evhttp_connection* conn) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
     204             :     {
     205        9104 :         LOCK(m_mutex);
     206        9104 :         auto it{m_tracker.find(Assert(conn))};
     207        9104 :         if (it != m_tracker.end()) RemoveConnectionInternal(it);
     208        9104 :     }
     209        3030 :     size_t CountActiveConnections() const EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
     210             :     {
     211        6060 :         return WITH_LOCK(m_mutex, return m_tracker.size());
     212             :     }
     213             :     //! Wait until there are no more connections with active requests in the tracker
     214        3030 :     void WaitUntilEmpty() const EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
     215             :     {
     216        3030 :         WAIT_LOCK(m_mutex, lock);
     217        6060 :         m_cv.wait(lock, [this]() EXCLUSIVE_LOCKS_REQUIRED(m_mutex) { return m_tracker.empty(); });
     218        3030 :     }
     219             : };
     220             : //! Track active requests
     221        3308 : static HTTPRequestTracker g_requests;
     222             : 
     223             : /** Check if a network address is allowed to access the HTTP server */
     224      500214 : static bool ClientAllowed(const CNetAddr& netaddr)
     225             : {
     226      500214 :     if (!netaddr.IsValid())
     227           0 :         return false;
     228      500214 :     for(const CSubNet& subnet : rpc_allow_subnets)
     229      500214 :         if (subnet.Match(netaddr))
     230      500214 :             return true;
     231           0 :     return false;
     232      500214 : }
     233             : 
     234             : /** Initialize ACL list for HTTP server */
     235        3019 : static bool InitHTTPAllowList()
     236             : {
     237        3019 :     rpc_allow_subnets.clear();
     238        3019 :     rpc_allow_subnets.emplace_back(LookupHost("127.0.0.1", false).value(), 8);  // always allow IPv4 local subnet
     239        3019 :     rpc_allow_subnets.emplace_back(LookupHost("::1", false).value());  // always allow IPv6 localhost
     240        3021 :     for (const std::string& strAllow : gArgs.GetArgs("-rpcallowip")) {
     241           2 :         const CSubNet subnet{LookupSubNet(strAllow)};
     242           2 :         if (!subnet.IsValid()) {
     243           0 :             uiInterface.ThreadSafeMessageBox(
     244           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),
     245           0 :                 "", CClientUIInterface::MSG_ERROR);
     246           0 :             return false;
     247             :         }
     248           2 :         rpc_allow_subnets.push_back(subnet);
     249           2 :     }
     250        3019 :     std::string strAllowed;
     251        9059 :     for (const CSubNet& subnet : rpc_allow_subnets)
     252        6040 :         strAllowed += subnet.ToString() + " ";
     253        3019 :     LogPrint(BCLog::HTTP, "Allowing HTTP connections from: %s\n", strAllowed);
     254        3019 :     return true;
     255        3019 : }
     256             : 
     257             : /** HTTP request method as string - use for logging only */
     258      500196 : std::string RequestMethodString(HTTPRequest::RequestMethod m)
     259             : {
     260      500196 :     switch (m) {
     261             :     case HTTPRequest::GET:
     262         102 :         return "GET";
     263             :     case HTTPRequest::POST:
     264      500094 :         return "POST";
     265             :     case HTTPRequest::HEAD:
     266           0 :         return "HEAD";
     267             :     case HTTPRequest::PUT:
     268           0 :         return "PUT";
     269             :     case HTTPRequest::UNKNOWN:
     270           0 :         return "unknown";
     271             :     } // no default case, so the compiler can warn about missing cases
     272           0 :     assert(false);
     273      500196 : }
     274             : 
     275             : /** HTTP request callback */
     276      500214 : static void http_request_cb(struct evhttp_request* req, void* arg)
     277             : {
     278      500214 :     evhttp_connection* conn{evhttp_request_get_connection(req)};
     279             :     // Track active requests
     280             :     {
     281      500214 :         g_requests.AddRequest(req);
     282     1000428 :         evhttp_request_set_on_complete_cb(req, [](struct evhttp_request* req, void*) {
     283      500214 :             g_requests.RemoveRequest(req);
     284      500214 :         }, nullptr);
     285      509318 :         evhttp_connection_set_closecb(conn, [](evhttp_connection* conn, void* arg) {
     286        9104 :             g_requests.RemoveConnection(conn);
     287        9104 :         }, nullptr);
     288             :     }
     289             : 
     290             :     // Disable reading to work around a libevent bug, fixed in 2.1.9
     291             :     // See https://github.com/libevent/libevent/commit/5ff8eb26371c4dc56f384b2de35bea2d87814779
     292             :     // and https://github.com/bitcoin/bitcoin/pull/11593.
     293      500214 :     if (event_get_version_number() >= 0x02010600 && event_get_version_number() < 0x02010900) {
     294           0 :         if (conn) {
     295           0 :             bufferevent* bev = evhttp_connection_get_bufferevent(conn);
     296           0 :             if (bev) {
     297           0 :                 bufferevent_disable(bev, EV_READ);
     298           0 :             }
     299           0 :         }
     300           0 :     }
     301      500214 :     auto hreq{std::make_unique<HTTPRequest>(req)};
     302             : 
     303             :     // Early address-based allow check
     304      500214 :     if (!ClientAllowed(hreq->GetPeer())) {
     305           0 :         LogPrint(BCLog::HTTP, "HTTP request from %s rejected: Client network is not allowed RPC access\n",
     306             :                  hreq->GetPeer().ToStringAddrPort());
     307           0 :         hreq->WriteReply(HTTP_FORBIDDEN);
     308           0 :         return;
     309             :     }
     310             : 
     311             :     // Early reject unknown HTTP methods
     312      500214 :     if (hreq->GetRequestMethod() == HTTPRequest::UNKNOWN) {
     313           0 :         LogPrint(BCLog::HTTP, "HTTP request from %s rejected: Unknown HTTP request method\n",
     314             :                  hreq->GetPeer().ToStringAddrPort());
     315           0 :         hreq->WriteReply(HTTP_BAD_METHOD);
     316           0 :         return;
     317             :     }
     318             : 
     319      500214 :     LogPrint(BCLog::HTTP, "Received a %s request for %s from %s\n",
     320             :              RequestMethodString(hreq->GetRequestMethod()), SanitizeString(hreq->GetURI(), SAFE_CHARS_URI).substr(0, 100), hreq->GetPeer().ToStringAddrPort());
     321             : 
     322             :     // Find registered handler for prefix
     323      500214 :     std::string strURI = hreq->GetURI();
     324      500214 :     std::string path;
     325      500214 :     LOCK(g_httppathhandlers_mutex);
     326      500214 :     std::vector<HTTPPathHandler>::const_iterator i = pathHandlers.begin();
     327      500214 :     std::vector<HTTPPathHandler>::const_iterator iend = pathHandlers.end();
     328      516961 :     for (; i != iend; ++i) {
     329      516959 :         bool match = false;
     330      516959 :         if (i->exactMatch)
     331      500214 :             match = (strURI == i->prefix);
     332             :         else
     333       16745 :             match = (strURI.substr(0, i->prefix.size()) == i->prefix);
     334      516959 :         if (match) {
     335      500212 :             path = strURI.substr(i->prefix.size());
     336      500212 :             break;
     337             :         }
     338       16747 :     }
     339     1000428 :     const bool is_external_request = [&hreq]() -> bool {
     340      500214 :         if (g_external_usernames.empty()) return false;
     341             : 
     342          32 :         const std::string strAuth = hreq->GetHeader("authorization").second;
     343          32 :         if (strAuth.substr(0, 6) != "Basic ")
     344           0 :             return false;
     345             : 
     346          32 :         std::string strUserPass64 = TrimString(strAuth.substr(6));
     347          32 :         auto opt_strUserPass = DecodeBase64(strUserPass64);
     348          32 :         if (!opt_strUserPass.has_value()) return false;
     349          32 :         auto it = std::find(opt_strUserPass->begin(), opt_strUserPass->end(), ':');
     350          32 :         if (it == opt_strUserPass->end()) return false;
     351          32 :         const std::string username = std::string(opt_strUserPass->begin(), it);
     352          32 :         return find(g_external_usernames.begin(), g_external_usernames.end(), username) != g_external_usernames.end();
     353      500214 :     }();
     354             : 
     355             :     // Dispatch to worker thread
     356      500214 :     if (i != iend) {
     357      500212 :         auto item{std::make_unique<HTTPWorkItem>(std::move(hreq), path, i->handler)};
     358      500212 :         assert(g_work_queue);
     359             : 
     360      500212 :         if (g_work_queue->Enqueue(item.get(), is_external_request)) {
     361      500209 :             item.release(); /* if true, queue took ownership */
     362      500209 :         } else {
     363           3 :             if (is_external_request)
     364             :             {
     365           0 :                 LogPrintf("WARNING: request rejected because http work queue depth of externals exceeded, it can be increased with the -rpcexternalworkqueue= setting\n");
     366           0 :                 item->req->WriteReply(HTTP_SERVICE_UNAVAILABLE, "Work queue depth of externals exceeded");
     367           0 :             } else {
     368           3 :                 LogPrintf("WARNING: request rejected because http work queue depth exceeded, it can be increased with the -rpcworkqueue= setting\n");
     369           3 :                 item->req->WriteReply(HTTP_SERVICE_UNAVAILABLE, "Work queue depth exceeded");
     370             :             }
     371             :         }
     372      500212 :     } else {
     373           2 :         hreq->WriteReply(HTTP_NOT_FOUND);
     374             :     }
     375      500214 : }
     376             : 
     377             : /** Callback to reject HTTP requests after shutdown. */
     378           0 : static void http_reject_request_cb(struct evhttp_request* req, void*)
     379             : {
     380           0 :     LogPrint(BCLog::HTTP, "Rejecting request while shutting down\n");
     381           0 :     evhttp_send_error(req, HTTP_SERVUNAVAIL, nullptr);
     382           0 : }
     383             : 
     384             : /** Event dispatcher thread */
     385        3007 : static void ThreadHTTP(struct event_base* base)
     386             : {
     387        3007 :     util::ThreadRename("http");
     388        3007 :     LogPrint(BCLog::HTTP, "Entering http event loop\n");
     389        3007 :     event_base_dispatch(base);
     390             :     // Event loop will be interrupted by InterruptHTTPServer()
     391        3007 :     LogPrint(BCLog::HTTP, "Exited http event loop\n");
     392        3007 : }
     393             : 
     394             : /** Bind HTTP server to specified addresses */
     395        3019 : static bool HTTPBindAddresses(struct evhttp* http)
     396             : {
     397        3019 :     uint16_t http_port{static_cast<uint16_t>(gArgs.GetIntArg("-rpcport", BaseParams().RPCPort()))};
     398        3019 :     std::vector<std::pair<std::string, uint16_t>> endpoints;
     399             : 
     400             :     // Determine what addresses to bind to
     401        3021 :     if (!(gArgs.IsArgSet("-rpcallowip") && gArgs.IsArgSet("-rpcbind"))) { // Default to loopback if not allowing external IPs
     402        3017 :         endpoints.emplace_back("::1", http_port);
     403        3017 :         endpoints.emplace_back("127.0.0.1", http_port);
     404        3017 :         if (gArgs.IsArgSet("-rpcallowip")) {
     405           0 :             LogPrintf("WARNING: option -rpcallowip was specified without -rpcbind; this doesn't usually make sense\n");
     406           0 :         }
     407        3017 :         if (gArgs.IsArgSet("-rpcbind")) {
     408           0 :             LogPrintf("WARNING: option -rpcbind was ignored because -rpcallowip was not specified, refusing to allow everyone to connect\n");
     409           0 :         }
     410        3019 :     } else if (gArgs.IsArgSet("-rpcbind")) { // Specific bind address
     411           6 :         for (const std::string& strRPCBind : gArgs.GetArgs("-rpcbind")) {
     412           4 :             uint16_t port{http_port};
     413           4 :             std::string host;
     414           4 :             SplitHostPort(strRPCBind, port, host);
     415           4 :             endpoints.emplace_back(host, port);
     416           4 :         }
     417           2 :     } else { // No specific bind address specified, bind to any
     418           0 :         endpoints.push_back(std::make_pair("::", http_port));
     419           0 :         endpoints.push_back(std::make_pair("0.0.0.0", http_port));
     420             :     }
     421             : 
     422             :     // Bind addresses
     423        9057 :     for (std::vector<std::pair<std::string, uint16_t> >::iterator i = endpoints.begin(); i != endpoints.end(); ++i) {
     424        6038 :         LogPrintf("Binding RPC on address %s port %i\n", i->first, i->second);
     425        6038 :         evhttp_bound_socket *bind_handle = evhttp_bind_socket_with_handle(http, i->first.empty() ? nullptr : i->first.c_str(), i->second);
     426        6038 :         if (bind_handle) {
     427        6036 :             const std::optional<CNetAddr> addr{LookupHost(i->first, false)};
     428        6036 :             if (i->first.empty() || (addr.has_value() && addr->IsBindAny())) {
     429        6036 :                 LogPrintf("WARNING: the RPC server is not safe to expose to untrusted networks such as the public internet\n");
     430           0 :             }
     431           0 :             boundSockets.push_back(bind_handle);
     432        6036 :         } else {
     433           2 :             LogPrintf("Binding RPC on address %s port %i failed.\n", i->first, i->second);
     434             :         }
     435        6038 :     }
     436        3019 :     return !boundSockets.empty();
     437       15091 : }
     438             : 
     439             : /** Simple wrapper to set thread name and run work queue */
     440       11899 : static void HTTPWorkQueueRun(WorkQueue<HTTPClosure>* queue, int worker_num)
     441             : {
     442       11899 :     util::ThreadRename(strprintf("httpworker.%i", worker_num));
     443       11899 :     queue->Run();
     444       11899 : }
     445             : 
     446             : /** libevent event log callback */
     447           0 : static void libevent_log_cb(int severity, const char *msg)
     448             : {
     449             :     BCLog::Level level;
     450           0 :     switch (severity) {
     451             :     case EVENT_LOG_DEBUG:
     452           0 :         level = BCLog::Level::Debug;
     453           0 :         break;
     454             :     case EVENT_LOG_MSG:
     455           0 :         level = BCLog::Level::Info;
     456           0 :         break;
     457             :     case EVENT_LOG_WARN:
     458           0 :         level = BCLog::Level::Warning;
     459           0 :         break;
     460             :     default: // EVENT_LOG_ERR and others are mapped to error
     461           0 :         level = BCLog::Level::Error;
     462           0 :         break;
     463             :     }
     464           0 :     LogPrintLevel(BCLog::LIBEVENT, level, "%s\n", msg);
     465           0 : }
     466             : 
     467        3019 : bool InitHTTPServer()
     468             : {
     469        3019 :     if (!InitHTTPAllowList())
     470           0 :         return false;
     471             : 
     472             :     // Redirect libevent's logging to our own log
     473        3019 :     event_set_log_callback(&libevent_log_cb);
     474             :     // Update libevent's log handling.
     475        3019 :     UpdateHTTPServerLogging(LogInstance().WillLogCategory(BCLog::LIBEVENT));
     476             : 
     477             : #ifdef WIN32
     478             :     evthread_use_windows_threads();
     479             : #else
     480        3019 :     evthread_use_pthreads();
     481             : #endif
     482             : 
     483        3019 :     raii_event_base base_ctr = obtain_event_base();
     484             : 
     485             :     /* Create a new evhttp object to handle requests. */
     486        3019 :     raii_evhttp http_ctr = obtain_evhttp(base_ctr.get());
     487        3019 :     struct evhttp* http = http_ctr.get();
     488        3019 :     if (!http) {
     489           0 :         LogPrintf("couldn't create evhttp. Exiting.\n");
     490           0 :         return false;
     491             :     }
     492             : 
     493        3019 :     evhttp_set_timeout(http, gArgs.GetIntArg("-rpcservertimeout", DEFAULT_HTTP_SERVER_TIMEOUT));
     494        3019 :     evhttp_set_max_headers_size(http, MAX_HEADERS_SIZE);
     495        3019 :     evhttp_set_max_body_size(http, MAX_SIZE);
     496        3019 :     evhttp_set_gencb(http, http_request_cb, nullptr);
     497             : 
     498        3019 :     if (!HTTPBindAddresses(http)) {
     499           0 :         LogPrintf("Unable to bind any endpoint for RPC server\n");
     500           0 :         return false;
     501             :     }
     502             : 
     503        3019 :     LogPrint(BCLog::HTTP, "Initialized HTTP server\n");
     504        3019 :     int workQueueDepth = std::max((long)gArgs.GetIntArg("-rpcworkqueue", DEFAULT_HTTP_WORKQUEUE), 1L);
     505        3019 :     int workQueueDepthExternal = 0;
     506        3023 :     if (const std::string rpc_externaluser{gArgs.GetArg("-rpcexternaluser", "")}; !rpc_externaluser.empty()) {
     507           4 :         workQueueDepthExternal = std::max((long)gArgs.GetIntArg("-rpcexternalworkqueue", DEFAULT_HTTP_WORKQUEUE), 1L);
     508           4 :         g_external_usernames = SplitString(rpc_externaluser, ',');
     509           4 :     }
     510        3019 :     LogDebug(BCLog::HTTP, "creating work queue of depth %d external_depth %d\n", workQueueDepth, workQueueDepthExternal);
     511        3019 :     g_work_queue = std::make_unique<WorkQueue<HTTPClosure>>(workQueueDepth, workQueueDepthExternal);
     512             :     // transfer ownership to eventBase/HTTP via .release()
     513        3019 :     eventBase = base_ctr.release();
     514        3019 :     eventHTTP = http_ctr.release();
     515        3019 :     return true;
     516        3019 : }
     517             : 
     518        3019 : void UpdateHTTPServerLogging(bool enable) {
     519        3019 :     if (enable) {
     520           0 :         event_enable_debug_logging(EVENT_DBG_ALL);
     521           0 :     } else {
     522        3019 :         event_enable_debug_logging(EVENT_DBG_NONE);
     523             :     }
     524        3019 : }
     525             : 
     526        3308 : static std::thread g_thread_http;
     527             : static std::vector<std::thread> g_thread_http_workers;
     528             : 
     529        3007 : void StartHTTPServer()
     530             : {
     531        3007 :     int rpcThreads = std::max((long)gArgs.GetIntArg("-rpcthreads", DEFAULT_HTTP_THREADS), 1L);
     532        3007 :     LogInfo("Starting HTTP server with %d worker threads\n", rpcThreads);
     533        3007 :     g_thread_http = std::thread(ThreadHTTP, eventBase);
     534             : 
     535       15029 :     for (int i = 0; i < rpcThreads; i++) {
     536       12022 :         g_thread_http_workers.emplace_back(HTTPWorkQueueRun, g_work_queue.get(), i);
     537       12022 :     }
     538        3007 : }
     539             : 
     540        3030 : void InterruptHTTPServer()
     541             : {
     542        3030 :     LogPrint(BCLog::HTTP, "Interrupting HTTP server\n");
     543        3030 :     if (eventHTTP) {
     544             :         // Reject requests on current connections
     545        3019 :         evhttp_set_gencb(eventHTTP, http_reject_request_cb, nullptr);
     546        3019 :     }
     547        3030 :     if (g_work_queue) {
     548        3019 :         g_work_queue->Interrupt();
     549        3019 :     }
     550        3030 : }
     551             : 
     552        3030 : void StopHTTPServer()
     553             : {
     554        3030 :     LogPrint(BCLog::HTTP, "Stopping HTTP server\n");
     555        3030 :     if (g_work_queue) {
     556        3019 :         LogPrint(BCLog::HTTP, "Waiting for HTTP worker threads to exit\n");
     557       15041 :         for (auto& thread : g_thread_http_workers) {
     558       12022 :             thread.join();
     559             :         }
     560        3019 :         g_thread_http_workers.clear();
     561        3019 :     }
     562             :     // Unlisten sockets, these are what make the event loop running, which means
     563             :     // that after this and all connections are closed the event loop will quit.
     564        9066 :     for (evhttp_bound_socket *socket : boundSockets) {
     565        6036 :         evhttp_del_accept_socket(eventHTTP, socket);
     566             :     }
     567        3030 :     boundSockets.clear();
     568             :     {
     569        3030 :         if (const auto n_connections{g_requests.CountActiveConnections()}; n_connections != 0) {
     570           0 :             LogPrint(BCLog::HTTP, "Waiting for %d connections to stop HTTP server\n", n_connections);
     571           0 :         }
     572        3030 :         g_requests.WaitUntilEmpty();
     573             :     }
     574        3030 :     if (eventHTTP) {
     575             :         // Schedule a callback to call evhttp_free in the event base thread, so
     576             :         // that evhttp_free does not need to be called again after the handling
     577             :         // of unfinished request connections that follows.
     578        5222 :         event_base_once(eventBase, -1, EV_TIMEOUT, [](evutil_socket_t, short, void*) {
     579        2203 :             evhttp_free(eventHTTP);
     580        2203 :             eventHTTP = nullptr;
     581        2203 :         }, nullptr, nullptr);
     582        3019 :     }
     583        3030 :     if (eventBase) {
     584        3019 :         LogPrint(BCLog::HTTP, "Waiting for HTTP event thread to exit\n");
     585        3019 :         if (g_thread_http.joinable()) g_thread_http.join();
     586        3019 :         event_base_free(eventBase);
     587        3019 :         eventBase = nullptr;
     588        3019 :     }
     589        3030 :     g_work_queue.reset();
     590        3030 :     LogPrint(BCLog::HTTP, "Stopped HTTP server\n");
     591        3030 : }
     592             : 
     593        3007 : struct event_base* EventBase()
     594             : {
     595        3007 :     return eventBase;
     596             : }
     597             : 
     598      500230 : static void httpevent_callback_fn(evutil_socket_t, short, void* data)
     599             : {
     600             :     // Static handler: simply call inner handler
     601      500230 :     HTTPEvent *self = static_cast<HTTPEvent*>(data);
     602      500230 :     self->handler();
     603      500230 :     if (self->deleteWhenTriggered)
     604      500214 :         delete self;
     605      500230 : }
     606             : 
     607     1000708 : HTTPEvent::HTTPEvent(struct event_base* base, bool _deleteWhenTriggered, const std::function<void()>& _handler):
     608      500354 :     deleteWhenTriggered(_deleteWhenTriggered), handler(_handler)
     609      500354 : {
     610      500354 :     ev = event_new(base, -1, 0, httpevent_callback_fn, this);
     611      500354 :     assert(ev);
     612     1000708 : }
     613     1000708 : HTTPEvent::~HTTPEvent()
     614      500354 : {
     615      500354 :     event_free(ev);
     616     1000708 : }
     617      500354 : void HTTPEvent::trigger(struct timeval* tv)
     618             : {
     619      500354 :     if (tv == nullptr)
     620      500214 :         event_active(ev, 0, 0); // immediately trigger event in main thread
     621             :     else
     622         140 :         evtimer_add(ev, tv); // trigger after timeval passed
     623      500354 : }
     624     1000428 : HTTPRequest::HTTPRequest(struct evhttp_request* _req, bool _replySent) : req(_req), replySent(_replySent)
     625      500214 : {
     626     1000428 : }
     627             : 
     628     1000428 : HTTPRequest::~HTTPRequest()
     629      500214 : {
     630      500214 :     if (!replySent) {
     631             :         // Keep track of whether reply was sent to avoid request leaks
     632           0 :         LogPrintf("%s: Unhandled request\n", __func__);
     633           0 :         WriteReply(HTTP_INTERNAL_SERVER_ERROR, "Unhandled request");
     634           0 :     }
     635             :     // evhttpd cleans up the request, as long as a reply was sent.
     636     1000428 : }
     637             : 
     638      500129 : std::pair<bool, std::string> HTTPRequest::GetHeader(const std::string& hdr) const
     639             : {
     640      500129 :     const struct evkeyvalq* headers = evhttp_request_get_input_headers(req);
     641      500129 :     assert(headers);
     642      500129 :     const char* val = evhttp_find_header(headers, hdr.c_str());
     643      500129 :     if (val)
     644      500129 :         return std::make_pair(true, val);
     645             :     else
     646           0 :         return std::make_pair(false, "");
     647      500129 : }
     648             : 
     649      500083 : std::string HTTPRequest::ReadBody()
     650             : {
     651      500083 :     struct evbuffer* buf = evhttp_request_get_input_buffer(req);
     652      500083 :     if (!buf)
     653           0 :         return "";
     654      500083 :     size_t size = evbuffer_get_length(buf);
     655             :     /** Trivial implementation: if this is ever a performance bottleneck,
     656             :      * internal copying can be avoided in multi-segment buffers by using
     657             :      * evbuffer_peek and an awkward loop. Though in that case, it'd be even
     658             :      * better to not copy into an intermediate string but use a stream
     659             :      * abstraction to consume the evbuffer on the fly in the parsing algorithm.
     660             :      */
     661      500083 :     const char* data = (const char*)evbuffer_pullup(buf, size);
     662      500083 :     if (!data) // returns nullptr in case of empty buffer
     663          24 :         return "";
     664      500059 :     std::string rv(data, size);
     665      500059 :     evbuffer_drain(buf, size);
     666      500059 :     return rv;
     667     1000142 : }
     668             : 
     669      502992 : void HTTPRequest::WriteHeader(const std::string& hdr, const std::string& value)
     670             : {
     671      502992 :     struct evkeyvalq* headers = evhttp_request_get_output_headers(req);
     672      502992 :     assert(headers);
     673      502992 :     evhttp_add_header(headers, hdr.c_str(), value.c_str());
     674      502992 : }
     675             : 
     676             : /** Closure sent to main thread to request a reply to be sent to
     677             :  * a HTTP request.
     678             :  * Replies must be sent in the main loop in the main http thread,
     679             :  * this cannot be done from worker threads.
     680             :  */
     681      500214 : void HTTPRequest::WriteReply(int nStatus, const std::string& strReply)
     682             : {
     683      500214 :     assert(!replySent && req);
     684      500214 :     if (ShutdownRequested()) {
     685        2805 :         WriteHeader("Connection", "close");
     686        2805 :     }
     687             :     // Send event to main http thread to send reply message
     688      500214 :     struct evbuffer* evb = evhttp_request_get_output_buffer(req);
     689      500214 :     assert(evb);
     690      500214 :     evbuffer_add(evb, strReply.data(), strReply.size());
     691      500214 :     auto req_copy = req;
     692     1000428 :     HTTPEvent* ev = new HTTPEvent(eventBase, true, [req_copy, nStatus]{
     693      500214 :         evhttp_send_reply(req_copy, nStatus, nullptr, nullptr);
     694             :         // Re-enable reading from the socket. This is the second part of the libevent
     695             :         // workaround above.
     696      500214 :         if (event_get_version_number() >= 0x02010600 && event_get_version_number() < 0x02010900) {
     697           0 :             evhttp_connection* conn = evhttp_request_get_connection(req_copy);
     698           0 :             if (conn) {
     699           0 :                 bufferevent* bev = evhttp_connection_get_bufferevent(conn);
     700           0 :                 if (bev) {
     701           0 :                     bufferevent_enable(bev, EV_READ | EV_WRITE);
     702           0 :                 }
     703           0 :             }
     704           0 :         }
     705      500214 :     });
     706      500214 :     ev->trigger(nullptr);
     707      500214 :     replySent = true;
     708      500214 :     req = nullptr; // transferred back to main thread
     709      500214 : }
     710             : 
     711     1500507 : CService HTTPRequest::GetPeer() const
     712             : {
     713     1500507 :     evhttp_connection* con = evhttp_request_get_connection(req);
     714     1500507 :     CService peer;
     715     1500507 :     if (con) {
     716             :         // evhttp retains ownership over returned address string
     717     1500507 :         const char* address = "";
     718     1500507 :         uint16_t port = 0;
     719             : 
     720             : #ifdef HAVE_EVHTTP_CONNECTION_GET_PEER_CONST_CHAR
     721             :         evhttp_connection_get_peer(con, &address, &port);
     722             : #else
     723     1500507 :         evhttp_connection_get_peer(con, (char**)&address, &port);
     724             : #endif // HAVE_EVHTTP_CONNECTION_GET_PEER_CONST_CHAR
     725             : 
     726     1500507 :         peer = MaybeFlipIPv6toCJDNS(LookupNumeric(address, port));
     727     1500507 :     }
     728     1500507 :     return peer;
     729     1500507 : }
     730             : 
     731     1500463 : std::string HTTPRequest::GetURI() const
     732             : {
     733     1500463 :     return evhttp_request_get_uri(req);
     734             : }
     735             : 
     736     1500507 : HTTPRequest::RequestMethod HTTPRequest::GetRequestMethod() const
     737             : {
     738     1500507 :     switch (evhttp_request_get_command(req)) {
     739             :     case EVHTTP_REQ_GET:
     740         204 :         return GET;
     741             :     case EVHTTP_REQ_POST:
     742     1500303 :         return POST;
     743             :     case EVHTTP_REQ_HEAD:
     744           0 :         return HEAD;
     745             :     case EVHTTP_REQ_PUT:
     746           0 :         return PUT;
     747             :     default:
     748           0 :         return UNKNOWN;
     749             :     }
     750     1500507 : }
     751             : 
     752          32 : std::optional<std::string> HTTPRequest::GetQueryParameter(const std::string& key) const
     753             : {
     754          32 :     const char* uri{evhttp_request_get_uri(req)};
     755             : 
     756          32 :     return GetQueryParameterFromUri(uri, key);
     757             : }
     758             : 
     759          39 : std::optional<std::string> GetQueryParameterFromUri(const char* uri, const std::string& key)
     760             : {
     761          39 :     evhttp_uri* uri_parsed{evhttp_uri_parse(uri)};
     762          39 :     const char* query{evhttp_uri_get_query(uri_parsed)};
     763          39 :     std::optional<std::string> result;
     764             : 
     765          39 :     if (query) {
     766             :         // Parse the query string into a key-value queue and iterate over it
     767             :         struct evkeyvalq params_q;
     768          33 :         evhttp_parse_query_str(query, &params_q);
     769             : 
     770          35 :         for (struct evkeyval* param{params_q.tqh_first}; param != nullptr; param = param->next.tqe_next) {
     771          34 :             if (param->key == key) {
     772          32 :                 result = param->value;
     773          32 :                 break;
     774             :             }
     775           2 :         }
     776          33 :         evhttp_clear_headers(&params_q);
     777          33 :     }
     778          39 :     evhttp_uri_free(uri_parsed);
     779             : 
     780          39 :     return result;
     781          39 : }
     782             : 
     783        6034 : void RegisterHTTPHandler(const std::string &prefix, bool exactMatch, const HTTPRequestHandler &handler)
     784             : {
     785        6034 :     LogPrint(BCLog::HTTP, "Registering HTTP handler for %s (exactmatch %d)\n", prefix, exactMatch);
     786        6034 :     LOCK(g_httppathhandlers_mutex);
     787        6034 :     pathHandlers.emplace_back(prefix, exactMatch, handler);
     788        6034 : }
     789             : 
     790       36360 : void UnregisterHTTPHandler(const std::string &prefix, bool exactMatch)
     791             : {
     792       36360 :     LOCK(g_httppathhandlers_mutex);
     793       36360 :     std::vector<HTTPPathHandler>::iterator i = pathHandlers.begin();
     794       36360 :     std::vector<HTTPPathHandler>::iterator iend = pathHandlers.end();
     795       36360 :     for (; i != iend; ++i)
     796        6034 :         if (i->prefix == prefix && i->exactMatch == exactMatch)
     797        6034 :             break;
     798       36360 :     if (i != iend)
     799             :     {
     800        6034 :         LogPrint(BCLog::HTTP, "Unregistering HTTP handler for %s (exactmatch %d)\n", prefix, exactMatch);
     801        6034 :         pathHandlers.erase(i);
     802        6034 :     }
     803       36360 : }

Generated by: LCOV version 1.16