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.
1001if (content_length_header) {1002auto maybe_len = ToIntegral<size_t>(*content_length_header);1003if (!maybe_len) {1004throw HTTPError{"Invalid Content-Length"};1005}1006content_length = *maybe_len;1007}1008}1010// Remove headers data from buffer, so only initial body data remains1011buffer.erase(0, headers_end);1013// Read remaining body1014if (chunked) {1015// Handle chunked transfer encoding1016std::string body;1018while (true) {1019// Try to parse a chunk from current buffer1020std::string_view chunk_data{buffer};1021size_t line_end = chunk_data.find("\r\n");1023if (line_end != std::string::npos) {1024// Parse chunk size1025std::string_view size_str = chunk_data.substr(0, line_end);1026// Ignore chunk extensions1027size_t semi = size_str.find(';');1028if (semi != std::string::npos) {1029size_str = size_str.substr(0, semi);1030}1032const auto chunk_size{ToIntegral<uint64_t>(util::TrimStringView(size_str), /*base=*/16)};1033if (!chunk_size) {1034throw HTTPError{"Invalid chunk size"};1035}1037if (*chunk_size == 0) {1038// Allow (but ignore) Chunked Trailer section, by1039// reading CRLF-terminated lines until we read an empty line,1040// which indicates the end of this response.1041// See https://httpwg.org/specs/rfc9112.html#rfc.section.7.1.21042buffer.erase(0, line_end + 2);1043while (true) {1044size_t crlf_pos = buffer.find("\r\n");1045if (crlf_pos == std::string::npos) {1046// Need more data1047if (auto result{Recv(deadline)}) {1048buffer.append(*result);1049} else {1050std::this_thread::yield();1051}1052continue;1053}1054buffer.erase(0, crlf_pos + 2);1055if (crlf_pos == 0) break;1056}1057break;1058}1060// Check if we have the full chunk1061size_t chunk_start = line_end + 2;1062if (*chunk_size > std::numeric_limits<size_t>::max() - chunk_start - 2) {1063throw HTTPError{"Chunk size too large"};1064}1065size_t chunk_end = chunk_start + *chunk_size + 2; // +2 for trailing CRLF1067if (buffer.size() >= chunk_end) {1068// Extract chunk data1069body.append(buffer, chunk_start, *chunk_size);1071// Remove processed data1072buffer.erase(0, chunk_end);1073continue;1074}1075}1077// Need more data1078while (true) {1079if (auto result{Recv(deadline)}) {1080buffer.append(*result);1081break;1082} else {1083std::this_thread::yield();1084}1085}1086}1088response.body = std::move(body);1089} else if (content_length > 0) {1090// Fixed content length1091while (buffer.size() < content_length) {1092if (auto result{Recv(deadline)}) {1093buffer.append(*result);1094} else {1095std::this_thread::yield();1096}1097}1099// Possibly shrink buffer in case we got a larger response than1100// originally specified.1101buffer.resize(content_length);1102response.body = std::move(buffer);1103} else {1104// No Content-Length and not chunked: read until the peer closes the1105// connection (RFC 9112 §6.3, HTTP/1.0 fallback).1106try {1107while (true) {1108if (auto result{Recv(deadline)}) {1109buffer.append(*result);1110} else {1111std::this_thread::yield();1112}1113}1114} catch (const RecvEOF&) {}1115response.body = std::move(buffer);1116}1118return response;1119}1121std::optional<std::string> HTTPClient::Recv(const std::chrono::time_point<std::chrono::steady_clock> deadline)1122{1123auto wait_for_readable{[this](std::chrono::milliseconds timeout) -> bool {1124Sock::Event event{0};1125if (!m_socket->Wait(timeout, Sock::RecvEvent, &event)) {1126return false;1127}1128return (event & Sock::RecvEvent) != 0;1129}};1131auto time_left = std::chrono::duration_cast<std::chrono::milliseconds>(1132deadline - std::chrono::steady_clock::now());1133if (time_left.count() <= 0 || !wait_for_readable(time_left)) {1134throw CConnectionFailed{"timeout"};1135}1137char recv_buf[4096];1138ssize_t nrecv = m_socket->Recv(recv_buf, sizeof(recv_buf), /*flags=*/0);1140if (nrecv < 0) {1141int err = WSAGetLastError();1142if (!IOErrorIsPermanent(err)) {1143return std::nullopt;1144}1145throw CConnectionFailed{strprintf("Read error: %s", NetworkErrorString(err))};1146}1148if (nrecv == 0) {1149throw RecvEOF{"EOF"};1150}1152return std::string{recv_buf, static_cast<size_t>(nrecv)};1153}1155static UniValue CallRPC(BaseRequestHandler* rh, const std::string& strMethod, const std::vector<std::string>& args, const std::string& endpoint, const std::string& username)1156{1157std::string host;1158// In preference order, we choose the following for the port:1159// 1. -rpcport1160// 2. port in -rpcconnect (ie following : in ipv4 or ]: in ipv6)1161// 3. default port for chain1162uint16_t port{BaseParams().RPCPort()};1163{1164uint16_t rpcconnect_port{0};1165const std::string rpcconnect_str = gArgs.GetArg("-rpcconnect", DEFAULT_RPCCONNECT);1166if (!SplitHostPort(rpcconnect_str, rpcconnect_port, host)) {1167// Uses argument provided as-is1168// (rather than value parsed)1169// to aid the user in troubleshooting1170throw std::runtime_error(strprintf("Invalid port provided in -rpcconnect: %s", rpcconnect_str));1171} else {1172if (rpcconnect_port != 0) {1173// Use the valid port provided in rpcconnect1174port = rpcconnect_port;1175} // else, no port was provided in rpcconnect (continue using default one)1176}1178if (std::optional<std::string> rpcport_arg = gArgs.GetArg("-rpcport")) {1179// -rpcport was specified1180const uint16_t rpcport_int{ToIntegral<uint16_t>(rpcport_arg.value()).value_or(0)};1181if (rpcport_int == 0) {1182// Uses argument provided as-is1183// (rather than value parsed)1184// to aid the user in troubleshooting1185throw std::runtime_error(strprintf("Invalid port provided in -rpcport: %s", rpcport_arg.value()));1186}1188// Use the valid port provided1189port = rpcport_int;1191// If there was a valid port provided in rpcconnect,1192// rpcconnect_port is non-zero.1193if (rpcconnect_port != 0) {1194tfm::format(std::cerr, "Warning: Port specified in both -rpcconnect and -rpcport. Using -rpcport %u\n", port);1195}1196}1197}1199// Set connection timeout1200const int timeout = gArgs.GetIntArg("-rpcclienttimeout", DEFAULT_HTTP_CLIENT_TIMEOUT);1201std::chrono::seconds timeout_duration;1202if (timeout > 0) {1203timeout_duration = std::chrono::seconds(timeout);1204} else {1205// Use 5 year timeout for "indefinite"1206timeout_duration = std::chrono::years(5);1207}1209// Get credentials1210std::string rpc_credentials;1211std::optional<AuthCookieResult> auth_cookie_result;1212if (gArgs.GetArg("-rpcpassword", "") == "") {1213// Try fall back to cookie-based authentication if no password is provided1214auth_cookie_result = GetAuthCookie(rpc_credentials);1215} else {1216rpc_credentials = username + ":" + gArgs.GetArg("-rpcpassword", "");1217}1219const std::pair<std::string, std::string> headers[]{1220{"Content-Type", "application/json"},1221{"Authorization", "Basic " + EncodeBase64(rpc_credentials)},1222};1223std::string strRequest = rh->PrepareRequest(strMethod, args).write() + "\n";1225HTTPResponse response;1226try {1227HTTPClient client{HTTPClient::Connect(host, port, timeout_duration)};1228response = client.Post(endpoint, headers, strRequest);1229} catch (const CConnectionFailed& e) {1230const std::string formatted_error{*e.what() ? strprintf(" (%s)", e.what()) : ""};1231throw CConnectionFailed(strprintf("Error while attempting to communicate with server %s:%d%s\n\n"1232"Make sure the bitcoind server is running and that you are connecting to the correct RPC port.\n"1233"Use \"bitcoin-cli -help\" for more info.",1234host, port, formatted_error));1235}1237if (response.status == HTTP_UNAUTHORIZED) {1238std::string error{"Authorization failed: "};1239if (auth_cookie_result.has_value()) {1240switch (*auth_cookie_result) {1241case AuthCookieResult::Error:1242error += "Failed to read cookie file and no rpcpassword was specified.";1243break;1244case AuthCookieResult::Disabled:1245error += "Cookie file was disabled via -norpccookiefile and no rpcpassword was specified.";1246break;1247case AuthCookieResult::Ok:1248error += "Cookie file credentials were invalid and no rpcpassword was specified.";1249break;1250}