13#include <boost/test/unit_test.hpp>
28 "Connection: close\r\n"
29 "Content-Type: application/json\r\n"
30 "Authorization: Basic X19jb29raWVfXzo5OGQ5ODQ3MWNmNjg0NzAzYTkzN2EzNzk0ZDFlODQ1NjZmYTRkZjJiMzFkYjhhODI4ZGY4MjVjOTg5ZGI4OTVl\r\n"
31 "Content-Length: 46\r\n"
33 R
"({"method":"getblockcount","params":[],"id":1})""\n";
35BOOST_FIXTURE_TEST_SUITE(httpserver_tests, SocketTestingSetup)
37BOOST_AUTO_TEST_CASE(test_query_parameters)
41 // Tolerate a URI with invalid characters (% not followed by hex digits)
42 uri = "/rest/endpoint/someresource.json?p1=v1&p2=v2%";
43 BOOST_CHECK_EQUAL(GetQueryParameterFromUri(uri, "p1"), "v1");
44 BOOST_CHECK_EQUAL(GetQueryParameterFromUri(uri, "p2"), "v2%");
47 uri = "localhost:8080/rest/headers/someresource.json";
48 BOOST_CHECK(!GetQueryParameterFromUri(uri, "p1"));
51 uri = "localhost:8080/rest/endpoint/someresource.json?p1=v1";
52 BOOST_CHECK_EQUAL(GetQueryParameterFromUri(uri, "p1"), "v1");
53 BOOST_CHECK(!GetQueryParameterFromUri(uri, "p2"));
55 // Multiple parameters
56 uri = "/rest/endpoint/someresource.json?p1=v1&p2=v2";
57 BOOST_CHECK_EQUAL(GetQueryParameterFromUri(uri, "p1"), "v1");
58 BOOST_CHECK_EQUAL(GetQueryParameterFromUri(uri, "p2"), "v2");
60 // If the query string contains duplicate keys, the first value is returned
61 uri = "/rest/endpoint/someresource.json?p1=v1&p1=v2";
62 BOOST_CHECK_EQUAL(GetQueryParameterFromUri(uri, "p1"), "v1");
64 // Invalid query string syntax is the same as not having parameters
65 uri = "/rest/endpoint/someresource.json&p1=v1&p2=v2";
66 BOOST_CHECK(!GetQueryParameterFromUri(uri, "p1"));
68 // Multiple parameters, some characters encoded
69 uri = "/rest/endpoint/someresource.json?p1=v1%20&p2=100%25";
70 BOOST_CHECK_EQUAL(GetQueryParameterFromUri(uri, "p1"), "v1 ");
71 BOOST_CHECK_EQUAL(GetQueryParameterFromUri(uri, "p2"), "100%");
73 // Encoded query delimiters are part of the parameter value, not structure.
74 uri = "/rest/endpoint/someresource.json?p=a%26b%3Dc%23frag&other=x";
75 BOOST_CHECK_EQUAL(GetQueryParameterFromUri(uri, "p"), "a&b=c#frag");
76 BOOST_CHECK_EQUAL(GetQueryParameterFromUri(uri, "other"), "x");
78 // An encoded question mark in the path does not introduce a query section.
79 uri = "/rest/endpoint/someresource.json%3Fp1%3Dv1%26p2%3D100%25";
80 BOOST_CHECK(!GetQueryParameterFromUri(uri, "p1"));
83BOOST_AUTO_TEST_CASE(http_headers_tests)
86 // Writing response headers
87 HTTPHeaders headers{};
88 BOOST_CHECK(!headers.FindFirst("Cache-Control"));
89 headers.Write("Cache-Control", "no-cache");
90 // Check case-insensitive key matching
91 BOOST_CHECK_EQUAL(headers.FindFirst("Cache-Control"), "no-cache");
92 BOOST_CHECK_EQUAL(headers.FindFirst("cache-control"), "no-cache");
93 // Additional values are appended, compared case-insensitive
94 headers.Write("cache-control", "max-age=60");
95 BOOST_CHECK_EQUAL(headers.FindFirst("Cache-Control"), "no-cache");
96 BOOST_CHECK((headers.FindAll("Cache-Control") == std::vector<std::string_view>{"no-cache", "max-age=60"}));
98 headers.Write("Pie", "apple");
99 headers.Write("Sandwich", "ham");
100 headers.Write("Coffee", "black");
101 BOOST_CHECK_EQUAL(headers.FindFirst("Pie"), "apple");
103 headers.RemoveAll("Pie");
104 BOOST_CHECK(!headers.FindFirst("Pie"));
105 // Combine for transmission
106 std::string headers_string{headers.Stringify()};
107 BOOST_CHECK_EQUAL(headers_string, "Cache-Control: no-cache\r\n"
108 "cache-control: max-age=60\r\n"
114 // Reading request headers captured from bitcoin-cli
115 constexpr std::string_view bitcoin_cli_headers = "Host: 127.0.0.1\r\n"
116 "Connection: close\r\n"
117 "Content-Type: application/json\r\n"
118 "Authorization: Basic X19jb29raWVfXzozYzJkNTAxNDFlMGJiYmVhMTI5ODg3NzI5MTM3NTRmNThkNjc2OWMwZTYxZjgzNTgyNzEwYTY1OGRkYjVmZGQ3\r\n"
119 "Content-Length: 46\r\n";
120 util::LineReader reader(bitcoin_cli_headers, /*max_line_length=*/MAX_HEADERS_SIZE);
121 HTTPHeaders headers{};
122 headers.Read(reader);
123 BOOST_CHECK_EQUAL(headers.FindFirst("Host"), "127.0.0.1");
124 BOOST_CHECK_EQUAL(headers.FindFirst("Connection"), "close");
125 BOOST_CHECK_EQUAL(headers.FindFirst("Content-Type"), "application/json");
126 BOOST_CHECK_EQUAL(headers.FindFirst("Authorization"), "Basic X19jb29raWVfXzozYzJkNTAxNDFlMGJiYmVhMTI5ODg3NzI5MTM3NTRmNThkNjc2OWMwZTYxZjgzNTgyNzEwYTY1OGRkYjVmZGQ3");
127 BOOST_CHECK_EQUAL(headers.FindFirst("Content-Length"), "46");
128 BOOST_CHECK(!headers.FindFirst("Pizza"));
130 // Ensure invalid headers are rejected
133 util::LineReader reader{"key value\n", /*max_line_length=*/MAX_HEADERS_SIZE};
134 BOOST_CHECK_EXCEPTION(HTTPHeaders{}.Read(reader), std::runtime_error, HasReason{"HTTP header missing colon (:)"});
164 lines.reserve(820 * 10);
165 for (
int i = 0; i < 820; ++i) {
166 lines.append(
"key:value\n");
196 "HTTP/1.1 200 OK\r\n"
197 "Content-Length: 41\r\n"
224 // Malformed: no spaces between data
226 LineReader reader("GET/HTTP/1.0\r\nHost: 127.0.0.1\r\n\r\n", MAX_HEADERS_SIZE);
227 BOOST_CHECK_EXCEPTION(req.LoadControlData(reader), std::runtime_error, HasReason{"HTTP request line too short"});
230 // Malformed: too many spaces
232 LineReader reader("GET / HTTP / 1.0\r\nHost: 127.0.0.1\r\n\r\n", MAX_HEADERS_SIZE);
233 BOOST_CHECK_EXCEPTION(req.LoadControlData(reader), std::runtime_error, HasReason{"HTTP request line malformed"});
236 // Malformed: slash missing before version
238 LineReader reader("GET / HTTP1.0\r\nHost: 127.0.0.1\r\n\r\n", MAX_HEADERS_SIZE);
239 BOOST_CHECK_EXCEPTION(req.LoadControlData(reader), std::runtime_error, HasReason{"HTTP request line too short"});
242 // Malformed: no decimal in version
244 LineReader reader("GET / HTTP/11\r\nHost: 127.0.0.1\r\n\r\n", MAX_HEADERS_SIZE);
245 BOOST_CHECK_EXCEPTION(req.LoadControlData(reader), std::runtime_error, HasReason{"HTTP request line too short"});
248 // Malformed: version is not a number
250 LineReader reader("GET / HTTP/1.x\r\nHost: 127.0.0.1\r\n\r\n", MAX_HEADERS_SIZE);
251 BOOST_CHECK_EXCEPTION(req.LoadControlData(reader), std::runtime_error, HasReason{"HTTP bad version"});
254 // Malformed: version is out of range
256 LineReader reader("GET / HTTP/2.0\r\nHost: 127.0.0.1\r\n\r\n", MAX_HEADERS_SIZE);
257 BOOST_CHECK_EXCEPTION(req.LoadControlData(reader), std::runtime_error, HasReason{"HTTP bad version"});
260 // Malformed: version is out of range
262 LineReader reader("GET / HTTP/0.9\r\nHost: 127.0.0.1\r\n\r\n", MAX_HEADERS_SIZE);
263 BOOST_CHECK_EXCEPTION(req.LoadControlData(reader), std::runtime_error, HasReason{"HTTP bad version"});
266 // Malformed: version is out of range
268 LineReader reader("GET / HTTP/-1.0\r\nHost: 127.0.0.1\r\n\r\n", MAX_HEADERS_SIZE);
269 BOOST_CHECK_EXCEPTION(req.LoadControlData(reader), std::runtime_error, HasReason{"HTTP bad version"});
272 // Malformed: version is not exactly two integers and a dot
274 LineReader reader("GET / HTTP/1.00\r\nHost: 127.0.0.1\r\n\r\n", MAX_HEADERS_SIZE);
275 BOOST_CHECK_EXCEPTION(req.LoadControlData(reader), std::runtime_error, HasReason{"HTTP bad version"});
278 // Malformed: contains NUL
280 LineReader 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};
281 BOOST_CHECK_EXCEPTION(req.LoadControlData(reader), std::runtime_error, HasReason{"Invalid request line contains NUL"});
284 // Malformed: differing Content-Length values, case insensitive
285 constexpr std::string_view differing_length = "GET / HTTP/1.1\n"
287 "Content-Length: 8\n"
288 "content-length: 9\n\n"
291 util::LineReader reader{differing_length, /*max_line_length=*/MAX_HEADERS_SIZE};
292 BOOST_CHECK(req.LoadControlData(reader));
293 BOOST_CHECK(req.LoadHeaders(reader));
294 BOOST_CHECK_EXCEPTION(req.LoadBody(reader), std::runtime_error, HasReason{"Differing Content-Length values"});
297 // Ok: multiple same Content-Length values
298 constexpr std::string_view differing_length = "GET / HTTP/1.1\n"
300 "Content-Length: 8\n"
301 "content-length: 8\n\n"
304 util::LineReader reader{differing_length, /*max_line_length=*/MAX_HEADERS_SIZE};
305 BOOST_CHECK(req.LoadControlData(reader));
306 BOOST_CHECK(req.LoadHeaders(reader));
307 BOOST_CHECK(req.LoadBody(reader));
312 LineReader reader("GET / HTTP/1.0\r\nHost: 127.0.0.1\r\n\r\n", MAX_HEADERS_SIZE);
313 BOOST_CHECK(req.LoadControlData(reader));
314 BOOST_CHECK(req.LoadHeaders(reader));
315 BOOST_CHECK(req.LoadBody(reader));
316 BOOST_CHECK_EQUAL(req.m_method, HTTPRequestMethod::GET);
317 BOOST_CHECK_EQUAL(req.m_target, "/");
318 BOOST_CHECK_EQUAL(req.m_version.major, 1);
319 BOOST_CHECK_EQUAL(req.m_version.minor, 0);
320 BOOST_CHECK_EQUAL(req.m_headers.FindFirst("Host"), "127.0.0.1");
322 BOOST_CHECK_EQUAL(req.m_body.size(), 0);
325 // Malformed: missing colon
327 LineReader reader("GET / HTTP/1.0\r\nHost=127.0.0.1\r\n\r\n", MAX_HEADERS_SIZE);
328 BOOST_CHECK(req.LoadControlData(reader));
329 BOOST_CHECK_EXCEPTION(req.LoadHeaders(reader), std::runtime_error, HasReason{"HTTP header missing colon (:)"});
369 std::string huge_body(excessive_size,
'x');
370 const std::string request{
"GET / HTTP/1.0\r\nContent-Length: " +
util::ToString(excessive_size) +
"\r\n\r\n" + std::move(huge_body)};
380 const std::string request{
"GET / HTTP/1.0\r\nContent-Length: " +
util::ToString(
MAX_BODY_SIZE) +
"\r\n\r\n" + std::move(max_body)};
399 std::string_view ok_chunked =
"GET / HTTP/1.0\n"
400 "Transfer-Encoding: chunked\n"
403 R
"({"method":"getbl)""\n"
408 LineReader reader(ok_chunked, MAX_HEADERS_SIZE);
409 BOOST_CHECK(req.LoadControlData(reader));
410 BOOST_CHECK(req.LoadHeaders(reader));
411 BOOST_CHECK(req.LoadBody(reader));
412 BOOST_CHECK_EQUAL(req.m_body, R"({"method":"getblockcount"})");
417 std::string_view excessive_chunk_size =
"GET / HTTP/1.0\n"
418 "Transfer-Encoding: chunked\n"
421 R
"({"method":"getbl)""\n"
426 LineReader reader(excessive_chunk_size, MAX_HEADERS_SIZE);
427 BOOST_CHECK(req.LoadControlData(reader));
428 BOOST_CHECK(req.LoadHeaders(reader));
429 BOOST_CHECK_EXCEPTION(req.LoadBody(reader), http_bitcoin::ContentTooLargeError, HasReason{"Chunk will exceed max body size"});
432 // Allow (but ignore) Chunk Extensions
434 std::string_view ok_chunked = "GET / HTTP/1.0\n"
435 "Transfer-Encoding: chunked\n"
437 "10;sha256=715790e8a3b09d704ac9641f42d183a5ebc5fd939663de23da548519ac2165e5\n"
438 R"({"method":"getbl)""\n"
441 "0;why;would;anyone;do;this;\n"
442 "Expires: Wed, 21 Oct 2026 07:28:00 GMT\n"
444 LineReader reader(ok_chunked, MAX_HEADERS_SIZE);
445 BOOST_CHECK(req.LoadControlData(reader));
446 BOOST_CHECK(req.LoadHeaders(reader));
447 BOOST_CHECK(req.LoadBody(reader));
448 BOOST_CHECK_EQUAL(req.m_body, R"({"method":"getblockcount"})");
455 std::string_view invalid_chunked =
"GET / HTTP/1.0\n"
456 "Transfer-Encoding: chunked\n"
459 R
"({"method":"getbl)""\n"
464 LineReader reader(invalid_chunked, MAX_HEADERS_SIZE);
465 BOOST_CHECK(req.LoadControlData(reader));
466 BOOST_CHECK(req.LoadHeaders(reader));
467 BOOST_CHECK_EXCEPTION(req.LoadBody(reader), std::runtime_error, HasReason{"Cannot parse chunk length value"});
470 // Invalid "chunked" transfer, missing chunk termination \n
472 std::string_view invalid_chunked = "GET / HTTP/1.0\n"
473 "Transfer-Encoding: chunked\n"
476 R"({"method":"getbl)"
489 std::string delayed_chunked =
"GET / HTTP/1.0\n"
490 "Transfer-Encoding: chunked\n"
493 R
"({"method":"getbl)""\n"
501 delayed_chunked +=
"\n0\n\n";
517 Mutex requests_mutex;
518 std::deque<std::unique_ptr<HTTPRequest>> requests;
519 auto StoreRequest = [&](std::unique_ptr<HTTPRequest>&& req) {
520 LOCK(requests_mutex);
521 requests.push_back(std::move(req));
528 CService onion_address{
Lookup(
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaam2dqd.onion", 0,
false).value()};
529 auto result{server.BindAndStartListening(onion_address)};
530 BOOST_REQUIRE(!result);
531 BOOST_CHECK_EQUAL(result.error(),
"Bind address family for aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaam2dqd.onion:0 not supported");
538 BOOST_REQUIRE_EQUAL(server.GetListeningSocketCount(), 0);
540 BOOST_REQUIRE(server.BindAndStartListening(addr_bind));
542 BOOST_REQUIRE_EQUAL(server.GetListeningSocketCount(), 1);
545 server.StartSocketsThreads();
553 std::shared_ptr<DynSock::Pipes> mock_client_socket_pipes{ConnectClient(std::as_bytes(std::span(
full_request)))};
557 while (server.GetConnectionsCount() < 1) {
558 std::this_thread::sleep_for(10ms);
559 BOOST_REQUIRE(--attempts > 0);
563 std::shared_ptr<HTTPRemoteClient> client;
571 LOCK(requests_mutex);
573 if (requests.size() == 1) {
575 BOOST_CHECK_EQUAL(requests.front()->m_body, R
"({"method":"getblockcount","params":[],"id":1})""\n");
576 BOOST_CHECK_EQUAL(requests.front()->GetPeer().ToStringAddrPort(), "5.5.5.5:6789");
578 // Inspect the connection pointed to from the request
579 client = requests.front()->m_client;
580 BOOST_CHECK_EQUAL(client->m_origin, "5.5.5.5:6789");
582 // Respond to request
583 requests.front()->WriteReply(HTTP_OK, "874140\n");
588 std::this_thread::sleep_for(10ms);
589 BOOST_REQUIRE(--attempts > 0);
592 // Check the sent response from the mock client at the other end of the mock socket
594 // Wait up to one minute for all the bytes to appear in the "send" pipe.
595 char buf[0x10000] = {};
599 ssize_t bytes_read = mock_client_socket_pipes->send.GetBytes(buf, sizeof(buf), 0);
600 if (bytes_read > 0) {
601 actual.append(buf, bytes_read);
602 if (actual.length() == 146) {
606 std::this_thread::sleep_for(10ms);
609 BOOST_CHECK(actual.starts_with("HTTP/1.1 200 OK\r\n"));
610 BOOST_CHECK(actual.ends_with("\r\n874140\n"));
611 // Headers can be sorted in any order, and will be, since we use unordered_map
612 BOOST_CHECK(actual.find("Connection: close\r\n") != std::string::npos);
613 BOOST_CHECK(actual.find("Content-Length: 7\r\n") != std::string::npos);
614 BOOST_CHECK(actual.find("Content-Type: text/html; charset=ISO-8859-1\r\n") != std::string::npos);
615 BOOST_CHECK(actual.find("Date: Wed, 11 Dec 2024 00:47:09 GMT\r\n") != std::string::npos);
617 // Wait up to one minute for connection to be automatically closed, because
618 // keep-alive was not set by the client and we are done responding to their request.
620 while (server.GetConnectionsCount() != 0) {
621 std::this_thread::sleep_for(10ms);
622 BOOST_REQUIRE(--attempts > 0);
625 // Stop the I/O loop and shutdown
626 server.InterruptNet();
627 // Wait for I/O loop to finish, after all connected sockets are closed
628 server.JoinSocketsThreads();
629 // Close all listening sockets
630 server.StopListening();
633BOOST_AUTO_TEST_CASE(http_socket_error_tests)
635 // Create a tiny threadpool for the HTTPRequest handler
636 ThreadPool workers("http");
639 // Hard-code the server's request handler to respond to each request with
640 // an incremented block count. Handle the replies in the worker thread.
641 std::atomic<int> height{0};
642 HTTPServer server{[&](std::shared_ptr<HTTPRequest> req) {
643 auto item = [req, &height]() {
644 const int h = height.fetch_add(1);
645 req->WriteReply(HTTP_OK, strprintf("height: %d\n", h));
647 // Can't call BOOST_REQUIRE from worker thread
648 Assert(workers.Submit(std::move(item)));
651 // All replies will be the same size
652 static constexpr std::size_t reply_length = std::string_view{
653 "HTTP/1.1 200 OK\r\n"
654 "Date: Thu, 01 Jan 2026 00:00:00 GMT\r\n" // All RFC1123 dates are 29 characters
655 "Content-Length: 10\r\n"
656 "Content-Type: text/html; charset=ISO-8859-1\r\n"
670 class ErrorSock : public DynSock
673 explicit ErrorSock(std::shared_ptr<Pipes> pipes) : DynSock{std::move(pipes)} {}
674 DynSock& operator=(Sock&&) override { assert(false); return *this; }
676 ssize_t Send(const void* buf, size_t len, int flags) const override
678 if (len <= reply_length && !m_have_sent) {
680 WSASetLastError(WSAEWOULDBLOCK);
687 return DynSock::Send(buf, len, flags);
691 mutable bool m_have_sent{false};
694 // Simpler server startup than the last test
695 CService addr_bind{Lookup("0.0.0.0", /*portDefault=*/0, /*fAllowLookup=*/false).value()};
696 BOOST_REQUIRE(server.BindAndStartListening(addr_bind));
697 server.StartSocketsThreads();
699 // Prepare initial requests
700 int num_requests = 2;
701 // Use keep-alive so the server holds the connection open for all requests.
702 std::string keepalive_request{full_request};
703 keepalive_request.replace(keepalive_request.find("Connection: close"), 17, "Connection: keep-alive");
704 // Combine all requests so they are read from the socket on a single iteration of the I/O loop
705 std::string all_requests;
706 for (int i = 0; i < num_requests; i++) {
707 all_requests += keepalive_request;
710 // Watch the log messages to ensure that the first two replies were sent
711 // together. This indicates the non-optimistic send path was used
712 // because a reply was already sitting in the send buffer when a second reply
714 DebugLogHelper find_two_replies{strprintf("Sent %d bytes to client", reply_length * 2),
715 [&](const std::string* s) {
718 // Last reply should be sent on its own by optimistic send path, because
719 // the send buffer was empty when the reply was written.
720 DebugLogHelper find_one_reply{strprintf("Sent %d bytes to client", reply_length),
721 [&](const std::string* s) {
725 // Connect the ErrorSock as mock client with the preloaded data and get a handle on the I/O pipes
726 std::shared_ptr<ErrorSock::Pipes> mock_client_socket_pipes{
727 ConnectClient<ErrorSock>(std::as_bytes(std::span(all_requests)))
730 // Wait up to one minute for the last reply from the server
732 char buf[0x10000] = {};
736 ssize_t bytes_read = mock_client_socket_pipes->send.GetBytes(buf, sizeof(buf), 0);
737 if (bytes_read > 0) {
738 actual.append(buf, bytes_read);
739 if (actual.find(strprintf("height: %d", num_requests - 1)) != std::string::npos) {
743 std::this_thread::sleep_for(10ms);
747 // Send the third request.
748 // If there was a race between WriteReply() in the worker thread setting m_send_ready=true
749 // and SocketHandlerConnected() in the I/O thread flushing the send buffer,
750 // then the socket would be stuck in write mode with nothing to write,
751 // the server would never read from the socket, and this request would time out.
752 // Wait a second to ensure both the worker thread and I/O thread are idle.
753 // If we send the next request too soon it might get accepted by the server before
754 // it gets wedged shut.
755 std::this_thread::sleep_for(1000ms);
756 mock_client_socket_pipes->recv.PushBytes(keepalive_request.data(), keepalive_request.size());
759 // Wait up to one minute for reply
763 ssize_t bytes_read = mock_client_socket_pipes->send.GetBytes(buf, sizeof(buf), 0);
764 if (bytes_read > 0) {
765 actual.append(buf, bytes_read);
766 if (actual.find(strprintf("height: %d", num_requests - 1)) != std::string::npos) {
770 std::this_thread::sleep_for(10ms);
774 // All replies were received
775 for (int i = 0; i < num_requests; i++) {
776 BOOST_REQUIRE(actual.find(strprintf("height: %d", i)) != std::string::npos);
779 // Close the keep-alive connection
780 server.DisconnectAllClients();
784 server.InterruptNet();
785 server.JoinSocketsThreads();
786 server.StopListening();
789BOOST_AUTO_TEST_SUITE_END()
A combination of a network address (CNetAddr) and a (TCP) port.
BOOST_CHECK_EXCEPTION predicates to check the specific validation error.
std::string GetURI() const
HTTPRequestMethod m_method
bool LoadHeaders(LineReader &reader)
bool LoadControlData(LineReader &reader)
Methods that attempt to parse HTTP request fields line-by-line from a receive buffer.
HTTPRequestMethod GetRequestMethod() const
bool LoadBody(LineReader &reader)
std::string StringifyHeaders() const
BOOST_AUTO_TEST_CASE(http_response_tests)
std::string_view excessive_headers
BOOST_CHECK_GT(excessive_headers.size(), MAX_HEADERS_SIZE)
BOOST_CHECK_EQUAL(headers.FindFirst("key"), "value")
BOOST_CHECK_EXCEPTION(HTTPHeaders{}.Read(reader), std::runtime_error, HasReason{"Empty HTTP header name"})
constexpr std::string_view full_request
std::optional< std::string > GetQueryParameterFromUri(const std::string_view uri, const std::string_view key)
constexpr uint64_t MAX_BODY_SIZE
Maximum size of an HTTP request body.
constexpr size_t MAX_HEADERS_SIZE
Maximum size of each headers line in an HTTP request, also the maximum size of all headers total.
std::string ToString(const T &t)
Locale-independent version of std::to_string.
std::vector< CService > Lookup(const std::string &name, uint16_t portDefault, bool fAllowLookup, unsigned int nMaxSolutions, DNSLookupFn dns_lookup_function)
Resolve a service string to its corresponding service.
#define BOOST_CHECK(expr)
Thrown when a request body exceeds MAX_BODY_SIZE (or will exceed, in chunked transfer) so the server ...
uint8_t major
Default HTTP protocol version 1.1 is used by error responses when a request is unreadable.
size_t Remaining() const
Returns remaining size of bytes in buffer.
void SetMockTime(int64_t nMockTimeIn)
DEPRECATED Use SetMockTime with chrono type.