Inside Bitcoin
Opening the selected file…
Inside Bitcoin
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.
501}502}504// We read all the chunks but never got the last chunk, wait for client to send more505return false;506} else {507// No Content-length or Transfer-Encoding header means no body, see libevent evhttp_get_body()508auto content_length_values{m_headers.FindAll("Content-Length")};509if (content_length_values.empty()) return true;511// Duplicate Content-Length headers are allowed only if they all have the same value512// https://www.rfc-editor.org/rfc/rfc7230#section-3.3.3513const auto& first_content_length_value{content_length_values[0]};514for (size_t i = 1; i < content_length_values.size(); ++i) {515if (content_length_values[i] != first_content_length_value) throw std::runtime_error("Differing Content-Length values");516}518const auto content_length{ToIntegral<uint64_t>(first_content_length_value)};519if (!content_length) throw std::runtime_error("Cannot parse Content-Length value");521if (*content_length > MAX_BODY_SIZE) throw ContentTooLargeError("Max body size exceeded");523// A large body may arrive over multiple I/O loop iterations. Copy524// whatever the buffer has now; m_body's size tracks our progress.525const uint64_t body_need{*content_length - m_body.size()};526const uint64_t buffer_has{std::min(body_need, static_cast<uint64_t>(reader.Remaining()))};528// Pack [partial] body on and update state529m_body += reader.ReadLength(buffer_has);531return m_body.size() == *content_length;532}533}535void HTTPRequest::WriteReply(HTTPStatusCode status, std::span<const std::byte> reply_body)536{537HTTPResponse res;539// Some response headers are determined in advance and stored in the request540res.headers = std::move(m_response_headers);542// Response version matches request version543res.version = m_version;545// Add response code546res.status = status;548// See libevent evhttp_response_needs_body()549// Response headers are different if no body is needed550bool needs_body{status != HTTP_NO_CONTENT && (status < 100 || status >= 200)};551bool needs_content_length{false};553bool keep_alive{false};555// See libevent evhttp_make_header_response()556// Expected response headers depend on protocol version557if (m_version.major == 1) {558// HTTP/1.0559if (m_version.minor == 0) {560auto connection_header{m_headers.FindFirst("Connection")};561if (connection_header && ToLower(connection_header.value()) == "keep-alive") {562res.headers.Write("Connection", "keep-alive");563keep_alive = true;564// HTTP/1.0 connections are closed by default so EOF is sufficient565// to indicate end of the body. Adding Content-Length a special case.566if (needs_body) needs_content_length = true;567}568}570// HTTP/1.1571if (m_version.minor >= 1) {572const int64_t now_seconds{TicksSinceEpoch<std::chrono::seconds>(NodeClock::now())};573res.headers.Write("Date", FormatRFC1123DateTime(now_seconds));575// HTTP/1.1 connections are kept alive by default and always require Content-Length.576if (needs_body) needs_content_length = true;578// Default for HTTP/1.1579keep_alive = true;580}581}583if (needs_content_length) {584res.headers.Write("Content-Length", util::ToString(reply_body.size()));585}587if (needs_body && !res.headers.FindFirst("Content-Type")) {588// Default type from libevent evhttp_new_object()589res.headers.Write("Content-Type", "text/html; charset=ISO-8859-1");590}592auto connection_header{m_headers.FindFirst("Connection")};593if (connection_header && ToLower(connection_header.value()) == "close") {594// Might not exist already but we need to replace it, not append to it595res.headers.RemoveAll("Connection");597res.headers.Write("Connection", "close");598keep_alive = false;599}601if (std::shared_ptr client{m_client.lock()}) {602client->Send(res, reply_body, keep_alive);603}604}606void HTTPRemoteClient::Send(const HTTPResponse& res, std::span<const std::byte> reply_body, bool keep_alive)607{608m_keep_alive = keep_alive;610// Serialize the response headers611const std::string headers{res.StringifyHeaders()};612const auto headers_bytes{std::as_bytes(std::span{headers})};614bool send_buffer_was_empty{false};615// Fill the send buffer with the complete serialized response headers + body616{617LOCK(m_send_mutex);618send_buffer_was_empty = m_send_buffer.empty();619m_send_buffer.insert(m_send_buffer.end(), headers_bytes.begin(), headers_bytes.end());621// We've been using std::span up until now but it is finally time to copy622// data. The original data will go out of scope when WriteReply() returns.623// This is analogous to the memcpy() in libevent's evbuffer_add()624m_send_buffer.insert(m_send_buffer.end(), reply_body.begin(), reply_body.end());626// If the buffer already held data, the I/O thread is (or soon will be)627// draining it, so flag that there is more data to send. This must happen628// while holding m_send_mutex and while the buffer is known non-empty:629// setting m_send_ready after releasing the lock would race with the I/O630// thread draining the buffer to empty and clearing m_send_ready in631// between, leaving m_send_ready set on an empty buffer. The I/O loop would632// then only ever poll the socket for writeability, never read the client's633// next request, and wedge the connection.634if (!send_buffer_was_empty) m_send_ready = true;635}637LogDebug(638BCLog::HTTP,639"HTTPResponse (status code: %d size: %lld) added to send buffer for client %s (id=%llu)",640res.status,641headers_bytes.size() + reply_body.size(),642m_origin,643m_id);645// If the send buffer was empty before we wrote this reply, we can try an646// optimistic send akin to CConnman::PushMessage() in which we647// push the data directly out the socket to client right now, instead648// of waiting for the next iteration of the I/O loop.649if (send_buffer_was_empty) {650MaybeSendBytesFromBuffer();651}653// Signal to the I/O loop that we are ready to handle the next request.654m_req_busy = false;655}657CService HTTPRequest::GetPeer() const658{659if (std::shared_ptr c{m_client.lock()}) {660return c->GetPeer();661} else {662return {};663}664}666std::optional<std::string> HTTPRequest::GetQueryParameter(const std::string_view key) const667{668return GetQueryParameterFromUri(m_target, key);669}671// See libevent http.c evhttp_parse_query_impl()672// and https://www.rfc-editor.org/rfc/rfc3986#section-3.4673std::optional<std::string> GetQueryParameterFromUri(const std::string_view uri, const std::string_view key)674{675// find query in URI676size_t start = uri.find('?');677if (start == std::string::npos) return std::nullopt;678size_t end = uri.find('#', start);679if (end == std::string::npos) {680end = uri.length();681}682const std::string_view query{uri.data() + start + 1, end - start - 1};683// find requested parameter in query684const std::vector<std::string_view> params{Split<std::string_view>(query, "&")};685for (const std::string_view& param : params) {686size_t delim = param.find('=');687if (key == UrlDecode(param.substr(0, delim))) {688if (delim == std::string::npos) {689return "";690} else {691return std::string(UrlDecode(param.substr(delim + 1)));692}693}694}695return std::nullopt;696}698std::optional<std::string> HTTPRequest::GetHeader(const std::string_view hdr) const699{700return m_headers.FindFirst(hdr);701}703void HTTPRequest::WriteHeader(std::string&& hdr, std::string&& value)704{705m_response_headers.Write(std::move(hdr), std::move(value));706}708util::Expected<void, std::string> HTTPServer::BindAndStartListening(const CService& to)709{710// Create socket for listening for incoming connections711sockaddr_storage storage;712auto sa = reinterpret_cast<sockaddr*>(&storage);713socklen_t len{sizeof(storage)};714if (!to.GetSockAddr(sa, &len)) {715return util::Unexpected{strprintf("Bind address family for %s not supported", to.ToStringAddrPort())};716}718std::unique_ptr<Sock> sock{CreateSock(to.GetSAFamily(), SOCK_STREAM, IPPROTO_TCP)};719if (!sock) {720return util::Unexpected{strprintf("Cannot create %s listen socket: %s",721to.ToStringAddrPort(),722NetworkErrorString(WSAGetLastError()))};723}725#ifdef WIN32726// Prevent another application from binding to the same address and port and727// intercepting RPC credentials.728// SO_REUSEADDR on Windows is non-exclusive so another process could bind to729// the same port.730if (sock->SetSockOpt(SOL_SOCKET, SO_EXCLUSIVEADDRUSE, &SOCKET_OPTION_TRUE, sizeof(SOCKET_OPTION_TRUE)) == SOCKET_ERROR) {731return util::Unexpected{strprintf("Cannot set SO_EXCLUSIVEADDRUSE on %s listen socket: %s",732to.ToStringAddrPort(),733NetworkErrorString(WSAGetLastError()))};734}735#else736// Allow binding if the port is still in TIME_WAIT state after737// the program was closed and restarted.738if (sock->SetSockOpt(SOL_SOCKET, SO_REUSEADDR, &SOCKET_OPTION_TRUE, sizeof(SOCKET_OPTION_TRUE)) == SOCKET_ERROR) {739LogDebug(BCLog::HTTP,740"Cannot set SO_REUSEADDR on %s listen socket: %s, continuing anyway",741to.ToStringAddrPort(),742NetworkErrorString(WSAGetLastError()));743}744#endif746// some systems don't have IPV6_V6ONLY but are always v6only; others do have the option747// and enable it by default or not. Try to enable it, if possible.748if (to.IsIPv6()) {749#ifdef IPV6_V6ONLY750if (sock->SetSockOpt(IPPROTO_IPV6, IPV6_V6ONLY, &SOCKET_OPTION_TRUE, sizeof(SOCKET_OPTION_TRUE)) == SOCKET_ERROR) {
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.