httpserver.cpp
1 // Copyright (c) 2015-present 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 <common/args.h> 9 #include <common/messages.h> 10 #include <compat/compat.h> 11 #include <logging.h> 12 #include <netbase.h> 13 #include <node/interface_ui.h> 14 #include <rpc/protocol.h> 15 #include <sync.h> 16 #include <util/check.h> 17 #include <util/signalinterrupt.h> 18 #include <util/strencodings.h> 19 #include <util/threadnames.h> 20 #include <util/threadpool.h> 21 #include <util/translation.h> 22 23 #include <condition_variable> 24 #include <cstdio> 25 #include <cstdlib> 26 #include <deque> 27 #include <memory> 28 #include <optional> 29 #include <span> 30 #include <string> 31 #include <thread> 32 #include <unordered_map> 33 #include <vector> 34 35 #include <sys/types.h> 36 #include <sys/stat.h> 37 38 #include <event2/buffer.h> 39 #include <event2/bufferevent.h> 40 #include <event2/http.h> 41 #include <event2/http_struct.h> 42 #include <event2/keyvalq_struct.h> 43 #include <event2/thread.h> 44 #include <event2/util.h> 45 46 #include <support/events.h> 47 48 using common::InvalidPortErrMsg; 49 50 /** Maximum size of http request (request line + headers) */ 51 static const size_t MAX_HEADERS_SIZE = 8192; 52 53 struct HTTPPathHandler 54 { 55 HTTPPathHandler(std::string _prefix, bool _exactMatch, HTTPRequestHandler _handler): 56 prefix(_prefix), exactMatch(_exactMatch), handler(_handler) 57 { 58 } 59 std::string prefix; 60 bool exactMatch; 61 HTTPRequestHandler handler; 62 }; 63 64 /** HTTP module state */ 65 66 //! libevent event loop 67 static struct event_base* eventBase = nullptr; 68 //! HTTP server 69 static struct evhttp* eventHTTP = nullptr; 70 //! List of subnets to allow RPC connections from 71 static std::vector<CSubNet> rpc_allow_subnets; 72 //! Handlers for (sub)paths 73 static GlobalMutex g_httppathhandlers_mutex; 74 static std::vector<HTTPPathHandler> pathHandlers GUARDED_BY(g_httppathhandlers_mutex); 75 //! Bound listening sockets 76 static std::vector<evhttp_bound_socket *> boundSockets; 77 //! Http thread pool - future: encapsulate in HttpContext 78 static ThreadPool g_threadpool_http("http"); 79 static int g_max_queue_depth{100}; 80 81 /** 82 * @brief Helps keep track of open `evhttp_connection`s with active `evhttp_requests` 83 * 84 */ 85 class HTTPRequestTracker 86 { 87 private: 88 mutable Mutex m_mutex; 89 mutable std::condition_variable m_cv; 90 //! For each connection, keep a counter of how many requests are open 91 std::unordered_map<const evhttp_connection*, size_t> m_tracker GUARDED_BY(m_mutex); 92 93 void RemoveConnectionInternal(const decltype(m_tracker)::iterator it) EXCLUSIVE_LOCKS_REQUIRED(m_mutex) 94 { 95 m_tracker.erase(it); 96 if (m_tracker.empty()) m_cv.notify_all(); 97 } 98 public: 99 //! Increase request counter for the associated connection by 1 100 void AddRequest(evhttp_request* req) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex) 101 { 102 const evhttp_connection* conn{Assert(evhttp_request_get_connection(Assert(req)))}; 103 WITH_LOCK(m_mutex, ++m_tracker[conn]); 104 } 105 //! Decrease request counter for the associated connection by 1, remove connection if counter is 0 106 void RemoveRequest(evhttp_request* req) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex) 107 { 108 const evhttp_connection* conn{Assert(evhttp_request_get_connection(Assert(req)))}; 109 LOCK(m_mutex); 110 auto it{m_tracker.find(conn)}; 111 if (it != m_tracker.end() && it->second > 0) { 112 if (--(it->second) == 0) RemoveConnectionInternal(it); 113 } 114 } 115 //! Remove a connection entirely 116 void RemoveConnection(const evhttp_connection* conn) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex) 117 { 118 LOCK(m_mutex); 119 auto it{m_tracker.find(Assert(conn))}; 120 if (it != m_tracker.end()) RemoveConnectionInternal(it); 121 } 122 size_t CountActiveConnections() const EXCLUSIVE_LOCKS_REQUIRED(!m_mutex) 123 { 124 return WITH_LOCK(m_mutex, return m_tracker.size()); 125 } 126 //! Wait until there are no more connections with active requests in the tracker 127 void WaitUntilEmpty() const EXCLUSIVE_LOCKS_REQUIRED(!m_mutex) 128 { 129 WAIT_LOCK(m_mutex, lock); 130 m_cv.wait(lock, [this]() EXCLUSIVE_LOCKS_REQUIRED(m_mutex) { return m_tracker.empty(); }); 131 } 132 }; 133 //! Track active requests 134 static HTTPRequestTracker g_requests; 135 136 /** Check if a network address is allowed to access the HTTP server */ 137 static bool ClientAllowed(const CNetAddr& netaddr) 138 { 139 if (!netaddr.IsValid()) 140 return false; 141 for(const CSubNet& subnet : rpc_allow_subnets) 142 if (subnet.Match(netaddr)) 143 return true; 144 return false; 145 } 146 147 /** Initialize ACL list for HTTP server */ 148 static bool InitHTTPAllowList() 149 { 150 rpc_allow_subnets.clear(); 151 rpc_allow_subnets.emplace_back(LookupHost("127.0.0.1", false).value(), 8); // always allow IPv4 local subnet 152 rpc_allow_subnets.emplace_back(LookupHost("::1", false).value()); // always allow IPv6 localhost 153 for (const std::string& strAllow : gArgs.GetArgs("-rpcallowip")) { 154 const CSubNet subnet{LookupSubNet(strAllow)}; 155 if (!subnet.IsValid()) { 156 uiInterface.ThreadSafeMessageBox( 157 Untranslated(strprintf("Invalid -rpcallowip subnet specification: %s. Valid values are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0), a network/CIDR (e.g. 1.2.3.4/24), all ipv4 (0.0.0.0/0), or all ipv6 (::/0). RFC4193 is allowed only if -cjdnsreachable=0.", strAllow)), 158 CClientUIInterface::MSG_ERROR); 159 return false; 160 } 161 rpc_allow_subnets.push_back(subnet); 162 } 163 std::string strAllowed; 164 for (const CSubNet& subnet : rpc_allow_subnets) 165 strAllowed += subnet.ToString() + " "; 166 LogDebug(BCLog::HTTP, "Allowing HTTP connections from: %s\n", strAllowed); 167 return true; 168 } 169 170 /** HTTP request method as string - use for logging only */ 171 std::string RequestMethodString(HTTPRequest::RequestMethod m) 172 { 173 switch (m) { 174 case HTTPRequest::GET: 175 return "GET"; 176 case HTTPRequest::POST: 177 return "POST"; 178 case HTTPRequest::HEAD: 179 return "HEAD"; 180 case HTTPRequest::PUT: 181 return "PUT"; 182 case HTTPRequest::UNKNOWN: 183 return "unknown"; 184 } // no default case, so the compiler can warn about missing cases 185 assert(false); 186 } 187 188 /** HTTP request callback */ 189 static void http_request_cb(struct evhttp_request* req, void* arg) 190 { 191 evhttp_connection* conn{evhttp_request_get_connection(req)}; 192 // Track active requests 193 { 194 g_requests.AddRequest(req); 195 evhttp_request_set_on_complete_cb(req, [](struct evhttp_request* req, void*) { 196 g_requests.RemoveRequest(req); 197 }, nullptr); 198 evhttp_connection_set_closecb(conn, [](evhttp_connection* conn, void* arg) { 199 g_requests.RemoveConnection(conn); 200 }, nullptr); 201 } 202 203 // Disable reading to work around a libevent bug, fixed in 2.1.9 204 // See https://github.com/libevent/libevent/commit/5ff8eb26371c4dc56f384b2de35bea2d87814779 205 // and https://github.com/bitcoin/bitcoin/pull/11593. 206 if (event_get_version_number() >= 0x02010600 && event_get_version_number() < 0x02010900) { 207 if (conn) { 208 bufferevent* bev = evhttp_connection_get_bufferevent(conn); 209 if (bev) { 210 bufferevent_disable(bev, EV_READ); 211 } 212 } 213 } 214 auto hreq{std::make_shared<HTTPRequest>(req, *static_cast<const util::SignalInterrupt*>(arg))}; 215 216 // Early address-based allow check 217 if (!ClientAllowed(hreq->GetPeer())) { 218 LogDebug(BCLog::HTTP, "HTTP request from %s rejected: Client network is not allowed RPC access\n", 219 hreq->GetPeer().ToStringAddrPort()); 220 hreq->WriteReply(HTTP_FORBIDDEN); 221 return; 222 } 223 224 // Early reject unknown HTTP methods 225 if (hreq->GetRequestMethod() == HTTPRequest::UNKNOWN) { 226 LogDebug(BCLog::HTTP, "HTTP request from %s rejected: Unknown HTTP request method\n", 227 hreq->GetPeer().ToStringAddrPort()); 228 hreq->WriteReply(HTTP_BAD_METHOD); 229 return; 230 } 231 232 LogDebug(BCLog::HTTP, "Received a %s request for %s from %s\n", 233 RequestMethodString(hreq->GetRequestMethod()), SanitizeString(hreq->GetURI(), SAFE_CHARS_URI).substr(0, 100), hreq->GetPeer().ToStringAddrPort()); 234 235 // Find registered handler for prefix 236 std::string strURI = hreq->GetURI(); 237 std::string path; 238 LOCK(g_httppathhandlers_mutex); 239 std::vector<HTTPPathHandler>::const_iterator i = pathHandlers.begin(); 240 std::vector<HTTPPathHandler>::const_iterator iend = pathHandlers.end(); 241 for (; i != iend; ++i) { 242 bool match = false; 243 if (i->exactMatch) 244 match = (strURI == i->prefix); 245 else 246 match = strURI.starts_with(i->prefix); 247 if (match) { 248 path = strURI.substr(i->prefix.size()); 249 break; 250 } 251 } 252 253 // Dispatch to worker thread 254 if (i != iend) { 255 if (static_cast<int>(g_threadpool_http.WorkQueueSize()) >= g_max_queue_depth) { 256 LogWarning("Request rejected because http work queue depth exceeded, it can be increased with the -rpcworkqueue= setting"); 257 hreq->WriteReply(HTTP_SERVICE_UNAVAILABLE, "Work queue depth exceeded"); 258 return; 259 } 260 261 auto item = [req = hreq, in_path = std::move(path), fn = i->handler]() { 262 std::string err_msg; 263 try { 264 fn(req.get(), in_path); 265 return; 266 } catch (const std::exception& e) { 267 LogWarning("Unexpected error while processing request for '%s'. Error msg: '%s'", req->GetURI(), e.what()); 268 err_msg = e.what(); 269 } catch (...) { 270 LogWarning("Unknown error while processing request for '%s'", req->GetURI()); 271 err_msg = "unknown error"; 272 } 273 // Reply so the client doesn't hang waiting for the response. 274 req->WriteHeader("Connection", "close"); 275 // TODO: Implement specific error formatting for the REST and JSON-RPC servers responses. 276 req->WriteReply(HTTP_INTERNAL_SERVER_ERROR, err_msg); 277 }; 278 279 if (auto res = g_threadpool_http.Submit(std::move(item)); !res.has_value()) { 280 Assume(hreq.use_count() == 1); // ensure request will be deleted 281 // Both SubmitError::Inactive and SubmitError::Interrupted mean shutdown 282 LogWarning("HTTP request rejected during server shutdown: '%s'", SubmitErrorString(res.error())); 283 hreq->WriteReply(HTTP_SERVICE_UNAVAILABLE, "Request rejected during server shutdown"); 284 return; 285 } 286 } else { 287 hreq->WriteReply(HTTP_NOT_FOUND); 288 } 289 } 290 291 /** Callback to reject HTTP requests after shutdown. */ 292 static void http_reject_request_cb(struct evhttp_request* req, void*) 293 { 294 LogDebug(BCLog::HTTP, "Rejecting request while shutting down\n"); 295 evhttp_send_error(req, HTTP_SERVUNAVAIL, nullptr); 296 } 297 298 /** Event dispatcher thread */ 299 static void ThreadHTTP(struct event_base* base) 300 { 301 util::ThreadRename("http"); 302 LogDebug(BCLog::HTTP, "Entering http event loop\n"); 303 event_base_dispatch(base); 304 // Event loop will be interrupted by InterruptHTTPServer() 305 LogDebug(BCLog::HTTP, "Exited http event loop\n"); 306 } 307 308 /** Bind HTTP server to specified addresses */ 309 static bool HTTPBindAddresses(struct evhttp* http) 310 { 311 uint16_t http_port{static_cast<uint16_t>(gArgs.GetIntArg("-rpcport", BaseParams().RPCPort()))}; 312 std::vector<std::pair<std::string, uint16_t>> endpoints; 313 314 // Determine what addresses to bind to 315 // To prevent misconfiguration and accidental exposure of the RPC 316 // interface, require -rpcallowip and -rpcbind to both be specified 317 // together. If either is missing, ignore both values, bind to localhost 318 // instead, and log warnings. 319 if (gArgs.GetArgs("-rpcallowip").empty() || gArgs.GetArgs("-rpcbind").empty()) { // Default to loopback if not allowing external IPs 320 endpoints.emplace_back("::1", http_port); 321 endpoints.emplace_back("127.0.0.1", http_port); 322 if (!gArgs.GetArgs("-rpcallowip").empty()) { 323 LogWarning("Option -rpcallowip was specified without -rpcbind; this doesn't usually make sense"); 324 } 325 if (!gArgs.GetArgs("-rpcbind").empty()) { 326 LogWarning("Option -rpcbind was ignored because -rpcallowip was not specified, refusing to allow everyone to connect"); 327 } 328 } else { // Specific bind addresses 329 for (const std::string& strRPCBind : gArgs.GetArgs("-rpcbind")) { 330 uint16_t port{http_port}; 331 std::string host; 332 if (!SplitHostPort(strRPCBind, port, host)) { 333 LogError("%s\n", InvalidPortErrMsg("-rpcbind", strRPCBind).original); 334 return false; 335 } 336 endpoints.emplace_back(host, port); 337 } 338 } 339 340 // Bind addresses 341 for (std::vector<std::pair<std::string, uint16_t> >::iterator i = endpoints.begin(); i != endpoints.end(); ++i) { 342 LogInfo("Binding RPC on address %s port %i", i->first, i->second); 343 evhttp_bound_socket *bind_handle = evhttp_bind_socket_with_handle(http, i->first.empty() ? nullptr : i->first.c_str(), i->second); 344 if (bind_handle) { 345 const std::optional<CNetAddr> addr{LookupHost(i->first, false)}; 346 if (i->first.empty() || (addr.has_value() && addr->IsBindAny())) { 347 LogWarning("The RPC server is not safe to expose to untrusted networks such as the public internet"); 348 } 349 // Set the no-delay option (disable Nagle's algorithm) on the TCP socket. 350 evutil_socket_t fd = evhttp_bound_socket_get_fd(bind_handle); 351 int one = 1; 352 if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char*>(&one), sizeof(one)) == SOCKET_ERROR) { 353 LogInfo("WARNING: Unable to set TCP_NODELAY on RPC server socket, continuing anyway\n"); 354 } 355 boundSockets.push_back(bind_handle); 356 } else { 357 LogWarning("Binding RPC on address %s port %i failed.", i->first, i->second); 358 } 359 } 360 return !boundSockets.empty(); 361 } 362 363 /** libevent event log callback */ 364 static void libevent_log_cb(int severity, const char *msg) 365 { 366 switch (severity) { 367 case EVENT_LOG_DEBUG: 368 LogDebug(BCLog::LIBEVENT, "%s", msg); 369 break; 370 case EVENT_LOG_MSG: 371 LogInfo("libevent: %s", msg); 372 break; 373 case EVENT_LOG_WARN: 374 LogWarning("libevent: %s", msg); 375 break; 376 default: // EVENT_LOG_ERR and others are mapped to error 377 LogError("libevent: %s", msg); 378 break; 379 } 380 } 381 382 bool InitHTTPServer(const util::SignalInterrupt& interrupt) 383 { 384 if (!InitHTTPAllowList()) 385 return false; 386 387 // Redirect libevent's logging to our own log 388 event_set_log_callback(&libevent_log_cb); 389 // Update libevent's log handling. 390 UpdateHTTPServerLogging(LogInstance().WillLogCategory(BCLog::LIBEVENT)); 391 392 #ifdef WIN32 393 evthread_use_windows_threads(); 394 #else 395 evthread_use_pthreads(); 396 #endif 397 398 raii_event_base base_ctr = obtain_event_base(); 399 400 /* Create a new evhttp object to handle requests. */ 401 raii_evhttp http_ctr = obtain_evhttp(base_ctr.get()); 402 struct evhttp* http = http_ctr.get(); 403 if (!http) { 404 LogError("Couldn't create evhttp. Exiting."); 405 return false; 406 } 407 408 evhttp_set_timeout(http, gArgs.GetIntArg("-rpcservertimeout", DEFAULT_HTTP_SERVER_TIMEOUT)); 409 evhttp_set_max_headers_size(http, MAX_HEADERS_SIZE); 410 evhttp_set_max_body_size(http, MAX_SIZE); 411 evhttp_set_gencb(http, http_request_cb, (void*)&interrupt); 412 413 if (!HTTPBindAddresses(http)) { 414 LogError("Unable to bind any endpoint for RPC server"); 415 return false; 416 } 417 418 LogDebug(BCLog::HTTP, "Initialized HTTP server\n"); 419 g_max_queue_depth = std::max(gArgs.GetArg("-rpcworkqueue", DEFAULT_HTTP_WORKQUEUE), 1); 420 LogDebug(BCLog::HTTP, "set work queue of depth %d", g_max_queue_depth); 421 422 // transfer ownership to eventBase/HTTP via .release() 423 eventBase = base_ctr.release(); 424 eventHTTP = http_ctr.release(); 425 return true; 426 } 427 428 void UpdateHTTPServerLogging(bool enable) { 429 if (enable) { 430 event_enable_debug_logging(EVENT_DBG_ALL); 431 } else { 432 event_enable_debug_logging(EVENT_DBG_NONE); 433 } 434 } 435 436 static std::thread g_thread_http; 437 438 void StartHTTPServer() 439 { 440 int rpcThreads = std::max(gArgs.GetArg("-rpcthreads", DEFAULT_HTTP_THREADS), 1); 441 LogInfo("Starting HTTP server with %d worker threads", rpcThreads); 442 g_threadpool_http.Start(rpcThreads); 443 g_thread_http = std::thread(ThreadHTTP, eventBase); 444 } 445 446 void InterruptHTTPServer() 447 { 448 LogDebug(BCLog::HTTP, "Interrupting HTTP server\n"); 449 if (eventHTTP) { 450 // Reject requests on current connections 451 evhttp_set_gencb(eventHTTP, http_reject_request_cb, nullptr); 452 } 453 // Interrupt pool after disabling requests 454 g_threadpool_http.Interrupt(); 455 } 456 457 void StopHTTPServer() 458 { 459 LogDebug(BCLog::HTTP, "Stopping HTTP server\n"); 460 461 LogDebug(BCLog::HTTP, "Waiting for HTTP worker threads to exit\n"); 462 g_threadpool_http.Stop(); 463 464 // Unlisten sockets, these are what make the event loop running, which means 465 // that after this and all connections are closed the event loop will quit. 466 for (evhttp_bound_socket *socket : boundSockets) { 467 evhttp_del_accept_socket(eventHTTP, socket); 468 } 469 boundSockets.clear(); 470 { 471 if (const auto n_connections{g_requests.CountActiveConnections()}; n_connections != 0) { 472 LogDebug(BCLog::HTTP, "Waiting for %d connections to stop HTTP server\n", n_connections); 473 } 474 g_requests.WaitUntilEmpty(); 475 } 476 if (eventHTTP) { 477 // Schedule a callback to call evhttp_free in the event base thread, so 478 // that evhttp_free does not need to be called again after the handling 479 // of unfinished request connections that follows. 480 event_base_once(eventBase, -1, EV_TIMEOUT, [](evutil_socket_t, short, void*) { 481 evhttp_free(eventHTTP); 482 eventHTTP = nullptr; 483 }, nullptr, nullptr); 484 } 485 if (eventBase) { 486 LogDebug(BCLog::HTTP, "Waiting for HTTP event thread to exit\n"); 487 if (g_thread_http.joinable()) g_thread_http.join(); 488 event_base_free(eventBase); 489 eventBase = nullptr; 490 } 491 LogDebug(BCLog::HTTP, "Stopped HTTP server\n"); 492 } 493 494 struct event_base* EventBase() 495 { 496 return eventBase; 497 } 498 499 static void httpevent_callback_fn(evutil_socket_t, short, void* data) 500 { 501 // Static handler: simply call inner handler 502 HTTPEvent *self = static_cast<HTTPEvent*>(data); 503 self->handler(); 504 if (self->deleteWhenTriggered) 505 delete self; 506 } 507 508 HTTPEvent::HTTPEvent(struct event_base* base, bool _deleteWhenTriggered, const std::function<void()>& _handler): 509 deleteWhenTriggered(_deleteWhenTriggered), handler(_handler) 510 { 511 ev = event_new(base, -1, 0, httpevent_callback_fn, this); 512 assert(ev); 513 } 514 HTTPEvent::~HTTPEvent() 515 { 516 event_free(ev); 517 } 518 void HTTPEvent::trigger(struct timeval* tv) 519 { 520 if (tv == nullptr) 521 event_active(ev, 0, 0); // immediately trigger event in main thread 522 else 523 evtimer_add(ev, tv); // trigger after timeval passed 524 } 525 HTTPRequest::HTTPRequest(struct evhttp_request* _req, const util::SignalInterrupt& interrupt, bool _replySent) 526 : req(_req), m_interrupt(interrupt), replySent(_replySent) 527 { 528 } 529 530 HTTPRequest::~HTTPRequest() 531 { 532 if (!replySent) { 533 // Keep track of whether reply was sent to avoid request leaks 534 LogWarning("Unhandled HTTP request"); 535 WriteReply(HTTP_INTERNAL_SERVER_ERROR, "Unhandled request"); 536 } 537 // evhttpd cleans up the request, as long as a reply was sent. 538 } 539 540 std::pair<bool, std::string> HTTPRequest::GetHeader(const std::string& hdr) const 541 { 542 const struct evkeyvalq* headers = evhttp_request_get_input_headers(req); 543 assert(headers); 544 const char* val = evhttp_find_header(headers, hdr.c_str()); 545 if (val) 546 return std::make_pair(true, val); 547 else 548 return std::make_pair(false, ""); 549 } 550 551 std::string HTTPRequest::ReadBody() 552 { 553 struct evbuffer* buf = evhttp_request_get_input_buffer(req); 554 if (!buf) 555 return ""; 556 size_t size = evbuffer_get_length(buf); 557 /** Trivial implementation: if this is ever a performance bottleneck, 558 * internal copying can be avoided in multi-segment buffers by using 559 * evbuffer_peek and an awkward loop. Though in that case, it'd be even 560 * better to not copy into an intermediate string but use a stream 561 * abstraction to consume the evbuffer on the fly in the parsing algorithm. 562 */ 563 const char* data = (const char*)evbuffer_pullup(buf, size); 564 if (!data) // returns nullptr in case of empty buffer 565 return ""; 566 std::string rv(data, size); 567 evbuffer_drain(buf, size); 568 return rv; 569 } 570 571 void HTTPRequest::WriteHeader(const std::string& hdr, const std::string& value) 572 { 573 struct evkeyvalq* headers = evhttp_request_get_output_headers(req); 574 assert(headers); 575 evhttp_add_header(headers, hdr.c_str(), value.c_str()); 576 } 577 578 /** Closure sent to main thread to request a reply to be sent to 579 * a HTTP request. 580 * Replies must be sent in the main loop in the main http thread, 581 * this cannot be done from worker threads. 582 */ 583 void HTTPRequest::WriteReply(int nStatus, std::span<const std::byte> reply) 584 { 585 assert(!replySent && req); 586 if (m_interrupt) { 587 WriteHeader("Connection", "close"); 588 } 589 // Send event to main http thread to send reply message 590 struct evbuffer* evb = evhttp_request_get_output_buffer(req); 591 assert(evb); 592 evbuffer_add(evb, reply.data(), reply.size()); 593 auto req_copy = req; 594 HTTPEvent* ev = new HTTPEvent(eventBase, true, [req_copy, nStatus]{ 595 evhttp_send_reply(req_copy, nStatus, nullptr, nullptr); 596 // Re-enable reading from the socket. This is the second part of the libevent 597 // workaround above. 598 if (event_get_version_number() >= 0x02010600 && event_get_version_number() < 0x02010900) { 599 evhttp_connection* conn = evhttp_request_get_connection(req_copy); 600 if (conn) { 601 bufferevent* bev = evhttp_connection_get_bufferevent(conn); 602 if (bev) { 603 bufferevent_enable(bev, EV_READ | EV_WRITE); 604 } 605 } 606 } 607 }); 608 ev->trigger(nullptr); 609 replySent = true; 610 req = nullptr; // transferred back to main thread 611 } 612 613 CService HTTPRequest::GetPeer() const 614 { 615 evhttp_connection* con = evhttp_request_get_connection(req); 616 CService peer; 617 if (con) { 618 // evhttp retains ownership over returned address string 619 const char* address = ""; 620 uint16_t port = 0; 621 622 #ifdef HAVE_EVHTTP_CONNECTION_GET_PEER_CONST_CHAR 623 evhttp_connection_get_peer(con, &address, &port); 624 #else 625 evhttp_connection_get_peer(con, (char**)&address, &port); 626 #endif // HAVE_EVHTTP_CONNECTION_GET_PEER_CONST_CHAR 627 628 peer = MaybeFlipIPv6toCJDNS(LookupNumeric(address, port)); 629 } 630 return peer; 631 } 632 633 std::string HTTPRequest::GetURI() const 634 { 635 return evhttp_request_get_uri(req); 636 } 637 638 HTTPRequest::RequestMethod HTTPRequest::GetRequestMethod() const 639 { 640 switch (evhttp_request_get_command(req)) { 641 case EVHTTP_REQ_GET: 642 return GET; 643 case EVHTTP_REQ_POST: 644 return POST; 645 case EVHTTP_REQ_HEAD: 646 return HEAD; 647 case EVHTTP_REQ_PUT: 648 return PUT; 649 default: 650 return UNKNOWN; 651 } 652 } 653 654 std::optional<std::string> HTTPRequest::GetQueryParameter(const std::string& key) const 655 { 656 const char* uri{evhttp_request_get_uri(req)}; 657 658 return GetQueryParameterFromUri(uri, key); 659 } 660 661 std::optional<std::string> GetQueryParameterFromUri(const char* uri, const std::string& key) 662 { 663 evhttp_uri* uri_parsed{evhttp_uri_parse(uri)}; 664 if (!uri_parsed) { 665 throw std::runtime_error("URI parsing failed, it likely contained RFC 3986 invalid characters"); 666 } 667 const char* query{evhttp_uri_get_query(uri_parsed)}; 668 std::optional<std::string> result; 669 670 if (query) { 671 // Parse the query string into a key-value queue and iterate over it 672 struct evkeyvalq params_q; 673 evhttp_parse_query_str(query, ¶ms_q); 674 675 for (struct evkeyval* param{params_q.tqh_first}; param != nullptr; param = param->next.tqe_next) { 676 if (param->key == key) { 677 result = param->value; 678 break; 679 } 680 } 681 evhttp_clear_headers(¶ms_q); 682 } 683 evhttp_uri_free(uri_parsed); 684 685 return result; 686 } 687 688 void RegisterHTTPHandler(const std::string &prefix, bool exactMatch, const HTTPRequestHandler &handler) 689 { 690 LogDebug(BCLog::HTTP, "Registering HTTP handler for %s (exactmatch %d)\n", prefix, exactMatch); 691 LOCK(g_httppathhandlers_mutex); 692 pathHandlers.emplace_back(prefix, exactMatch, handler); 693 } 694 695 void UnregisterHTTPHandler(const std::string &prefix, bool exactMatch) 696 { 697 LOCK(g_httppathhandlers_mutex); 698 std::vector<HTTPPathHandler>::iterator i = pathHandlers.begin(); 699 std::vector<HTTPPathHandler>::iterator iend = pathHandlers.end(); 700 for (; i != iend; ++i) 701 if (i->prefix == prefix && i->exactMatch == exactMatch) 702 break; 703 if (i != iend) 704 { 705 LogDebug(BCLog::HTTP, "Unregistering HTTP handler for %s (exactmatch %d)\n", prefix, exactMatch); 706 pathHandlers.erase(i); 707 } 708 }