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.
751auto req{HTTPRemoteClient::TryReadRequest(client)};752BOOST_CHECK_EQUAL(req->GetState(), HTTPRequest::State::Complete);753BOOST_CHECK_EQUAL(req->ReadBody(), "x");754}755{756// Ensure chunk trailer counts towards the headers size limit757std::shared_ptr<DummyClient> client{std::make_shared<DummyClient>()};758BOOST_CHECK(!client->GetRequest());760client->receive("POST /huge HTTP/1.0\n"761"Transfer-Encoding: chunked\n"); // 27 bytes762for (int i = 0; i < 816; ++i) {763client->receive("key:value\n"); // 8160764}765client->receive("\n" // 1766"1\n"767"x\n"768"0\n");769BOOST_CHECK(!HTTPRemoteClient::TryReadRequest(client));770BOOST_CHECK_EQUAL(client->GetRequest()->GetState(), HTTPRequest::State::NeedsBody);772// We're in the trailer section with a total of 8188 bytes of headers.773// The limit is 8192, five more bytes should throw.774client->receive("k:vv\n");775BOOST_CHECK(!HTTPRemoteClient::TryReadRequest(client));776BOOST_CHECK_EQUAL(client->GetRequest()->GetState(), HTTPRequest::State::Error);777}778}780BOOST_AUTO_TEST_CASE(http_server_socket_tests)781{782// Hard code the timestamp for the Date header in the HTTP response783// Wed Dec 11 00:47:09 2024 UTC784FakeNodeClock clock{1733878029s};786// Prepare a request handler that just stores received requests so we can examine them.787// Mutex is required to prevent a race between this test's main thread and the server's I/O loop.788Mutex requests_mutex;789std::deque<std::unique_ptr<HTTPRequest>> requests;790auto StoreRequest = [&](std::unique_ptr<HTTPRequest>&& req) {791LOCK(requests_mutex);792requests.push_back(std::move(req));793};795HTTPServer server{StoreRequest};796server.InitHTTPAllowList();798{799// We can only bind to NET_IPV4 and NET_IPV6800CService onion_address{Lookup("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaam2dqd.onion", /*portDefault=*/0, /*fAllowLookup=*/false).value()};801auto result{server.BindAndStartListening(onion_address)};802BOOST_REQUIRE(!result);803BOOST_CHECK_EQUAL(result.error(), "Bind address family for aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaam2dqd.onion:0 not supported");804}806// This VALID address won't actually get used because we stubbed CreateSock()807CService addr_bind{Lookup("0.0.0.0", /*portDefault=*/0, /*fAllowLookup=*/false).value()};809// Init state810BOOST_REQUIRE_EQUAL(server.GetListeningSocketCount(), 0);811// Bind to mock Listening Socket812BOOST_REQUIRE(server.BindAndStartListening(addr_bind));813// We are bound and listening814BOOST_REQUIRE_EQUAL(server.GetListeningSocketCount(), 1);816// Start the I/O loop817server.StartSocketsThreads();819// No connections yet820BOOST_CHECK_EQUAL(server.GetConnectionsCount(), 0);822// Create a mock client with pre-loaded request data and add it to the local CreateSock queue.823// Keep a handle for the mock client's send and receive pipes so we can examine824// the data it "receives".825std::shared_ptr<DynSock::Pipes> mock_client_socket_pipes{ConnectClient(std::as_bytes(std::span(full_request)))};827// Wait up to a minute to find and connect the client in the I/O loop828int attempts{6000};829while (server.GetConnectionsCount() < 1) {830std::this_thread::sleep_for(10ms);831BOOST_REQUIRE(--attempts > 0);832}834// Prepare a pointer to the client, we'll assign it from the request itself.835std::shared_ptr<HTTPRemoteClient> client;837// Wait up to a minute to read the request from the client.838// Given that the mock client is itself a mock socket839// with hard-coded data it should only take a fraction of that.840attempts = 6000;841while (true) {842{843LOCK(requests_mutex);844// Connected client should have one request already from the static content.845if (requests.size() == 1) {846// Check the received request847BOOST_CHECK_EQUAL(requests.front()->ReadBody(), R"({"method":"getblockcount","params":[],"id":1})""\n");848BOOST_CHECK_EQUAL(requests.front()->GetPeer().ToStringAddrPort(), "5.5.5.5:6789");850// Inspect the connection pointed to from the request851client = requests.front()->GetClient();852BOOST_REQUIRE(client);853BOOST_CHECK_EQUAL(client->GetOrigin(), "5.5.5.5:6789");855// Respond to request856requests.front()->WriteReply(HTTP_OK, "874140\n");858break;859}860}861std::this_thread::sleep_for(10ms);862BOOST_REQUIRE(--attempts > 0);863}865// Check the sent response from the mock client at the other end of the mock socket866std::string actual;867// Wait up to one minute for all the bytes to appear in the "send" pipe.868char buf[0x10000] = {};869attempts = 6000;870while (attempts > 0)871{872ssize_t bytes_read = mock_client_socket_pipes->send.GetBytes(buf, sizeof(buf), 0);873if (bytes_read > 0) {874actual.append(buf, bytes_read);875if (actual.length() == 146) {876break;877}878}879std::this_thread::sleep_for(10ms);880--attempts;881}882BOOST_CHECK(actual.starts_with("HTTP/1.1 200 OK\r\n"));883BOOST_CHECK(actual.ends_with("\r\n874140\n"));884// Headers can be sorted in any order, and will be, since we use unordered_map885BOOST_CHECK(actual.find("Connection: close\r\n") != std::string::npos);886BOOST_CHECK(actual.find("Content-Length: 7\r\n") != std::string::npos);887BOOST_CHECK(actual.find("Content-Type: text/html; charset=ISO-8859-1\r\n") != std::string::npos);888BOOST_CHECK(actual.find("Date: Wed, 11 Dec 2024 00:47:09 GMT\r\n") != std::string::npos);890// Wait up to one minute for connection to be automatically closed, because891// keep-alive was not set by the client and we are done responding to their request.892attempts = 6000;893while (server.GetConnectionsCount() != 0) {894std::this_thread::sleep_for(10ms);895BOOST_REQUIRE(--attempts > 0);896}898// Stop the I/O loop and shutdown899server.InterruptNet();900// Wait for I/O loop to finish, after all connected sockets are closed901server.JoinSocketsThreads();902// Close all listening sockets903server.StopListening();904}906BOOST_AUTO_TEST_CASE(http_socket_error_tests)907{908// Create a tiny threadpool for the HTTPRequest handler909ThreadPool workers("http");910workers.Start(1);912// Hard-code the server's request handler to respond to each request with913// an incremented block count. Handle the replies in the worker thread.914std::atomic<int> height{0};915HTTPServer server{[&](std::shared_ptr<HTTPRequest> req) {916auto item = [req, &height]() {917const int h = height.fetch_add(1);918req->WriteReply(HTTP_OK, strprintf("height: %d\n", h));919};920// Can't call BOOST_REQUIRE from worker thread921Assert(workers.Submit(std::move(item)));922}};923server.InitHTTPAllowList();925// All replies will be the same size926static constexpr std::size_t reply_length = std::string_view{927"HTTP/1.1 200 OK\r\n"928"Date: Thu, 01 Jan 2026 00:00:00 GMT\r\n" // All RFC1123 dates are 29 characters929"Content-Length: 10\r\n"930"Content-Type: text/html; charset=ISO-8859-1\r\n"931"\r\n"932"height: 0\n"933}.size();935/**936* A mocked Sock derived from DynSock whose Send() only succeeds when there is more than937* one reply being sent (send buffer length > reply_length). Otherwise it returns938* a recoverable error (WSAEAGAIN).939*940* After it sends successfully once, it continues to always succeed.941*942* Useful for testing "try again" logic around non-blocking socket Send() failures.943*/944class ErrorSock : public DynSock945{946public:947explicit ErrorSock(std::shared_ptr<Pipes> pipes) : DynSock{std::move(pipes)} {}948DynSock& operator=(Sock&&) override { assert(false); return *this; }950ssize_t Send(const void* buf, size_t len, int flags) const override951{952if (len <= reply_length && !m_have_sent) {953#ifdef WIN32954WSASetLastError(WSAEWOULDBLOCK);955#else956errno = WSAEAGAIN;957#endif958return -1;959} else {960m_have_sent = true;961return DynSock::Send(buf, len, flags);962}963}965mutable bool m_have_sent{false};966};968// Simpler server startup than the last test969CService addr_bind{Lookup("0.0.0.0", /*portDefault=*/0, /*fAllowLookup=*/false).value()};970BOOST_REQUIRE(server.BindAndStartListening(addr_bind));971server.StartSocketsThreads();973// Prepare initial requests974int num_requests = 2;975// Use keep-alive so the server holds the connection open for all requests.976std::string keepalive_request{full_request};977keepalive_request.replace(keepalive_request.find("Connection: close"), 17, "Connection: keep-alive");978// Combine all requests so they are read from the socket on a single iteration of the I/O loop979std::string all_requests;980for (int i = 0; i < num_requests; i++) {981all_requests += keepalive_request;982}984// Watch the log messages to ensure that the first two replies were sent985// together. This indicates the non-optimistic send path was used986// because a reply was already sitting in the send buffer when a second reply987// was added.988DebugLogHelper find_two_replies{strprintf("Sent %d bytes to client", reply_length * 2),989[&](const std::string* s) {990return true;991}};992// Last reply should be sent on its own by optimistic send path, because993// the send buffer was empty when the reply was written.994DebugLogHelper find_one_reply{strprintf("Sent %d bytes to client", reply_length),995[&](const std::string* s) {996return true;997}};999// Connect the ErrorSock as mock client with the preloaded data and get a handle on the I/O pipes1000std::shared_ptr<ErrorSock::Pipes> mock_client_socket_pipes{
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.