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};
41 unsigned int statusCode_{200};
46 TestHTTPServer() : acceptor_(ioc_), port_(0), 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(socket, buffer, req, boost::asio::use_awaitable);
153 boost::beast::http::response<boost::beast::http::string_body> res;
154 res.version(req.version());
155 res.result(statusCode_);
156 res.set(boost::beast::http::field::server,
"TestServer");
159 res.body() = responseBody_;
160 res.prepare_payload();
165 for (
auto const& [name, value] : customHeaders_)
167 res.set(name, value);
171 co_await boost::beast::http::async_write(socket, res, boost::asio::use_awaitable);
174 boost::system::error_code shutdownEc;
175 socket.shutdown(boost::asio::ip::tcp::socket::shutdown_send, shutdownEc);
180 JLOG(j_.
debug()) <<
"Connection error: " << e.
what();
188 TestHTTPServer& server,
193 boost::system::error_code& resultError)
209 [&](boost::system::error_code
const& ec,
int status,
std::string const& data) ->
bool {
211 resultStatus = status;
220 while (server.ioc().run_one() != 0)
249 for (
auto const& headerName : headerCases)
251 TestHTTPServer server;
253 server.setResponseBody(testBody);
256 bool completed{
false};
259 boost::system::error_code resultError;
261 bool testCompleted = runHTTPTest(server,
"/test", completed, resultStatus, resultData, resultError);
264 EXPECT_TRUE(testCompleted);
265 EXPECT_FALSE(resultError);
266 EXPECT_EQ(resultStatus, 200);
267 EXPECT_EQ(resultData, testBody);
273 TestHTTPServer server;
275 server.setResponseBody(testBody);
276 server.setHeader(
"Content-Type",
"text/plain");
278 bool completed{
false};
281 boost::system::error_code resultError;
283 bool testCompleted = runHTTPTest(server,
"/basic", completed, resultStatus, resultData, resultError);
285 EXPECT_TRUE(testCompleted);
286 EXPECT_FALSE(resultError);
287 EXPECT_EQ(resultStatus, 200);
288 EXPECT_EQ(resultData, testBody);
293 TestHTTPServer server;
294 server.setResponseBody(
"");
295 server.setHeader(
"Content-Length",
"0");
297 bool completed{
false};
300 boost::system::error_code resultError;
302 bool testCompleted = runHTTPTest(server,
"/empty", completed, resultStatus, resultData, resultError);
304 EXPECT_TRUE(testCompleted);
305 EXPECT_FALSE(resultError);
306 EXPECT_EQ(resultStatus, 200);
307 EXPECT_TRUE(resultData.
empty());
314 for (
auto status : statusCodes)
316 TestHTTPServer server;
317 server.setStatusCode(status);
320 bool completed{
false};
323 boost::system::error_code resultError;
325 bool testCompleted = runHTTPTest(server,
"/status", completed, resultStatus, resultData, resultError);
327 EXPECT_TRUE(testCompleted);
328 EXPECT_FALSE(resultError);
329 EXPECT_EQ(resultStatus,
static_cast<int>(status));
A generic endpoint for log messages.
Provides an asynchronous HTTP client implementation with optional SSL.
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 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.