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.
1001}1002}1004HTTPServer::IOReadiness HTTPServer::GenerateWaitSockets() const1005{1006IOReadiness io_readiness;1008// If the server is already handling its max connected clients count,1009// don't bother checking the listening sockets for new inbound connections.1010// Leave them in the kernel's queue until space in the application opens1011// up (or the client times out on its own).1012if (GetConnectionsCount() < static_cast<size_t>(m_rpcmaxconnections)) {1013for (const auto& sock : m_listen) {1014io_readiness.events_per_sock.emplace(sock, Sock::Events{Sock::RecvEvent});1015}1016}1018for (const auto& http_client : m_connected) {1019// Safely copy the shared pointer to the socket1020std::shared_ptr<Sock> sock{http_client->GetSock()};1022// Event choice:1023// 1. ReadyToSend() (m_send_ready set) -> Send1024// m_send_ready stays set while the send buffer still has data to1025// drain, so we keep sending and do not Recv. This is also how the1026// send-throttle applies backpressure: while the send buffer is1027// full, TryReadRequest() holds a completed request back from a1028// worker, so nothing new is read until send has drained.1029// 2. Else, m_req is incomplete and needs more data, or there is no1030// m_req at all and the recv buffer is empty -> Recv1031// 3. Else (no parse in progress, leftover bytes in m_recv_buffer) -> 01032// Stay in the I/O map so TryReadRequest() drains the buffer first.1033// Extra pipelined data waits in the kernel socket buffer1034// (TCP backpressure), not in m_recv_buffer.1035//1036// Lock-order safety: the convention established by1037// MaybeSendBytesFromBuffer() is to take m_send_mutex before m_sock_mutex.1038// In this loop GetSock() (above) takes m_sock_mutex and ReadyToSend()1039// (below) takes m_send_mutex; both are scoped, so each lock is released1040// before the next is taken and they stay separate critical sections.1041// Holding m_sock_mutex while acquiring m_send_mutex would invert that1042// order and risk a lock-order-inversion deadlock.1043Sock::Event event{0};1044if (http_client->ReadyToSend()) {1045event = Sock::SendEvent;1046} else if (http_client->GetRequest() != nullptr || http_client->ReceiveBufferEmpty()) {1047// Mid-parse (need more bytes) or buffer empty.1048event = Sock::RecvEvent;1049}1051io_readiness.events_per_sock.emplace(sock, Sock::Events{event});1052io_readiness.httpclients_per_sock.emplace(sock, http_client);1053}1055return io_readiness;1056}1058/// \anchor http1059void HTTPServer::ThreadSocketHandler()1060{1061while (!m_interrupt_net) {1062// Check for the readiness of the already connected sockets and the1063// listening sockets in one call ("readiness" as in poll(2) or1064// select(2)). If none are ready, wait for a short while and return1065// empty sets.1066auto io_readiness{GenerateWaitSockets()};1067if (io_readiness.events_per_sock.empty() ||1068// WaitMany() may as well be a static method, the context of the first Sock in the vector is not relevant.1069!io_readiness.events_per_sock.begin()->first->WaitMany(SELECT_TIMEOUT,1070io_readiness.events_per_sock)) {1071m_interrupt_net.sleep_for(SELECT_TIMEOUT);1072}1074// Service (send/receive) each of the already connected sockets.1075SocketHandlerConnected(io_readiness);1077// Accept new connections from listening sockets.1078SocketHandlerListening(io_readiness.events_per_sock);1080// Disconnect any clients that have been flagged.1081DisconnectClients();1082}1083}1085std::unique_ptr<HTTPRequest> HTTPRemoteClient::TryReadRequest(const std::shared_ptr<HTTPRemoteClient>& client)1086{1087// If we are already handling a request from1088// this client, do nothing. We'll check again on the next I/O1089// loop iteration.1090if (client->m_req_busy) return nullptr;1092if (!client->m_req) {1093client->m_req = std::make_unique<HTTPRequest>(client);1094}1096try {1097// Read data from the buffer into the current request1098client->ReadRequest(*client->m_req);1099} catch (const ContentTooLargeError& e) {1100LogDebug(1101BCLog::HTTP,1102"HTTP request body too large from client %s (id=%llu): %s",1103client->m_origin,1104client->m_id,1105e.what());1107WriteNoStoreErrorReply(*client->m_req, HTTP_CONTENT_TOO_LARGE);1108client->m_disconnect = true;1109return nullptr;1110} catch (const std::runtime_error& e) {1111LogDebug(1112BCLog::HTTP,1113"Error reading HTTP request from client %s (id=%llu): %s",1114client->m_origin,1115client->m_id,1116e.what());1118// We failed to read a complete request from the buffer1119WriteNoStoreErrorReply(*client->m_req, HTTP_BAD_REQUEST);1120client->m_disconnect = true;1121return nullptr;1122}1124// If the request is ready, hand it to a worker.1125if (client->m_req->GetState() == HTTPRequest::State::Complete) {1126// Unless this client's send buffer is full: in that case hold the1127// parsed request here instead of moving it to a worker. This prevents1128// the server from reading any more data from this client until they1129// drain their end of the socket, and prevents the server from packing1130// more responses into the send buffer.1131const size_t buffer_used{WITH_LOCK(1132client->m_send_mutex,1133return client->m_send_buffer.size();)};1134if (buffer_used > MAX_BODY_SIZE) return nullptr;1135LogDebug(1136BCLog::HTTP,1137"Received a %s request for %s from %s (id=%llu)",1138RequestMethodString(client->m_req->GetRequestMethod()),1139client->m_req->GetURI(),1140client->m_origin,1141client->m_id);1143client->m_req_busy = true;1144return std::move(client->m_req);1145}1147return nullptr;1148}1150void HTTPServer::DisconnectClients()1151{1152const auto now{Now<SteadySeconds>()};1153size_t erased = std::erase_if(m_connected,1154[&](auto& client) {1155return client->MaybeDisconnect(now,1156m_rpcservertimeout,1157/*disconnect_all=*/m_disconnect_all_clients);1158});1159if (erased > 0) {1160// Report back to the main thread1161m_connected_size.fetch_sub(erased, std::memory_order_relaxed);1162}1163}1165bool HTTPRemoteClient::MaybeDisconnect(std::chrono::time_point<SteadyClock> now, std::chrono::seconds rpcservertimeout, bool disconnect_all)1166{1167// First check for idle timeout. We reset the timer when we send and receive data,1168// but if the server is busy handling a request we should ignore the timeout until1169// the reply is sent. If we did erase the shared_ptr<HTTPRemoteClient> reference in m_connected1170// while the server is busy with a request, it might be prematurely dropped before1171// the response has been sent, or if the HTTPRequest was holding a temporary shared_ptr1172// client on a worker thread - it would keep the socket open even after "disconnecting".1173const bool is_idle{rpcservertimeout.count() > 0 &&1174now - m_idle_since.load() > rpcservertimeout &&1175!m_req_busy};1177// Disconnect this client due to error, end of communication, or idle timeout.1178// May drop unsent data if we are closing due to error.1179if (m_disconnect || is_idle) {1180if (is_idle) {1181LogDebug(BCLog::HTTP,1182"HTTP client idle timeout %s (id=%llu)",1183m_origin,1184m_id);1185}1186} else {1187// Disconnect this client because the server is shutting1188// down and we need to disconnect all clients...1189if (disconnect_all) {1190// ...unless we still have data for this client.1191if (m_connection_busy) {1192// There is still data for this healthy-connected client.1193// Continue the I/O loop until all data is sent or an error is encountered.1194return false;1195} else {1196// This is a healthy persistent connection (e.g. keep-alive)1197// but it's time to say goodbye.1198;1199}1200} else {1201// No reason to disconnect.1202return false;1203}1204}1205// No reason NOT to disconnect, log and remove.1206LogDebug(BCLog::HTTP,1207"Disconnecting HTTP client %s (id=%llu)",1208m_origin,1209m_id);1210return true;1211}1213void HTTPServer::ClearConnectedClients()1214{1215Assume(!m_thread_socket_handler.joinable()); // must be called after JoinSocketsThreads()1216if (m_connected.empty()) return;1217LogWarning("Force-disconnecting %d HTTP client(s) that did not disconnect gracefully", m_connected.size());1218m_connected_size.fetch_sub(m_connected.size(), std::memory_order_relaxed);1219m_connected.clear();1220}1222void HTTPRemoteClient::ReadRequest(HTTPRequest& req)1223{1224if (m_recv_buffer.empty()) return;1226LineReader reader(m_recv_buffer, MAX_HEADERS_SIZE);1228try {1229switch (req.GetState()) {1230case HTTPRequest::State::Init:1231if (!req.LoadControlData(reader)) break;1232req.SetState(HTTPRequest::State::NeedsHeaders);1233[[fallthrough]];1235case HTTPRequest::State::NeedsHeaders:1236if (!req.LoadHeaders(reader)) break;1237req.SetState(HTTPRequest::State::NeedsBody);1238[[fallthrough]];1240case HTTPRequest::State::NeedsBody:1241if (!req.LoadBody(reader)) break;1242req.SetState(HTTPRequest::State::Complete);1243[[fallthrough]];1245case HTTPRequest::State::Complete:1246break;1248case HTTPRequest::State::Error:1249break;1250}
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.