refactor: Coroutine based webserver (#1699)

Code of new coroutine-based web server. The new server is not connected
to Clio and not ready to use yet.
For #919.
This commit is contained in:
Sergey Kuznetsov
2024-10-24 16:50:26 +01:00
committed by Alex Kremer
parent 5c77e59374
commit b8f1deb90f
58 changed files with 5581 additions and 488 deletions

View File

@@ -20,7 +20,9 @@
#include "util/AssignRandomPort.hpp"
#include "util/LoggerFixtures.hpp"
#include "util/MockPrometheus.hpp"
#include "util/TestHttpSyncClient.hpp"
#include "util/TestHttpClient.hpp"
#include "util/TestWebSocketClient.hpp"
#include "util/TmpFile.hpp"
#include "util/config/Config.hpp"
#include "util/prometheus/Gauge.hpp"
#include "util/prometheus/Label.hpp"
@@ -45,6 +47,7 @@
#include <fmt/core.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <test_data/SslCert.hpp>
#include <condition_variable>
#include <cstdint>
@@ -103,14 +106,6 @@ generateJSONDataOverload(std::string_view port)
));
}
boost::json::value
addSslConfig(boost::json::value config)
{
config.as_object()["ssl_key_file"] = TEST_DATA_SSL_KEY_PATH;
config.as_object()["ssl_cert_file"] = TEST_DATA_SSL_CERT_PATH;
return config;
}
struct WebServerTest : NoLoggerFixture {
~WebServerTest() override
{
@@ -126,6 +121,14 @@ struct WebServerTest : NoLoggerFixture {
runner.emplace([this] { ctx.run(); });
}
boost::json::value
addSslConfig(boost::json::value config) const
{
config.as_object()["ssl_key_file"] = sslKeyFile.path;
config.as_object()["ssl_cert_file"] = sslCertFile.path;
return config;
}
// this ctx is for dos timer
boost::asio::io_context ctxSync;
std::string const port = std::to_string(tests::util::generateFreePort());
@@ -141,6 +144,9 @@ struct WebServerTest : NoLoggerFixture {
// this ctx is for http server
boost::asio::io_context ctx;
TmpFile sslCertFile{tests::sslCertFile()};
TmpFile sslKeyFile{tests::sslKeyFile()};
private:
std::optional<boost::asio::io_service::work> work;
std::optional<std::thread> runner;
@@ -212,7 +218,7 @@ TEST_F(WebServerTestsWithMockPrometheus, Http)
{
auto e = std::make_shared<EchoExecutor>();
auto const server = makeServerSync(cfg, ctx, dosGuard, e);
auto const res = HttpSyncClient::syncPost("localhost", port, R"({"Hello":1})");
auto const res = HttpSyncClient::post("localhost", port, R"({"Hello":1})");
EXPECT_EQ(res, R"({"Hello":1})");
}
@@ -236,7 +242,7 @@ TEST_F(WebServerTestsWithMockPrometheus, HttpInternalError)
{
auto e = std::make_shared<ExceptionExecutor>();
auto const server = makeServerSync(cfg, ctx, dosGuard, e);
auto const res = HttpSyncClient::syncPost("localhost", port, R"({})");
auto const res = HttpSyncClient::post("localhost", port, R"({})");
EXPECT_EQ(
res,
R"({"error":"internal","error_code":73,"error_message":"Internal error.","status":"error","type":"response"})"
@@ -286,7 +292,7 @@ TEST_F(WebServerTestsWithMockPrometheus, IncompleteSslConfig)
auto e = std::make_shared<EchoExecutor>();
auto jsonConfig = generateJSONWithDynamicPort(port);
jsonConfig.as_object()["ssl_key_file"] = TEST_DATA_SSL_KEY_PATH;
jsonConfig.as_object()["ssl_key_file"] = sslKeyFile.path;
auto const server = makeServerSync(Config{jsonConfig}, ctx, dosGuard, e);
EXPECT_EQ(server, nullptr);
@@ -297,7 +303,7 @@ TEST_F(WebServerTestsWithMockPrometheus, WrongSslConfig)
auto e = std::make_shared<EchoExecutor>();
auto jsonConfig = generateJSONWithDynamicPort(port);
jsonConfig.as_object()["ssl_key_file"] = TEST_DATA_SSL_KEY_PATH;
jsonConfig.as_object()["ssl_key_file"] = sslKeyFile.path;
jsonConfig.as_object()["ssl_cert_file"] = "wrong_path";
auto const server = makeServerSync(Config{jsonConfig}, ctx, dosGuard, e);
@@ -334,9 +340,9 @@ TEST_F(WebServerTestsWithMockPrometheus, HttpRequestOverload)
{
auto e = std::make_shared<EchoExecutor>();
auto const server = makeServerSync(cfg, ctx, dosGuardOverload, e);
auto res = HttpSyncClient::syncPost("localhost", port, R"({})");
auto res = HttpSyncClient::post("localhost", port, R"({})");
EXPECT_EQ(res, "{}");
res = HttpSyncClient::syncPost("localhost", port, R"({})");
res = HttpSyncClient::post("localhost", port, R"({})");
EXPECT_EQ(
res,
R"({"error":"slowDown","error_code":10,"error_message":"You are placing too much load on the server.","status":"error","type":"response"})"
@@ -372,7 +378,7 @@ TEST_F(WebServerTestsWithMockPrometheus, HttpPayloadOverload)
std::string const s100(100, 'a');
auto e = std::make_shared<EchoExecutor>();
auto server = makeServerSync(cfg, ctx, dosGuardOverload, e);
auto const res = HttpSyncClient::syncPost("localhost", port, fmt::format(R"({{"payload":"{}"}})", s100));
auto const res = HttpSyncClient::post("localhost", port, fmt::format(R"({{"payload":"{}"}})", s100));
EXPECT_EQ(
res,
R"({"payload":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","warning":"load","warnings":[{"id":2003,"message":"You are about to be rate limited"}]})"
@@ -535,7 +541,7 @@ TEST_P(WebServerAdminTest, HttpAdminCheck)
auto server = makeServerSync(serverConfig, ctx, dosGuardOverload, e);
std::string const request = "Why hello";
uint32_t const webServerPort = serverConfig.value<uint32_t>("server.port");
auto const res = HttpSyncClient::syncPost("localhost", std::to_string(webServerPort), request, GetParam().headers);
auto const res = HttpSyncClient::post("localhost", std::to_string(webServerPort), request, GetParam().headers);
EXPECT_EQ(res, fmt::format("{} {}", request, GetParam().expectedResponse));
}
@@ -653,7 +659,7 @@ TEST_F(WebServerPrometheusTest, rejectedWithoutAdminPassword)
uint32_t const webServerPort = tests::util::generateFreePort();
Config const serverConfig{boost::json::parse(JSONServerConfigWithAdminPassword(webServerPort))};
auto server = makeServerSync(serverConfig, ctx, dosGuard, e);
auto const res = HttpSyncClient::syncGet("localhost", std::to_string(webServerPort), "", "/metrics");
auto const res = HttpSyncClient::get("localhost", std::to_string(webServerPort), "", "/metrics");
EXPECT_EQ(res, "Only admin is allowed to collect metrics");
}
@@ -676,7 +682,7 @@ TEST_F(WebServerPrometheusTest, rejectedIfPrometheusIsDisabled)
Config const serverConfig{boost::json::parse(JSONServerConfigWithDisabledPrometheus)};
PrometheusService::init(serverConfig);
auto server = makeServerSync(serverConfig, ctx, dosGuard, e);
auto const res = HttpSyncClient::syncGet(
auto const res = HttpSyncClient::get(
"localhost",
std::to_string(webServerPort),
"",
@@ -697,7 +703,7 @@ TEST_F(WebServerPrometheusTest, validResponse)
auto e = std::make_shared<EchoExecutor>();
Config const serverConfig{boost::json::parse(JSONServerConfigWithAdminPassword(webServerPort))};
auto server = makeServerSync(serverConfig, ctx, dosGuard, e);
auto const res = HttpSyncClient::syncGet(
auto const res = HttpSyncClient::get(
"localhost",
std::to_string(webServerPort),
"",