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.
251HTTPRequest req;252LineReader reader("GET / HTTP/0.9\r\nHost: 127.0.0.1\r\n\r\n", MAX_HEADERS_SIZE);253BOOST_CHECK_EXCEPTION(req.LoadControlData(reader), std::runtime_error, HasReason{"HTTP bad version"});254}255{256// Malformed: version is out of range257HTTPRequest req;258LineReader reader("GET / HTTP/-1.0\r\nHost: 127.0.0.1\r\n\r\n", MAX_HEADERS_SIZE);259BOOST_CHECK_EXCEPTION(req.LoadControlData(reader), std::runtime_error, HasReason{"HTTP bad version"});260}261{262// Malformed: version is not exactly two integers and a dot263HTTPRequest req;264LineReader reader("GET / HTTP/1.00\r\nHost: 127.0.0.1\r\n\r\n", MAX_HEADERS_SIZE);265BOOST_CHECK_EXCEPTION(req.LoadControlData(reader), std::runtime_error, HasReason{"HTTP bad version"});266}267{268// Malformed: contains NUL269HTTPRequest req;270LineReader reader{std::string_view{"GET /safe\0/etc/passwd HTTP/1.00\r\nHost: 127.0.0.1\r\n\r\n", 50}, MAX_HEADERS_SIZE};271BOOST_CHECK_EXCEPTION(req.LoadControlData(reader), std::runtime_error, HasReason{"Invalid request line contains NUL"});272}273{274// Malformed: differing Content-Length values, case insensitive275constexpr std::string_view differing_length = "GET / HTTP/1.1\n"276"Host: 127.0.0.1\n"277"Content-Length: 8\n"278"content-length: 9\n\n"279"12345678";280HTTPRequest req;281util::LineReader reader{differing_length, /*max_line_length=*/MAX_HEADERS_SIZE};282BOOST_CHECK(req.LoadControlData(reader));283BOOST_CHECK(req.LoadHeaders(reader));284BOOST_CHECK_EXCEPTION(req.LoadBody(reader), std::runtime_error, HasReason{"Differing Content-Length values"});285}286{287// Ok: multiple same Content-Length values288constexpr std::string_view differing_length = "GET / HTTP/1.1\n"289"Host: 127.0.0.1\n"290"Content-Length: 8\n"291"content-length: 8\n\n"292"12345678";293HTTPRequest req;294util::LineReader reader{differing_length, /*max_line_length=*/MAX_HEADERS_SIZE};295BOOST_CHECK(req.LoadControlData(reader));296BOOST_CHECK(req.LoadHeaders(reader));297BOOST_CHECK(req.LoadBody(reader));298}299{300// Ok301HTTPRequest req;302LineReader reader("GET / HTTP/1.0\r\nHost: 127.0.0.1\r\n\r\n", MAX_HEADERS_SIZE);303BOOST_CHECK(req.LoadControlData(reader));304BOOST_CHECK(req.LoadHeaders(reader));305BOOST_CHECK(req.LoadBody(reader));306BOOST_CHECK_EQUAL(req.GetRequestMethod(), HTTPRequestMethod::GET);307BOOST_CHECK_EQUAL(req.GetURI(), "/");308BOOST_CHECK_EQUAL(req.GetVersion().major, 1);309BOOST_CHECK_EQUAL(req.GetVersion().minor, 0);310BOOST_CHECK_EQUAL(req.GetHeader("Host"), "127.0.0.1");311// no body is OK312BOOST_CHECK_EQUAL(req.ReadBody(), "");313}314{315// Malformed: missing colon316HTTPRequest req;317LineReader reader("GET / HTTP/1.0\r\nHost=127.0.0.1\r\n\r\n", MAX_HEADERS_SIZE);318BOOST_CHECK(req.LoadControlData(reader));319BOOST_CHECK_EXCEPTION(req.LoadHeaders(reader), std::runtime_error, HasReason{"HTTP header missing colon (:)"});320}321{322// We might not have received enough data from the client which is not323// an error. We return false so the caller can try again later when the324// buffer has more data.325HTTPRequest req;326LineReader reader("GET / HTTP/1.0\r\nHost: ", MAX_HEADERS_SIZE);327BOOST_CHECK(req.LoadControlData(reader));328BOOST_CHECK(!req.LoadHeaders(reader));329}330{331// No Content-Length: body is not read332HTTPRequest req;333LineReader reader("GET / HTTP/1.0\r\n\r\n" R"({"method":"getblockcount"})", MAX_HEADERS_SIZE);334BOOST_CHECK(req.LoadControlData(reader));335BOOST_CHECK(req.LoadHeaders(reader));336BOOST_CHECK(req.LoadBody(reader));337// Don't try to read request body if Content-Length is missing338BOOST_CHECK_EQUAL(req.ReadBody(), "");339}340{341// Malformed: Content-Length is not a number342HTTPRequest req;343LineReader reader("GET / HTTP/1.0\r\nContent-Length: eleven\r\n\r\n" R"({"method":"getblockcount"})", MAX_HEADERS_SIZE);344BOOST_CHECK(req.LoadControlData(reader));345BOOST_CHECK(req.LoadHeaders(reader));346BOOST_CHECK_EXCEPTION(req.LoadBody(reader), std::runtime_error, HasReason{"Cannot parse Content-Length value"});347}348{349// Malformed: Content-Length is negative350HTTPRequest req;351LineReader reader("GET / HTTP/1.0\r\nContent-Length: -8\r\n\r\n" R"({"method":"getblockcount"})", MAX_HEADERS_SIZE);352BOOST_CHECK(req.LoadControlData(reader));353BOOST_CHECK(req.LoadHeaders(reader));354BOOST_CHECK_EXCEPTION(req.LoadBody(reader), std::runtime_error, HasReason{"Cannot parse Content-Length value"});355}356{357// Content-Length exceeds limit358constexpr auto excessive_size{MAX_BODY_SIZE + 1};359std::string huge_body(excessive_size, 'x');360const std::string request{"GET / HTTP/1.0\r\nContent-Length: " + util::ToString(excessive_size) + "\r\n\r\n" + std::move(huge_body)};361HTTPRequest req;362LineReader reader(request, MAX_HEADERS_SIZE);363BOOST_CHECK(req.LoadControlData(reader));364BOOST_CHECK(req.LoadHeaders(reader));365BOOST_CHECK_EXCEPTION(req.LoadBody(reader), ContentTooLargeError, HasReason{"Max body size exceeded"});366}367{368// Content-Length exactly on the limit369std::string max_body(MAX_BODY_SIZE, 'x');370const std::string request{"GET / HTTP/1.0\r\nContent-Length: " + util::ToString(MAX_BODY_SIZE) + "\r\n\r\n" + std::move(max_body)};371HTTPRequest req;372LineReader reader(request, MAX_HEADERS_SIZE);373BOOST_CHECK(req.LoadControlData(reader));374BOOST_CHECK(req.LoadHeaders(reader));375BOOST_CHECK(req.LoadBody(reader));376}377{378// Content-Length indicates more data than we have in the buffer.379// Not an error; we wait for more data before completing the body.380HTTPRequest req;381LineReader reader("GET / HTTP/1.0\r\nContent-Length: 1024\r\n\r\n" R"({"method":"getblockcount"})", MAX_HEADERS_SIZE);382BOOST_CHECK(req.LoadControlData(reader));383BOOST_CHECK(req.LoadHeaders(reader));384BOOST_CHECK(!req.LoadBody(reader));385}386{387// Support "chunked" transfer. Chunk lengths are ascii-encoded hex integers, whitespace ignored388HTTPRequest req;389std::string_view ok_chunked = "GET / HTTP/1.0\n"390"Transfer-Encoding: chunked\n"391"\n"392"10\n"393R"({"method":"getbl)""\n"394" a \n"395R"(ockcount"})""\n"396"0\n"397"\n";398LineReader reader(ok_chunked, MAX_HEADERS_SIZE);399BOOST_CHECK(req.LoadControlData(reader));400BOOST_CHECK(req.LoadHeaders(reader));401BOOST_CHECK(req.LoadBody(reader));402BOOST_CHECK_EQUAL(req.ReadBody(), R"({"method":"getblockcount"})");403}404{405// Prevent "chunked" transfer from exceeding size limit406HTTPRequest req;407std::string_view excessive_chunk_size = "GET / HTTP/1.0\n"408"Transfer-Encoding: chunked\n"409"\n"410"10\n"411R"({"method":"getbl)""\n"412"20000000\n"413R"(ockcount"})""\n"414"0\n"415"\n";416LineReader reader(excessive_chunk_size, MAX_HEADERS_SIZE);417BOOST_CHECK(req.LoadControlData(reader));418BOOST_CHECK(req.LoadHeaders(reader));419BOOST_CHECK_EXCEPTION(req.LoadBody(reader), ContentTooLargeError, HasReason{"Chunk will exceed max body size"});420}421{422// Allow (but ignore) Chunk Extensions423HTTPRequest req;424std::string_view ok_chunked = "GET / HTTP/1.0\n"425"Transfer-Encoding: chunked\n"426"\n"427"10;sha256=715790e8a3b09d704ac9641f42d183a5ebc5fd939663de23da548519ac2165e5\n"428R"({"method":"getbl)""\n"429" a ; compressed\n"430R"(ockcount"})""\n"431"0;why;would;anyone;do;this;\n"432"Expires: Wed, 21 Oct 2026 07:28:00 GMT\n"433"\n";434LineReader reader(ok_chunked, MAX_HEADERS_SIZE);435BOOST_CHECK(req.LoadControlData(reader));436BOOST_CHECK(req.LoadHeaders(reader));437BOOST_CHECK(req.LoadBody(reader));438BOOST_CHECK_EQUAL(req.ReadBody(), R"({"method":"getblockcount"})");439// Chunk Trailer was parsed, but ignored440BOOST_CHECK_EQUAL(reader.Remaining(), 0);441BOOST_CHECK(!req.GetHeader("Expires"));442}443{444// Invalid "chunked" transfer, using roman numerals instead of hex for chunk length445HTTPRequest req;446std::string_view invalid_chunked = "GET / HTTP/1.0\n"447"Transfer-Encoding: chunked\n"448"\n"449"XVI\n"450R"({"method":"getbl)""\n"451"X\n"452R"(ockcount"})""\n"453"0\n"454"\n";455LineReader reader(invalid_chunked, MAX_HEADERS_SIZE);456BOOST_CHECK(req.LoadControlData(reader));457BOOST_CHECK(req.LoadHeaders(reader));458BOOST_CHECK_EXCEPTION(req.LoadBody(reader), std::runtime_error, HasReason{"Cannot parse chunk length value"});459}460{461// Invalid "chunked" transfer, missing chunk termination \n462HTTPRequest req;463std::string_view invalid_chunked = "GET / HTTP/1.0\n"464"Transfer-Encoding: chunked\n"465"\n"466"10\n"467R"({"method":"getbl)"468"a\n" // interpreted as extra data at the end of `0x10`-sized chunk469R"(ockcount"})"470"0\n"471"\n";472LineReader reader(invalid_chunked, MAX_HEADERS_SIZE);473BOOST_CHECK(req.LoadControlData(reader));474BOOST_CHECK(req.LoadHeaders(reader));475BOOST_CHECK_EXCEPTION(req.LoadBody(reader), std::runtime_error, HasReason{"Improperly terminated chunk"});476}477}479BOOST_AUTO_TEST_CASE(http_request_state_tests)480{481// For these tests we just need a receive buffer for the requests to read from.482class DummyClient : public HTTPRemoteClient483{484public:485DummyClient() : HTTPRemoteClient{/*id=*/0, /*addr=*/CService(), /*socket=*/CreateSock(0, 0, 0)} {}487void receive(std::string_view s)488{489MutateRecvBuffer().append(s);490}491};493{494// Step through state machine495std::shared_ptr<DummyClient> client{std::make_shared<DummyClient>()};496BOOST_CHECK(!client->GetRequest());498client->receive("POST / HTTP/1.0\n");499BOOST_CHECK(!HTTPRemoteClient::TryReadRequest(client));500BOOST_CHECK_EQUAL(client->GetRequest()->GetState(), HTTPRequest::State::NeedsHeaders);
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.