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.
751LogDebug(BCLog::HTTP,752"Cannot set IPV6_V6ONLY on %s listen socket: %s, continuing anyway",753to.ToStringAddrPort(),754NetworkErrorString(WSAGetLastError()));755}756#endif757#ifdef WIN32758int prot_level{PROTECTION_LEVEL_UNRESTRICTED};759if (sock->SetSockOpt(IPPROTO_IPV6,760IPV6_PROTECTION_LEVEL,761&prot_level,762sizeof(prot_level)) == SOCKET_ERROR) {763LogDebug(BCLog::HTTP,764"Cannot set IPV6_PROTECTION_LEVEL on %s listen socket: %s, continuing anyway",765to.ToStringAddrPort(),766NetworkErrorString(WSAGetLastError()));767}768#endif769}771if (sock->Bind(sa, len) == SOCKET_ERROR) {772const int err{WSAGetLastError()};773if (err == WSAEADDRINUSE) {774return util::Unexpected{strprintf("Unable to bind to %s on this computer. %s is probably already running.",775to.ToStringAddrPort(),776CLIENT_NAME)};777} else {778return util::Unexpected{strprintf("Unable to bind to %s on this computer (bind returned error %s)",779to.ToStringAddrPort(),780NetworkErrorString(err))};781}782}784// Listen for incoming connections785if (sock->Listen(SOMAXCONN) == SOCKET_ERROR) {786return util::Unexpected{strprintf("Cannot listen on %s: %s",787to.ToStringAddrPort(),788NetworkErrorString(WSAGetLastError()))};789}791m_listen.emplace_back(std::move(sock));793return {};794}796void HTTPServer::StopListening()797{798m_listen.clear();799}801void HTTPServer::StartSocketsThreads()802{803// The socket handler reads m_allow_subnets in ClientAllowed(). InitHTTPAllowList()804// must have populated it first; localhost entries are always added, so an empty805// list means it was never called and every connection is rejected.806Assume(!m_allow_subnets.empty());808m_thread_socket_handler = std::thread(&util::TraceThread,809"http",810[this] { ThreadSocketHandler(); });811}813void HTTPServer::JoinSocketsThreads()814{815if (m_thread_socket_handler.joinable()) {816m_thread_socket_handler.join();817}818}820std::unique_ptr<Sock> HTTPServer::AcceptConnection(const Sock& listen_sock, CService& addr)821{822// Make sure we only operate on our own listening sockets823Assume(std::ranges::any_of(m_listen, [&](const auto& sock) { return sock.get() == &listen_sock; }));825sockaddr_storage storage;826socklen_t len{sizeof(storage)};827auto sa = reinterpret_cast<sockaddr*>(&storage);829auto sock{listen_sock.Accept(sa, &len)};831if (!sock) {832const int err{WSAGetLastError()};833if (err != WSAEWOULDBLOCK) {834LogDebug(BCLog::HTTP,835"Cannot accept new connection: %s",836NetworkErrorString(err));837}838return {};839}841// The OS handed us a valid socket but we can't determine its source address.842if (!addr.SetSockAddr(sa, len)) {843LogDebug(BCLog::HTTP,844"Unknown socket family");845}847// Early address-based allow check848if (!ClientAllowed(addr)) {849LogDebug(BCLog::HTTP, "Connection from %s rejected: Client network is not allowed HTTP access\n",850addr.ToStringAddrPort());851// Socket destroyed, connection aborted852return {};853}855return sock;856}858HTTPServer::Id HTTPServer::GetNewId()859{860return m_next_id.fetch_add(1, std::memory_order_relaxed);861}863void HTTPServer::NewSockAccepted(std::unique_ptr<Sock>&& sock, const CService& addr)864{865if (!sock->IsSelectable()) {866LogDebug(BCLog::HTTP,867"connection from %s dropped: non-selectable socket",868addr.ToStringAddrPort());869return;870}872// According to the internet TCP_NODELAY is not carried into accepted sockets873// on all platforms. Set it again here just to be sure.874if (sock->SetSockOpt(IPPROTO_TCP, TCP_NODELAY, &SOCKET_OPTION_TRUE, sizeof(SOCKET_OPTION_TRUE)) == SOCKET_ERROR) {875LogDebug(BCLog::HTTP, "connection from %s: unable to set TCP_NODELAY, continuing anyway",876addr.ToStringAddrPort());877}879const Id id{GetNewId()};881m_connected.push_back(std::make_shared<HTTPRemoteClient>(id, addr, std::move(sock)));882// Report back to the main thread883m_connected_size.fetch_add(1, std::memory_order_relaxed);885LogDebug(BCLog::HTTP,886"HTTP Connection accepted from %s (id=%llu)",887addr.ToStringAddrPort(), id);888}890void HTTPServer::SocketHandlerConnected(const IOReadiness& io_readiness) const891{892for (const auto& [sock, events] : io_readiness.events_per_sock) {893if (m_interrupt_net) {894return;895}897auto it{io_readiness.httpclients_per_sock.find(sock)};898if (it == io_readiness.httpclients_per_sock.end()) {899continue;900}901const std::shared_ptr<HTTPRemoteClient>& client{it->second};903bool send_ready = events.occurred & Sock::SendEvent;904bool recv_ready = events.occurred & Sock::RecvEvent;905bool err_ready = events.occurred & Sock::ErrorEvent;907if (send_ready) {908// Try to send as much data as is ready for this client.909// If there's an error we can skip the receive phase for this client910// because we need to disconnect.911if (!client->MaybeSendBytesFromBuffer()) {912recv_ready = false;913}914}916if (recv_ready || err_ready) {917client->Receive();918}919// Process as much received data as we can.920// This executes for every client whether or not reading or writing921// took place because it also (might) parse a request we have already922// received and pass it to a worker thread.923if (std::unique_ptr<HTTPRequest> request{HTTPRemoteClient::TryReadRequest(client)})924{925LOCK(m_request_dispatcher_mutex);926m_request_dispatcher(std::move(request));927}928}929}931void HTTPRemoteClient::Receive()932{933char buf[0x10000]; // typical socket buffer is 8K-64K935const ssize_t nrecv{WITH_LOCK(936m_sock_mutex,937return m_sock->Recv(buf, sizeof(buf), MSG_DONTWAIT);)};939if (nrecv < 0) {940const int err = WSAGetLastError();941if (IOErrorIsPermanent(err)) {942LogDebug(943BCLog::HTTP,944"Permanent read error from %s (id=%llu): %s",945m_origin,946m_id,947NetworkErrorString(err));948m_disconnect = true;949}950} else if (nrecv == 0) {951LogDebug(952BCLog::HTTP,953"Received EOF from %s (id=%llu)",954m_origin,955m_id);956m_disconnect = true;957} else {958// Reset idle timeout959m_idle_since = Now<SteadySeconds>();961// Prevent disconnect until all requests are completely handled.962m_connection_busy = true;964// Copy data from socket buffer to client receive buffer965m_recv_buffer.insert(966m_recv_buffer.end(),967buf,968buf + nrecv);970m_total_bytes_received += static_cast<uint64_t>(nrecv);971LogDebug(972BCLog::HTTP,973"Received %d bytes from %s (id=%llu): total=%llu buffered=%zu",974nrecv,975m_origin,976m_id,977m_total_bytes_received,978m_recv_buffer.size());979}980}982void HTTPServer::SocketHandlerListening(const Sock::EventsPerSock& events_per_sock)983{984if (m_stop_accepting) return;985for (const auto& sock : m_listen) {986if (m_interrupt_net) {987return;988}989const auto it = events_per_sock.find(sock);990if (it != events_per_sock.end() && it->second.occurred & Sock::RecvEvent) {991// Drain all pending connections from this socket up to the limit.992// Stop early if the kernel queue is empty (AcceptConnection returns null)993// or if accepting the last connection brought us to the limit.994while (GetConnectionsCount() < static_cast<size_t>(m_rpcmaxconnections)) {995CService addr_accepted;996auto sock_accepted{AcceptConnection(*sock, addr_accepted)};997if (!sock_accepted) break;998NewSockAccepted(std::move(sock_accepted), addr_accepted);999}1000}
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.