Add clang-tidy, clang-format and fixes

This commit is contained in:
sha512sum 2024-07-03 17:58:27 +00:00
parent c6a1198ed2
commit 7c00dc1860
22 changed files with 459 additions and 499 deletions

13
.clang-format Normal file
View file

@ -0,0 +1,13 @@
BasedOnStyle: Google
IndentWidth: 2
ColumnLimit: 140
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
BreakConstructorInitializers: AfterColon
AlwaysBreakAfterReturnType: None
SpaceBeforeParens: Never
AllowShortFunctionsOnASingleLine: None
AllowShortLambdasOnASingleLine: Empty
BinPackArguments: false
BinPackParameters: false
AlwaysBreakTemplateDeclarations: true

1
.clang-tidy Normal file
View file

@ -0,0 +1 @@
Checks: '-*,google-*,cppcoreguidelines-*,-cppcoreguidelines-c-copy-assignment-signature,-cppcoreguidelines-special-member-functions,-cppcoreguidelines-avoid-const-or-ref-data-members,modernize-*'

View file

@ -1,12 +1,11 @@
#pragma once #pragma once
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <cserver/clients/http/request.hpp> #include <cserver/clients/http/request.hpp>
#include <cserver/clients/http/response.hpp> #include <cserver/clients/http/response.hpp>
#include <cserver/engine/components.hpp> #include <cserver/engine/components.hpp>
#include <cserver/engine/coroutine.hpp> #include <cserver/engine/coroutine.hpp>
#include <cserver/utils/boost_error_wrapper.hpp>
#include <cserver/engine/use_streaming.hpp> #include <cserver/engine/use_streaming.hpp>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <utempl/constexpr_string.hpp> #include <utempl/constexpr_string.hpp>
namespace cserver::server::http { namespace cserver::server::http {
@ -16,7 +15,6 @@ inline constexpr auto ParseHttpHeader(std::string header) -> std::pair<std::stri
std::string key = header.substr(0, pos); std::string key = header.substr(0, pos);
std::string value = header.substr(pos + 2, header.size() - 1); std::string value = header.substr(pos + 2, header.size() - 1);
return std::make_pair(key, value); return std::make_pair(key, value);
}; };
} // namespace cserver::server::http } // namespace cserver::server::http
namespace cserver::components { namespace cserver::components {
@ -32,7 +30,7 @@ struct HttpClient {
inline constexpr HttpClient(auto, auto& context) : inline constexpr HttpClient(auto, auto& context) :
taskProcessor(context.template FindComponent<"basicTaskProcessor">()), taskProcessor(context.template FindComponent<"basicTaskProcessor">()),
ctx(boost::asio::ssl::context::method::sslv23_client), ctx(boost::asio::ssl::context::method::sslv23_client),
resolver(this->taskProcessor.ioContext) {} resolver(this->taskProcessor.ioContext) {};
template <utempl::ConstexprString name, Options Options, typename T> template <utempl::ConstexprString name, Options Options, typename T>
static consteval auto Adder(const T& context) { static consteval auto Adder(const T& context) {
@ -76,14 +74,18 @@ private:
inline auto ReadBody(Socket&& socket, std::size_t length, auto&&...) const -> cserver::Task<std::string> { inline auto ReadBody(Socket&& socket, std::size_t length, auto&&...) const -> cserver::Task<std::string> {
std::string response; std::string response;
response.reserve(length); response.reserve(length);
co_await boost::asio::async_read(socket, boost::asio::dynamic_buffer(response), boost::asio::transfer_at_least(length), boost::asio::use_awaitable); co_await boost::asio::async_read(
socket, boost::asio::dynamic_buffer(response), boost::asio::transfer_at_least(length), boost::asio::use_awaitable);
co_return response; co_return response;
}; };
template <typename Socket> template <typename Socket>
inline auto ReadBody(Socket&& socket, auto&&...) const -> cserver::Task<std::string> { inline auto ReadBody(Socket&& socket, auto&&...) const -> cserver::Task<std::string> {
std::string response; std::string response;
for(;;) { for(;;) {
auto [ec, n] = co_await boost::asio::async_read(socket, boost::asio::dynamic_buffer(response), boost::asio::transfer_at_least(1), boost::asio::as_tuple(boost::asio::use_awaitable)); auto [ec, n] = co_await boost::asio::async_read(socket,
boost::asio::dynamic_buffer(response),
boost::asio::transfer_at_least(1),
boost::asio::as_tuple(boost::asio::use_awaitable));
if(ec && ec == boost::asio::error::eof) { if(ec && ec == boost::asio::error::eof) {
break; break;
}; };
@ -93,19 +95,23 @@ private:
}; };
co_return response; co_return response;
}; };
public: public:
template <typename... Flags, typename T> template <typename... Flags, typename T>
inline auto PerformRequest(T&& request, Flags... flags) -> cserver::Task<decltype(this->GetPerformReturnType<boost::asio::ssl::stream<boost::asio::ip::tcp::socket>>(flags...))> { inline auto PerformRequest(T&& request, Flags... flags)
-> cserver::Task<decltype(this->GetPerformReturnType<boost::asio::ssl::stream<boost::asio::ip::tcp::socket>>(flags...))> {
constexpr bool kUseStreaming = utempl::Find<UseStreaming>(utempl::kTypeList<Flags...>) != sizeof...(Flags); constexpr bool kUseStreaming = utempl::Find<UseStreaming>(utempl::kTypeList<Flags...>) != sizeof...(Flags);
boost::asio::ssl::stream<boost::asio::ip::tcp::socket> socket(this->taskProcessor.ioContext, this->ctx); boost::asio::ssl::stream<boost::asio::ip::tcp::socket> socket(this->taskProcessor.ioContext, this->ctx);
boost::asio::ip::tcp::resolver::iterator endpoint = boost::asio::ip::tcp::resolver::iterator endpoint = co_await this->resolver.async_resolve(
co_await this->resolver.async_resolve({request.request.url.host(), request.request.url.has_port() ? request.request.url.port() : request.request.url.scheme()}, boost::asio::use_awaitable); {request.request.url.host(), request.request.url.has_port() ? request.request.url.port() : request.request.url.scheme()},
boost::asio::use_awaitable);
co_await boost::asio::async_connect(socket.lowest_layer(), endpoint, boost::asio::use_awaitable); co_await boost::asio::async_connect(socket.lowest_layer(), endpoint, boost::asio::use_awaitable);
co_await socket.async_handshake(boost::asio::ssl::stream_base::client, boost::asio::use_awaitable); co_await socket.async_handshake(boost::asio::ssl::stream_base::client, boost::asio::use_awaitable);
std::string req(request.request.ToString()); std::string req(request.request.ToString());
co_await boost::asio::async_write(socket, boost::asio::buffer(req.data(), req.size()), boost::asio::transfer_all(), boost::asio::use_awaitable); co_await boost::asio::async_write(
socket, boost::asio::buffer(req.data(), req.size()), boost::asio::transfer_all(), boost::asio::use_awaitable);
auto response = co_await this->ReadHeaders(socket, flags...); auto response = co_await this->ReadHeaders(socket, flags...);
if constexpr(kUseStreaming) { if constexpr(kUseStreaming) {
co_return clients::http::Response<HttpClient<TaskProcessor>, decltype(socket)>{*this, std::move(socket), std::move(response)}; co_return clients::http::Response<HttpClient<TaskProcessor>, decltype(socket)>{*this, std::move(socket), std::move(response)};
@ -120,4 +126,4 @@ public:
}; };
}; };
} // namespace cserver::clients::http } // namespace cserver::components

View file

@ -1,69 +1,67 @@
#pragma once #pragma once
#include <cserver/engine/coroutine.hpp>
#include <cserver/server/http/http_request.hpp> #include <cserver/server/http/http_request.hpp>
#include <cserver/server/http/http_response.hpp> #include <cserver/server/http/http_response.hpp>
#include <cserver/engine/coroutine.hpp>
namespace cserver::clients::http { namespace cserver::clients::http {
template <typename HttpClient> template <typename HttpClient>
struct Request { struct Request {
HttpClient& client; HttpClient& client;
server::http::HttpRequest request; server::http::HttpRequest request;
Request(HttpClient& client) : explicit Request(HttpClient& client) : client(client) {
client(client) {
this->AddHeader("User-Agent", "cserver/1"); this->AddHeader("User-Agent", "cserver/1");
}; };
inline constexpr auto Post(this auto&& self) -> decltype(auto) { constexpr auto Post(this auto&& self) -> decltype(auto) {
self.request.method = "POST"; self.request.method = "POST";
return self; return self;
}; };
inline constexpr auto Get(this auto&& self) -> decltype(auto) { constexpr auto Get(this auto&& self) -> decltype(auto) {
self.request.method = "GET"; self.request.method = "GET";
return self; return self;
}; };
inline constexpr auto Put(this auto&& self) -> decltype(auto) { constexpr auto Put(this auto&& self) -> decltype(auto) {
self.request.method = "PUT"; self.request.method = "PUT";
return self; return self;
}; };
inline constexpr auto SetCustomMethod(this auto&& self, std::string method) -> decltype(auto) { constexpr auto SetCustomMethod(this auto&& self, std::string method) -> decltype(auto) {
self.request.method = std::move(method); self.request.method = std::move(method);
return self; return self;
}; };
inline constexpr auto AddHeader(this auto&& self, std::string first, std::string second) -> decltype(auto) { constexpr auto AddHeader(this auto&& self, std::string first, std::string second) -> decltype(auto) {
self.request.headers.emplace(std::move(first), std::move(second)); self.request.headers.emplace(std::move(first), std::move(second));
return self; return self;
}; };
template <typename Self> template <typename Self>
inline constexpr auto AddHeaderIfNotExists(this Self&& self, constexpr auto AddHeaderIfNotExists(this Self&& self, std::string check, std::string first, std::string second) -> decltype(auto) {
std::string check,
std::string first,
std::string second) -> decltype(auto) {
if(!self.request.headers.contains(std::move(check))) { if(!self.request.headers.contains(std::move(check))) {
return std::forward<Self>(self).AddHeader(std::move(first), std::move(second)); return std::forward<Self>(self).AddHeader(std::move(first), std::move(second));
}; };
return self; return self;
}; };
template <typename Self> template <typename Self>
inline constexpr auto Url(this Self&& self, constexpr auto Url(this Self&& self, std::string url) -> auto&& {
std::string url) -> auto&& {
self.request.url = boost::urls::url{std::move(url)}; self.request.url = boost::urls::url{std::move(url)};
auto authority = self.request.url.authority(); auto authority = self.request.url.authority();
return std::forward<Self>(self).AddHeader("Host", std::string{authority.data(), authority.size()}); return std::forward<Self>(self).AddHeader("Host", std::string{authority.data(), authority.size()});
}; };
inline constexpr auto Data(this auto&& self, std::string data) -> decltype(auto) { constexpr auto Data(this auto&& self, std::string data) -> decltype(auto) {
self.request.body = std::move(data); self.request.body = std::move(data);
return self; return self;
}; };
inline constexpr auto ToString() const -> std::string { [[nodiscard]] constexpr auto ToString() const -> std::string {
return this->request.ToString(); return this->request.ToString();
}; };
template <typename Self, typename... Flags> template <typename Self, typename... Flags>
inline auto Perform(this Self&& self, Flags&&... flags) inline auto Perform(this Self&& self, Flags&&... flags)
-> decltype(self.client.PerformRequest(std::forward<Self>(self).AddHeaderIfNotExists("Transfer-Encoding", "Content-Length", std::to_string(self.request.body.size())), std::forward<Flags>(flags)...)) { -> decltype(self.client.PerformRequest(
std::forward<Self>(self).AddHeaderIfNotExists("Transfer-Encoding", "Content-Length", std::to_string(self.request.body.size())),
std::forward<Flags>(flags)...)) {
HttpClient& client = self.client; HttpClient& client = self.client;
std::string size = std::to_string(self.request.body.size()); std::string size = std::to_string(self.request.body.size());
co_return co_await client.PerformRequest(std::forward<Self>(self).AddHeaderIfNotExists("Transfer-Encoding", "Content-Length", std::move(size)), std::forward<Flags>(flags)...); co_return co_await client.PerformRequest(
std::forward<Self>(self).AddHeaderIfNotExists("Transfer-Encoding", "Content-Length", std::move(size)),
std::forward<Flags>(flags)...);
};
}; };
}; } // namespace cserver::clients::http
};

View file

@ -1,7 +1,7 @@
#pragma once #pragma once
#include <cserver/server/http/http_response.hpp>
#include <cserver/engine/coroutine.hpp>
#include <boost/asio/ssl.hpp> #include <boost/asio/ssl.hpp>
#include <cserver/engine/coroutine.hpp>
#include <cserver/server/http/http_response.hpp>
namespace cserver::clients::http { namespace cserver::clients::http {
@ -12,8 +12,10 @@ class Response : public server::http::HttpResponse {
public: public:
inline auto ReadChunk() -> cserver::Task<bool> { inline auto ReadChunk() -> cserver::Task<bool> {
this->body.resize(4479); this->body.resize(4479); // NOLINT
auto [ec, n] = co_await this->socket.async_read_some(boost::asio::buffer(this->body.data(), 4479), boost::asio::as_tuple(boost::asio::use_awaitable));
auto [ec, n] = co_await this->socket.async_read_some(boost::asio::buffer(this->body.data(), 4479), // NOLINT
boost::asio::as_tuple(boost::asio::use_awaitable));
if(ec == boost::asio::error::eof || ec == boost::asio::error::operation_aborted || ec == boost::asio::ssl::error::stream_truncated) { if(ec == boost::asio::error::eof || ec == boost::asio::error::operation_aborted || ec == boost::asio::ssl::error::stream_truncated) {
co_return false; co_return false;
}; };
@ -23,12 +25,10 @@ public:
this->body.resize(n); this->body.resize(n);
co_return true; co_return true;
}; };
inline constexpr Response(Response&&) = default; constexpr Response(Response&&) = default;
inline constexpr Response(const Response&) = default; constexpr Response(const Response&) = default;
inline constexpr Response(HttpClient& client, Socket socket, server::http::HttpResponse response) : inline Response(HttpClient& client, Socket socket, server::http::HttpResponse response) :
client(client), client(client), socket(std::move(socket)), HttpResponse(std::move(response)) {};
socket(std::move(socket)),
HttpResponse(std::move(response)) {};
}; };
} // namespace cserver::clients::http } // namespace cserver::clients::http

View file

@ -1,10 +1,10 @@
#pragma once #pragma once
#include <utempl/optional.hpp>
#include <cserver/engine/not_implemented.hpp>
#include <cserver/engine/components.hpp>
#include <boost/program_options.hpp> #include <boost/program_options.hpp>
#include <nameof.hpp> #include <cserver/engine/components.hpp>
#include <cserver/engine/not_implemented.hpp>
#include <iostream> #include <iostream>
#include <nameof.hpp>
#include <utempl/optional.hpp>
namespace cserver::cli { namespace cserver::cli {
@ -22,25 +22,19 @@ struct StructConfig {
static constexpr auto kValue = utempl::Tuple{Configs...}; static constexpr auto kValue = utempl::Tuple{Configs...};
}; };
template <typename T, std::size_t N, std::size_t NN> template <typename T, std::size_t N, std::size_t NN>
consteval auto CreateOptionConfig(utempl::ConstexprString<N> name, consteval auto CreateOptionConfig(utempl::ConstexprString<N> name, utempl::ConstexprString<NN> description) -> OptionConfig<N, NN, T> {
utempl::ConstexprString<NN> description) -> OptionConfig<N, NN, T> {
return {name, description}; return {name, description};
}; };
template <StructConfig... Configs> template <StructConfig... Configs>
struct Manager { struct Manager {
boost::program_options::variables_map variableMap; boost::program_options::variables_map variableMap;
static constexpr utempl::ConstexprString kName = "cliManager"; static constexpr utempl::ConstexprString kName = "cliManager";
constexpr Manager(auto& context) { explicit constexpr Manager(auto& context) {
boost::program_options::options_description general("General options"); boost::program_options::options_description general("General options");
general.add_options() general.add_options()("help,h", "Show help");
("help,h", "Show help"); // clang-format off
([&]{ ([&]{
using Current = decltype(Configs)::Type; using Current = decltype(Configs)::Type;
boost::program_options::options_description desc(fmt::format("{} options", boost::program_options::options_description desc(fmt::format("{} options",
@ -48,7 +42,6 @@ struct Manager {
utempl::Unpack(utempl::PackConstexprWrapper<decltype(Configs)::kValue>(), [&](auto... vs) { utempl::Unpack(utempl::PackConstexprWrapper<decltype(Configs)::kValue>(), [&](auto... vs) {
auto&& add = desc.add_options(); auto&& add = desc.add_options();
([&]{ ([&]{
//static_assert((std::ignore = utempl::kWrapper<*vs>, false));
if constexpr((*vs).description.size() == 0) { if constexpr((*vs).description.size() == 0) {
add((*vs).name.data.begin(), add((*vs).name.data.begin(),
boost::program_options::value<typename decltype(*vs)::Type>()); boost::program_options::value<typename decltype(*vs)::Type>());
@ -61,6 +54,7 @@ struct Manager {
}); });
general.add(desc); general.add(desc);
}(), ...); }(), ...);
// clang-format-on
boost::program_options::store(boost::program_options::parse_command_line(context.argc, context.argv, general), this->variableMap); boost::program_options::store(boost::program_options::parse_command_line(context.argc, context.argv, general), this->variableMap);
boost::program_options::notify(this->variableMap); boost::program_options::notify(this->variableMap);
if(this->variableMap.count("help")) { if(this->variableMap.count("help")) {

View file

@ -1,9 +1,9 @@
#pragma once #pragma once
#include <cserver/engine/not_implemented.hpp>
#include <cserver/engine/components.hpp>
#include <cserver/components/cli/manager.hpp>
#include <utempl/attributes.hpp>
#include <boost/pfr.hpp> #include <boost/pfr.hpp>
#include <cserver/components/cli/manager.hpp>
#include <cserver/engine/components.hpp>
#include <cserver/engine/not_implemented.hpp>
#include <utempl/attributes.hpp>
namespace cserver::cli { namespace cserver::cli {
@ -17,15 +17,15 @@ struct Description {
static constexpr utempl::ConstexprString kValue = Value; static constexpr utempl::ConstexprString kValue = Value;
}; };
template <typename T> template <typename T>
struct IsNameM { struct IsNameM {
static constexpr bool value = utempl::Overloaded( static constexpr bool value = utempl::Overloaded(
[]<utempl::ConstexprString V>(utempl::TypeList<Name<V>>) { []<utempl::ConstexprString V>(utempl::TypeList<Name<V>>) {
return true; return true;
}, },
[](auto) {return false;} [](auto) {
)(utempl::kType<T>); return false;
})(utempl::kType<T>);
}; };
template <typename T> template <typename T>
@ -34,15 +34,11 @@ struct IsDescriptionM {
[]<utempl::ConstexprString V>(utempl::TypeList<Description<V>>) { []<utempl::ConstexprString V>(utempl::TypeList<Description<V>>) {
return true; return true;
}, },
[](auto) {return false;} [](auto) {
)(utempl::kType<T>); return false;
})(utempl::kType<T>);
}; };
template <typename Self, typename T> template <typename Self, typename T>
struct Struct : T { struct Struct : T {
static constexpr utempl::ConstexprString kCliManagerName = "cliManager"; static constexpr utempl::ConstexprString kCliManagerName = "cliManager";
@ -61,7 +57,6 @@ struct Struct : T {
}; };
}; };
static consteval auto GetConfig() { static consteval auto GetConfig() {
static constexpr auto names = boost::pfr::names_as_array<T>(); static constexpr auto names = boost::pfr::names_as_array<T>();
return [&](auto... is) { return [&](auto... is) {
@ -73,11 +68,7 @@ struct Struct : T {
} | utempl::kSeq<boost::pfr::tuple_size_v<T>>; } | utempl::kSeq<boost::pfr::tuple_size_v<T>>;
}; };
explicit constexpr Struct(auto& context) : T(context.template FindComponent<Self::kCliManagerName>().template Get<GetConfig()>()) {};
constexpr Struct(auto& context) :
T(context.template FindComponent<Self::kCliManagerName>().template Get<GetConfig()>()) {};
template <utempl::ConstexprString Name, Options> template <utempl::ConstexprString Name, Options>
static consteval auto CliStructAdder(const auto& context) { static consteval auto CliStructAdder(const auto& context) {

View file

@ -7,18 +7,23 @@ namespace cserver {
struct ComponentBase { struct ComponentBase {
Logging& logging; Logging& logging;
template <typename T> template <typename T>
constexpr ComponentBase(T& context) : explicit constexpr ComponentBase(T& context) : logging(context.template FindComponent<"logging">()){};
logging(context.template FindComponent<"logging">()) {};
}; };
#define LOG_TRACE \
#define LOG_TRACE if(this->logging.level == ::cserver::LoggingLevel::kTrace) this->logging.Trace if(this->logging.level == ::cserver::LoggingLevel::kTrace) \
#define LOG_DEBUG if(this->logging.level <= ::cserver::LoggingLevel::kDebug) this->logging.Debug this->logging.Trace
#define LOG_INFO if(this->logging.level <= ::cserver::LoggingLevel::kInfo) this->logging.Info #define LOG_DEBUG \
#define LOG_WARNING if(this->logging.level <= ::cserver::LoggingLevel::kWarning) this->logging.Warning if(this->logging.level <= ::cserver::LoggingLevel::kDebug) \
#define LOG_ERROR if(this->logging.level <= ::cserver::LoggingLevel::kError) this->logging.Error this->logging.Debug
#define LOG_INFO \
if(this->logging.level <= ::cserver::LoggingLevel::kInfo) \
this->logging.Info
#define LOG_WARNING \
if(this->logging.level <= ::cserver::LoggingLevel::kWarning) \
this->logging.Warning
#define LOG_ERROR \
if(this->logging.level <= ::cserver::LoggingLevel::kError) \
this->logging.Error
} // namespace cserver } // namespace cserver

View file

@ -1,26 +1,20 @@
#pragma once #pragma once
#include <utempl/constexpr_string.hpp>
#include <array>
#include <fmt/compile.h> #include <fmt/compile.h>
#include <fmt/format.h> #include <fmt/format.h>
#include <array>
#include <iostream> #include <iostream>
#include <utempl/constexpr_string.hpp>
namespace cserver { namespace cserver {
enum class LoggingLevel { kTrace, kDebug, kInfo, kWarning, kError };
enum class LoggingLevel {
kTrace,
kDebug,
kInfo,
kWarning,
kError
};
struct Logging { struct Logging {
static constexpr utempl::ConstexprString kName = "logging"; static constexpr utempl::ConstexprString kName = "logging";
LoggingLevel level; LoggingLevel level;
template <typename T> template <typename T>
constexpr Logging(T&) : level(T::kConfig.template Get<T::kName>().template Get<"level">()) { explicit constexpr Logging(T&) : level(T::kConfig.template Get<T::kName>().template Get<"level">()) {
std::ios::sync_with_stdio(false); std::ios::sync_with_stdio(false);
}; };
@ -28,13 +22,12 @@ struct Logging {
this->level = level; this->level = level;
}; };
template <std::size_t N, std::size_t NN> template <std::size_t N, std::size_t NN>
static consteval auto GetFormatStringFor(utempl::ConstexprString<N> fmt, utempl::ConstexprString<NN> level) { static consteval auto GetFormatStringFor(utempl::ConstexprString<N> fmt, utempl::ConstexprString<NN> level) {
constexpr auto f = FMT_COMPILE("[{}]: {}\n"); constexpr auto f = FMT_COMPILE("[{}]: {}\n");
constexpr auto size = N + NN + 6; constexpr auto size = N + NN + 6;
char data[size]{}; std::array<char, size> data{};
fmt::format_to(data, f, static_cast<std::string_view>(level), static_cast<std::string_view>(fmt)); fmt::format_to(data.begin(), f, static_cast<std::string_view>(level), static_cast<std::string_view>(fmt));
return utempl::ConstexprString<size>(data); return utempl::ConstexprString<size>(data);
}; };
@ -42,7 +35,6 @@ struct Logging {
std::cout << data; std::cout << data;
}; };
template <utempl::ConstexprString Fmt, typename... Ts> template <utempl::ConstexprString Fmt, typename... Ts>
constexpr auto Debug(Ts&&... args) { constexpr auto Debug(Ts&&... args) {
static constexpr auto fmt = GetFormatStringFor(Fmt, utempl::ConstexprString{"DEBUG"}); static constexpr auto fmt = GetFormatStringFor(Fmt, utempl::ConstexprString{"DEBUG"});
@ -74,5 +66,4 @@ struct Logging {
}; };
}; };
} // namespace cserver } // namespace cserver

View file

@ -3,11 +3,9 @@
namespace cserver { namespace cserver {
struct StopBlocker { struct StopBlocker {
boost::asio::io_context::work guard; boost::asio::io_context::work guard;
inline constexpr StopBlocker(auto& context) : explicit constexpr StopBlocker(auto& context) : guard(context.template FindComponent<kBasicTaskProcessorName>().ioContext) {};
guard(context.template FindComponent<kBasicTaskProcessorName>().ioContext) {};
}; };
} // namespace cserver } // namespace cserver

View file

@ -1,19 +1,16 @@
#pragma once #pragma once
#include <utempl/constexpr_string.hpp>
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include <thread> #include <thread>
#include <utempl/constexpr_string.hpp>
namespace cserver::engine::basic { namespace cserver::engine::basic {
template <std::size_t Size = 0> template <std::size_t Size = 0>
struct TaskProcessor { struct TaskProcessor {
boost::asio::io_context ioContext; boost::asio::io_context ioContext;
std::array<std::optional<std::thread>, Size> pool; std::array<std::optional<std::thread>, Size> pool{};
static constexpr utempl::ConstexprString kName = "basicTaskProcessor"; static constexpr utempl::ConstexprString kName = "basicTaskProcessor";
inline constexpr TaskProcessor(auto, auto&) : inline constexpr TaskProcessor(auto, auto&) : ioContext{} {};
ioContext{},
pool{} {
};
inline ~TaskProcessor() { inline ~TaskProcessor() {
for(auto& thread : this->pool) { for(auto& thread : this->pool) {
@ -38,4 +35,4 @@ struct TaskProcessor {
}; };
}; };
} } // namespace cserver::engine::basic

View file

@ -1,10 +1,11 @@
#pragma once #pragma once
#include <cxxabi.h>
#include <boost/core/demangle.hpp>
#include <cserver/engine/basic/task_processor.hpp> #include <cserver/engine/basic/task_processor.hpp>
#include <cserver/engine/coroutine.hpp> #include <cserver/engine/coroutine.hpp>
#include <utempl/utils.hpp>
#include <utempl/loopholes/counter.hpp> #include <utempl/loopholes/counter.hpp>
#include <cxxabi.h> #include <utempl/utils.hpp>
#include <boost/core/demangle.hpp>
namespace cserver { namespace cserver {
@ -29,9 +30,8 @@ struct NamedValue {
}; };
template <typename T> template <typename T>
using GetTypeFromComponentConfig = decltype( using GetTypeFromComponentConfig = decltype([]<utempl::ConstexprString name, typename TT, Options Options>(
[]<utempl::ConstexprString name, typename TT, Options Options>(const ComponentConfig<name, TT, Options>&) -> TT { const ComponentConfig<name, TT, Options>&) -> TT {}(std::declval<T>()));
}(std::declval<T>()));
template <typename T> template <typename T>
inline constexpr utempl::ConstexprString kNameFromComponentConfig = inline constexpr utempl::ConstexprString kNameFromComponentConfig =
@ -48,7 +48,6 @@ inline constexpr auto TransformIfOk(T&& value, auto&& f) {
}; };
}; };
} // namespace impl } // namespace impl
template <typename... Ts> template <typename... Ts>
@ -81,22 +80,21 @@ inline constexpr auto GetInitFlagFor(auto&... args) -> InitFlag& {
template <typename InitFlag, utempl::Tuple Tuple> template <typename InitFlag, utempl::Tuple Tuple>
constexpr auto TransformDependencyGraphToTupleWithDependenciesToInitedFlagChanges(auto&&... args) { constexpr auto TransformDependencyGraphToTupleWithDependenciesToInitedFlagChanges(auto&&... args) {
return utempl::Map(utempl::PackConstexprWrapper<Tuple>(), [&]<auto Array>(utempl::Wrapper<Array>) { return utempl::Map(utempl::PackConstexprWrapper<Tuple>(), [&]<auto Array>(utempl::Wrapper<Array>) {
return utempl::Map(utempl::PackConstexprWrapper<Array, utempl::Tuple<>>(), [&](auto elem) { return utempl::Map(
utempl::PackConstexprWrapper<Array, utempl::Tuple<>>(),
[&](auto elem) {
return &GetInitFlagFor<InitFlag, static_cast<std::size_t>(elem)>(args...); return &GetInitFlagFor<InitFlag, static_cast<std::size_t>(elem)>(args...);
}, utempl::kType<std::array<InitFlag*, Array.size()>>); },
utempl::kType<std::array<InitFlag*, Array.size()>>);
}); });
}; };
struct AsyncConditionVariable { struct AsyncConditionVariable {
inline AsyncConditionVariable(boost::asio::io_context& ioContext) : explicit inline AsyncConditionVariable(boost::asio::io_context& ioContext) : mtx{}, deadlineTimer(ioContext) {};
mtx{},
deadlineTimer(ioContext),
flag{} {};
template <typename Token = boost::asio::use_awaitable_t<>> template <typename Token = boost::asio::use_awaitable_t<>>
inline auto AsyncWait(Token&& token = {}) -> Task<> { inline auto AsyncWait(Token&& token = {}) -> Task<> {
return boost::asio::async_initiate< return boost::asio::async_initiate<Token, void(void)>(
Token, [this]<typename Handler>(Handler&& handler) -> void {
void(void)>([this]<typename Handler>(Handler&& handler) -> void {
std::unique_lock lc(this->mtx); std::unique_lock lc(this->mtx);
if(this->flag) { if(this->flag) {
return handler(); return handler();
@ -104,7 +102,8 @@ struct AsyncConditionVariable {
this->deadlineTimer.async_wait([h = std::make_unique<Handler>(std::forward<Handler>(handler))](const boost::system::error_code&) { this->deadlineTimer.async_wait([h = std::make_unique<Handler>(std::forward<Handler>(handler))](const boost::system::error_code&) {
return (*h)(); return (*h)();
}); });
}, token); },
token);
}; };
inline auto Wait() -> void { inline auto Wait() -> void {
this->deadlineTimer.wait(); this->deadlineTimer.wait();
@ -120,7 +119,7 @@ struct AsyncConditionVariable {
}; };
std::mutex mtx; std::mutex mtx;
boost::asio::deadline_timer deadlineTimer; boost::asio::deadline_timer deadlineTimer;
bool flag; bool flag{};
}; };
template <typename Component, typename T> template <typename Component, typename T>
@ -136,8 +135,8 @@ struct ServiceContextForComponent {
}; };
template <utempl::ConstexprString Name> template <utempl::ConstexprString Name>
constexpr auto FindComponent() -> auto& constexpr auto FindComponent() -> auto& requires(Name == kBasicTaskProcessorName ||
requires (Name == kBasicTaskProcessorName || requires {this->FindComponent<kUtils.template GetIndexByName(Name)>();}) { requires { this->FindComponent<kUtils.template GetIndexByName(Name)>(); }) {
if constexpr(Name == kBasicTaskProcessorName) { if constexpr(Name == kBasicTaskProcessorName) {
return this->context.taskProcessor; return this->context.taskProcessor;
} else { } else {
@ -168,13 +167,9 @@ struct ServiceContextForComponentWithCliArgs : ServiceContextForComponent<Compon
const char** argv; const char** argv;
constexpr ServiceContextForComponentWithCliArgs(T& context, int argc, const char** argv) : constexpr ServiceContextForComponentWithCliArgs(T& context, int argc, const char** argv) :
ServiceContextForComponent<Component, T>(context), ServiceContextForComponent<Component, T>(context), argc(argc), argv(argv) {};
argc(argc),
argv(argv) {};
}; };
template <utempl::Tuple DependencyGraph, typename T> template <utempl::Tuple DependencyGraph, typename T>
inline constexpr auto InitComponents(T& ccontext, int ac, const char** av) -> void { inline constexpr auto InitComponents(T& ccontext, int ac, const char** av) -> void {
static auto& context = ccontext; static auto& context = ccontext;
@ -189,9 +184,11 @@ inline constexpr auto InitComponents(T& ccontext, int ac, const char** av) -> vo
return ""; return "";
}; };
}(); }();
static utempl::Tuple inited = TransformDependencyGraphToTupleWithDependenciesToInitedFlagChanges<AsyncConditionVariable, DependencyGraph>(ioContext); static utempl::Tuple inited =
TransformDependencyGraphToTupleWithDependenciesToInitedFlagChanges<AsyncConditionVariable, DependencyGraph>(ioContext);
auto work = make_work_guard(ioContext); auto work = make_work_guard(ioContext);
[]<std::size_t... Is>(std::index_sequence<Is...>) { []<std::size_t... Is>(std::index_sequence<Is...>) {
// clang-format off
(boost::asio::co_spawn(ioContext, []() -> cserver::Task<> { (boost::asio::co_spawn(ioContext, []() -> cserver::Task<> {
using Current = decltype(Get<Is>(decltype(T::kUtils)::kComponentConfigs)); using Current = decltype(Get<Is>(decltype(T::kUtils)::kComponentConfigs));
auto& dependencies = Get<Is>(inited); auto& dependencies = Get<Is>(inited);
@ -226,6 +223,7 @@ inline constexpr auto InitComponents(T& ccontext, int ac, const char** av) -> vo
Get<Is>(context.storage)->Run(); Get<Is>(context.storage)->Run();
}; };
}(), boost::asio::detached), ...); }(), boost::asio::detached), ...);
// clang-format on
}(std::make_index_sequence<utempl::kTupleSize<decltype(DependencyGraph)>>()); }(std::make_index_sequence<utempl::kTupleSize<decltype(DependencyGraph)>>());
}; };
@ -260,21 +258,19 @@ struct DependenciesUtils {
} else { } else {
return std::array<std::size_t, 0>{}; return std::array<std::size_t, 0>{};
}; };
}()...);} | utempl::kSeq<sizeof...(Ts)>; }()...);
} | utempl::kSeq<sizeof...(Ts)>;
}; };
template <std::size_t I> template <std::size_t I>
static consteval auto GetName() { static consteval auto GetName() {
return Get<I>(kNames); return Get<I>(kNames);
}; };
}; };
} // namespace impl } // namespace impl
#define COMPONENT_REQUIRES(name, impl) \ #define COMPONENT_REQUIRES(name, impl) /* NOLINT */ \
template <typename T> \ template <typename T> \
concept name = impl; \ concept name = impl; \
template <typename T> \ template <typename T> \
@ -282,29 +278,25 @@ struct DependenciesUtils {
static constexpr auto value = name<T>; \ static constexpr auto value = name<T>; \
} }
template <ConstexprConfig config, utempl::Tuple DependencyGraph, typename... Ts> template <ConstexprConfig config, utempl::Tuple DependencyGraph, typename... Ts>
struct ServiceContext { struct ServiceContext {
static constexpr auto kConfig = config; static constexpr auto kConfig = config;
static constexpr auto kThreadsCount = config.template Get<"threads">(); static constexpr auto kThreadsCount = config.template Get<"threads">();
static constexpr impl::DependenciesUtils<Ts...> kUtils; static constexpr impl::DependenciesUtils<Ts...> kUtils;
engine::basic::TaskProcessor<kThreadsCount - 1> taskProcessor; engine::basic::TaskProcessor<kThreadsCount - 1> taskProcessor;
utempl::Tuple<std::optional<typename Ts::Type>...> storage; utempl::Tuple<std::optional<typename Ts::Type>...> storage{};
constexpr ServiceContext() : constexpr ServiceContext() : taskProcessor{utempl::Wrapper<utempl::ConstexprString{kBasicTaskProcessorName}>{}, *this} {};
taskProcessor{utempl::Wrapper<utempl::ConstexprString{kBasicTaskProcessorName}>{}, *this}
,storage{} {
};
constexpr auto Run(int argc, const char** argv) { constexpr auto Run(int argc, const char** argv) {
impl::InitComponents<DependencyGraph>(*this, argc, argv); impl::InitComponents<DependencyGraph>(*this, argc, argv);
this->taskProcessor.Run(); this->taskProcessor.Run();
}; };
template <typename Component> template <typename Component>
constexpr auto GetContextFor() /* Internal Use Only */ { constexpr auto GetContextFor() /* Internal Use Only */ {
return impl::ServiceContextForComponent<Component, std::decay_t<decltype(*this)>>{*this}; return impl::ServiceContextForComponent<Component, std::decay_t<decltype(*this)>>{*this};
}; };
private: private:
static constexpr struct { static constexpr struct {
constexpr auto operator=(auto&&) const noexcept {}; constexpr auto operator=(auto&&) const noexcept {};
@ -318,6 +310,7 @@ private:
static constexpr struct { static constexpr struct {
constexpr auto operator=(auto&&) const noexcept {}; constexpr auto operator=(auto&&) const noexcept {};
} RequiredComponent; } RequiredComponent;
public: public:
template <typename Current, std::size_t I> template <typename Current, std::size_t I>
constexpr auto FindComponent() -> decltype(*Get<I>(this->storage))& { constexpr auto FindComponent() -> decltype(*Get<I>(this->storage))& {
@ -325,21 +318,19 @@ public:
constexpr auto J = utempl::loopholes::Counter<Current, utempl::Wrapper<I>>(); constexpr auto J = utempl::loopholes::Counter<Current, utempl::Wrapper<I>>();
constexpr auto dependencies = Get<Index>(DependencyGraph); constexpr auto dependencies = Get<Index>(DependencyGraph);
static_assert(( static_assert((ComponentType = utempl::kType<typename Current::Type>,
ComponentType = utempl::kType<typename Current::Type>,
ComponentName = utempl::kWrapper<Current::kName>, ComponentName = utempl::kWrapper<Current::kName>,
ComponentOptions = utempl::kWrapper<Current::kOptions>, ComponentOptions = utempl::kWrapper<Current::kOptions>,
RequiredComponent = utempl::kType<decltype(Get<I>(kUtils.kComponentConfigs))>, RequiredComponent = utempl::kType<decltype(Get<I>(kUtils.kComponentConfigs))>,
dependencies.size() > J), "Constructor is not declared as constexpr"); dependencies.size() > J),
"Constructor is not declared as constexpr");
return *Get<I>(this->storage); return *Get<I>(this->storage);
}; };
}; };
namespace impl { namespace impl {
template <typename Current, std::size_t I> template <typename Current, std::size_t I>
struct DependencyInfoKey {}; struct DependencyInfoKey {};
@ -363,7 +354,8 @@ struct DependencyInfoInjector {
if constexpr(name == kBasicTaskProcessorName) { if constexpr(name == kBasicTaskProcessorName) {
return [] -> engine::basic::TaskProcessor<Config.template Get<"threads">() - 1> { return [] -> engine::basic::TaskProcessor<Config.template Get<"threads">() - 1> {
std::unreachable(); std::unreachable();
}(); }
();
} else { } else {
return [] -> decltype(Get<kUtils.GetIndexByName(name)>(kUtils.kTypeList)) { return [] -> decltype(Get<kUtils.GetIndexByName(name)>(kUtils.kTypeList)) {
std::unreachable(); std::unreachable();
@ -371,56 +363,34 @@ struct DependencyInfoInjector {
}; };
}; };
template < template <utempl::ConstexprString name,
utempl::ConstexprString name,
typename..., typename...,
std::size_t I = utempl::loopholes::Counter<Current, utempl::Wrapper<name>>(), std::size_t I = utempl::loopholes::Counter<Current, utempl::Wrapper<name>>(),
auto = utempl::loopholes::Injector< auto = utempl::loopholes::Injector<DependencyInfoKey<Current, I>{}, name>{}>
DependencyInfoKey<
Current,
I
>{},
name
>{}
>
static auto FindComponent() -> decltype(FindComponentType<name>())&; static auto FindComponent() -> decltype(FindComponentType<name>())&;
template <typename T, typename..., typename R = decltype(FindComponent<Get<kUtils.template GetIndexByType<T>()>(kUtils.kNames)>())>
template <
typename T,
typename...,
typename R = decltype(FindComponent<Get<kUtils.template GetIndexByType<T>()>(kUtils.kNames)>())
>
static auto FindComponent() -> R; static auto FindComponent() -> R;
template <template <typename...> typename F,
template <
template <typename...> typename F,
typename..., typename...,
auto Arr = std::to_array<bool>({F<typename Ts::Type>::value...}), auto Arr = std::to_array<bool>({F<typename Ts::Type>::value...}),
std::size_t I = std::ranges::find(Arr, true) - Arr.begin(), std::size_t I = std::ranges::find(Arr, true) - Arr.begin(),
typename R = decltype(FindComponent<decltype(Get<I>(utempl::TypeList<Ts...>{}))::kName>()) typename R = decltype(FindComponent<decltype(Get<I>(utempl::TypeList<Ts...>{}))::kName>())>
>
static auto FindComponent() -> R; static auto FindComponent() -> R;
private: private:
template <std::size_t... Ids> template <std::size_t... Ids>
static auto FindAllComponentsImpl() -> utempl::Tuple<decltype(FindComponent<decltype(Get<Ids>(utempl::TypeList<Ts...>{}))::kName>())...> {}; static auto FindAllComponentsImpl() -> utempl::Tuple<decltype(FindComponent<decltype(Get<Ids>(utempl::TypeList<Ts...>{}))::kName>())...> {
};
public: public:
template <template <typename...> typename F,
template <
template <typename...> typename F,
typename..., typename...,
typename R = decltype(utempl::Unpack(utempl::PackConstexprWrapper<kUtils.template GetAllIndexes<F>(), utempl::Tuple<>>(), typename R = decltype(utempl::Unpack(utempl::PackConstexprWrapper<kUtils.template GetAllIndexes<F>(), utempl::Tuple<>>(),
[](auto... is) -> decltype(FindAllComponentsImpl<is...>()) { [](auto... is) -> decltype(FindAllComponentsImpl<is...>()) {
std::unreachable(); std::unreachable();
})) }))>
>
static auto FindAllComponents() -> R; static auto FindAllComponents() -> R;
template <auto = 0> template <auto = 0>
@ -433,7 +403,8 @@ public:
} else { } else {
return utempl::Tuple{response}; return utempl::Tuple{response};
}; };
}() + ... + utempl::Tuple{}); }() + ... +
utempl::Tuple{});
} | utempl::kSeq<utempl::loopholes::Counter<Current>()>; } | utempl::kSeq<utempl::loopholes::Counter<Current>()>;
}; };
static inline consteval auto Inject() { static inline consteval auto Inject() {
@ -446,7 +417,6 @@ public:
template <utempl::ConstexprString Name, utempl::Tuple Dependencies> template <utempl::ConstexprString Name, utempl::Tuple Dependencies>
struct DependencyGraphElement {}; struct DependencyGraphElement {};
template <typename... Ts> template <typename... Ts>
struct DependencyGraph { struct DependencyGraph {
static constexpr auto kValue = utempl::TypeList<Ts...>{}; static constexpr auto kValue = utempl::TypeList<Ts...>{};
@ -460,7 +430,7 @@ struct CompileTimeStack {
std::size_t current{}; std::size_t current{};
inline constexpr auto Push(T value) { inline constexpr auto Push(T value) {
if(this->current == Size) { if(this->current == Size) {
throw std::out_of_range{0}; throw std::out_of_range{nullptr};
}; };
this->data[current].emplace(std::move(value)); this->data[current].emplace(std::move(value));
this->current++; this->current++;
@ -476,20 +446,19 @@ struct CompileTimeStack {
template <utempl::ConstexprString... Names, utempl::Tuple... Dependencies> template <utempl::ConstexprString... Names, utempl::Tuple... Dependencies>
consteval auto TopologicalSort(const DependencyGraph<DependencyGraphElement<Names, Dependencies>...>&) { consteval auto TopologicalSort(const DependencyGraph<DependencyGraphElement<Names, Dependencies>...>&) {
constexpr utempl::Tuple names = {Names...}; constexpr utempl::Tuple names = {Names...};
constexpr utempl::Tuple storage = Map(utempl::Tuple{Dependencies...}, constexpr utempl::Tuple storage = Map(utempl::Tuple{Dependencies...}, [&]<utempl::TupleLike Tuple>(Tuple&& tuple) {
[&]<utempl::TupleLike Tuple>(Tuple&& tuple){
static constexpr auto Size = utempl::kTupleSize<Tuple>; static constexpr auto Size = utempl::kTupleSize<Tuple>;
return utempl::Unpack(std::forward<Tuple>(tuple), return utempl::Unpack(std::forward<Tuple>(tuple), [&]<typename... Args>(Args&&... args) -> std::array<std::size_t, Size> {
[&]<typename... Args>(Args&&... args) -> std::array<std::size_t, Size> {
return {Find(names, std::forward<Args>(args))...}; return {Find(names, std::forward<Args>(args))...};
} });
);
}); });
constexpr auto Size = utempl::kTupleSize<decltype(storage)>; constexpr auto Size = utempl::kTupleSize<decltype(storage)>;
const std::array adj = utempl::Map(storage, const std::array adj = utempl::Map(
storage,
[](std::ranges::range auto& arg) { [](std::ranges::range auto& arg) {
return std::ranges::subrange(arg.data(), arg.data() + arg.size()); return std::ranges::subrange(arg.data(), arg.data() + arg.size());
}, utempl::kType<std::array<std::size_t, 0>>); },
utempl::kType<std::array<std::size_t, 0>>);
std::array<bool, Size> visited{}; std::array<bool, Size> visited{};
constexpr auto ResultSize = sizeof...(Dependencies); constexpr auto ResultSize = sizeof...(Dependencies);
impl::CompileTimeStack<std::size_t, ResultSize> response{}; impl::CompileTimeStack<std::size_t, ResultSize> response{};
@ -512,31 +481,36 @@ consteval auto TopologicalSort(const DependencyGraph<DependencyGraphElement<Name
}); });
}; };
template <ConstexprConfig config = {}, typename... ComponentConfigs> template <ConstexprConfig config = {}, typename... ComponentConfigs>
struct ServiceContextBuilder { struct ServiceContextBuilder {
static constexpr auto kList = utempl::kTypeList<ComponentConfigs...>; static constexpr auto kList = utempl::kTypeList<ComponentConfigs...>;
static constexpr impl::DependenciesUtils<ComponentConfigs...> kUtils; static constexpr impl::DependenciesUtils<ComponentConfigs...> kUtils;
static constexpr auto kConfig = config; static constexpr auto kConfig = config;
template <typename T, utempl::ConstexprString name = T::kName, typename... Os> template <typename T, utempl::ConstexprString name = T::kName, typename... Os>
static consteval auto Append(Options<Os...> = {}) -> decltype(T::template Adder<name, Options<Os...>{}>(ServiceContextBuilder<config, ComponentConfigs..., ComponentConfig<name, T, Options<Os...>{}>>{})) { static consteval auto Append(Options<Os...> = {})
-> decltype(T::template Adder<name, Options<Os...>{}>(
ServiceContextBuilder<config, ComponentConfigs..., ComponentConfig<name, T, Options<Os...>{}>>{})) {
return {}; return {};
}; };
template <typename T, utempl::ConstexprString name = T::kName, typename... Os> template <typename T, utempl::ConstexprString name = T::kName, typename... Os>
static consteval auto Append(Options<Os...> = {}) -> ServiceContextBuilder<config, ComponentConfigs..., ComponentConfig<name, T, Options<Os...>{}>> static consteval auto Append(Options<Os...> = {})
requires (!requires(ServiceContextBuilder<config, ComponentConfigs..., ComponentConfig<name, T, Options<Os...>{}>> builder) {T::template Adder<name, Options<Os...>{}>(builder);}) { -> ServiceContextBuilder<config, ComponentConfigs..., ComponentConfig<name, T, Options<Os...>{}>>
requires(!requires(ServiceContextBuilder<config, ComponentConfigs..., ComponentConfig<name, T, Options<Os...>{}>> builder) {
T::template Adder<name, Options<Os...>{}>(builder);
})
{
return {}; return {};
}; };
template <utempl::ConstexprString Key, auto Value> template <utempl::ConstexprString Key, auto Value>
static consteval auto AppendConfigParam() -> ServiceContextBuilder<config.template Append<Key>(Value), ComponentConfigs...> { static consteval auto AppendConfigParam() -> ServiceContextBuilder<config.template Append<Key>(Value), ComponentConfigs...> {
return {}; return {};
}; };
template <typename F> template <typename F>
static consteval auto TransformComponents(F&& f) -> ServiceContextBuilder<config, decltype(impl::TransformIfOk(ComponentConfigs{}, f))...> { static consteval auto TransformComponents(F&& f)
-> ServiceContextBuilder<config, decltype(impl::TransformIfOk(ComponentConfigs{}, f))...> {
return {}; return {};
}; };
template <utempl::ConstexprString name> template <utempl::ConstexprString name>
@ -544,10 +518,11 @@ struct ServiceContextBuilder {
if constexpr(name == kBasicTaskProcessorName) { if constexpr(name == kBasicTaskProcessorName) {
return [] -> engine::basic::TaskProcessor<config.template Get<"threads">() - 1> { return [] -> engine::basic::TaskProcessor<config.template Get<"threads">() - 1> {
std::unreachable(); std::unreachable();
}(); }
();
} else { } else {
return []<typename... TTs, utempl::ConstexprString... names, Options... Options> return []<typename... TTs, utempl::ConstexprString... names, Options... Options>(
(const ServiceContextBuilder<config, ComponentConfig<names, TTs, Options>...>&) const ServiceContextBuilder<config, ComponentConfig<names, TTs, Options>...>&)
-> decltype(utempl::Get<Find(utempl::Tuple{names...}, name)>(utempl::TypeList<TTs...>{})) { -> decltype(utempl::Get<Find(utempl::Tuple{names...}, name)>(utempl::TypeList<TTs...>{})) {
std::unreachable(); std::unreachable();
}(ServiceContextBuilder<config, ComponentConfigs...>{}); }(ServiceContextBuilder<config, ComponentConfigs...>{});
@ -567,19 +542,16 @@ struct ServiceContextBuilder {
}); });
}; };
static consteval auto Config() { static consteval auto Config() {
return config; return config;
}; };
static consteval auto GetDependencyGraph() { static consteval auto GetDependencyGraph() {
return [&]<utempl::ConstexprString... names, typename... Components, Options... Options> return [&]<utempl::ConstexprString... names, typename... Components, Options... Options>(
(ComponentConfig<names, Components, Options>...){ ComponentConfig<names, Components, Options>...) {
return DependencyGraph<DependencyGraphElement<names, return DependencyGraph<DependencyGraphElement<names,
[]<utempl::ConstexprString name, typename Component, ::cserver::Options OOptions> []<utempl::ConstexprString name, typename Component, ::cserver::Options OOptions>(
(ComponentConfig<name, Component, OOptions>) { ComponentConfig<name, Component, OOptions>) {
impl::DependencyInfoInjector<Component, name, config, ComponentConfigs...> injector; impl::DependencyInfoInjector<Component, name, config, ComponentConfigs...> injector;
injector.Inject(); injector.Inject();
return injector.GetDependencies(); return injector.GetDependencies();
@ -588,9 +560,10 @@ struct ServiceContextBuilder {
}; };
static consteval auto GetIndexDependencyGraph() { static consteval auto GetIndexDependencyGraph() {
return []<utempl::ConstexprString... Names, utempl::Tuple... Dependencies> return []<utempl::ConstexprString... Names, utempl::Tuple... Dependencies>(
(DependencyGraph<DependencyGraphElement<Names, Dependencies>...>){ DependencyGraph<DependencyGraphElement<Names, Dependencies>...>) {
return utempl::Tuple{Unpack(Dependencies, [](auto... dependencies) -> std::array<std::size_t, sizeof...(dependencies)> { return utempl::Tuple{Unpack(
Dependencies, [](auto... dependencies) -> std::array<std::size_t, sizeof...(dependencies)> {
return {Find(utempl::Tuple{Names...}, dependencies)...}; return {Find(utempl::Tuple{Names...}, dependencies)...};
})...}; })...};
}(GetDependencyGraph()); }(GetDependencyGraph());

View file

@ -62,17 +62,16 @@ public:
using Executor = any_io_executor; using Executor = any_io_executor;
#if !defined(BOOST_ASIO_DISABLE_AWAITABLE_FRAME_RECYCLING) #if !defined(BOOST_ASIO_DISABLE_AWAITABLE_FRAME_RECYCLING)
inline auto operator new(std::size_t size) -> void* { inline auto operator new(std::size_t size) -> void* {
return boost::asio::detail::thread_info_base::allocate( return boost::asio::detail::thread_info_base::allocate(boost::asio::detail::thread_info_base::awaitable_frame_tag(),
boost::asio::detail::thread_info_base::awaitable_frame_tag(),
boost::asio::detail::thread_context::top_of_thread_call_stack(), boost::asio::detail::thread_context::top_of_thread_call_stack(),
size); size);
}; };
inline auto operator delete(void* pointer, std::size_t size) -> void { inline auto operator delete(void* pointer, std::size_t size) -> void {
boost::asio::detail::thread_info_base::deallocate( boost::asio::detail::thread_info_base::deallocate(boost::asio::detail::thread_info_base::awaitable_frame_tag(),
boost::asio::detail::thread_info_base::awaitable_frame_tag(),
boost::asio::detail::thread_context::top_of_thread_call_stack(), boost::asio::detail::thread_context::top_of_thread_call_stack(),
pointer, size); pointer,
size);
}; };
#endif // !defined(BOOST_ASIO_DISABLE_AWAITABLE_FRAME_RECYCLING) #endif // !defined(BOOST_ASIO_DISABLE_AWAITABLE_FRAME_RECYCLING)
@ -137,7 +136,8 @@ public:
constraint_t<is_async_operation<Op>::value> = 0 constraint_t<is_async_operation<Op>::value> = 0
#if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING) #if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
#if defined(BOOST_ASIO_HAS_SOURCE_LOCATION) #if defined(BOOST_ASIO_HAS_SOURCE_LOCATION)
, detail::source_location location = detail::source_location::current() ,
detail::source_location location = detail::source_location::current()
#endif // defined(BOOST_ASIO_HAS_SOURCE_LOCATION) #endif // defined(BOOST_ASIO_HAS_SOURCE_LOCATION)
#endif // defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING) #endif // defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
) { ) {
@ -147,12 +147,12 @@ public:
}; };
}; };
return awaitable_async_op< return awaitable_async_op<completion_signature_of_t<Op>, decay_t<Op>, Executor>{std::forward<Op>(op),
completion_signature_of_t<Op>, decay_t<Op>, Executor>{ this
std::forward<Op>(op), this
#if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING) #if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
#if defined(BOOST_ASIO_HAS_SOURCE_LOCATION) #if defined(BOOST_ASIO_HAS_SOURCE_LOCATION)
, location ,
location
#endif // defined(BOOST_ASIO_HAS_SOURCE_LOCATION) #endif // defined(BOOST_ASIO_HAS_SOURCE_LOCATION)
#endif // defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING) #endif // defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
}; };
@ -219,8 +219,7 @@ public:
// This await transformation resets the associated cancellation state. // This await transformation resets the associated cancellation state.
template <typename Filter> template <typename Filter>
inline constexpr auto await_transform( inline constexpr auto await_transform(this_coro::reset_cancellation_state_1_t<Filter> reset) noexcept {
this_coro::reset_cancellation_state_1_t<Filter> reset) noexcept {
struct Result { struct Result {
awaitable_frame_base* this_; awaitable_frame_base* this_;
Filter filter_; Filter filter_;
@ -232,8 +231,7 @@ public:
inline constexpr auto await_suspend(coroutine_handle<void>) noexcept -> void{}; inline constexpr auto await_suspend(coroutine_handle<void>) noexcept -> void{};
inline constexpr auto await_resume() { inline constexpr auto await_resume() {
return this_->attached_thread_->reset_cancellation_state( return this_->attached_thread_->reset_cancellation_state(static_cast<Filter&&>(filter_));
static_cast<Filter&&>(filter_));
}; };
}; };
@ -246,8 +244,7 @@ public:
// This await transformation resets the associated cancellation state. // This await transformation resets the associated cancellation state.
template <typename InFilter, typename OutFilter> template <typename InFilter, typename OutFilter>
inline constexpr auto await_transform( inline constexpr auto await_transform(this_coro::reset_cancellation_state_2_t<InFilter, OutFilter> reset) noexcept {
this_coro::reset_cancellation_state_2_t<InFilter, OutFilter> reset) noexcept {
struct Result { struct Result {
awaitable_frame_base* this_; awaitable_frame_base* this_;
InFilter in_filter_; InFilter in_filter_;
@ -260,15 +257,12 @@ public:
inline constexpr auto await_suspend(coroutine_handle<void>) noexcept -> void{}; inline constexpr auto await_suspend(coroutine_handle<void>) noexcept -> void{};
inline constexpr auto await_resume() { inline constexpr auto await_resume() {
return this_->attached_thread_->reset_cancellation_state( return this_->attached_thread_->reset_cancellation_state(static_cast<InFilter&&>(in_filter_),
static_cast<InFilter&&>(in_filter_),
static_cast<OutFilter&&>(out_filter_)); static_cast<OutFilter&&>(out_filter_));
}; };
}; };
return Result{this, return Result{this, static_cast<InFilter&&>(reset.in_filter), static_cast<OutFilter&&>(reset.out_filter)};
static_cast<InFilter&&>(reset.in_filter),
static_cast<OutFilter&&>(reset.out_filter)};
}; };
// This await transformation determines whether cancellation is propagated as // This await transformation determines whether cancellation is propagated as
@ -317,13 +311,9 @@ public:
// immediate resumption of the coroutine in another thread does not cause a // immediate resumption of the coroutine in another thread does not cause a
// race condition. // race condition.
template <typename Function> template <typename Function>
inline constexpr auto await_transform(Function f, inline constexpr auto await_transform(
enable_if_t< Function f,
is_convertible< enable_if_t<is_convertible<result_of_t<Function(awaitable_frame_base*)>, awaitable_thread<Executor>*>::value>* = nullptr) {
result_of_t<Function(awaitable_frame_base*)>,
awaitable_thread<Executor>*
>::value
>* = nullptr) {
struct Result { struct Result {
Function function_; Function function_;
awaitable_frame_base* this_; awaitable_frame_base* this_;
@ -334,11 +324,11 @@ public:
inline constexpr auto await_suspend(coroutine_handle<void>) noexcept -> void { inline constexpr auto await_suspend(coroutine_handle<void>) noexcept -> void {
this_->after_suspend( this_->after_suspend(
[](void* arg) [](void* arg) {
{
Result* r = static_cast<Result*>(arg); Result* r = static_cast<Result*>(arg);
r->function_(r->this_); r->function_(r->this_);
}, this); },
this);
}; };
inline constexpr auto await_resume() const noexcept -> void {}; inline constexpr auto await_resume() const noexcept -> void {};
@ -424,6 +414,7 @@ public:
return awaitable; return awaitable;
}; };
std::size_t threadId{}; std::size_t threadId{};
protected: protected:
coroutine_handle<void> coro_ = nullptr; coroutine_handle<void> coro_ = nullptr;
awaitable_thread<Executor>* attached_thread_ = nullptr; awaitable_thread<Executor>* attached_thread_ = nullptr;

View file

@ -9,15 +9,16 @@ class TcpResolver;
class IoContext { class IoContext {
boost::asio::io_context impl; boost::asio::io_context impl;
public: public:
inline constexpr IoContext() : impl() {}; inline IoContext() : impl() {};
class Work { class Work {
boost::asio::io_context::work impl; boost::asio::io_context::work impl;
public: public:
inline constexpr Work(IoContext& ioContext) : explicit inline Work(IoContext& ioContext) : impl(ioContext.impl) {};
impl(ioContext.impl) {}; inline Work(Work&&) = default;
inline constexpr Work(Work&&) = default; inline Work(const Work&) = default;
inline constexpr Work(const Work&) = default;
}; };
friend DeadLineTimer; friend DeadLineTimer;
@ -26,13 +27,12 @@ public:
friend TcpResolver; friend TcpResolver;
}; };
class DeadLineTimer { class DeadLineTimer {
boost::asio::deadline_timer impl; boost::asio::deadline_timer impl;
public: public:
inline constexpr DeadLineTimer(IoContext& ioContext) : explicit inline DeadLineTimer(IoContext& ioContext) : impl(ioContext.impl) {};
impl(ioContext.impl) {}; inline auto AsyncWait() -> Task<> {
inline constexpr auto AsyncWait() -> Task<> {
return this->impl.async_wait(boost::asio::use_awaitable); return this->impl.async_wait(boost::asio::use_awaitable);
}; };
inline constexpr auto Cancel() -> void { inline constexpr auto Cancel() -> void {
@ -45,11 +45,10 @@ public:
class TcpSocket { class TcpSocket {
boost::asio::ip::tcp::socket impl; boost::asio::ip::tcp::socket impl;
public: public:
inline constexpr TcpSocket(IoContext& ioContext) : explicit inline TcpSocket(IoContext& ioContext) : impl(ioContext.impl) {};
impl(ioContext.impl) {}; inline TcpSocket(TcpSocket&& other) : impl(std::move(other.impl)) {};
inline constexpr TcpSocket(TcpSocket&& other) :
impl(std::move(other.impl)) {};
template <typename Buffer, typename CompletionCondition> template <typename Buffer, typename CompletionCondition>
friend inline constexpr auto AsyncWrite(TcpSocket& socket, Buffer buffer, CompletionCondition completion); friend inline constexpr auto AsyncWrite(TcpSocket& socket, Buffer buffer, CompletionCondition completion);
@ -68,40 +67,38 @@ class TcpEntry;
class TcpEndpoint { class TcpEndpoint {
boost::asio::ip::tcp::endpoint impl; boost::asio::ip::tcp::endpoint impl;
inline constexpr TcpEndpoint(boost::asio::ip::tcp::endpoint impl) : impl(std::move(impl)) {}; explicit inline TcpEndpoint(boost::asio::ip::tcp::endpoint impl) : impl(std::move(impl)) {};
public: public:
friend TcpEntry; friend TcpEntry;
}; };
class TcpEntry { class TcpEntry {
boost::asio::ip::tcp::resolver::results_type::value_type impl; boost::asio::ip::tcp::resolver::results_type::value_type impl;
public: public:
inline constexpr TcpEntry() = default; inline TcpEntry() = default;
inline constexpr TcpEntry(const TcpEndpoint& ep, inline TcpEntry(const TcpEndpoint& ep, std::string_view host, std::string_view service) : impl(ep.impl, host, service) {};
std::string_view host, inline auto Endpoint() -> TcpEndpoint {
std::string_view service) : impl(ep.impl, host, service) {}; return TcpEndpoint{this->impl.endpoint()};
inline constexpr auto Endpoint() -> TcpEndpoint {
return {this->impl.endpoint()};
}; };
inline constexpr auto HostName() -> std::string { inline auto HostName() -> std::string {
return this->impl.host_name(); return this->impl.host_name();
}; };
inline constexpr auto ServiceName() -> std::string { inline constexpr auto ServiceName() -> std::string {
return this->impl.service_name(); return this->impl.service_name();
}; };
inline constexpr operator TcpEndpoint() { explicit inline operator TcpEndpoint() {
return {this->impl}; return TcpEndpoint{this->impl};
}; };
}; };
class TcpResolver { class TcpResolver {
boost::asio::ip::tcp::resolver impl; boost::asio::ip::tcp::resolver impl;
public:
inline constexpr TcpResolver(IoContext& ioContext) : impl(ioContext.impl) {};
};
public:
explicit inline TcpResolver(IoContext& ioContext) : impl(ioContext.impl) {};
};
template <typename T> template <typename T>
inline constexpr auto Buffer(const T* ptr, std::size_t size) { inline constexpr auto Buffer(const T* ptr, std::size_t size) {
@ -113,33 +110,32 @@ inline constexpr auto DynamicBuffer(T&& arg) -> decltype(boost::asio::dynamic_bu
return boost::asio::dynamic_buffer(std::forward<T>(arg)); return boost::asio::dynamic_buffer(std::forward<T>(arg));
}; };
template <typename Socket, typename Buffer> template <typename Socket, typename Buffer>
inline constexpr auto AsyncWrite(Socket&& socket, Buffer buffer) -> Task<> { inline auto AsyncWrite(Socket&& socket, Buffer buffer) -> Task<> {
return boost::asio::async_write(std::forward<Socket>(socket).impl, std::move(buffer), boost::asio::use_awaitable); return boost::asio::async_write(std::forward<Socket>(socket).impl, std::move(buffer), boost::asio::use_awaitable);
}; };
template <typename Socket, typename Buffer, typename CompletionCondition> template <typename Socket, typename Buffer, typename CompletionCondition>
inline constexpr auto AsyncWrite(Socket&& socket, Buffer buffer, CompletionCondition completion) -> Task<> { inline auto AsyncWrite(Socket&& socket, Buffer buffer, CompletionCondition completion) -> Task<> {
return boost::asio::async_write(std::forward<Socket>(socket).impl, std::move(buffer), std::move(completion), boost::asio::use_awaitable); return boost::asio::async_write(std::forward<Socket>(socket).impl, std::move(buffer), std::move(completion), boost::asio::use_awaitable);
}; };
template <typename Socket, typename Buffer> template <typename Socket, typename Buffer>
inline constexpr auto AsyncRead(Socket&& socket, Buffer buffer) -> Task<> { inline auto AsyncRead(Socket&& socket, Buffer buffer) -> Task<> {
return boost::asio::async_read(std::forward<Socket>(socket).impl, std::move(buffer), boost::asio::use_awaitable); return boost::asio::async_read(std::forward<Socket>(socket).impl, std::move(buffer), boost::asio::use_awaitable);
}; };
template <typename Socket, typename Buffer, typename CompletionCondition> template <typename Socket, typename Buffer, typename CompletionCondition>
inline constexpr auto AsyncRead(Socket&& socket, Buffer buffer, CompletionCondition completion) -> Task<> { inline auto AsyncRead(Socket&& socket, Buffer buffer, CompletionCondition completion) -> Task<> {
return boost::asio::async_read(std::forward<Socket>(socket).impl, std::move(buffer), std::move(completion), boost::asio::use_awaitable); return boost::asio::async_read(std::forward<Socket>(socket).impl, std::move(buffer), std::move(completion), boost::asio::use_awaitable);
}; };
template <typename Socket, typename Buffer, typename Match> template <typename Socket, typename Buffer, typename Match>
inline constexpr auto AsyncReadUntil(Socket&& socket, Buffer buffer, Match match) -> Task<> { inline auto AsyncReadUntil(Socket&& socket, Buffer buffer, Match match) -> Task<> {
return boost::asio::async_read_until(std::forward<Socket>(socket).impl, std::move(buffer), std::move(match), boost::asio::use_awaitable); return boost::asio::async_read_until(std::forward<Socket>(socket).impl, std::move(buffer), std::move(match), boost::asio::use_awaitable);
}; };
inline constexpr auto TransferAll() { inline auto TransferAll() {
return boost::asio::transfer_all(); return boost::asio::transfer_all();
}; };

View file

@ -1,14 +1,13 @@
#pragma once #pragma once
#include <cserver/components/loggable_component_base.hpp>
#include <cserver/engine/components.hpp> #include <cserver/engine/components.hpp>
#include <cserver/engine/coroutine.hpp>
#include <cserver/server/http/http_request.hpp> #include <cserver/server/http/http_request.hpp>
#include <cserver/server/http/http_response.hpp> #include <cserver/server/http/http_response.hpp>
#include <cserver/server/http/http_stream.hpp> #include <cserver/server/http/http_stream.hpp>
#include <cserver/engine/coroutine.hpp>
#include <cserver/components/loggable_component_base.hpp>
namespace cserver::server::handlers { namespace cserver::server::handlers {
struct HttpHandlerBase : ComponentBase { struct HttpHandlerBase : ComponentBase {
template <typename T, utempl::ConstexprString name, Options> template <typename T, utempl::ConstexprString name, Options>
static consteval auto HttpHandlerAdder(const auto& context) { static consteval auto HttpHandlerAdder(const auto& context) {
@ -17,25 +16,31 @@ struct HttpHandlerBase : ComponentBase {
}); });
}; };
template <typename Self> template <typename Self>
inline auto HandleRequest(this Self&& self, http::HttpRequest&& request inline auto HandleRequest(this Self&& self, http::HttpRequest&& request) -> Task<http::HttpResponse>
) -> Task<http::HttpResponse> requires requires{self.HandleRequestThrow(std::move(request));} { requires requires { self.HandleRequestThrow(std::move(request)); }
{
using T = std::remove_cvref_t<Self>; using T = std::remove_cvref_t<Self>;
try { try {
co_return co_await std::forward<Self>(self).HandleRequestThrow(std::move(request)); co_return co_await std::forward<Self>(self).HandleRequestThrow(std::move(request));
} catch(const std::exception& err) { } catch(const std::exception& err) {
auto typeName = boost::core::demangle(__cxxabiv1::__cxa_current_exception_type()->name()); auto typeName = boost::core::demangle(__cxxabiv1::__cxa_current_exception_type()->name());
if(self.logging.level <= LoggingLevel::kWarning) if(self.logging.level <= LoggingLevel::kWarning) {
self.logging.template Warning<"In handler with default name {} uncaught exception of type {}: {}">(T::kName, typeName, err.what()); self.logging.template Warning<"In handler with default name {} uncaught exception of type {}: {}">(T::kName, typeName, err.what());
};
} catch(...) { } catch(...) {
auto typeName = boost::core::demangle(__cxxabiv1::__cxa_current_exception_type()->name()); auto typeName = boost::core::demangle(__cxxabiv1::__cxa_current_exception_type()->name());
if(self.logging.level <= LoggingLevel::kWarning) if(self.logging.level <= LoggingLevel::kWarning) {
self.logging.template Warning<"In handler with default name {} uncaught exception of type {}">(T::kName, typeName); self.logging.template Warning<"In handler with default name {} uncaught exception of type {}">(T::kName, typeName);
}; };
co_return http::HttpResponse{.statusCode = 500, .statusMessage = "Internal Server Error", .body = "Internal Server Error"}; };
co_return http::HttpResponse{.statusCode = 500, .statusMessage = "Internal Server Error", .body = "Internal Server Error"}; // NOLINT
}; };
template <typename Self> template <typename Self>
inline auto HandleRequestStream(this Self&& self, cserver::server::http::HttpRequest&& request, inline auto HandleRequestStream(this Self&& self,
cserver::server::http::HttpStream& stream) -> Task<void> requires requires{self.HandleRequestStreamThrow(std::move(request), stream);} { cserver::server::http::HttpRequest&& request,
cserver::server::http::HttpStream& stream) -> Task<void>
requires requires { self.HandleRequestStreamThrow(std::move(request), stream); }
{
using T = std::remove_cvref_t<Self>; using T = std::remove_cvref_t<Self>;
try { try {
co_await std::forward<Self>(self).HandleRequestStreamThrow(std::move(request), stream); co_await std::forward<Self>(self).HandleRequestStreamThrow(std::move(request), stream);
@ -46,8 +51,7 @@ struct HttpHandlerBase : ComponentBase {
}; };
co_await stream.Close(); co_await stream.Close();
}; };
inline constexpr HttpHandlerBase(auto& context) : explicit constexpr HttpHandlerBase(auto& context) : ComponentBase(context) {};
ComponentBase(context) {};
}; };
template <typename T> template <typename T>
struct HttpHandlerAdderType { struct HttpHandlerAdderType {
@ -58,9 +62,7 @@ struct HttpHandlerAdderType {
}; };
template <typename T> template <typename T>
struct HttpHandlerBaseWithAdder : HttpHandlerBase, HttpHandlerAdderType<T> { struct HttpHandlerBaseWithAdder : HttpHandlerBase, HttpHandlerAdderType<T> {
inline constexpr HttpHandlerBaseWithAdder(auto& context) : explicit constexpr HttpHandlerBaseWithAdder(auto& context) : HttpHandlerBase(context), HttpHandlerAdderType<T>{} {};
HttpHandlerBase(context),
HttpHandlerAdderType<T>{} {};
}; };
} // namespace cserver::server::handlers } // namespace cserver::server::handlers

View file

@ -1,8 +1,9 @@
#pragma once #pragma once
#include <fmt/format.h> #include <fmt/format.h>
#include <unordered_map>
#include <sstream>
#include <boost/url.hpp> #include <boost/url.hpp>
#include <sstream>
#include <unordered_map>
namespace cserver::server::http { namespace cserver::server::http {
@ -11,7 +12,7 @@ struct HttpRequest {
boost::urls::url url = {}; boost::urls::url url = {};
std::unordered_map<std::string, std::string> headers = {}; std::unordered_map<std::string, std::string> headers = {};
std::string body = {}; std::string body = {};
inline auto ToString() const -> std::string { [[nodiscard]] inline auto ToString() const -> std::string {
std::ostringstream stream; std::ostringstream stream;
stream << fmt::format("{} {} Http/1.1\r\n", this->method, this->url.path()); stream << fmt::format("{} {} Http/1.1\r\n", this->method, this->url.path());
for(const auto& header : this->headers) { for(const auto& header : this->headers) {

View file

@ -1,7 +1,8 @@
#pragma once #pragma once
#include <cserver/server/http/http_request.hpp>
#include <llhttp.h> #include <llhttp.h>
#include <cserver/server/http/http_request.hpp>
namespace cserver::server::http { namespace cserver::server::http {
struct HttpRequestParser : private llhttp_t, public HttpRequest { struct HttpRequestParser : private llhttp_t, public HttpRequest {
@ -10,7 +11,7 @@ struct HttpRequestParser : private llhttp_t, public HttpRequest {
std::string headerField = {}; std::string headerField = {};
std::string headerValue = {}; std::string headerValue = {};
std::string urlString = {}; std::string urlString = {};
inline HttpRequestParser(std::string_view data) { inline HttpRequestParser(std::string_view data) { // NOLINT
llhttp_settings_t settings; llhttp_settings_t settings;
llhttp_settings_init(&settings); llhttp_settings_init(&settings);
settings.on_method = HttpRequestParser::OnMethod; settings.on_method = HttpRequestParser::OnMethod;
@ -25,44 +26,44 @@ struct HttpRequestParser : private llhttp_t, public HttpRequest {
llhttp_execute(this, data.data(), data.size()); llhttp_execute(this, data.data(), data.size());
}; };
static inline auto OnMethod(llhttp_t* parser, const char* data, std::size_t size) -> int { static inline auto OnMethod(llhttp_t* parser, const char* data, std::size_t size) -> int {
auto* self = static_cast<HttpRequest*>(static_cast<HttpRequestParser*>(parser)); auto* self = static_cast<HttpRequest*>(static_cast<HttpRequestParser*>(parser)); // NOLINT
self->method.append(data, size); self->method.append(data, size);
return 0; return 0;
}; };
static inline auto OnUrl(llhttp_t* parser, const char* data, std::size_t size) -> int { static inline auto OnUrl(llhttp_t* parser, const char* data, std::size_t size) -> int {
auto* self = static_cast<HttpRequestParser*>(parser); auto* self = static_cast<HttpRequestParser*>(parser); // NOLINT
self->urlString.append(data, size); self->urlString.append(data, size);
return 0; return 0;
}; };
static inline auto OnUrlComplete(llhttp_t* parser) -> int { static inline auto OnUrlComplete(llhttp_t* parser) -> int {
auto* self = static_cast<HttpRequestParser*>(parser); auto* self = static_cast<HttpRequestParser*>(parser); // NOLINT
self->url = boost::urls::url(self->urlString); self->url = boost::urls::url(self->urlString);
return 0; return 0;
}; };
static inline auto OnHeaderField(llhttp_t* parser, const char* data, std::size_t size) -> int { static inline auto OnHeaderField(llhttp_t* parser, const char* data, std::size_t size) -> int {
auto* self = static_cast<HttpRequestParser*>(parser); auto* self = static_cast<HttpRequestParser*>(parser); // NOLINT
self->headerField.append(data, size); self->headerField.append(data, size);
return 0; return 0;
}; };
static inline auto OnHeaderValue(llhttp_t* parser, const char* data, std::size_t size) -> int { static inline auto OnHeaderValue(llhttp_t* parser, const char* data, std::size_t size) -> int {
auto* self = static_cast<HttpRequestParser*>(parser); auto* self = static_cast<HttpRequestParser*>(parser); // NOLINT
self->headerValue.append(data, size); self->headerValue.append(data, size);
return 0; return 0;
}; };
static inline auto OnHeaderComplete(llhttp_t* parser) -> int { static inline auto OnHeaderComplete(llhttp_t* parser) -> int {
auto* self = static_cast<HttpRequestParser*>(parser); auto* self = static_cast<HttpRequestParser*>(parser); // NOLINT
self->headers.emplace(std::move(self->headerField), std::move(self->headerValue)); self->headers.emplace(std::move(self->headerField), std::move(self->headerValue));
self->headerValue.clear(); self->headerValue.clear();
self->headerField.clear(); self->headerField.clear();
return 0; return 0;
}; };
static inline auto OnBody(llhttp_t* parser, const char* data, std::size_t size) -> int { static inline auto OnBody(llhttp_t* parser, const char* data, std::size_t size) -> int {
auto* self = static_cast<HttpRequestParser*>(parser); auto* self = static_cast<HttpRequestParser*>(parser); // NOLINT
self->body.append(data, size); self->body.append(data, size);
return 0; return 0;
}; };
static inline auto OnMessageComplete(llhttp_t* parser) -> int { static inline auto OnMessageComplete(llhttp_t* parser) -> int {
auto* self = static_cast<HttpRequestParser*>(parser); auto* self = static_cast<HttpRequestParser*>(parser); // NOLINT
self->done = true; self->done = true;
return 0; return 0;
}; };

View file

@ -1,16 +1,17 @@
#pragma once #pragma once
#include <unordered_map>
#include <fmt/format.h> #include <fmt/format.h>
#include <sstream> #include <sstream>
#include <unordered_map>
namespace cserver::server::http { namespace cserver::server::http {
struct HttpResponse { struct HttpResponse {
unsigned short statusCode = 200; std::uint16_t statusCode = 200; // NOLINT
std::string statusMessage = "OK"; std::string statusMessage = "OK";
std::unordered_map<std::string, std::string> headers = {}; std::unordered_map<std::string, std::string> headers = {};
std::string body = {}; std::string body = {};
inline auto ToString() const -> std::string { [[nodiscard]] inline auto ToString() const -> std::string {
std::ostringstream stream; std::ostringstream stream;
stream << fmt::format("Http/1.1 {} {}\r\n", this->statusCode, this->statusMessage); stream << fmt::format("Http/1.1 {} {}\r\n", this->statusCode, this->statusMessage);
for(const auto& header : this->headers) { for(const auto& header : this->headers) {

View file

@ -1,7 +1,8 @@
#pragma once #pragma once
#include <cserver/engine/coroutine.hpp>
#include <fmt/format.h> #include <fmt/format.h>
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include <cserver/engine/coroutine.hpp>
namespace cserver::server::http { namespace cserver::server::http {
@ -10,11 +11,13 @@ struct HttpStream {
std::stringstream stream = {}; std::stringstream stream = {};
inline auto SetMethod(std::string method) -> Task<void> { inline auto SetMethod(std::string method) -> Task<void> {
method += " "; method += " ";
co_await boost::asio::async_write(this->socket, boost::asio::buffer(method.data(), method.size()), boost::asio::transfer_all(), boost::asio::use_awaitable); co_await boost::asio::async_write(
this->socket, boost::asio::buffer(method.data(), method.size()), boost::asio::transfer_all(), boost::asio::use_awaitable);
}; };
inline auto SetStatus(std::string status) -> Task<void> { inline auto SetStatus(std::string status) -> Task<void> {
status = fmt::format("Http/1.1 {}\r\n", std::move(status)); status = fmt::format("Http/1.1 {}\r\n", std::move(status));
co_await boost::asio::async_write(this->socket, boost::asio::buffer(status.data(), status.size()), boost::asio::transfer_all(), boost::asio::use_awaitable); co_await boost::asio::async_write(
this->socket, boost::asio::buffer(status.data(), status.size()), boost::asio::transfer_all(), boost::asio::use_awaitable);
}; };
inline auto SetHeader(std::string first, std::string second) -> Task<void> { inline auto SetHeader(std::string first, std::string second) -> Task<void> {
@ -24,10 +27,12 @@ struct HttpStream {
inline auto SetEndOfHeaders() -> Task<void> { inline auto SetEndOfHeaders() -> Task<void> {
this->stream << "\r\n"; this->stream << "\r\n";
auto str = this->stream.str(); auto str = this->stream.str();
co_await boost::asio::async_write(this->socket, boost::asio::buffer(str.data(), str.size()), boost::asio::transfer_all(), boost::asio::use_awaitable); co_await boost::asio::async_write(
this->socket, boost::asio::buffer(str.data(), str.size()), boost::asio::transfer_all(), boost::asio::use_awaitable);
}; };
inline auto PushBodyChunk(std::string_view chunk) -> Task<void> { inline auto PushBodyChunk(std::string_view chunk) -> Task<void> {
co_await boost::asio::async_write(this->socket, boost::asio::buffer(chunk.data(), chunk.size()), boost::asio::transfer_all(), boost::asio::use_awaitable); co_await boost::asio::async_write(
this->socket, boost::asio::buffer(chunk.data(), chunk.size()), boost::asio::transfer_all(), boost::asio::use_awaitable);
}; };
inline auto Close() -> Task<void> { inline auto Close() -> Task<void> {
this->socket.close(); this->socket.close();

View file

@ -1,22 +1,20 @@
#pragma once #pragma once
#include <boost/asio.hpp>
#include <cserver/components/work_guard.hpp>
#include <cserver/engine/components.hpp> #include <cserver/engine/components.hpp>
#include <cserver/engine/coroutine.hpp>
#include <cserver/server/http/http_request_parser.hpp> #include <cserver/server/http/http_request_parser.hpp>
#include <cserver/server/http/http_response.hpp> #include <cserver/server/http/http_response.hpp>
#include <cserver/engine/coroutine.hpp>
#include <cserver/server/http/http_stream.hpp> #include <cserver/server/http/http_stream.hpp>
#include <cserver/components/work_guard.hpp>
#include <boost/asio.hpp>
namespace cserver::server::server { namespace cserver::server::server {
template <utempl::ConstexprString TPName = "basicTaskProcessor", typename TaskProcessor = int, typename... Ts> template <utempl::ConstexprString TPName = "basicTaskProcessor", typename TaskProcessor = int, typename... Ts>
struct Server : StopBlocker { struct Server : StopBlocker {
TaskProcessor& taskProcessor; TaskProcessor& taskProcessor;
utempl::Tuple<impl::GetTypeFromComponentConfig<Ts>&...> handlers; utempl::Tuple<impl::GetTypeFromComponentConfig<Ts>&...> handlers;
static constexpr utempl::ConstexprString kName = "server"; static constexpr utempl::ConstexprString kName = "server";
unsigned short port; std::uint16_t port;
static constexpr utempl::Tuple kNames = {impl::kNameFromComponentConfig<Ts>...}; static constexpr utempl::Tuple kNames = {impl::kNameFromComponentConfig<Ts>...};
static constexpr utempl::Tuple kPaths = {impl::GetTypeFromComponentConfig<Ts>::kPath...}; static constexpr utempl::Tuple kPaths = {impl::GetTypeFromComponentConfig<Ts>::kPath...};
template <utempl::ConstexprString name, Options Options, typename T> template <utempl::ConstexprString name, Options Options, typename T>
@ -29,26 +27,22 @@ struct Server : StopBlocker {
}; };
}(); }();
using TP = decltype(context.template FindComponent<tpName>()); using TP = decltype(context.template FindComponent<tpName>());
return context.TransformComponents( return context.TransformComponents([&](const ComponentConfig<name, Server<TPName, int, Ts...>, Options>&)
[&](const ComponentConfig<name, Server<TPName, int, Ts...>, Options>&) -> ComponentConfig<name, Server<tpName, TP, Ts...>, Options> { -> ComponentConfig<name, Server<tpName, TP, Ts...>, Options> {
return {}; return {};
}); });
}; };
template < template <typename T, std::size_t... Is>
typename T, constexpr Server(std::index_sequence<Is...>, T& context) :
std::size_t... Is>
inline constexpr Server(std::index_sequence<Is...>, T& context) :
StopBlocker(context), StopBlocker(context),
taskProcessor(context.template FindComponent<TPName>()), taskProcessor(context.template FindComponent<TPName>()),
handlers{context.template FindComponent<Get<Is>(kNames)>()...}, handlers{context.template FindComponent<Get<Is>(kNames)>()...},
port(T::kConfig.template Get<T::kName>().template Get<"port">()){ port(T::kConfig.template Get<T::kName>().template Get<"port">()){
}; };
inline constexpr Server(auto& context) : explicit constexpr Server(auto& context) : Server(std::index_sequence_for<Ts...>{}, context) {};
Server(std::index_sequence_for<Ts...>{}, context) {
};
template <auto I, typename Socket> template <auto I, typename Socket>
auto ProcessHandler(Socket&& socket, http::HttpRequest request) -> Task<void> { inline auto ProcessHandler(Socket&& socket, http::HttpRequest request) -> Task<void> {
if constexpr(requires(http::HttpStream& stream) { Get<I>(this->handlers).HandleRequestStream(std::move(request), stream); }) { if constexpr(requires(http::HttpStream& stream) { Get<I>(this->handlers).HandleRequestStream(std::move(request), stream); }) {
http::HttpStream stream{std::move(socket)}; http::HttpStream stream{std::move(socket)};
co_await Get<I>(this->handlers).HandleRequestStream(std::move(request), stream); co_await Get<I>(this->handlers).HandleRequestStream(std::move(request), stream);
@ -66,23 +60,26 @@ struct Server : StopBlocker {
std::string buffer; std::string buffer;
buffer.reserve(socket.available()); buffer.reserve(socket.available());
co_await boost::asio::async_read_until(socket, boost::asio::dynamic_buffer(buffer), "\r\n\r\n", boost::asio::use_awaitable); co_await boost::asio::async_read_until(socket, boost::asio::dynamic_buffer(buffer), "\r\n\r\n", boost::asio::use_awaitable);
http::HttpRequest request = http::HttpRequestParser{buffer}; http::HttpRequest request = http::HttpRequestParser{buffer}; // NOLINT
bool flag = false; bool flag = false;
co_await [&]<auto... Is>(std::index_sequence<Is...>) -> cserver::Task<void> { co_await [&]<auto... Is>(std::index_sequence<Is...>) -> cserver::Task<void> {
(co_await [&] -> cserver::Task<void> { (
co_await [&] -> cserver::Task<void> {
if(request.url.path().substr(0, Get<Is>(kPaths).size()) == Get<Is>(kPaths)) { if(request.url.path().substr(0, Get<Is>(kPaths).size()) == Get<Is>(kPaths)) {
co_await this->ProcessHandler<Is>(std::move(socket), std::move(request)); co_await this->ProcessHandler<Is>(std::move(socket), std::move(request));
}; };
}(), ...); }(),
...);
}(std::index_sequence_for<Ts...>()); }(std::index_sequence_for<Ts...>());
constexpr std::string_view error404 = "Http/1.1 404 Not Found\r\n" constexpr std::string_view error404 =
"Http/1.1 404 Not Found\r\n"
"Content-Length: 0\r\n" "Content-Length: 0\r\n"
"\r\n"; "\r\n";
if(!flag) { if(!flag) {
co_await boost::asio::async_write(socket, boost::asio::buffer(error404.data(), error404.size()), boost::asio::use_awaitable); co_await boost::asio::async_write(socket, boost::asio::buffer(error404.data(), error404.size()), boost::asio::use_awaitable);
}; };
}; };
auto Task() -> Task<void> { inline auto Task() -> Task<void> {
auto executor = co_await boost::asio::this_coro::executor; auto executor = co_await boost::asio::this_coro::executor;
boost::asio::ip::tcp::acceptor acceptor{executor, {boost::asio::ip::tcp::v6(), this->port}}; boost::asio::ip::tcp::acceptor acceptor{executor, {boost::asio::ip::tcp::v6(), this->port}};
for(;;) { for(;;) {
@ -93,8 +90,7 @@ struct Server : StopBlocker {
inline constexpr auto Run() -> void { inline constexpr auto Run() -> void {
boost::asio::co_spawn(this->taskProcessor.ioContext, this->Task(), boost::asio::detached); boost::asio::co_spawn(this->taskProcessor.ioContext, this->Task(), boost::asio::detached);
}; };
inline ~Server() { inline ~Server() = default;
};
template <typename Handler> template <typename Handler>
using AddHandler = Server<TPName, TaskProcessor, Ts..., Handler>; using AddHandler = Server<TPName, TaskProcessor, Ts..., Handler>;
}; };