mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-09-11 21:20:39 +02:00
refactor: Expose HTTPRemoteClient fields to tests through methods
Enables making the fields private later.
This commit is contained in:
@@ -596,6 +596,16 @@ public:
|
||||
* @returns false if we are done with this client and HTTPServer can skip the next read operation from it.
|
||||
*/
|
||||
bool MaybeSendBytesFromBuffer() EXCLUSIVE_LOCKS_REQUIRED(!m_send_mutex, !m_sock_mutex);
|
||||
|
||||
//! Used for tests.
|
||||
//! @{
|
||||
const std::string& GetRecvBuffer() const { return m_recv_buffer; }
|
||||
const HTTPRequest* GetRequest() const { return m_req.get(); }
|
||||
//! @}
|
||||
|
||||
protected:
|
||||
//! Used for tests.
|
||||
std::string& MutateRecvBuffer() { return m_recv_buffer; }
|
||||
};
|
||||
|
||||
/** Initialize HTTP server.
|
||||
|
||||
@@ -493,119 +493,114 @@ BOOST_AUTO_TEST_CASE(http_request_state_tests)
|
||||
|
||||
void receive(std::string_view s)
|
||||
{
|
||||
m_recv_buffer.insert(
|
||||
m_recv_buffer.end(),
|
||||
s.begin(),
|
||||
s.end());
|
||||
MutateRecvBuffer().append(s);
|
||||
}
|
||||
};
|
||||
|
||||
{
|
||||
// Step through state machine
|
||||
std::shared_ptr<DummyClient> client{std::make_shared<DummyClient>()};
|
||||
client->m_req = std::make_unique<HTTPRequest>(client);
|
||||
BOOST_CHECK_EQUAL(client->m_req->GetState(), HTTPRequest::State::Init);
|
||||
BOOST_CHECK(!client->GetRequest());
|
||||
|
||||
client->receive("POST / HTTP/1.0\n");
|
||||
client->ReadRequest(*client->m_req);
|
||||
BOOST_CHECK_EQUAL(client->m_req->GetState(), HTTPRequest::State::NeedsHeaders);
|
||||
BOOST_CHECK(!HTTPRemoteClient::TryReadRequest(client));
|
||||
BOOST_CHECK_EQUAL(client->GetRequest()->GetState(), HTTPRequest::State::NeedsHeaders);
|
||||
|
||||
client->receive("Host: 127.0.0.1\n"
|
||||
"Content-Length: 10\n\n");
|
||||
client->ReadRequest(*client->m_req);
|
||||
BOOST_CHECK_EQUAL(client->m_req->GetState(), HTTPRequest::State::NeedsBody);
|
||||
BOOST_CHECK(!HTTPRemoteClient::TryReadRequest(client));
|
||||
BOOST_CHECK_EQUAL(client->GetRequest()->GetState(), HTTPRequest::State::NeedsBody);
|
||||
|
||||
client->receive("I miss you\n");
|
||||
client->ReadRequest(*client->m_req);
|
||||
BOOST_CHECK_EQUAL(client->m_req->GetState(), HTTPRequest::State::Complete);
|
||||
auto req{HTTPRemoteClient::TryReadRequest(client)};
|
||||
BOOST_REQUIRE(req);
|
||||
BOOST_CHECK_EQUAL(req->GetState(), HTTPRequest::State::Complete);
|
||||
}
|
||||
{
|
||||
// Read body over multiple data pushes, multiple requests in same push
|
||||
std::shared_ptr<DummyClient> client{std::make_shared<DummyClient>()};
|
||||
client->m_req = std::make_unique<HTTPRequest>(client);
|
||||
BOOST_CHECK_EQUAL(client->m_req->GetState(), HTTPRequest::State::Init);
|
||||
BOOST_CHECK(!client->GetRequest());
|
||||
|
||||
client->receive("POST / HTTP/1.0\n"
|
||||
"Host: 127.0.0.1\n"
|
||||
"Content-Length: 10\n\n"
|
||||
"I miss");
|
||||
client->ReadRequest(*client->m_req);
|
||||
BOOST_CHECK(!HTTPRemoteClient::TryReadRequest(client));
|
||||
// Because of the Content-Length header we know the body is not complete
|
||||
BOOST_CHECK_EQUAL(client->m_req->GetState(), HTTPRequest::State::NeedsBody);
|
||||
BOOST_CHECK_EQUAL(client->GetRequest()->GetState(), HTTPRequest::State::NeedsBody);
|
||||
|
||||
// Finish sending first request and include second request in the same buffer
|
||||
client->receive(" you"
|
||||
"GET /endpoint HTTP/1.0\n\n");
|
||||
client->ReadRequest(*client->m_req);
|
||||
BOOST_CHECK_EQUAL(client->m_req->GetState(), HTTPRequest::State::Complete);
|
||||
BOOST_CHECK_EQUAL(client->m_req->ReadBody(), "I miss you");
|
||||
auto req{HTTPRemoteClient::TryReadRequest(client)};
|
||||
BOOST_REQUIRE(req);
|
||||
BOOST_CHECK_EQUAL(req->GetState(), HTTPRequest::State::Complete);
|
||||
BOOST_CHECK_EQUAL(req->GetURI(), "/");
|
||||
BOOST_CHECK(!client->GetRequest());
|
||||
BOOST_CHECK_EQUAL(req->ReadBody(), "I miss you");
|
||||
req->WriteReply(HTTP_OK, ""); // Mark client as no longer busy
|
||||
// Next request sitting in buffer
|
||||
BOOST_CHECK_EQUAL(client->m_recv_buffer.size(), 24);
|
||||
// Complete first request hasn't been moved yet, expect no-op
|
||||
client->ReadRequest(*client->m_req);
|
||||
BOOST_CHECK_EQUAL(client->m_recv_buffer.size(), 24);
|
||||
|
||||
// Reset m_req
|
||||
client->m_req = std::make_unique<HTTPRequest>(client);
|
||||
BOOST_CHECK_EQUAL(client->m_req->GetState(), HTTPRequest::State::Init);
|
||||
BOOST_CHECK_EQUAL(client->GetRecvBuffer().size(), 24);
|
||||
|
||||
// Read second request
|
||||
client->ReadRequest(*client->m_req);
|
||||
BOOST_CHECK_EQUAL(client->m_req->GetState(), HTTPRequest::State::Complete);
|
||||
BOOST_CHECK_EQUAL(client->m_req->GetURI(), "/endpoint");
|
||||
BOOST_CHECK_EQUAL(client->m_req->ReadBody().size(), 0);
|
||||
req = HTTPRemoteClient::TryReadRequest(client);
|
||||
BOOST_REQUIRE(req);
|
||||
BOOST_CHECK(!client->GetRequest());
|
||||
BOOST_CHECK_EQUAL(req->GetState(), HTTPRequest::State::Complete);
|
||||
BOOST_CHECK_EQUAL(req->GetURI(), "/endpoint");
|
||||
BOOST_CHECK_EQUAL(req->ReadBody().size(), 0);
|
||||
// Buffer is cleared
|
||||
BOOST_CHECK_EQUAL(client->m_recv_buffer.size(), 0);
|
||||
BOOST_CHECK_EQUAL(client->GetRecvBuffer().size(), 0);
|
||||
}
|
||||
{
|
||||
// A Content-Length body is drained out of the receive buffer as it
|
||||
// arrives, instead of accumulating there until the request is complete.
|
||||
|
||||
std::shared_ptr<DummyClient> client{std::make_shared<DummyClient>()};
|
||||
client->m_req = std::make_unique<HTTPRequest>(client);
|
||||
BOOST_CHECK_EQUAL(client->m_req->GetState(), HTTPRequest::State::Init);
|
||||
BOOST_CHECK(!client->GetRequest());
|
||||
|
||||
client->receive("POST / HTTP/1.0\n"
|
||||
"Content-Length: 30000\n\n");
|
||||
client->ReadRequest(*client->m_req);
|
||||
BOOST_CHECK_EQUAL(client->m_req->GetState(), HTTPRequest::State::NeedsBody);
|
||||
HTTPRemoteClient::TryReadRequest(client);
|
||||
BOOST_CHECK_EQUAL(client->GetRequest()->GetState(), HTTPRequest::State::NeedsBody);
|
||||
|
||||
// Body arrives in 10kB pieces. Each one is copied onto m_body and
|
||||
// erased from the receive buffer, which never holds more than one piece.
|
||||
for (int i = 1; i <= 3; ++i) {
|
||||
client->receive(std::string(10000, 'x'));
|
||||
BOOST_CHECK_EQUAL(client->m_recv_buffer.size(), 10000);
|
||||
client->ReadRequest(*client->m_req);
|
||||
BOOST_CHECK_EQUAL(client->m_req->ReadBody().size(), 10000 * i);
|
||||
BOOST_CHECK_EQUAL(client->m_recv_buffer.size(), 0);
|
||||
BOOST_CHECK_EQUAL(client->GetRecvBuffer().size(), 10000);
|
||||
auto req{HTTPRemoteClient::TryReadRequest(client)};
|
||||
if (i < 3) {
|
||||
BOOST_CHECK(!req.get());
|
||||
BOOST_CHECK_EQUAL(client->GetRequest()->ReadBody().size(), 10000 * i);
|
||||
} else {
|
||||
BOOST_CHECK(req.get());
|
||||
BOOST_CHECK_EQUAL(req->ReadBody().size(), 10000 * i);
|
||||
BOOST_CHECK_EQUAL(req->GetState(), HTTPRequest::State::Complete);
|
||||
BOOST_CHECK(!client->GetRequest());
|
||||
}
|
||||
BOOST_CHECK_EQUAL(client->GetRecvBuffer().size(), 0);
|
||||
}
|
||||
BOOST_CHECK_EQUAL(client->m_req->GetState(), HTTPRequest::State::Complete);
|
||||
}
|
||||
{
|
||||
// A body sent in the same push as the next request is split correctly
|
||||
std::shared_ptr<DummyClient> client{std::make_shared<DummyClient>()};
|
||||
client->m_req = std::make_unique<HTTPRequest>(client);
|
||||
BOOST_CHECK_EQUAL(client->m_req->GetState(), HTTPRequest::State::Init);
|
||||
BOOST_CHECK(!client->GetRequest());
|
||||
|
||||
client->receive("POST / HTTP/1.0\n"
|
||||
"Content-Length: 4\n\n"
|
||||
"body"
|
||||
"GET /next HTTP/1.0\n\n");
|
||||
client->ReadRequest(*client->m_req);
|
||||
BOOST_CHECK_EQUAL(client->m_req->GetState(), HTTPRequest::State::Complete);
|
||||
BOOST_CHECK_EQUAL(client->m_req->ReadBody(), "body");
|
||||
auto req{HTTPRemoteClient::TryReadRequest(client)};
|
||||
BOOST_CHECK_EQUAL(req->GetState(), HTTPRequest::State::Complete);
|
||||
BOOST_CHECK_EQUAL(req->ReadBody(), "body");
|
||||
// Only the second request is left over
|
||||
BOOST_CHECK_EQUAL(client->m_recv_buffer.size(), 20);
|
||||
BOOST_CHECK_EQUAL(client->GetRecvBuffer().size(), 20);
|
||||
}
|
||||
{
|
||||
// Chunked transfer with state
|
||||
std::shared_ptr<DummyClient> client{std::make_shared<DummyClient>()};
|
||||
client->m_req = std::make_unique<HTTPRequest>(client);
|
||||
|
||||
BOOST_CHECK(!client->m_req->GetChunkSize());
|
||||
BOOST_CHECK_EQUAL(client->m_req->GetChunkProgress(), 0);
|
||||
BOOST_CHECK_EQUAL(client->m_req->ReadBody().size(), 0);
|
||||
BOOST_CHECK_EQUAL(client->m_req->GetState(), HTTPRequest::State::Init);
|
||||
BOOST_CHECK(!client->GetRequest());
|
||||
|
||||
// First chunk is incomplete
|
||||
client->receive("GET / HTTP/1.0\n"
|
||||
@@ -613,136 +608,129 @@ BOOST_AUTO_TEST_CASE(http_request_state_tests)
|
||||
"\n"
|
||||
"10\n"
|
||||
R"({"method)");
|
||||
client->ReadRequest(*client->m_req);
|
||||
BOOST_CHECK(client->m_req->GetChunkSize());
|
||||
BOOST_CHECK_EQUAL(*client->m_req->GetChunkSize(), 16);
|
||||
BOOST_CHECK_EQUAL(client->m_req->GetChunkProgress(), 8);
|
||||
BOOST_CHECK_EQUAL(client->m_req->ReadBody().size(), 8);
|
||||
BOOST_CHECK_EQUAL(client->m_req->GetState(), HTTPRequest::State::NeedsBody);
|
||||
BOOST_CHECK(!HTTPRemoteClient::TryReadRequest(client));
|
||||
BOOST_REQUIRE(client->GetRequest()->GetChunkSize());
|
||||
BOOST_CHECK_EQUAL(*client->GetRequest()->GetChunkSize(), 16);
|
||||
BOOST_CHECK_EQUAL(client->GetRequest()->GetChunkProgress(), 8);
|
||||
BOOST_CHECK_EQUAL(client->GetRequest()->ReadBody().size(), 8);
|
||||
BOOST_CHECK_EQUAL(client->GetRequest()->GetState(), HTTPRequest::State::NeedsBody);
|
||||
|
||||
// More data arrives, chunk is completed.
|
||||
client->receive(R"(":"getbl)""\n");
|
||||
client->ReadRequest(*client->m_req);
|
||||
BOOST_CHECK(!HTTPRemoteClient::TryReadRequest(client));
|
||||
// State is reset
|
||||
BOOST_CHECK(!client->m_req->GetChunkSize());
|
||||
BOOST_CHECK_EQUAL(client->m_req->GetChunkProgress(), 0);
|
||||
BOOST_CHECK(!client->GetRequest()->GetChunkSize());
|
||||
BOOST_CHECK_EQUAL(client->GetRequest()->GetChunkProgress(), 0);
|
||||
// New data is added to body but body is still incomplete
|
||||
BOOST_CHECK_EQUAL(client->m_req->ReadBody().size(), 16);
|
||||
BOOST_CHECK_EQUAL(client->m_req->GetState(), HTTPRequest::State::NeedsBody);
|
||||
BOOST_CHECK_EQUAL(client->GetRequest()->ReadBody().size(), 16);
|
||||
BOOST_CHECK_EQUAL(client->GetRequest()->GetState(), HTTPRequest::State::NeedsBody);
|
||||
|
||||
// Next chunk arrives without terminal CRLF
|
||||
client->receive("a\n"
|
||||
R"(ockcount"})");
|
||||
client->ReadRequest(*client->m_req);
|
||||
BOOST_CHECK(client->m_req->GetChunkSize());
|
||||
BOOST_CHECK_EQUAL(*client->m_req->GetChunkSize(), 10);
|
||||
BOOST_CHECK_EQUAL(client->m_req->GetChunkProgress(), 10);
|
||||
BOOST_CHECK_EQUAL(client->m_req->ReadBody().size(), 26);
|
||||
BOOST_CHECK_EQUAL(client->m_req->GetState(), HTTPRequest::State::NeedsBody);
|
||||
BOOST_CHECK(!HTTPRemoteClient::TryReadRequest(client));
|
||||
BOOST_CHECK(client->GetRequest()->GetChunkSize());
|
||||
BOOST_CHECK_EQUAL(*client->GetRequest()->GetChunkSize(), 10);
|
||||
BOOST_CHECK_EQUAL(client->GetRequest()->GetChunkProgress(), 10);
|
||||
BOOST_CHECK_EQUAL(client->GetRequest()->ReadBody().size(), 26);
|
||||
BOOST_CHECK_EQUAL(client->GetRequest()->GetState(), HTTPRequest::State::NeedsBody);
|
||||
|
||||
// Chunk terminal CRLF arrives with final (size 0) chunk
|
||||
client->receive("\n0\n\n");
|
||||
client->ReadRequest(*client->m_req);
|
||||
auto req{HTTPRemoteClient::TryReadRequest(client)};
|
||||
// Body size hasn't changed
|
||||
BOOST_CHECK_EQUAL(client->m_req->ReadBody().size(), 26);
|
||||
BOOST_CHECK_EQUAL(req->ReadBody().size(), 26);
|
||||
// We're done
|
||||
BOOST_CHECK_EQUAL(client->m_req->GetState(), HTTPRequest::State::Complete);
|
||||
BOOST_CHECK_EQUAL(client->m_req->ReadBody(), R"({"method":"getblockcount"})");
|
||||
BOOST_CHECK_EQUAL(req->GetState(), HTTPRequest::State::Complete);
|
||||
BOOST_CHECK_EQUAL(req->ReadBody(), R"({"method":"getblockcount"})");
|
||||
}
|
||||
{
|
||||
// Invalid headers: error state stops reading
|
||||
std::shared_ptr<DummyClient> client{std::make_shared<DummyClient>()};
|
||||
client->m_req = std::make_unique<HTTPRequest>(client);
|
||||
|
||||
// Request is in the buffer
|
||||
client->receive("POST / HTTP/1.0\n"
|
||||
"Host: 127.0.0.1\n"
|
||||
"Invalid header with no colon\n"
|
||||
"Host: 127.0.0.1\n");
|
||||
BOOST_CHECK(!client->GetRecvBuffer().empty());
|
||||
BOOST_CHECK(!HTTPRemoteClient::TryReadRequest(client));
|
||||
BOOST_CHECK_EQUAL(client->GetRequest()->GetState(), HTTPRequest::State::NeedsHeaders);
|
||||
client->receive("Invalid header with no colon\n"
|
||||
"\n"
|
||||
"body is not read");
|
||||
BOOST_CHECK(!client->m_recv_buffer.empty());
|
||||
|
||||
// Reading throws an error, sets state
|
||||
BOOST_CHECK_EXCEPTION(client->ReadRequest(*client->m_req),
|
||||
std::runtime_error,
|
||||
HasReason{"HTTP header missing colon (:)"});
|
||||
BOOST_CHECK_EQUAL(client->m_req->GetState(), HTTPRequest::State::Error);
|
||||
BOOST_CHECK(!HTTPRemoteClient::TryReadRequest(client));
|
||||
BOOST_CHECK_EQUAL(client->GetRequest()->GetState(), HTTPRequest::State::Error);
|
||||
|
||||
// We read up to the invalid line
|
||||
BOOST_CHECK_EQUAL(client->m_req->GetHeader("Host"), "127.0.0.1");
|
||||
BOOST_CHECK_EQUAL(client->GetRequest()->GetHeader("Host"), "127.0.0.1");
|
||||
// Buffer was cleared, client should just be disconnected now
|
||||
BOOST_CHECK(client->m_recv_buffer.empty());
|
||||
BOOST_CHECK(client->GetRecvBuffer().empty());
|
||||
|
||||
// Even if more data comes in, trying to read again in error state is a no-op
|
||||
client->receive("Content-Length: 2\n\nok");
|
||||
BOOST_CHECK_EQUAL(client->m_recv_buffer.size(), 21);
|
||||
client->ReadRequest(*client->m_req);
|
||||
BOOST_CHECK_EQUAL(client->m_recv_buffer.size(), 21);
|
||||
BOOST_CHECK_EQUAL(client->GetRecvBuffer().size(), 21);
|
||||
BOOST_CHECK(!HTTPRemoteClient::TryReadRequest(client));
|
||||
BOOST_CHECK_EQUAL(client->GetRecvBuffer().size(), 21);
|
||||
}
|
||||
{
|
||||
// Headers sent in batches that are below MAX_HEADERS_SIZE but the total is excessive
|
||||
std::shared_ptr<DummyClient> client{std::make_shared<DummyClient>()};
|
||||
client->m_req = std::make_unique<HTTPRequest>(client);
|
||||
client->ReadRequest(*client->m_req);
|
||||
BOOST_CHECK_EQUAL(client->m_req->GetState(), HTTPRequest::State::Init);
|
||||
BOOST_CHECK(!client->GetRequest());
|
||||
BOOST_CHECK(!HTTPRemoteClient::TryReadRequest(client));
|
||||
BOOST_CHECK_EQUAL(client->GetRequest()->GetState(), HTTPRequest::State::Init);
|
||||
|
||||
client->receive("POST /huge HTTP/1.0\n");
|
||||
client->ReadRequest(*client->m_req);
|
||||
BOOST_CHECK_EQUAL(client->m_req->GetState(), HTTPRequest::State::NeedsHeaders);
|
||||
BOOST_CHECK(!HTTPRemoteClient::TryReadRequest(client));
|
||||
BOOST_CHECK_EQUAL(client->GetRequest()->GetState(), HTTPRequest::State::NeedsHeaders);
|
||||
|
||||
for (int i = 0; i < 410; ++i) {
|
||||
client->receive("key:value\n");
|
||||
}
|
||||
client->ReadRequest(*client->m_req);
|
||||
BOOST_CHECK_EQUAL(client->m_req->GetState(), HTTPRequest::State::NeedsHeaders);
|
||||
BOOST_CHECK(!HTTPRemoteClient::TryReadRequest(client));
|
||||
BOOST_CHECK_EQUAL(client->GetRequest()->GetState(), HTTPRequest::State::NeedsHeaders);
|
||||
|
||||
for (int i = 0; i < 409; ++i) {
|
||||
client->receive("key:value\n");
|
||||
}
|
||||
client->ReadRequest(*client->m_req);
|
||||
BOOST_CHECK_EQUAL(client->m_req->GetState(), HTTPRequest::State::NeedsHeaders);
|
||||
BOOST_CHECK(!HTTPRemoteClient::TryReadRequest(client));
|
||||
BOOST_CHECK_EQUAL(client->GetRequest()->GetState(), HTTPRequest::State::NeedsHeaders);
|
||||
|
||||
// We're at 819 x 10-byte headers
|
||||
// The limit is 8192, three more bytes should throw.
|
||||
client->receive("k:\n");
|
||||
BOOST_CHECK_EXCEPTION(client->ReadRequest(*client->m_req),
|
||||
std::runtime_error,
|
||||
HasReason{"HTTP headers exceed size limit"});
|
||||
BOOST_CHECK_EQUAL(client->m_req->GetState(), HTTPRequest::State::Error);
|
||||
BOOST_CHECK(!HTTPRemoteClient::TryReadRequest(client));
|
||||
BOOST_CHECK_EQUAL(client->GetRequest()->GetState(), HTTPRequest::State::Error);
|
||||
}
|
||||
{
|
||||
// Client sends chunks that are below the limit but the total is excessive
|
||||
std::shared_ptr<DummyClient> client{std::make_shared<DummyClient>()};
|
||||
client->m_req = std::make_unique<HTTPRequest>(client);
|
||||
client->ReadRequest(*client->m_req);
|
||||
BOOST_CHECK_EQUAL(client->m_req->GetState(), HTTPRequest::State::Init);
|
||||
BOOST_CHECK(!HTTPRemoteClient::TryReadRequest(client));
|
||||
BOOST_CHECK_EQUAL(client->GetRequest()->GetState(), HTTPRequest::State::Init);
|
||||
|
||||
client->receive("POST /huge HTTP/1.0\n");
|
||||
client->ReadRequest(*client->m_req);
|
||||
BOOST_CHECK_EQUAL(client->m_req->GetState(), HTTPRequest::State::NeedsHeaders);
|
||||
BOOST_CHECK(!HTTPRemoteClient::TryReadRequest(client));
|
||||
BOOST_CHECK_EQUAL(client->GetRequest()->GetState(), HTTPRequest::State::NeedsHeaders);
|
||||
|
||||
client->receive("Transfer-Encoding: chunked\n\n");
|
||||
client->ReadRequest(*client->m_req);
|
||||
BOOST_CHECK_EQUAL(client->m_req->GetState(), HTTPRequest::State::NeedsBody);
|
||||
BOOST_CHECK(!HTTPRemoteClient::TryReadRequest(client));
|
||||
BOOST_CHECK_EQUAL(client->GetRequest()->GetState(), HTTPRequest::State::NeedsBody);
|
||||
|
||||
// Send 16-byte chunk
|
||||
client->receive("10\nno auto updates!\n");
|
||||
client->ReadRequest(*client->m_req);
|
||||
BOOST_CHECK_EQUAL(client->m_req->GetState(), HTTPRequest::State::NeedsBody);
|
||||
BOOST_CHECK(!HTTPRemoteClient::TryReadRequest(client));
|
||||
BOOST_CHECK_EQUAL(client->GetRequest()->GetState(), HTTPRequest::State::NeedsBody);
|
||||
|
||||
// The next chunk will be of size 32MiB - 16 + 1, below the limit
|
||||
// on its own but not if it were added to the total cumulative body so far.
|
||||
// We don't need to actually send or prepare this amount of data.
|
||||
client->receive("1fffff1\n");
|
||||
BOOST_CHECK_EXCEPTION(client->ReadRequest(*client->m_req),
|
||||
http_bitcoin::ContentTooLargeError,
|
||||
HasReason{"Chunk will exceed max body size"});
|
||||
BOOST_CHECK_EQUAL(client->m_req->GetState(), HTTPRequest::State::Error);
|
||||
BOOST_CHECK(!HTTPRemoteClient::TryReadRequest(client));
|
||||
BOOST_CHECK_EQUAL(client->GetRequest()->GetState(), HTTPRequest::State::Error);
|
||||
}
|
||||
{
|
||||
// Ensure chunk trailer is parsed over state lines
|
||||
std::shared_ptr<DummyClient> client{std::make_shared<DummyClient>()};
|
||||
client->m_req = std::make_unique<HTTPRequest>(client);
|
||||
BOOST_CHECK(!client->GetRequest());
|
||||
|
||||
// Send a 1-byte chunk then send the 0-chunk with a trailer but no terminal CRLF
|
||||
client->receive("GET / HTTP/1.0\n"
|
||||
@@ -752,29 +740,29 @@ BOOST_AUTO_TEST_CASE(http_request_state_tests)
|
||||
"x\n"
|
||||
"0\n"
|
||||
"Digest: sha-4=deadbeef\n");
|
||||
client->ReadRequest(*client->m_req);
|
||||
BOOST_CHECK_EQUAL(client->m_req->GetState(), HTTPRequest::State::NeedsBody);
|
||||
BOOST_CHECK(!HTTPRemoteClient::TryReadRequest(client));
|
||||
BOOST_CHECK_EQUAL(client->GetRequest()->GetState(), HTTPRequest::State::NeedsBody);
|
||||
|
||||
// Send first part of another trailer line
|
||||
client->receive("Expires:");
|
||||
client->ReadRequest(*client->m_req);
|
||||
BOOST_CHECK_EQUAL(client->m_req->GetState(), HTTPRequest::State::NeedsBody);
|
||||
BOOST_CHECK(!HTTPRemoteClient::TryReadRequest(client));
|
||||
BOOST_CHECK_EQUAL(client->GetRequest()->GetState(), HTTPRequest::State::NeedsBody);
|
||||
|
||||
// Finish the trailer line
|
||||
client->receive("never\n");
|
||||
client->ReadRequest(*client->m_req);
|
||||
BOOST_CHECK_EQUAL(client->m_req->GetState(), HTTPRequest::State::NeedsBody);
|
||||
BOOST_CHECK(!HTTPRemoteClient::TryReadRequest(client));
|
||||
BOOST_CHECK_EQUAL(client->GetRequest()->GetState(), HTTPRequest::State::NeedsBody);
|
||||
|
||||
// Terminate
|
||||
client->receive("\n");
|
||||
client->ReadRequest(*client->m_req);
|
||||
BOOST_CHECK_EQUAL(client->m_req->GetState(), HTTPRequest::State::Complete);
|
||||
BOOST_CHECK_EQUAL(client->m_req->ReadBody(), "x");
|
||||
auto req{HTTPRemoteClient::TryReadRequest(client)};
|
||||
BOOST_CHECK_EQUAL(req->GetState(), HTTPRequest::State::Complete);
|
||||
BOOST_CHECK_EQUAL(req->ReadBody(), "x");
|
||||
}
|
||||
{
|
||||
// Ensure chunk trailer counts towards the headers size limit
|
||||
std::shared_ptr<DummyClient> client{std::make_shared<DummyClient>()};
|
||||
client->m_req = std::make_unique<HTTPRequest>(client);
|
||||
BOOST_CHECK(!client->GetRequest());
|
||||
|
||||
client->receive("POST /huge HTTP/1.0\n"
|
||||
"Transfer-Encoding: chunked\n"); // 27 bytes
|
||||
@@ -785,16 +773,14 @@ BOOST_AUTO_TEST_CASE(http_request_state_tests)
|
||||
"1\n"
|
||||
"x\n"
|
||||
"0\n");
|
||||
client->ReadRequest(*client->m_req);
|
||||
BOOST_CHECK_EQUAL(client->m_req->GetState(), HTTPRequest::State::NeedsBody);
|
||||
BOOST_CHECK(!HTTPRemoteClient::TryReadRequest(client));
|
||||
BOOST_CHECK_EQUAL(client->GetRequest()->GetState(), HTTPRequest::State::NeedsBody);
|
||||
|
||||
// We're in the trailer section with a total of 8188 bytes of headers.
|
||||
// The limit is 8192, five more bytes should throw.
|
||||
client->receive("k:vv\n");
|
||||
BOOST_CHECK_EXCEPTION(client->ReadRequest(*client->m_req),
|
||||
std::runtime_error,
|
||||
HasReason{"HTTP headers exceed size limit"});
|
||||
BOOST_CHECK_EQUAL(client->m_req->GetState(), HTTPRequest::State::Error);
|
||||
BOOST_CHECK(!HTTPRemoteClient::TryReadRequest(client));
|
||||
BOOST_CHECK_EQUAL(client->GetRequest()->GetState(), HTTPRequest::State::Error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user