1#include <xrpl/basics/Log.h>
2#include <xrpl/net/HTTPClient.h>
4#include <boost/algorithm/string/predicate.hpp>
5#include <boost/asio/co_spawn.hpp>
6#include <boost/asio/detached.hpp>
7#include <boost/asio/ip/tcp.hpp>
8#include <boost/asio/use_awaitable.hpp>
9#include <boost/asio/use_future.hpp>
10#include <boost/beast/core.hpp>
11#include <boost/beast/http.hpp>
12#include <boost/beast/version.hpp>
14#include <gtest/gtest.h>
15#include <helpers/TestSink.h>
31 boost::asio::io_context ioc_;
32 boost::asio::ip::tcp::acceptor acceptor_;
33 boost::asio::ip::tcp::endpoint endpoint_;
35 bool finished_{
false};
36 unsigned short port_{0};
41 unsigned int statusCode_{200};
46 TestHTTPServer() : acceptor_(ioc_), j_(
TestSink::instance())
49 endpoint_ = {boost::asio::ip::tcp::v4(), 0};
50 acceptor_.open(endpoint_.protocol());
51 acceptor_.set_option(boost::asio::socket_base::reuse_address(
true));
52 acceptor_.bind(endpoint_);
56 port_ = acceptor_.local_endpoint().port();
59 boost::asio::co_spawn(ioc_,
accept(), boost::asio::detached);
62 TestHTTPServer(TestHTTPServer&&) =
delete;
64 operator=(TestHTTPServer&&) =
delete;
68 XRPL_ASSERT(finished(),
"xrpl::TestHTTPServer::~TestHTTPServer : accept future ready");
71 boost::asio::io_context&
86 customHeaders_[name] = value;
96 setStatusCode(
unsigned int code)
115 boost::asio::awaitable<void>
122 auto socket =
co_await acceptor_.async_accept(boost::asio::use_awaitable);
128 co_await handleConnection(std::move(socket));
133 JLOG(j_.
debug()) <<
"Error: " << e.
what();
141 boost::asio::awaitable<void>
142 handleConnection(boost::asio::ip::tcp::socket socket)
146 boost::beast::flat_buffer buffer;
147 boost::beast::http::request<boost::beast::http::string_body> req;
150 co_await boost::beast::http::async_read(
151 socket, buffer, req, boost::asio::use_awaitable);
154 boost::beast::http::response<boost::beast::http::string_body> res;
155 res.version(req.version());
156 res.result(statusCode_);
157 res.set(boost::beast::http::field::server,
"TestServer");
160 res.body() = responseBody_;
161 res.prepare_payload();
166 for (
auto const& [name, value] : customHeaders_)
168 res.set(name, value);
172 co_await boost::beast::http::async_write(socket, res, boost::asio::use_awaitable);
175 boost::system::error_code shutdownEc;
178 socket.shutdown(boost::asio::ip::tcp::socket::shutdown_send, shutdownEc);
183 JLOG(j_.
debug()) <<
"Connection error: " << e.
what();
205 "" ,
"" ,
false ,
j_ );
220 TestHTTPServer& server,
225 boost::system::error_code& resultError)
235 [&](boost::system::error_code
const& ec,
int status,
std::string const& data) ->
bool {
237 resultStatus = status;
246 while (server.ioc().run_one() != 0)
278 for (
auto const& headerName : headerCases)
280 TestHTTPServer server;
282 server.setResponseBody(testBody);
285 bool completed{
false};
288 boost::system::error_code resultError;
290 bool const testCompleted =
291 runHTTPTest(server,
"/test", completed, resultStatus, resultData, resultError);
293 EXPECT_TRUE(testCompleted);
294 EXPECT_FALSE(resultError);
295 EXPECT_EQ(resultStatus, 200);
296 EXPECT_EQ(resultData, testBody);
302 TestHTTPServer server;
304 server.setResponseBody(testBody);
305 server.setHeader(
"Content-Type",
"text/plain");
307 bool completed{
false};
310 boost::system::error_code resultError;
312 bool const testCompleted =
313 runHTTPTest(server,
"/basic", completed, resultStatus, resultData, resultError);
315 EXPECT_TRUE(testCompleted);
316 EXPECT_FALSE(resultError);
317 EXPECT_EQ(resultStatus, 200);
318 EXPECT_EQ(resultData, testBody);
323 TestHTTPServer server;
324 server.setResponseBody(
"");
325 server.setHeader(
"Content-Length",
"0");
327 bool completed{
false};
330 boost::system::error_code resultError;
332 bool const testCompleted =
333 runHTTPTest(server,
"/empty", completed, resultStatus, resultData, resultError);
335 EXPECT_TRUE(testCompleted);
336 EXPECT_FALSE(resultError);
337 EXPECT_EQ(resultStatus, 200);
338 EXPECT_TRUE(resultData.
empty());
345 for (
auto status : statusCodes)
347 TestHTTPServer server;
348 server.setStatusCode(status);
351 bool completed{
false};
354 boost::system::error_code resultError;
356 bool const testCompleted =
357 runHTTPTest(server,
"/status", completed, resultStatus, resultData, resultError);
359 EXPECT_TRUE(testCompleted);
360 EXPECT_FALSE(resultError);
361 EXPECT_EQ(resultStatus,
static_cast<int>(status));
bool runHTTPTest(TestHTTPServer &server, std::string const &path, bool &completed, int &resultStatus, std::string &resultData, boost::system::error_code &resultError)
A generic endpoint for log messages.
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)
static void cleanupSSLContext()
Destroys the global SSL context created by initializeSSLContext().
static TestSink & instance()
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.