Bitcoin Core 32.99.0
P2P Digital Currency
Loading...
Searching...
No Matches
http_request.cpp
Go to the documentation of this file.
1// Copyright (c) 2020-present The Bitcoin Core developers
2// Distributed under the MIT software license, see the accompanying
3// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
5#include <httpserver.h>
6#include <netaddress.h>
8#include <test/fuzz/fuzz.h>
9#include <test/fuzz/util.h>
10#include <test/util/net.h>
11#include <test/util/time.h>
12#include <util/check.h>
14#include <util/strencodings.h>
15
16#include <cassert>
17#include <cstdint>
18#include <memory>
19#include <string>
20#include <string_view>
21#include <vector>
22
24
25using namespace bitcoin_http;
26
27namespace {
28
31{
32 const std::string body{req.ReadBody()};
33 const auto transfer_encoding{req.GetHeader("Transfer-Encoding")};
34 const auto content_length{req.GetHeader("Content-Length")};
35 if (transfer_encoding && ToLower(*transfer_encoding) == "chunked") {
36 assert(body.size() <= MAX_BODY_SIZE);
37 } else if (content_length) {
40 assert(body.size() == *parsed_length);
41 } else {
42 assert(body.empty());
43 }
44}
45
48{
49 using util::LineReader;
50
53 try {
54 if (http_request.Load(reader) != HTTPRequest::State::Complete) return;
55 } catch (const std::runtime_error&) {
56 return;
57 }
58
59 const HTTPRequestMethod request_method = http_request.GetRequestMethod();
61 (void)http_request.GetURI();
62 (void)http_request.GetHeader("Host");
63 std::string header = provider.ConsumeRandomLengthString(16);
64 (void)http_request.GetHeader(header);
65 (void)http_request.WriteHeader(std::string(header), provider.ConsumeRandomLengthString(16));
66 (void)http_request.GetHeader(header);
68}
69
71class FuzzClient : public HTTPRemoteClient
72{
73public:
74 FuzzClient() : HTTPRemoteClient{/*id=*/0, /*addr=*/CService(), /*socket=*/std::make_unique<ZeroSock>()} {}
75 void Receive(std::string_view s) { MutateRecvBuffer().append(s); }
76};
77
79{
80 switch (s) {
81 case HTTPRequest::State::Init: return 0;
82 case HTTPRequest::State::NeedsHeaders: return 1;
83 case HTTPRequest::State::NeedsBody: return 2;
84 case HTTPRequest::State::Complete: return 3;
85 case HTTPRequest::State::Error: return 4;
86 }
87 assert(false);
88}
89
90struct ParsedRequest {
91 HTTPRequestMethod method;
92 std::string uri;
93 std::string host;
94 std::string body;
95 bool operator==(const ParsedRequest&) const = default;
96};
97
98struct RunResult {
99 std::vector<ParsedRequest> requests;
100 bool errored{false};
101 std::string remainder;
102 HTTPRequest::State last_state{HTTPRequest::State::Init};
103};
104
107void Drain(const std::shared_ptr<FuzzClient>& client, RunResult& out)
108{
109 while (true) {
110 // A failed request is inert: further reads consume nothing.
111 if (const HTTPRequest* cur{client->GetRequest()};
112 cur && cur->GetState() == HTTPRequest::State::Error) {
113 const size_t buffered{client->GetRecvBuffer().size()};
115 assert(client->GetRecvBuffer().size() == buffered);
116 out.errored = true;
117 break;
118 }
119
120 std::unique_ptr<HTTPRequest> req{HTTPRemoteClient::TryReadRequest(client)};
121
122 if (!req) {
123 if (const HTTPRequest* cur{client->GetRequest()}) {
124 // TryReadRequest() returns empty if the HTTPRemoteClient is still
125 // holding on to the request. In this test, the request is guaranteed
126 // to be incomplete or failed, although in production it may be a result of
127 // a busy worker or a full send buffer.
128 assert(cur->GetState() != HTTPRequest::State::Complete);
129 assert(StateRank(cur->GetState()) >= StateRank(out.last_state));
130 out.last_state = cur->GetState();
131 if (cur->GetState() == HTTPRequest::State::Error) out.errored = true;
132 if (const auto chunk_size{cur->GetChunkSize()}) {
133 assert(cur->GetChunkProgress() <= *chunk_size);
134 }
135 assert(cur->ReadBody().size() <= MAX_BODY_SIZE);
136 }
137 break;
138 }
139
140 assert(req->GetState() == HTTPRequest::State::Complete);
142 out.requests.emplace_back(req->GetRequestMethod(), req->GetURI(),
143 req->GetHeader("Host").value_or(""), req->ReadBody());
144
145 // While a request is with a worker, nothing new is parsed or consumed.
146 const size_t buffered{client->GetRecvBuffer().size()};
148 assert(client->GetRecvBuffer().size() == buffered);
149
150 req->WriteReply(HTTP_OK, ""); // clears m_req_busy
151 out.last_state = HTTPRequest::State::Init;
152 }
153}
154
158void CheckSegmentationIndependence(const std::string& input, FuzzedDataProvider& provider)
159{
160 // WriteReply() stamps a wall-clock Date header and the client stamps a
161 // steady-clock idle time, both of which the fuzz determinism check rejects.
162 FakeNodeClock clock{1610000000s};
163 FakeSteadyClock steady_clock;
164
165 // The whole stream arrives in one I/O cycle. This is similar to
166 // SingleShotParse(), although here we pipe it through the client and also
167 // parse multiple requests.
168 RunResult one_shot;
169 {
170 auto client{std::make_shared<FuzzClient>()};
171 client->Receive(input);
173 one_shot.remainder = client->GetRecvBuffer();
174 }
175
176 // The same stream arrives in arbitrary pieces. Once the provider runs dry
177 // this becomes one byte per cycle, the maximally fragmented case.
178 RunResult sliced;
179 {
180 auto client{std::make_shared<FuzzClient>()};
181 std::string_view remaining{input};
182 while (!remaining.empty()) {
183 const size_t n{provider.ConsumeIntegralInRange<size_t>(1, remaining.size())};
184 client->Receive(remaining.substr(0, n));
185 remaining = remaining.substr(n);
187 }
188 sliced.remainder = client->GetRecvBuffer();
189 }
190
191 assert(one_shot.requests == sliced.requests);
192 assert(one_shot.errored == sliced.errored);
193 // The whole receive buffer is discarded on a parse error, so in the sliced case
194 // what is left over depends on how much had arrived when the error fired.
195 if (!one_shot.errored) assert(one_shot.remainder == sliced.remainder);
196}
197
198} // namespace
199
201{
202 FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
203 // MAX_HEADERS_SIZE is 8192: leave room for a headers section that can
204 // reach the limit, plus a body.
206
209}
#define Assert(val)
Identity function.
Definition check.h:119
A combination of a network address (CNetAddr) and a (TCP) port.
Definition netaddress.h:531
Helper to initialize the global NodeClock, let a duration elapse, and reset it after use in a test.
Definition time.h:54
Helper to initialize the global MockableSteadyClock, let a duration elapse, and reset it after use in...
Definition time.h:29
std::string ConsumeRandomLengthString(size_t max_length)
T ConsumeIntegralInRange(T min, T max)
std::string & MutateRecvBuffer()
Used for tests.
Definition httpserver.h:550
static std::unique_ptr< HTTPRequest > TryReadRequest(const std::shared_ptr< HTTPRemoteClient > &client) EXCLUSIVE_LOCKS_REQUIRED(!client -> m_send_mutex)
Try to read an HTTPRequest from a client's receive buffer.
void Receive() EXCLUSIVE_LOCKS_REQUIRED(!m_sock_mutex)
std::string GetURI() const
Definition httpserver.h:189
std::optional< std::string > GetHeader(std::string_view hdr) const
HTTPRequestMethod GetRequestMethod() const
Definition httpserver.h:191
std::string ReadBody() const
Definition httpserver.h:194
void WriteReply(HTTPStatusCode status, std::span< const std::byte > reply_body={})
State GetState() const
Definition httpserver.h:199
A mocked Sock alternative that succeeds on all operations.
Definition net.h:169
#define FUZZ_TARGET(...)
Definition fuzz.h:35
std::string_view RequestMethodString(HTTPRequestMethod m)
HTTP request method as string - use for logging only.
HTTPRequestMethod
Definition httpserver.h:49
util::LineReader reader
std::unique_ptr< ProxyClient< messages::FooInterface > > client
constexpr uint64_t MAX_BODY_SIZE
Maximum size of an HTTP request body received from a client.
Definition httpserver.h:82
constexpr size_t MAX_HEADERS_SIZE
Maximum size of each headers line in an HTTP request, also the maximum size of all headers total.
Definition httpserver.h:78
bool operator==(const CNetAddr &a, const CNetAddr &b)
@ HTTP_OK
Definition protocol.h:14
FuzzedDataProvider provider
std::string ToLower(std::string_view str)
Returns the lowercase equivalent of the given string.
constexpr auto Ticks(Dur2 d)
Helper to count the seconds of a duration/time_point.
Definition time.h:82
assert(!tx.IsCoinBase())
FuzzedDataProvider & fuzzed_data_provider
Definition fees.cpp:46
FakeNodeClock clock