Inside Bitcoin
Opening the selected file…
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
1// Copyright (c) 2015-present The Bitcoin Core developers2// Distributed under the MIT software license, see the accompanying3// file COPYING or http://www.opensource.org/licenses/mit-license.php.5#include <bitcoin-build-config.h> // IWYU pragma: keep7#include <httpserver.h>9#include <chainparamsbase.h>10#include <common/args.h>11#include <common/messages.h>12#include <common/url.h>13#include <compat/compat.h>14#include <logging.h>15#include <netbase.h>16#include <node/interface_ui.h>17#include <rpc/protocol.h>18#include <span.h>19#include <sync.h>20#include <util/check.h>21#include <util/signalinterrupt.h>22#include <util/sock.h>23#include <util/strencodings.h>24#include <util/thread.h>25#include <util/threadnames.h>26#include <util/threadpool.h>27#include <util/time.h>28#include <util/translation.h>30#include <condition_variable>31#include <cstdio>32#include <cstdlib>33#include <memory>34#include <optional>35#include <span>36#include <string>37#include <string_view>38#include <thread>39#include <unordered_map>40#include <vector>42#include <sys/types.h>43#include <sys/stat.h>45//! The set of sockets cannot be modified while waiting, so46//! the sleep time needs to be small to avoid new sockets stalling.47static constexpr auto SELECT_TIMEOUT{50ms};49//! Explicit alias for setting socket option methods.50static constexpr int SOCKET_OPTION_TRUE{1};52using common::InvalidPortErrMsg;53using util::LineReader;54using namespace bitcoin_http;56struct HTTPPathHandler57{58HTTPPathHandler(std::string _prefix, bool _exactMatch, HTTPRequestHandler _handler):59prefix(_prefix), exactMatch(_exactMatch), handler(_handler)60{61}62std::string prefix;63bool exactMatch;64HTTPRequestHandler handler;65};67/** HTTP module state */69static std::unique_ptr<HTTPServer> g_http_server{nullptr};70//! Handlers for (sub)paths71static GlobalMutex g_httppathhandlers_mutex;72static std::vector<HTTPPathHandler> pathHandlers GUARDED_BY(g_httppathhandlers_mutex);73/// \anchor http_pool74//! Http thread pool - future: encapsulate in HttpContext75static ThreadPool g_threadpool_http("http");76static int g_max_queue_depth{100};78/** Check if a network address is allowed to access the HTTP server */79bool HTTPServer::ClientAllowed(const CNetAddr& netaddr) const80{81if (!netaddr.IsValid())82return false;83for(const CSubNet& subnet : m_allow_subnets)84if (subnet.Match(netaddr))85return true;86return false;87}89/** Initialize ACL list for HTTP server */90bool HTTPServer::InitHTTPAllowList()91{92// Must be run before StartSocketThreads() because ThreadSocketHandler()93// will check m_allow_subnets from the I/O thread.94Assume(!m_thread_socket_handler.joinable());96m_allow_subnets.clear();97m_allow_subnets.emplace_back(LookupHost("127.0.0.1", false).value(), 8); // always allow IPv4 local subnet98m_allow_subnets.emplace_back(LookupHost("::1", false).value()); // always allow IPv6 localhost99for (const std::string& strAllow : gArgs.GetArgs("-rpcallowip")) {100const CSubNet subnet{LookupSubNet(strAllow)};101if (!subnet.IsValid()) {102uiInterface.ThreadSafeMessageBox(103Untranslated(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)),104CClientUIInterface::MSG_ERROR);105return false;106}107m_allow_subnets.push_back(subnet);108}109std::string strAllowed;110for (const CSubNet& subnet : m_allow_subnets)111strAllowed += subnet.ToString() + " ";112LogDebug(BCLog::HTTP, "Allowing HTTP connections from: %s\n", strAllowed);113return true;114}116/** HTTP request method as string - use for logging only */117std::string_view RequestMethodString(HTTPRequestMethod m)118{119switch (m) {120using enum HTTPRequestMethod;121case GET: return "GET";122case POST: return "POST";123case HEAD: return "HEAD";124case PUT: return "PUT";125case UNKNOWN: return "unknown";126} // no default case, so the compiler can warn about missing cases127assert(false);128}130static void WriteNoStoreErrorReply(HTTPRequest& req, HTTPStatusCode status, std::string_view reply = {})131{132req.WriteHeader("Cache-Control", "no-store");133req.WriteReply(status, reply);134}136static void MaybeDispatchRequestToWorker(std::shared_ptr<HTTPRequest> hreq)137{138// Early reject unknown HTTP methods139if (hreq->GetRequestMethod() == HTTPRequestMethod::UNKNOWN) {140LogDebug(BCLog::HTTP, "HTTP request from %s rejected: Unknown HTTP request method\n",141hreq->GetPeer().ToStringAddrPort());142WriteNoStoreErrorReply(*hreq, HTTP_BAD_METHOD);143return;144}146// Find registered handler for prefix147std::string strURI = hreq->GetURI();148std::string path;149LOCK(g_httppathhandlers_mutex);150std::vector<HTTPPathHandler>::const_iterator i = pathHandlers.begin();151std::vector<HTTPPathHandler>::const_iterator iend = pathHandlers.end();152for (; i != iend; ++i) {153bool match = false;154if (i->exactMatch)155match = (strURI == i->prefix);156else157match = strURI.starts_with(i->prefix);158if (match) {159path = strURI.substr(i->prefix.size());160break;161}162}164// Dispatch to worker thread165if (i != iend) {166if (static_cast<int>(g_threadpool_http.WorkQueueSize()) >= g_max_queue_depth) {167LogWarning("Request rejected because http work queue depth exceeded, it can be increased with the -rpcworkqueue= setting");168WriteNoStoreErrorReply(*hreq, HTTP_SERVICE_UNAVAILABLE, "Work queue depth exceeded");169return;170}172auto item = [req = hreq, in_path = std::move(path), fn = i->handler]() {173std::string err_msg;174try {175fn(req.get(), in_path);176return;177} catch (const std::exception& e) {178LogWarning("Unexpected error while processing request for '%s'. Error msg: '%s'", req->GetURI(), e.what());179err_msg = e.what();180} catch (...) {181LogWarning("Unknown error while processing request for '%s'", req->GetURI());182err_msg = "unknown error";183}184// Reply so the client doesn't hang waiting for the response.185req->WriteHeader("Connection", "close");186// TODO: Implement specific error formatting for the REST and JSON-RPC servers responses.187WriteNoStoreErrorReply(*req, HTTP_INTERNAL_SERVER_ERROR, err_msg);188};190if (auto res = g_threadpool_http.Submit(std::move(item)); !res.has_value()) {191Assume(hreq.use_count() == 1); // ensure request will be deleted192// Both SubmitError::Inactive and SubmitError::Interrupted mean shutdown193LogWarning("HTTP request rejected during server shutdown: '%s'", SubmitErrorString(res.error()));194WriteNoStoreErrorReply(*hreq, HTTP_SERVICE_UNAVAILABLE, "Request rejected during server shutdown");195return;196}197} else {198WriteNoStoreErrorReply(*hreq, HTTP_NOT_FOUND);199}200}202static void RejectRequest(std::unique_ptr<HTTPRequest> hreq)203{204LogDebug(BCLog::HTTP, "Rejecting request while shutting down");205WriteNoStoreErrorReply(*hreq, HTTP_SERVICE_UNAVAILABLE);206}208static std::vector<std::pair<std::string, uint16_t>> GetBindAddresses()209{210uint16_t http_port{static_cast<uint16_t>(gArgs.GetIntArg("-rpcport", BaseParams().RPCPort()))};211std::vector<std::pair<std::string, uint16_t>> endpoints;213// Determine what addresses to bind to214// To prevent misconfiguration and accidental exposure of the RPC215// interface, require -rpcallowip and -rpcbind to both be specified216// together. If either is missing, ignore both values, bind to localhost217// instead, and log warnings.218if (gArgs.GetArgs("-rpcallowip").empty() || gArgs.GetArgs("-rpcbind").empty()) { // Default to loopback if not allowing external IPs219endpoints.emplace_back("::1", http_port);220endpoints.emplace_back("127.0.0.1", http_port);221if (!gArgs.GetArgs("-rpcallowip").empty()) {222LogWarning("Option -rpcallowip was specified without -rpcbind; this doesn't usually make sense");223}224if (!gArgs.GetArgs("-rpcbind").empty()) {225LogWarning("Option -rpcbind was ignored because -rpcallowip was not specified, refusing to allow everyone to connect");226}227} else { // Specific bind addresses228for (const std::string& strRPCBind : gArgs.GetArgs("-rpcbind")) {229uint16_t port{http_port};230std::string host;231if (!SplitHostPort(strRPCBind, port, host)) {232LogError("%s\n", InvalidPortErrMsg("-rpcbind", strRPCBind).original);233return {}; // empty234}235endpoints.emplace_back(host, port);236}237}238return endpoints;239}241void RegisterHTTPHandler(const std::string &prefix, bool exactMatch, const HTTPRequestHandler &handler)242{243LogDebug(BCLog::HTTP, "Registering HTTP handler for %s (exactmatch %d)\n", prefix, exactMatch);244LOCK(g_httppathhandlers_mutex);245pathHandlers.emplace_back(prefix, exactMatch, handler);246}248void UnregisterHTTPHandler(const std::string &prefix, bool exactMatch)249{250LOCK(g_httppathhandlers_mutex);
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.