mirror of
https://github.com/XRPLF/clio.git
synced 2026-06-03 00:36:44 +00:00
fix: Add sending queue to ng web server (#2273)
This commit is contained in:
@@ -18,6 +18,7 @@
|
||||
//==============================================================================
|
||||
|
||||
#include "util/AsioContextTestFixture.hpp"
|
||||
#include "util/CoroutineGroup.hpp"
|
||||
#include "util/Taggable.hpp"
|
||||
#include "util/TestHttpClient.hpp"
|
||||
#include "util/TestHttpServer.hpp"
|
||||
@@ -42,8 +43,10 @@
|
||||
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <ranges>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
using namespace web::ng::impl;
|
||||
@@ -52,16 +55,16 @@ using namespace util::config;
|
||||
namespace http = boost::beast::http;
|
||||
|
||||
struct HttpConnectionTests : SyncAsioContextTest {
|
||||
PlainHttpConnection
|
||||
std::unique_ptr<PlainHttpConnection>
|
||||
acceptConnection(boost::asio::yield_context yield)
|
||||
{
|
||||
auto expectedSocket = httpServer_.accept(yield);
|
||||
[&]() { ASSERT_TRUE(expectedSocket.has_value()) << expectedSocket.error().message(); }();
|
||||
auto ip = expectedSocket->remote_endpoint().address().to_string();
|
||||
PlainHttpConnection connection{
|
||||
auto connection = std::make_unique<PlainHttpConnection>(
|
||||
std::move(expectedSocket).value(), std::move(ip), boost::beast::flat_buffer{}, tagDecoratorFactory_
|
||||
};
|
||||
connection.setTimeout(std::chrono::milliseconds{100});
|
||||
);
|
||||
connection->setTimeout(std::chrono::milliseconds{100});
|
||||
return connection;
|
||||
}
|
||||
|
||||
@@ -83,7 +86,7 @@ TEST_F(HttpConnectionTests, wasUpgraded)
|
||||
|
||||
runSpawn([this](boost::asio::yield_context yield) {
|
||||
auto connection = acceptConnection(yield);
|
||||
EXPECT_FALSE(connection.wasUpgraded());
|
||||
EXPECT_FALSE(connection->wasUpgraded());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -102,7 +105,7 @@ TEST_F(HttpConnectionTests, Receive)
|
||||
runSpawn([this](boost::asio::yield_context yield) {
|
||||
auto connection = acceptConnection(yield);
|
||||
|
||||
auto expectedRequest = connection.receive(yield);
|
||||
auto expectedRequest = connection->receive(yield);
|
||||
ASSERT_TRUE(expectedRequest.has_value()) << expectedRequest.error().message();
|
||||
ASSERT_TRUE(expectedRequest->isHttp());
|
||||
|
||||
@@ -126,8 +129,8 @@ TEST_F(HttpConnectionTests, ReceiveTimeout)
|
||||
|
||||
runSpawn([this](boost::asio::yield_context yield) {
|
||||
auto connection = acceptConnection(yield);
|
||||
connection.setTimeout(std::chrono::milliseconds{1});
|
||||
auto expectedRequest = connection.receive(yield);
|
||||
connection->setTimeout(std::chrono::milliseconds{1});
|
||||
auto expectedRequest = connection->receive(yield);
|
||||
EXPECT_FALSE(expectedRequest.has_value());
|
||||
});
|
||||
}
|
||||
@@ -142,8 +145,8 @@ TEST_F(HttpConnectionTests, ReceiveClientDisconnected)
|
||||
|
||||
runSpawn([this](boost::asio::yield_context yield) {
|
||||
auto connection = acceptConnection(yield);
|
||||
connection.setTimeout(std::chrono::milliseconds{1});
|
||||
auto expectedRequest = connection.receive(yield);
|
||||
connection->setTimeout(std::chrono::milliseconds{1});
|
||||
auto expectedRequest = connection->receive(yield);
|
||||
EXPECT_FALSE(expectedRequest.has_value());
|
||||
});
|
||||
}
|
||||
@@ -170,7 +173,7 @@ TEST_F(HttpConnectionTests, Send)
|
||||
|
||||
runSpawn([this, &response](boost::asio::yield_context yield) {
|
||||
auto connection = acceptConnection(yield);
|
||||
auto maybeError = connection.send(response, yield);
|
||||
auto maybeError = connection->send(response, yield);
|
||||
[&]() { ASSERT_FALSE(maybeError.has_value()) << maybeError->message(); }();
|
||||
});
|
||||
}
|
||||
@@ -186,7 +189,7 @@ TEST_F(HttpConnectionTests, SendMultipleTimes)
|
||||
|
||||
for ([[maybe_unused]] auto i : std::ranges::iota_view{0, 3}) {
|
||||
auto const expectedResponse = httpClient_.receive(yield, std::chrono::milliseconds{100});
|
||||
[&]() { ASSERT_TRUE(expectedResponse.has_value()) << maybeError->message(); }();
|
||||
[&]() { ASSERT_TRUE(expectedResponse.has_value()) << expectedResponse.error().message(); }();
|
||||
|
||||
auto const receivedResponse = expectedResponse.value();
|
||||
auto const sentResponse = Response{response}.intoHttpResponse();
|
||||
@@ -201,12 +204,77 @@ TEST_F(HttpConnectionTests, SendMultipleTimes)
|
||||
auto connection = acceptConnection(yield);
|
||||
|
||||
for ([[maybe_unused]] auto i : std::ranges::iota_view{0, 3}) {
|
||||
auto maybeError = connection.send(response, yield);
|
||||
auto maybeError = connection->send(response, yield);
|
||||
[&]() { ASSERT_FALSE(maybeError.has_value()) << maybeError->message(); }();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(HttpConnectionTests, SendMultipleTimesFromMultipleCoroutines)
|
||||
{
|
||||
Request const request{request_};
|
||||
Response const response{http::status::ok, "some response data", request};
|
||||
|
||||
boost::asio::spawn(ctx_, [this, response = response](boost::asio::yield_context yield) mutable {
|
||||
auto const maybeError =
|
||||
httpClient_.connect("localhost", httpServer_.port(), yield, std::chrono::milliseconds{100});
|
||||
[&]() { ASSERT_FALSE(maybeError.has_value()) << maybeError->message(); }();
|
||||
|
||||
for ([[maybe_unused]] auto i : std::ranges::iota_view{0, 3}) {
|
||||
auto const expectedResponse = httpClient_.receive(yield, std::chrono::milliseconds{100});
|
||||
[&]() { ASSERT_TRUE(expectedResponse.has_value()) << expectedResponse.error().message(); }();
|
||||
|
||||
auto const receivedResponse = expectedResponse.value();
|
||||
auto const sentResponse = Response{response}.intoHttpResponse();
|
||||
EXPECT_EQ(receivedResponse.result(), sentResponse.result());
|
||||
EXPECT_EQ(receivedResponse.body(), sentResponse.body());
|
||||
EXPECT_EQ(receivedResponse.version(), request_.version());
|
||||
EXPECT_TRUE(receivedResponse.keep_alive());
|
||||
}
|
||||
});
|
||||
|
||||
runSpawn([this, &response](boost::asio::yield_context yield) {
|
||||
auto connection = acceptConnection(yield);
|
||||
|
||||
util::CoroutineGroup group{yield};
|
||||
for ([[maybe_unused]] auto i : std::ranges::iota_view{0, 3}) {
|
||||
group.spawn(yield, [&response, &connection](boost::asio::yield_context innerYield) {
|
||||
auto const maybeError = connection->send(response, innerYield);
|
||||
[&]() { ASSERT_FALSE(maybeError.has_value()) << maybeError->message(); }();
|
||||
});
|
||||
}
|
||||
group.asyncWait(yield);
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(HttpConnectionTests, SendMultipleTimesClientDisconnected)
|
||||
{
|
||||
Response const response{http::status::ok, "some response data", Request{request_}};
|
||||
boost::asio::spawn(ctx_, [this, response = response](boost::asio::yield_context yield) mutable {
|
||||
auto const maybeError =
|
||||
httpClient_.connect("localhost", httpServer_.port(), yield, std::chrono::milliseconds{1});
|
||||
[&]() { ASSERT_FALSE(maybeError.has_value()) << maybeError->message(); }();
|
||||
auto const expectedResponse = httpClient_.receive(yield, std::chrono::milliseconds{100});
|
||||
[&]() { ASSERT_TRUE(expectedResponse.has_value()) << expectedResponse.error().message(); }();
|
||||
httpClient_.disconnect();
|
||||
});
|
||||
|
||||
runSpawn([this, &response](boost::asio::yield_context yield) {
|
||||
auto connection = acceptConnection(yield);
|
||||
connection->setTimeout(std::chrono::milliseconds{1});
|
||||
auto maybeError = connection->send(response, yield);
|
||||
size_t counter{1};
|
||||
while (not maybeError.has_value() and counter < 100) {
|
||||
++counter;
|
||||
maybeError = connection->send(response, yield);
|
||||
}
|
||||
// Sending after getting an error should be safe
|
||||
maybeError = connection->send(response, yield);
|
||||
EXPECT_TRUE(maybeError.has_value());
|
||||
EXPECT_LT(counter, 100);
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(HttpConnectionTests, SendClientDisconnected)
|
||||
{
|
||||
Response const response{http::status::ok, "some response data", Request{request_}};
|
||||
@@ -217,12 +285,12 @@ TEST_F(HttpConnectionTests, SendClientDisconnected)
|
||||
});
|
||||
runSpawn([this, &response](boost::asio::yield_context yield) {
|
||||
auto connection = acceptConnection(yield);
|
||||
connection.setTimeout(std::chrono::milliseconds{1});
|
||||
auto maybeError = connection.send(response, yield);
|
||||
connection->setTimeout(std::chrono::milliseconds{1});
|
||||
auto maybeError = connection->send(response, yield);
|
||||
size_t counter{1};
|
||||
while (not maybeError.has_value() and counter < 100) {
|
||||
++counter;
|
||||
maybeError = connection.send(response, yield);
|
||||
maybeError = connection->send(response, yield);
|
||||
}
|
||||
EXPECT_TRUE(maybeError.has_value());
|
||||
EXPECT_LT(counter, 100);
|
||||
@@ -246,8 +314,8 @@ TEST_F(HttpConnectionTests, Close)
|
||||
|
||||
runSpawn([this](boost::asio::yield_context yield) {
|
||||
auto connection = acceptConnection(yield);
|
||||
connection.setTimeout(std::chrono::milliseconds{1});
|
||||
connection.close(yield);
|
||||
connection->setTimeout(std::chrono::milliseconds{1});
|
||||
connection->close(yield);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -263,7 +331,7 @@ TEST_F(HttpConnectionTests, IsUpgradeRequested_GotHttpRequest)
|
||||
|
||||
runSpawn([this](boost::asio::yield_context yield) {
|
||||
auto connection = acceptConnection(yield);
|
||||
auto result = connection.isUpgradeRequested(yield);
|
||||
auto result = connection->isUpgradeRequested(yield);
|
||||
[&]() { ASSERT_TRUE(result.has_value()) << result.error().message(); }();
|
||||
EXPECT_FALSE(result.value());
|
||||
});
|
||||
@@ -278,8 +346,8 @@ TEST_F(HttpConnectionTests, IsUpgradeRequested_FailedToFetch)
|
||||
|
||||
runSpawn([this](boost::asio::yield_context yield) {
|
||||
auto connection = acceptConnection(yield);
|
||||
connection.setTimeout(std::chrono::milliseconds{1});
|
||||
auto result = connection.isUpgradeRequested(yield);
|
||||
connection->setTimeout(std::chrono::milliseconds{1});
|
||||
auto result = connection->isUpgradeRequested(yield);
|
||||
EXPECT_FALSE(result.has_value());
|
||||
});
|
||||
}
|
||||
@@ -295,11 +363,11 @@ TEST_F(HttpConnectionTests, Upgrade)
|
||||
|
||||
runSpawn([this](boost::asio::yield_context yield) {
|
||||
auto connection = acceptConnection(yield);
|
||||
auto const expectedResult = connection.isUpgradeRequested(yield);
|
||||
auto const expectedResult = connection->isUpgradeRequested(yield);
|
||||
[&]() { ASSERT_TRUE(expectedResult.has_value()) << expectedResult.error().message(); }();
|
||||
[&]() { ASSERT_TRUE(expectedResult.value()); }();
|
||||
|
||||
auto expectedWsConnection = connection.upgrade(tagDecoratorFactory_, yield);
|
||||
auto expectedWsConnection = connection->upgrade(tagDecoratorFactory_, yield);
|
||||
[&]() { ASSERT_TRUE(expectedWsConnection.has_value()) << expectedWsConnection.error().message(); }();
|
||||
});
|
||||
}
|
||||
@@ -313,7 +381,7 @@ TEST_F(HttpConnectionTests, Ip)
|
||||
|
||||
runSpawn([this](boost::asio::yield_context yield) {
|
||||
auto connection = acceptConnection(yield);
|
||||
EXPECT_TRUE(connection.ip() == "127.0.0.1" or connection.ip() == "::1") << connection.ip();
|
||||
EXPECT_TRUE(connection->ip() == "127.0.0.1" or connection->ip() == "::1") << connection->ip();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -329,13 +397,13 @@ TEST_F(HttpConnectionTests, isAdminSetAdmin)
|
||||
|
||||
runSpawn([&](boost::asio::yield_context yield) {
|
||||
auto connection = acceptConnection(yield);
|
||||
EXPECT_FALSE(connection.isAdmin());
|
||||
EXPECT_FALSE(connection->isAdmin());
|
||||
|
||||
connection.setIsAdmin(adminSetter.AsStdFunction());
|
||||
EXPECT_TRUE(connection.isAdmin());
|
||||
connection->setIsAdmin(adminSetter.AsStdFunction());
|
||||
EXPECT_TRUE(connection->isAdmin());
|
||||
|
||||
// Setter shouldn't not be called here because isAdmin is already set
|
||||
connection.setIsAdmin(adminSetter.AsStdFunction());
|
||||
EXPECT_TRUE(connection.isAdmin());
|
||||
connection->setIsAdmin(adminSetter.AsStdFunction());
|
||||
EXPECT_TRUE(connection->isAdmin());
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user