Compare commits

...

2 Commits

Author SHA1 Message Date
JCW
cea9894925 Fix the deadlock issue
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2026-01-20 14:10:39 +00:00
JCW
a4b6705584 Add a debug sink
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2026-01-20 12:58:18 +00:00
4 changed files with 134 additions and 50 deletions

View File

@@ -6,9 +6,19 @@ find_package(GTest REQUIRED)
# Custom target for all tests defined in this file
add_custom_target(xrpl.tests)
# Test helpers
add_library(xrpl.helpers.test STATIC)
target_sources(xrpl.helpers.test PRIVATE
helpers/DebugSink.cpp
)
target_include_directories(xrpl.helpers.test PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
)
target_link_libraries(xrpl.helpers.test PRIVATE xrpl.libxrpl)
# Common library dependencies for the rest of the tests.
add_library(xrpl.imports.test INTERFACE)
target_link_libraries(xrpl.imports.test INTERFACE gtest::gtest xrpl.libxrpl)
target_link_libraries(xrpl.imports.test INTERFACE gtest::gtest xrpl.libxrpl xrpl.helpers.test)
# One test for each module.
xrpl_add_test(basics)

View File

@@ -0,0 +1,28 @@
#include <helpers/DebugSink.h>
#include <iostream>
namespace xrpl {
DebugSink::DebugSink(beast::severities::Severity threshold)
: Sink(threshold, false)
{
}
void
DebugSink::write(beast::severities::Severity level, std::string const& text)
{
if (level < threshold())
return;
writeAlways(level, text);
}
void
DebugSink::writeAlways(
beast::severities::Severity level,
std::string const& text)
{
std::cerr << text << std::endl;
}
} // namespace xrpl

View File

@@ -0,0 +1,28 @@
#ifndef XRPL_DEBUGSINK_H
#define XRPL_DEBUGSINK_H
#include <xrpl/beast/utility/Journal.h>
namespace xrpl {
class DebugSink : public beast::Journal::Sink
{
public:
static DebugSink&
instance()
{
static DebugSink _;
return _;
}
DebugSink(
beast::severities::Severity threshold = beast::severities::kDebug);
void
write(beast::severities::Severity level, std::string const& text) override;
void
writeAlways(beast::severities::Severity level, std::string const& text)
override;
};
} // namespace xrpl
#endif // XRPL_DEBUGSINK_H

View File

@@ -7,10 +7,13 @@
#include <boost/beast/http.hpp>
#include <boost/beast/version.hpp>
#include "../helpers/DebugSink.h"
#include <gtest/gtest.h>
#include <helpers/DebugSink.h>
#include <atomic>
#include <map>
#include <memory>
#include <thread>
using namespace xrpl;
@@ -28,9 +31,9 @@ private:
unsigned short port_;
// Custom headers to return
std::map<std::string, std::string> custom_headers_;
std::string response_body_;
unsigned int status_code_{200};
std::map<std::string, std::string> customHeaders_;
std::string responseBody_;
unsigned int statusCode_{200};
public:
TestHTTPServer() : acceptor_(ioc_), port_(0)
@@ -68,19 +71,19 @@ public:
void
setHeader(std::string const& name, std::string const& value)
{
custom_headers_[name] = value;
customHeaders_[name] = value;
}
void
setResponseBody(std::string const& body)
{
response_body_ = body;
responseBody_ = body;
}
void
setStatusCode(unsigned int code)
{
status_code_ = code;
statusCode_ = code;
}
private:
@@ -115,54 +118,69 @@ private:
void
handleConnection(boost::asio::ip::tcp::socket socket)
{
try
{
// Read the HTTP request
boost::beast::flat_buffer buffer;
boost::beast::http::request<boost::beast::http::string_body> req;
boost::beast::http::read(socket, buffer, req);
// Use async operations to avoid blocking the io_context thread
// Use shared_ptr to keep objects alive during async operations
auto socketPtr =
std::make_shared<boost::asio::ip::tcp::socket>(std::move(socket));
auto buffer = std::make_shared<boost::beast::flat_buffer>();
auto req = std::make_shared<
boost::beast::http::request<boost::beast::http::string_body>>();
// Create response
boost::beast::http::response<boost::beast::http::string_body> res;
res.version(req.version());
res.result(status_code_);
res.set(boost::beast::http::field::server, "TestServer");
// Add custom headers
for (auto const& [name, value] : custom_headers_)
{
res.set(name, value);
}
// Set body and prepare payload first
res.body() = response_body_;
res.prepare_payload();
// Override Content-Length with custom headers after prepare_payload
// This allows us to test case-insensitive header parsing
for (auto const& [name, value] : custom_headers_)
{
if (boost::iequals(name, "Content-Length"))
// Read the HTTP request asynchronously
boost::beast::http::async_read(
*socketPtr,
*buffer,
*req,
[this, socketPtr, buffer, req](
boost::beast::error_code ec, std::size_t) {
if (ec)
{
res.erase(boost::beast::http::field::content_length);
res.set(name, value);
// Error reading, just close the connection
return;
}
}
// Send response
boost::beast::http::write(socket, res);
// Create response
auto res = std::make_shared<boost::beast::http::response<
boost::beast::http::string_body>>();
res->version(req->version());
res->result(statusCode_);
res->set(boost::beast::http::field::server, "TestServer");
// Shutdown socket gracefully
boost::system::error_code ec;
socket.shutdown(boost::asio::ip::tcp::socket::shutdown_send, ec);
}
catch (std::exception const&)
{
// Connection handling errors are expected
}
// Add custom headers
for (auto const& [name, value] : customHeaders_)
{
res->set(name, value);
}
if (running_)
accept();
// Set body and prepare payload first
res->body() = responseBody_;
res->prepare_payload();
// Override Content-Length with custom headers after
// prepare_payload This allows us to test case-insensitive
// header parsing
for (auto const& [name, value] : customHeaders_)
{
if (boost::iequals(name, "Content-Length"))
{
res->erase(boost::beast::http::field::content_length);
res->set(name, value);
}
}
// Send response asynchronously
boost::beast::http::async_write(
*socketPtr,
*res,
[socketPtr, res](boost::beast::error_code ec, std::size_t) {
// Shutdown socket gracefully
boost::system::error_code shutdownEc;
socketPtr->shutdown(
boost::asio::ip::tcp::socket::shutdown_send,
shutdownEc);
// Socket will close when shared_ptr is destroyed
});
});
}
};
@@ -177,7 +195,7 @@ runHTTPTest(
boost::system::error_code& result_error)
{
// Create a null journal for testing
beast::Journal j{beast::Journal::getNullSink()};
beast::Journal j{DebugSink::instance()};
// Initialize HTTPClient SSL context
HTTPClient::initializeSSLContext("", "", false, j);