Inside Bitcoin
Opening the selected file…
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.
251std::vector<HTTPPathHandler>::iterator i = pathHandlers.begin();252std::vector<HTTPPathHandler>::iterator iend = pathHandlers.end();253for (; i != iend; ++i)254if (i->prefix == prefix && i->exactMatch == exactMatch)255break;256if (i != iend)257{258LogDebug(BCLog::HTTP, "Unregistering HTTP handler for %s (exactmatch %d)\n", prefix, exactMatch);259pathHandlers.erase(i);260}261}263using util::Split;265std::optional<std::string> HTTPHeaders::FindFirst(const std::string_view key) const266{267for (const auto& item : m_headers) {268if (CaseInsensitiveEqual(key, item.first)) {269return item.second;270}271}272return std::nullopt;273}275std::vector<std::string_view> HTTPHeaders::FindAll(const std::string_view key) const276{277std::vector<std::string_view> ret;278for (const auto& item : m_headers) {279if (CaseInsensitiveEqual(key, item.first)) {280ret.push_back(item.second);281}282}283return ret;284}286void HTTPHeaders::Write(std::string&& key, std::string&& value)287{288m_headers.emplace_back(std::move(key), std::move(value));289}291void HTTPHeaders::RemoveAll(std::string_view key)292{293auto moved = std::ranges::remove_if(m_headers, [key] (auto& pair) {294return CaseInsensitiveEqual(key, pair.first);295});296m_headers.erase(moved.begin(), moved.end());297}299bool HTTPHeaders::Read(util::LineReader& reader, bool write)300{301// Headers https://httpwg.org/specs/rfc9110.html#rfc.section.6.3302// A sequence of Field Lines https://httpwg.org/specs/rfc9110.html#rfc.section.5.2303size_t start{reader.Consumed()};304while (auto maybe_line = reader.ReadLine()) {305if (reader.Consumed() - start + m_consumed > MAX_HEADERS_SIZE) throw std::runtime_error("HTTP headers exceed size limit");307const std::string_view& line = *maybe_line;309// An empty line indicates end of the headers section https://www.rfc-editor.org/rfc/rfc2616#section-4310if (line.empty()) {311// Ensure all headers are accounted for in case there is a chunked trailer312m_consumed += reader.Consumed() - start;313return true;314}316// "Field values containing CR, LF, or NUL characters are invalid and dangerous"317// https://httpwg.org/specs/rfc9110.html#rfc.section.5.5318// A sender MUST NOT generate a bare CR (a CR character not immediately followed by LF)319// within any protocol elements other than the content.320// A recipient of such a bare CR MUST consider that element to be invalid...321// https://httpwg.org/specs/rfc9112.html#rfc.section.2.2322if (line.find_first_of("\r\n\0", 0, 3) != std::string_view::npos) throw std::runtime_error("Header contains invalid character");324// Header line must have at least one ":"325// keys are not allowed to have delimiters like ":" but values are326// https://httpwg.org/specs/rfc9110.html#rfc.section.5.6.2327const size_t pos{line.find(':')};328if (pos == std::string_view::npos) throw std::runtime_error("HTTP header missing colon (:)");330// Whitespace is strictly not allowed in the field-name (key)331// https://www.rfc-editor.org/rfc/rfc9110.html#section-5.6.2332std::string_view key = line.substr(0, pos);333if (key.find_first_of(" \t\n\r\f\v") != std::string_view::npos) throw std::runtime_error("Invalid header field-name contains whitespace");334// Whitespace is optional in the value and can be trimmed335std::string value = util::TrimString(std::string_view(line).substr(pos + 1));337// Header keys are Field Names: https://httpwg.org/specs/rfc9110.html#fields.names338// which consist of "tokens": https://httpwg.org/specs/rfc9110.html#rfc.section.5.6.2339// that can not be empty.340if (key.empty()) throw std::runtime_error("Empty HTTP header name");342if (write) {343Write(std::string(key), std::move(value));344}345}347// We have not received all the request headers yet.348// Keep track of how much data we have already consumed to enforce349// the total limit over multiple read operations.350m_consumed += reader.Consumed() - start;352return false;353}355std::string HTTPHeaders::Stringify() const356{357std::string out;358for (const auto& [key, value] : m_headers) {359out += key + ": " + value + "\r\n";360}362// Headers are terminated by an empty line363out += "\r\n";365return out;366}368std::string HTTPResponse::StringifyHeaders() const369{370return strprintf("HTTP/%d.%d %d %s\r\n%s",371version.major,372version.minor,373status,374HTTPStatusReasonString(status),375headers.Stringify());376}378bool HTTPRequest::LoadControlData(LineReader& reader)379{380auto maybe_line = reader.ReadLine();381if (!maybe_line) return false;382const std::string_view& request_line = *maybe_line;384// Request Line aka Control Data https://httpwg.org/specs/rfc9110.html#rfc.section.6.2385// Three words separated by spaces, terminated by \n or \r\n386if (request_line.length() < MIN_REQUEST_LINE_LENGTH) throw std::runtime_error("HTTP request line too short");388// NUL is not a valid tchar and would silently truncate389// C-string-based parsers rather than being rejected as malformed.390// tchar: https://www.rfc-editor.org/info/rfc7230/#section-3.2.6391if (request_line.find('\0') != std::string_view::npos) throw std::runtime_error("Invalid request line contains NUL");393const std::vector<std::string_view> parts{Split<std::string_view>(request_line, " ")};394if (parts.size() != 3) throw std::runtime_error("HTTP request line malformed");396if (parts[0] == "GET") {397m_method = HTTPRequestMethod::GET;398} else if (parts[0] == "POST") {399m_method = HTTPRequestMethod::POST;400} else if (parts[0] == "HEAD") {401m_method = HTTPRequestMethod::HEAD;402} else if (parts[0] == "PUT") {403m_method = HTTPRequestMethod::PUT;404} else {405m_method = HTTPRequestMethod::UNKNOWN;406}408m_target = parts[1];410if (parts[2].rfind("HTTP/") != 0) throw std::runtime_error("HTTP request line malformed");412// Version is exactly two decimal digits separated by a decimal point413// https://httpwg.org/specs/rfc9110.html#rfc.section.2.5414const std::vector<std::string_view> version_parts{Split<std::string_view>(parts[2].substr(5), ".")};415if (version_parts.size() != 2) throw std::runtime_error("HTTP request line malformed");416if (version_parts[0].size() != 1 || version_parts[1].size() != 1) throw std::runtime_error("HTTP bad version");417auto major = ToIntegral<uint8_t>(version_parts[0]);418auto minor = ToIntegral<uint8_t>(version_parts[1]);419if (!major || !minor || major != 1 || minor > 9) throw std::runtime_error("HTTP bad version");420m_version.major = major.value();421m_version.minor = minor.value();423return true;424}426bool HTTPRequest::LoadHeaders(LineReader& reader)427{428return m_headers.Read(reader);429}431bool HTTPRequest::LoadBody(LineReader& reader)432{433// https://httpwg.org/specs/rfc9112.html#message.body434auto transfer_encoding_header = m_headers.FindFirst("Transfer-Encoding");435if (transfer_encoding_header && ToLower(transfer_encoding_header.value()) == "chunked") {436// Transfer-Encoding: https://datatracker.ietf.org/doc/html/rfc7230.html#section-3.3.1437// Chunked Transfer Coding: https://datatracker.ietf.org/doc/html/rfc7230.html#section-4.1438// see evhttp_handle_chunked_read() in libevent http.c439while (reader.Remaining() > 0) {440if (!m_chunk_size) {441auto maybe_chunk_size = reader.ReadLine();442if (!maybe_chunk_size) return false;444// Allow (but ignore) Chunk Extensions445// See https://www.rfc-editor.org/rfc/rfc9112.html#name-chunk-extensions446std::string_view chunk_size_noext{maybe_chunk_size.value()};447const auto semicolon_pos = chunk_size_noext.find(';');448if (semicolon_pos != chunk_size_noext.npos) {449chunk_size_noext.remove_suffix(chunk_size_noext.size() - semicolon_pos);450}452m_chunk_size = ToIntegral<uint64_t>(util::TrimStringView(chunk_size_noext), /*base=*/16);453if (!m_chunk_size) throw std::runtime_error("Cannot parse chunk length value");455if ((m_body.size() > MAX_BODY_SIZE) ||456(*m_chunk_size > MAX_BODY_SIZE - m_body.size()))457throw ContentTooLargeError("Chunk will exceed max body size");458}460// We either just read the chunk size, or we have it saved461// from a prior I/O loop iteration462Assume(m_chunk_size);464// Last chunk has size 0465if (*m_chunk_size == 0) {466// Validate Chunked Trailer section, which is used for467// additional headers sent at the end of the message.468// Data consumed here is counted towards MAX_HEADERS_SIZE469// along with the headers we read in the beginning of the request.470// At this time we ignore and drop these data after validating.471// See https://httpwg.org/specs/rfc9112.html#rfc.section.7.1.2472return m_headers.Read(reader, /*write=*/false);473}475// We have not read the entire chunk from the buffer yet476if (m_chunk_read < *m_chunk_size) {477// Get what we can from the buffer478const uint64_t chunk_need{*m_chunk_size - m_chunk_read};479const uint64_t buffer_has{std::min(chunk_need, static_cast<uint64_t>(reader.Remaining()))};481// Pack [partial] chunk onto body and update state482m_body += reader.ReadLength(buffer_has);483m_chunk_read += buffer_has;484}486// Even though every chunk size is explicitly declared,487// they are still terminated by a CRLF we don't need,488// just consume it here.489if (m_chunk_read == *m_chunk_size) {490auto crlf = reader.ReadLine();491if (!crlf) {492// CRLF not found before end of buffer: it has not been received by our socket yet.493return false;494}495// CRLF was found but there was unexpected data after the chunk_sized chunk496if (!crlf.value().empty()) throw std::runtime_error("Improperly terminated chunk");498// Clear state for next chunk499m_chunk_size.reset();500m_chunk_read = 0;
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.