20#include <xrpl/basics/Log.h>
21#include <xrpl/net/HTTPClient.h>
23#include <boost/algorithm/string/predicate.hpp>
24#include <boost/asio/ip/tcp.hpp>
25#include <boost/beast/core.hpp>
26#include <boost/beast/http.hpp>
27#include <boost/beast/version.hpp>
29#include <doctest/doctest.h>
43 boost::asio::io_context ioc_;
44 boost::asio::ip::tcp::acceptor acceptor_;
45 boost::asio::ip::tcp::endpoint endpoint_;
52 unsigned int status_code_{200};
55 TestHTTPServer() : acceptor_(ioc_), port_(0)
58 endpoint_ = {boost::asio::ip::tcp::v4(), 0};
59 acceptor_.open(endpoint_.protocol());
60 acceptor_.set_option(boost::asio::socket_base::reuse_address(
true));
61 acceptor_.bind(endpoint_);
65 port_ = acceptor_.local_endpoint().port();
75 boost::asio::io_context&
90 custom_headers_[name] = value;
96 response_body_ = body;
100 setStatusCode(
unsigned int code)
119 acceptor_.async_accept(
122 [&](boost::system::error_code
const& error,
123 boost::asio::ip::tcp::socket peer) {
129 handleConnection(std::move(peer));
135 handleConnection(boost::asio::ip::tcp::socket socket)
140 boost::beast::flat_buffer buffer;
141 boost::beast::http::request<boost::beast::http::string_body> req;
142 boost::beast::http::read(socket, buffer, req);
145 boost::beast::http::response<boost::beast::http::string_body> res;
146 res.version(req.version());
147 res.result(status_code_);
148 res.set(boost::beast::http::field::server,
"TestServer");
151 for (
auto const& [name, value] : custom_headers_)
153 res.set(name, value);
157 res.body() = response_body_;
158 res.prepare_payload();
162 for (
auto const& [name, value] : custom_headers_)
164 if (boost::iequals(name,
"Content-Length"))
166 res.
erase(boost::beast::http::field::content_length);
167 res.set(name, value);
172 boost::beast::http::write(socket, res);
175 boost::system::error_code ec;
176 socket.shutdown(boost::asio::ip::tcp::socket::shutdown_send, ec);
191 TestHTTPServer& server,
196 boost::system::error_code& result_error)
212 [&](boost::system::error_code
const& ec,
216 result_status = status;
228 if (server.ioc().run_one() == 0)
239TEST_CASE(
"HTTPClient case insensitive Content-Length")
250 for (
auto const& header_name : header_cases)
252 TestHTTPServer server;
254 server.setResponseBody(test_body);
260 boost::system::error_code result_error;
262 bool test_completed = runHTTPTest(
271 CHECK(test_completed);
272 CHECK(!result_error);
273 CHECK(result_status == 200);
274 CHECK(result_data == test_body);
278TEST_CASE(
"HTTPClient basic HTTP request")
280 TestHTTPServer server;
282 server.setResponseBody(test_body);
283 server.setHeader(
"Content-Type",
"text/plain");
288 boost::system::error_code result_error;
290 bool test_completed = runHTTPTest(
291 server,
"/basic", completed, result_status, result_data, result_error);
293 CHECK(test_completed);
294 CHECK(!result_error);
295 CHECK(result_status == 200);
296 CHECK(result_data == test_body);
301 TestHTTPServer server;
302 server.setResponseBody(
"");
303 server.setHeader(
"Content-Length",
"0");
308 boost::system::error_code result_error;
310 bool test_completed = runHTTPTest(
311 server,
"/empty", completed, result_status, result_data, result_error);
313 CHECK(test_completed);
314 CHECK(!result_error);
315 CHECK(result_status == 200);
319TEST_CASE(
"HTTPClient different status codes")
323 for (
auto status : status_codes)
325 TestHTTPServer server;
326 server.setStatusCode(status);
332 boost::system::error_code result_error;
334 bool test_completed = runHTTPTest(
342 CHECK(test_completed);
343 CHECK(!result_error);
344 CHECK(result_status ==
static_cast<int>(status));
A generic endpoint for log messages.
static Sink & getNullSink()
Returns a Sink which does nothing.
static void initializeSSLContext(std::string const &sslVerifyDir, std::string const &sslVerifyFile, bool sslVerify, beast::Journal j)
static void get(bool bSSL, boost::asio::io_context &io_context, std::deque< std::string > deqSites, unsigned short const port, std::string const &strPath, std::size_t responseMax, std::chrono::seconds timeout, std::function< bool(boost::system::error_code const &ecResult, int iStatus, std::string const &strData)> complete, beast::Journal &j)
Json::Value accept(jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
TEST_CASE("construct and compare Json::StaticString")