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
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <cserver/clients/http/request.hpp>
#include <cserver/clients/http/response.hpp>
#include <cserver/engine/components.hpp>
#include <cserver/engine/coroutine.hpp>
#include <cserver/utils/boost_error_wrapper.hpp>
#include <cserver/engine/use_streaming.hpp>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <utempl/constexpr_string.hpp>
namespace cserver::server::http {
@ -16,9 +15,8 @@ inline constexpr auto ParseHttpHeader(std::string header) -> std::pair<std::stri
std::string key = header.substr(0, pos);
std::string value = header.substr(pos + 2, header.size() - 1);
return std::make_pair(key, value);
};
} // namespace cserver::server::http
} // namespace cserver::server::http
namespace cserver::components {
template <typename TaskProcessor = int>
struct HttpClient {
@ -30,20 +28,20 @@ struct HttpClient {
return clients::http::Request{*this};
};
inline constexpr HttpClient(auto, auto& context) :
taskProcessor(context.template FindComponent<"basicTaskProcessor">()),
ctx(boost::asio::ssl::context::method::sslv23_client),
resolver(this->taskProcessor.ioContext) {}
taskProcessor(context.template FindComponent<"basicTaskProcessor">()),
ctx(boost::asio::ssl::context::method::sslv23_client),
resolver(this->taskProcessor.ioContext) {};
template <utempl::ConstexprString name, Options Options, typename T>
static consteval auto Adder(const T& context) {
static consteval auto Adder(const T& context) {
using Type = std::remove_cvref_t<decltype(context.template FindComponent<"basicTaskProcessor">())>;
return context.TransformComponents(
[&](const ComponentConfig<name, HttpClient<>, Options>&) -> ComponentConfig<name, HttpClient<Type>, Options> {
return {};
});
[&](const ComponentConfig<name, HttpClient<>, Options>&) -> ComponentConfig<name, HttpClient<Type>, Options> {
return {};
});
};
private:
private:
template <typename Socket, typename... Flags>
static consteval auto GetPerformReturnType(Flags...) {
constexpr auto kUseStreaming = utempl::Find<UseStreaming>(utempl::kTypeList<Flags...>) != sizeof...(Flags);
@ -54,7 +52,7 @@ private:
} else {
return [] -> server::http::HttpResponse {
std::unreachable();
}();
}();
};
};
template <typename Socket>
@ -76,14 +74,18 @@ private:
inline auto ReadBody(Socket&& socket, std::size_t length, auto&&...) const -> cserver::Task<std::string> {
std::string response;
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;
};
template <typename Socket>
inline auto ReadBody(Socket&& socket, auto&&...) const -> cserver::Task<std::string> {
std::string response;
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) {
break;
};
@ -93,19 +95,23 @@ private:
};
co_return response;
};
public:
public:
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);
boost::asio::ssl::stream<boost::asio::ip::tcp::socket> socket(this->taskProcessor.ioContext, this->ctx);
boost::asio::ip::tcp::resolver::iterator endpoint =
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);
boost::asio::ip::tcp::resolver::iterator endpoint = 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);
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);
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...);
if constexpr(kUseStreaming) {
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
#include <cserver/engine/coroutine.hpp>
#include <cserver/server/http/http_request.hpp>
#include <cserver/server/http/http_response.hpp>
#include <cserver/engine/coroutine.hpp>
namespace cserver::clients::http {
template <typename HttpClient>
struct Request {
HttpClient& client;
server::http::HttpRequest request;
Request(HttpClient& client) :
client(client) {
explicit Request(HttpClient& client) : client(client) {
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";
return self;
};
inline constexpr auto Get(this auto&& self) -> decltype(auto) {
constexpr auto Get(this auto&& self) -> decltype(auto) {
self.request.method = "GET";
return self;
};
inline constexpr auto Put(this auto&& self) -> decltype(auto) {
constexpr auto Put(this auto&& self) -> decltype(auto) {
self.request.method = "PUT";
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);
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));
return self;
};
template <typename Self>
inline constexpr auto AddHeaderIfNotExists(this Self&& self,
std::string check,
std::string first,
std::string second) -> decltype(auto) {
constexpr auto AddHeaderIfNotExists(this Self&& self, std::string check, std::string first, std::string second) -> decltype(auto) {
if(!self.request.headers.contains(std::move(check))) {
return std::forward<Self>(self).AddHeader(std::move(first), std::move(second));
};
return self;
};
template <typename Self>
inline constexpr auto Url(this Self&& self,
std::string url) -> auto&& {
constexpr auto Url(this Self&& self, std::string url) -> auto&& {
self.request.url = boost::urls::url{std::move(url)};
auto authority = self.request.url.authority();
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);
return self;
};
inline constexpr auto ToString() const -> std::string {
[[nodiscard]] constexpr auto ToString() const -> std::string {
return this->request.ToString();
};
template <typename Self, typename... 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;
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
#include <cserver/server/http/http_response.hpp>
#include <cserver/engine/coroutine.hpp>
#include <boost/asio/ssl.hpp>
#include <cserver/engine/coroutine.hpp>
#include <cserver/server/http/http_response.hpp>
namespace cserver::clients::http {
@ -10,10 +10,12 @@ class Response : public server::http::HttpResponse {
HttpClient& client;
Socket socket;
public:
public:
inline auto ReadChunk() -> cserver::Task<bool> {
this->body.resize(4479);
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));
this->body.resize(4479); // NOLINT
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) {
co_return false;
};
@ -23,12 +25,10 @@ public:
this->body.resize(n);
co_return true;
};
inline constexpr Response(Response&&) = default;
inline constexpr Response(const Response&) = default;
inline constexpr Response(HttpClient& client, Socket socket, server::http::HttpResponse response) :
client(client),
socket(std::move(socket)),
HttpResponse(std::move(response)) {};
constexpr Response(Response&&) = default;
constexpr Response(const Response&) = default;
inline Response(HttpClient& client, Socket socket, server::http::HttpResponse response) :
client(client), socket(std::move(socket)), HttpResponse(std::move(response)) {};
};
} // namespace cserver::clients::http
} // namespace cserver::clients::http

View file

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

View file

@ -1,9 +1,9 @@
#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 <cserver/components/cli/manager.hpp>
#include <cserver/engine/components.hpp>
#include <cserver/engine/not_implemented.hpp>
#include <utempl/attributes.hpp>
namespace cserver::cli {
@ -17,32 +17,28 @@ struct Description {
static constexpr utempl::ConstexprString kValue = Value;
};
template <typename T>
struct IsNameM {
static constexpr bool value = utempl::Overloaded(
[]<utempl::ConstexprString V>(utempl::TypeList<Name<V>>) {
return true;
},
[](auto) {return false;}
)(utempl::kType<T>);
[]<utempl::ConstexprString V>(utempl::TypeList<Name<V>>) {
return true;
},
[](auto) {
return false;
})(utempl::kType<T>);
};
template <typename T>
struct IsDescriptionM {
static constexpr bool value = utempl::Overloaded(
[]<utempl::ConstexprString V>(utempl::TypeList<Description<V>>) {
return true;
},
[](auto) {return false;}
)(utempl::kType<T>);
[]<utempl::ConstexprString V>(utempl::TypeList<Description<V>>) {
return true;
},
[](auto) {
return false;
})(utempl::kType<T>);
};
template <typename Self, typename T>
struct Struct : T {
static constexpr utempl::ConstexprString kCliManagerName = "cliManager";
@ -61,23 +57,18 @@ struct Struct : T {
};
};
static consteval auto GetConfig() {
static constexpr auto names = boost::pfr::names_as_array<T>();
return [&](auto... is) {
return StructConfig<Self, [&] {
return CreateOptionConfig<std::remove_reference_t<decltype(boost::pfr::get<is>(std::declval<T&>()))>>(
GetFirstOrDefault<is, IsNameM>(utempl::ConstexprString<names[is].size() + 1>(names[is].data())),
GetFirstOrDefault<is, IsDescriptionM>(utempl::ConstexprString<0>{}));
GetFirstOrDefault<is, IsNameM>(utempl::ConstexprString<names[is].size() + 1>(names[is].data())),
GetFirstOrDefault<is, IsDescriptionM>(utempl::ConstexprString<0>{}));
}()...>{};
} | utempl::kSeq<boost::pfr::tuple_size_v<T>>;
};
constexpr Struct(auto& context) :
T(context.template FindComponent<Self::kCliManagerName>().template Get<GetConfig()>()) {};
explicit constexpr Struct(auto& context) : T(context.template FindComponent<Self::kCliManagerName>().template Get<GetConfig()>()) {};
template <utempl::ConstexprString Name, Options>
static consteval auto CliStructAdder(const auto& context) {
@ -96,4 +87,4 @@ struct StructWithAdder : Struct<Self, T> {
};
};
} // namespace cserver::cli
} // namespace cserver::cli

View file

@ -7,18 +7,23 @@ namespace cserver {
struct ComponentBase {
Logging& logging;
template <typename T>
constexpr ComponentBase(T& context) :
logging(context.template FindComponent<"logging">()) {};
explicit constexpr ComponentBase(T& context) : logging(context.template FindComponent<"logging">()){};
};
#define LOG_TRACE \
if(this->logging.level == ::cserver::LoggingLevel::kTrace) \
this->logging.Trace
#define LOG_DEBUG \
if(this->logging.level <= ::cserver::LoggingLevel::kDebug) \
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
#define LOG_TRACE if(this->logging.level == ::cserver::LoggingLevel::kTrace) this->logging.Trace
#define LOG_DEBUG if(this->logging.level <= ::cserver::LoggingLevel::kDebug) 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
#include <utempl/constexpr_string.hpp>
#include <array>
#include <fmt/compile.h>
#include <fmt/format.h>
#include <array>
#include <iostream>
#include <utempl/constexpr_string.hpp>
namespace cserver {
enum class LoggingLevel {
kTrace,
kDebug,
kInfo,
kWarning,
kError
};
enum class LoggingLevel { kTrace, kDebug, kInfo, kWarning, kError };
struct Logging {
static constexpr utempl::ConstexprString kName = "logging";
LoggingLevel level;
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);
};
@ -28,13 +22,12 @@ struct Logging {
this->level = level;
};
template <std::size_t N, std::size_t NN>
static consteval auto GetFormatStringFor(utempl::ConstexprString<N> fmt, utempl::ConstexprString<NN> level) {
constexpr auto f = FMT_COMPILE("[{}]: {}\n");
constexpr auto size = N + NN + 6;
char data[size]{};
fmt::format_to(data, f, static_cast<std::string_view>(level), static_cast<std::string_view>(fmt));
std::array<char, size> data{};
fmt::format_to(data.begin(), f, static_cast<std::string_view>(level), static_cast<std::string_view>(fmt));
return utempl::ConstexprString<size>(data);
};
@ -42,7 +35,6 @@ struct Logging {
std::cout << data;
};
template <utempl::ConstexprString Fmt, typename... Ts>
constexpr auto Debug(Ts&&... args) {
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 {
struct StopBlocker {
boost::asio::io_context::work guard;
inline constexpr StopBlocker(auto& context) :
guard(context.template FindComponent<kBasicTaskProcessorName>().ioContext) {};
explicit constexpr StopBlocker(auto& context) : guard(context.template FindComponent<kBasicTaskProcessorName>().ioContext) {};
};
} // namespace cserver
} // namespace cserver

View file

@ -1,19 +1,16 @@
#pragma once
#include <utempl/constexpr_string.hpp>
#include <boost/asio.hpp>
#include <thread>
#include <utempl/constexpr_string.hpp>
namespace cserver::engine::basic {
template <std::size_t Size = 0>
struct TaskProcessor {
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";
inline constexpr TaskProcessor(auto, auto&) :
ioContext{},
pool{} {
};
inline constexpr TaskProcessor(auto, auto&) : ioContext{} {};
inline ~TaskProcessor() {
for(auto& thread : this->pool) {
@ -28,7 +25,7 @@ struct TaskProcessor {
if(this->ioContext.stopped()) {
return;
};
thread = std::thread([&]{
thread = std::thread([&] {
if(this->ioContext.stopped()) {
return;
};
@ -38,4 +35,4 @@ struct TaskProcessor {
};
};
}
} // namespace cserver::engine::basic

View file

@ -1,10 +1,11 @@
#pragma once
#include <cxxabi.h>
#include <boost/core/demangle.hpp>
#include <cserver/engine/basic/task_processor.hpp>
#include <cserver/engine/coroutine.hpp>
#include <utempl/utils.hpp>
#include <utempl/loopholes/counter.hpp>
#include <cxxabi.h>
#include <boost/core/demangle.hpp>
#include <utempl/utils.hpp>
namespace cserver {
@ -29,34 +30,32 @@ struct NamedValue {
};
template <typename T>
using GetTypeFromComponentConfig = decltype(
[]<utempl::ConstexprString name, typename TT, Options Options>(const ComponentConfig<name, TT, Options>&) -> TT {
}(std::declval<T>()));
using GetTypeFromComponentConfig = decltype([]<utempl::ConstexprString name, typename TT, Options Options>(
const ComponentConfig<name, TT, Options>&) -> TT {}(std::declval<T>()));
template <typename T>
inline constexpr utempl::ConstexprString kNameFromComponentConfig =
decltype([]<utempl::ConstexprString name, typename TT, Options Options>(const ComponentConfig<name, TT, Options>&) {
return utempl::Wrapper<name>{};
}(std::declval<T>()))::kValue;
inline constexpr utempl::ConstexprString kNameFromComponentConfig =
decltype([]<utempl::ConstexprString name, typename TT, Options Options>(const ComponentConfig<name, TT, Options>&) {
return utempl::Wrapper<name>{};
}(std::declval<T>()))::kValue;
template <typename T>
inline constexpr auto TransformIfOk(T&& value, auto&& f) {
if constexpr(requires{f(std::forward<T>(value));}) {
if constexpr(requires { f(std::forward<T>(value)); }) {
return f(std::forward<T>(value));
} else {
return value;
};
};
} // namespace impl
} // namespace impl
template <typename... Ts>
struct ConstexprConfig {
utempl::Tuple<Ts...> data;
template <utempl::ConstexprString Key>
inline constexpr auto Get() const -> auto {
return [&]<typename... TTs, utempl::ConstexprString... names>(const ConstexprConfig<impl::NamedValue<names, TTs>...>&){
return [&]<typename... TTs, utempl::ConstexprString... names>(const ConstexprConfig<impl::NamedValue<names, TTs>...>&) {
constexpr auto list = utempl::TypeList<utempl::Wrapper<names>...>{};
constexpr std::size_t I = Find<utempl::Wrapper<Key>>(list);
if constexpr(I < sizeof...(Ts)) {
@ -81,30 +80,30 @@ inline constexpr auto GetInitFlagFor(auto&... args) -> InitFlag& {
template <typename InitFlag, utempl::Tuple Tuple>
constexpr auto TransformDependencyGraphToTupleWithDependenciesToInitedFlagChanges(auto&&... args) {
return utempl::Map(utempl::PackConstexprWrapper<Tuple>(), [&]<auto Array>(utempl::Wrapper<Array>) {
return utempl::Map(utempl::PackConstexprWrapper<Array, utempl::Tuple<>>(), [&](auto elem) {
return &GetInitFlagFor<InitFlag, static_cast<std::size_t>(elem)>(args...);
}, utempl::kType<std::array<InitFlag*, Array.size()>>);
return utempl::Map(
utempl::PackConstexprWrapper<Array, utempl::Tuple<>>(),
[&](auto elem) {
return &GetInitFlagFor<InitFlag, static_cast<std::size_t>(elem)>(args...);
},
utempl::kType<std::array<InitFlag*, Array.size()>>);
});
};
struct AsyncConditionVariable {
inline AsyncConditionVariable(boost::asio::io_context& ioContext) :
mtx{},
deadlineTimer(ioContext),
flag{} {};
explicit inline AsyncConditionVariable(boost::asio::io_context& ioContext) : mtx{}, deadlineTimer(ioContext) {};
template <typename Token = boost::asio::use_awaitable_t<>>
inline auto AsyncWait(Token&& token = {}) -> Task<> {
return boost::asio::async_initiate<
Token,
void(void)>([this]<typename Handler>(Handler&& handler) -> void {
std::unique_lock lc(this->mtx);
if(this->flag) {
return handler();
};
this->deadlineTimer.async_wait([h = std::make_unique<Handler>(std::forward<Handler>(handler))](const boost::system::error_code&){
return (*h)();
});
}, token);
return boost::asio::async_initiate<Token, void(void)>(
[this]<typename Handler>(Handler&& handler) -> void {
std::unique_lock lc(this->mtx);
if(this->flag) {
return handler();
};
this->deadlineTimer.async_wait([h = std::make_unique<Handler>(std::forward<Handler>(handler))](const boost::system::error_code&) {
return (*h)();
});
},
token);
};
inline auto Wait() -> void {
this->deadlineTimer.wait();
@ -120,7 +119,7 @@ struct AsyncConditionVariable {
};
std::mutex mtx;
boost::asio::deadline_timer deadlineTimer;
bool flag;
bool flag{};
};
template <typename Component, typename T>
@ -136,8 +135,8 @@ struct ServiceContextForComponent {
};
template <utempl::ConstexprString Name>
constexpr auto FindComponent() -> auto&
requires (Name == kBasicTaskProcessorName || requires {this->FindComponent<kUtils.template GetIndexByName(Name)>();}) {
constexpr auto FindComponent() -> auto& requires(Name == kBasicTaskProcessorName ||
requires { this->FindComponent<kUtils.template GetIndexByName(Name)>(); }) {
if constexpr(Name == kBasicTaskProcessorName) {
return this->context.taskProcessor;
} else {
@ -155,7 +154,7 @@ struct ServiceContextForComponent {
};
template <template <typename...> typename F>
constexpr auto FindAllComponents() {
return utempl::Unpack(utempl::PackConstexprWrapper<kUtils.template GetAllIndexes<F>(), utempl::Tuple<>>(),
return utempl::Unpack(utempl::PackConstexprWrapper<kUtils.template GetAllIndexes<F>(), utempl::Tuple<>>(),
[&](auto... is) -> utempl::Tuple<decltype(*Get<is>(context.storage))&...> {
return {context.template FindComponent<Component, is>()...};
});
@ -168,13 +167,9 @@ struct ServiceContextForComponentWithCliArgs : ServiceContextForComponent<Compon
const char** argv;
constexpr ServiceContextForComponentWithCliArgs(T& context, int argc, const char** argv) :
ServiceContextForComponent<Component, T>(context),
argc(argc),
argv(argv) {};
ServiceContextForComponent<Component, T>(context), argc(argc), argv(argv) {};
};
template <utempl::Tuple DependencyGraph, typename T>
inline constexpr auto InitComponents(T& ccontext, int ac, const char** av) -> void {
static auto& context = ccontext;
@ -189,9 +184,11 @@ inline constexpr auto InitComponents(T& ccontext, int ac, const char** av) -> vo
return "";
};
}();
static utempl::Tuple inited = TransformDependencyGraphToTupleWithDependenciesToInitedFlagChanges<AsyncConditionVariable, DependencyGraph>(ioContext);
static utempl::Tuple inited =
TransformDependencyGraphToTupleWithDependenciesToInitedFlagChanges<AsyncConditionVariable, DependencyGraph>(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<> {
using Current = decltype(Get<Is>(decltype(T::kUtils)::kComponentConfigs));
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();
};
}(), boost::asio::detached), ...);
// clang-format on
}(std::make_index_sequence<utempl::kTupleSize<decltype(DependencyGraph)>>());
};
@ -239,7 +237,7 @@ struct DependenciesUtils {
static consteval auto GetIndexByType() -> std::size_t {
return utempl::Find<std::remove_cvref_t<T>>(kTypeList);
};
template <std::size_t N>
static consteval auto GetIndexByName(const utempl::ConstexprString<N>& name) -> std::size_t {
return utempl::Find(kNames, name);
@ -254,58 +252,52 @@ struct DependenciesUtils {
template <template <typename...> typename F>
static consteval auto GetAllIndexes() {
return [&](auto... is) {
return utempl::TupleCat(std::array<std::size_t, 0>{}, [&]{
return utempl::TupleCat(std::array<std::size_t, 0>{}, [&] {
if constexpr(F<typename Ts::Type>::value) {
return std::to_array<std::size_t>({is});
} else {
return std::array<std::size_t, 0>{};
};
}()...);} | utempl::kSeq<sizeof...(Ts)>;
}()...);
} | utempl::kSeq<sizeof...(Ts)>;
};
template <std::size_t I>
static consteval auto GetName() {
return Get<I>(kNames);
};
};
} // namespace impl
} // namespace impl
#define COMPONENT_REQUIRES(name, impl) \
template <typename T> \
concept name = impl ; \
template <typename T> \
struct name##M { \
static constexpr auto value = name<T>;\
#define COMPONENT_REQUIRES(name, impl) /* NOLINT */ \
template <typename T> \
concept name = impl; \
template <typename T> \
struct name##M { \
static constexpr auto value = name<T>; \
}
template <ConstexprConfig config, utempl::Tuple DependencyGraph, typename... Ts>
struct ServiceContext {
static constexpr auto kConfig = config;
static constexpr auto kThreadsCount = config.template Get<"threads">();
static constexpr impl::DependenciesUtils<Ts...> kUtils;
engine::basic::TaskProcessor<kThreadsCount - 1> taskProcessor;
utempl::Tuple<std::optional<typename Ts::Type>...> storage;
utempl::Tuple<std::optional<typename Ts::Type>...> storage{};
constexpr ServiceContext() :
taskProcessor{utempl::Wrapper<utempl::ConstexprString{kBasicTaskProcessorName}>{}, *this}
,storage{} {
};
constexpr ServiceContext() : taskProcessor{utempl::Wrapper<utempl::ConstexprString{kBasicTaskProcessorName}>{}, *this} {};
constexpr auto Run(int argc, const char** argv) {
impl::InitComponents<DependencyGraph>(*this, argc, argv);
this->taskProcessor.Run();
};
template <typename Component>
constexpr auto GetContextFor() /* Internal Use Only */ {
return impl::ServiceContextForComponent<Component, std::decay_t<decltype(*this)>>{*this};
};
private:
private:
static constexpr struct {
constexpr auto operator=(auto&&) const noexcept {};
} ComponentType{};
@ -318,28 +310,27 @@ private:
static constexpr struct {
constexpr auto operator=(auto&&) const noexcept {};
} RequiredComponent;
public:
public:
template <typename Current, std::size_t I>
constexpr auto FindComponent() -> decltype(*Get<I>(this->storage))& {
constexpr auto Index = kUtils.GetIndexByName(Current::kName);
constexpr auto J = utempl::loopholes::Counter<Current, utempl::Wrapper<I>>();
constexpr auto dependencies = Get<Index>(DependencyGraph);
static_assert((
ComponentType = utempl::kType<typename Current::Type>,
ComponentName = utempl::kWrapper<Current::kName>,
ComponentOptions = utempl::kWrapper<Current::kOptions>,
RequiredComponent = utempl::kType<decltype(Get<I>(kUtils.kComponentConfigs))>,
dependencies.size() > J), "Constructor is not declared as constexpr");
static_assert((ComponentType = utempl::kType<typename Current::Type>,
ComponentName = utempl::kWrapper<Current::kName>,
ComponentOptions = utempl::kWrapper<Current::kOptions>,
RequiredComponent = utempl::kType<decltype(Get<I>(kUtils.kComponentConfigs))>,
dependencies.size() > J),
"Constructor is not declared as constexpr");
return *Get<I>(this->storage);
};
};
namespace impl {
template <typename Current, std::size_t I>
struct DependencyInfoKey {};
@ -363,7 +354,8 @@ struct DependencyInfoInjector {
if constexpr(name == kBasicTaskProcessorName) {
return [] -> engine::basic::TaskProcessor<Config.template Get<"threads">() - 1> {
std::unreachable();
}();
}
();
} else {
return [] -> decltype(Get<kUtils.GetIndexByName(name)>(kUtils.kTypeList)) {
std::unreachable();
@ -371,69 +363,48 @@ struct DependencyInfoInjector {
};
};
template <
utempl::ConstexprString name,
typename...,
std::size_t I = utempl::loopholes::Counter<Current, utempl::Wrapper<name>>(),
auto = utempl::loopholes::Injector<
DependencyInfoKey<
Current,
I
>{},
name
>{}
>
template <utempl::ConstexprString name,
typename...,
std::size_t I = utempl::loopholes::Counter<Current, utempl::Wrapper<name>>(),
auto = utempl::loopholes::Injector<DependencyInfoKey<Current, I>{}, name>{}>
static auto FindComponent() -> decltype(FindComponentType<name>())&;
template <
typename T,
typename...,
typename R = decltype(FindComponent<Get<kUtils.template GetIndexByType<T>()>(kUtils.kNames)>())
>
static auto FindComponent() -> R;
template <
template <typename...> typename F,
typename...,
auto Arr = std::to_array<bool>({F<typename Ts::Type>::value...}),
std::size_t I = std::ranges::find(Arr, true) - Arr.begin(),
typename R = decltype(FindComponent<decltype(Get<I>(utempl::TypeList<Ts...>{}))::kName>())
>
template <typename T, typename..., typename R = decltype(FindComponent<Get<kUtils.template GetIndexByType<T>()>(kUtils.kNames)>())>
static auto FindComponent() -> R;
private:
template <template <typename...> typename F,
typename...,
auto Arr = std::to_array<bool>({F<typename Ts::Type>::value...}),
std::size_t I = std::ranges::find(Arr, true) - Arr.begin(),
typename R = decltype(FindComponent<decltype(Get<I>(utempl::TypeList<Ts...>{}))::kName>())>
static auto FindComponent() -> R;
private:
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:
template <
template <typename...> typename F,
typename...,
typename R = decltype(utempl::Unpack(utempl::PackConstexprWrapper<kUtils.template GetAllIndexes<F>(), utempl::Tuple<>>(),
[](auto... is) -> decltype(FindAllComponentsImpl<is...>()) {
std::unreachable();
}))
>
public:
template <template <typename...> typename F,
typename...,
typename R = decltype(utempl::Unpack(utempl::PackConstexprWrapper<kUtils.template GetAllIndexes<F>(), utempl::Tuple<>>(),
[](auto... is) -> decltype(FindAllComponentsImpl<is...>()) {
std::unreachable();
}))>
static auto FindAllComponents() -> R;
template <auto = 0>
static consteval auto GetDependencies() {
return [](auto... is) {
return ([&]{
return ([&] {
constexpr auto response = Magic(utempl::loopholes::Getter<DependencyInfoKey<Current, is>{}>{});
if constexpr(response == kBasicTaskProcessorName) {
return utempl::Tuple{};
} else {
return utempl::Tuple{response};
};
}() + ... + utempl::Tuple{});
}() + ... +
utempl::Tuple{});
} | utempl::kSeq<utempl::loopholes::Counter<Current>()>;
};
static inline consteval auto Inject() {
@ -441,12 +412,11 @@ public:
};
};
} // namespace impl
} // namespace impl
template <utempl::ConstexprString Name, utempl::Tuple Dependencies>
struct DependencyGraphElement {};
template <typename... Ts>
struct DependencyGraph {
static constexpr auto kValue = utempl::TypeList<Ts...>{};
@ -460,7 +430,7 @@ struct CompileTimeStack {
std::size_t current{};
inline constexpr auto Push(T value) {
if(this->current == Size) {
throw std::out_of_range{0};
throw std::out_of_range{nullptr};
};
this->data[current].emplace(std::move(value));
this->current++;
@ -471,25 +441,24 @@ struct CompileTimeStack {
return std::move(*this->data[tmp]);
};
};
} // namespace impl
} // namespace impl
template <utempl::ConstexprString... Names, utempl::Tuple... Dependencies>
consteval auto TopologicalSort(const DependencyGraph<DependencyGraphElement<Names, Dependencies>...>&) {
constexpr utempl::Tuple names = {Names...};
constexpr utempl::Tuple storage = Map(utempl::Tuple{Dependencies...},
[&]<utempl::TupleLike Tuple>(Tuple&& tuple){
static constexpr auto Size = utempl::kTupleSize<Tuple>;
return utempl::Unpack(std::forward<Tuple>(tuple),
[&]<typename... Args>(Args&&... args) -> std::array<std::size_t, Size> {
return {Find(names, std::forward<Args>(args))...};
}
);
});
constexpr utempl::Tuple storage = Map(utempl::Tuple{Dependencies...}, [&]<utempl::TupleLike Tuple>(Tuple&& tuple) {
static constexpr auto Size = utempl::kTupleSize<Tuple>;
return utempl::Unpack(std::forward<Tuple>(tuple), [&]<typename... Args>(Args&&... args) -> std::array<std::size_t, Size> {
return {Find(names, std::forward<Args>(args))...};
});
});
constexpr auto Size = utempl::kTupleSize<decltype(storage)>;
const std::array adj = utempl::Map(storage,
[](std::ranges::range auto& arg){
return std::ranges::subrange(arg.data(), arg.data() + arg.size());
}, utempl::kType<std::array<std::size_t, 0>>);
const std::array adj = utempl::Map(
storage,
[](std::ranges::range auto& arg) {
return std::ranges::subrange(arg.data(), arg.data() + arg.size());
},
utempl::kType<std::array<std::size_t, 0>>);
std::array<bool, Size> visited{};
constexpr auto ResultSize = sizeof...(Dependencies);
impl::CompileTimeStack<std::size_t, ResultSize> response{};
@ -507,36 +476,41 @@ consteval auto TopologicalSort(const DependencyGraph<DependencyGraphElement<Name
response.Push(v);
}(i);
};
return utempl::Map(std::move(response.data), [](auto&& data){
return utempl::Map(std::move(response.data), [](auto&& data) {
return *data;
});
};
template <ConstexprConfig config = {}, typename... ComponentConfigs>
struct ServiceContextBuilder {
static constexpr auto kList = utempl::kTypeList<ComponentConfigs...>;
static constexpr impl::DependenciesUtils<ComponentConfigs...> kUtils;
static constexpr auto kConfig = config;
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 {};
};
template <typename T, utempl::ConstexprString name = T::kName, typename... Os>
static consteval auto Append(Options<Os...> = {}) -> 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);}) {
static consteval auto Append(Options<Os...> = {})
-> 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 {};
};
template <utempl::ConstexprString Key, auto Value>
static consteval auto AppendConfigParam() -> ServiceContextBuilder<config.template Append<Key>(Value), ComponentConfigs...> {
return {};
};
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 {};
};
template <utempl::ConstexprString name>
@ -544,11 +518,12 @@ struct ServiceContextBuilder {
if constexpr(name == kBasicTaskProcessorName) {
return [] -> engine::basic::TaskProcessor<config.template Get<"threads">() - 1> {
std::unreachable();
}();
}
();
} else {
return []<typename... TTs, utempl::ConstexprString... names, Options... Options>
(const ServiceContextBuilder<config, ComponentConfig<names, TTs, Options>...>&)
-> decltype(utempl::Get<Find(utempl::Tuple{names...}, name)>(utempl::TypeList<TTs...>{})) {
return []<typename... TTs, utempl::ConstexprString... names, Options... Options>(
const ServiceContextBuilder<config, ComponentConfig<names, TTs, Options>...>&)
-> decltype(utempl::Get<Find(utempl::Tuple{names...}, name)>(utempl::TypeList<TTs...>{})) {
std::unreachable();
}(ServiceContextBuilder<config, ComponentConfigs...>{});
};
@ -561,44 +536,42 @@ struct ServiceContextBuilder {
template <template <typename...> typename F>
static consteval auto FindAllComponents() {
return utempl::Unpack(utempl::PackConstexprWrapper<kUtils.template GetAllIndexes<F>(), utempl::Tuple<>>(),
return utempl::Unpack(utempl::PackConstexprWrapper<kUtils.template GetAllIndexes<F>(), utempl::Tuple<>>(),
[](auto... is) -> utempl::Tuple<typename decltype(Get<is>(utempl::kTypeList<ComponentConfigs...>))::Type...> {
std::unreachable();
});
};
static consteval auto Config() {
return config;
};
static consteval auto GetDependencyGraph() {
return [&]<utempl::ConstexprString... names, typename... Components, Options... Options>
(ComponentConfig<names, Components, Options>...){
return DependencyGraph<DependencyGraphElement<names,
[]<utempl::ConstexprString name, typename Component, ::cserver::Options OOptions>
(ComponentConfig<name, Component, OOptions>) {
impl::DependencyInfoInjector<Component, name, config, ComponentConfigs...> injector;
injector.Inject();
return injector.GetDependencies();
}(ComponentConfigs{})>...>{};
return [&]<utempl::ConstexprString... names, typename... Components, Options... Options>(
ComponentConfig<names, Components, Options>...) {
return DependencyGraph<DependencyGraphElement<names,
[]<utempl::ConstexprString name, typename Component, ::cserver::Options OOptions>(
ComponentConfig<name, Component, OOptions>) {
impl::DependencyInfoInjector<Component, name, config, ComponentConfigs...> injector;
injector.Inject();
return injector.GetDependencies();
}(ComponentConfigs{})>...>{};
}(ComponentConfigs{}...);
};
static consteval auto GetIndexDependencyGraph() {
return []<utempl::ConstexprString... Names, utempl::Tuple... Dependencies>
(DependencyGraph<DependencyGraphElement<Names, Dependencies>...>){
return utempl::Tuple{Unpack(Dependencies, [](auto... dependencies) -> std::array<std::size_t, sizeof...(dependencies)> {
return {Find(utempl::Tuple{Names...}, dependencies)...};
})...};
return []<utempl::ConstexprString... Names, utempl::Tuple... Dependencies>(
DependencyGraph<DependencyGraphElement<Names, Dependencies>...>) {
return utempl::Tuple{Unpack(
Dependencies, [](auto... dependencies) -> std::array<std::size_t, sizeof...(dependencies)> {
return {Find(utempl::Tuple{Names...}, dependencies)...};
})...};
}(GetDependencyGraph());
};
static consteval auto Sort() {
constexpr auto sorted = TopologicalSort(GetDependencyGraph());
return [&]<std::size_t... Is>(std::index_sequence<Is...>){
return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
constexpr auto list = utempl::TypeList<ComponentConfigs...>{};
return ServiceContextBuilder<config, decltype(Get<sorted[Is]>(list))...>{};
}(std::index_sequence_for<ComponentConfigs...>{});
@ -614,4 +587,4 @@ struct ServiceContextBuilder {
context.taskProcessor.ioContext.run();
};
};
} // namespace cserver
} // namespace cserver

View file

@ -18,7 +18,7 @@ struct SetThreadId : public std::suspend_never {
std::size_t threadId;
};
} // namespace this_coro
} // namespace this_coro
template <typename T = void>
using Task = boost::asio::awaitable<T>;
@ -26,7 +26,7 @@ using Task = boost::asio::awaitable<T>;
template <typename...>
struct TaskAwaitable {};
} // namespace cserver
} // namespace cserver
namespace boost::asio::detail {
/*
@ -58,23 +58,22 @@ DEALINGS IN THE SOFTWARE.
*/
template <>
struct awaitable_frame_base<any_io_executor> {
public:
public:
using Executor = any_io_executor;
#if !defined(BOOST_ASIO_DISABLE_AWAITABLE_FRAME_RECYCLING)
inline auto operator new(std::size_t size) -> void* {
return boost::asio::detail::thread_info_base::allocate(
boost::asio::detail::thread_info_base::awaitable_frame_tag(),
boost::asio::detail::thread_context::top_of_thread_call_stack(),
size);
return boost::asio::detail::thread_info_base::allocate(boost::asio::detail::thread_info_base::awaitable_frame_tag(),
boost::asio::detail::thread_context::top_of_thread_call_stack(),
size);
};
inline auto operator delete(void* pointer, std::size_t size) -> void {
boost::asio::detail::thread_info_base::deallocate(
boost::asio::detail::thread_info_base::awaitable_frame_tag(),
boost::asio::detail::thread_context::top_of_thread_call_stack(),
pointer, size);
boost::asio::detail::thread_info_base::deallocate(boost::asio::detail::thread_info_base::awaitable_frame_tag(),
boost::asio::detail::thread_context::top_of_thread_call_stack(),
pointer,
size);
};
#endif // !defined(BOOST_ASIO_DISABLE_AWAITABLE_FRAME_RECYCLING)
#endif // !defined(BOOST_ASIO_DISABLE_AWAITABLE_FRAME_RECYCLING)
// The frame starts in a suspended state until the awaitable_thread object
// pumps the stack.
@ -124,8 +123,8 @@ public:
template <typename T>
inline constexpr auto await_transform(awaitable<T, Executor> a) const -> awaitable<T, Executor> {
if (attached_thread_->entry_point()->throw_if_cancelled_) {
if (!!attached_thread_->get_cancellation_state().cancelled()) {
if(attached_thread_->entry_point()->throw_if_cancelled_) {
if(!!attached_thread_->get_cancellation_state().cancelled()) {
throw_error(boost::asio::error::operation_aborted, "co_await");
};
};
@ -134,28 +133,29 @@ public:
template <typename Op>
inline constexpr auto await_transform(Op&& op,
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_HAS_SOURCE_LOCATION)
, detail::source_location location = detail::source_location::current()
# endif // defined(BOOST_ASIO_HAS_SOURCE_LOCATION)
#endif // defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
) {
if (attached_thread_->entry_point()->throw_if_cancelled_) {
if (!!attached_thread_->get_cancellation_state().cancelled()) {
#if defined(BOOST_ASIO_HAS_SOURCE_LOCATION)
,
detail::source_location location = detail::source_location::current()
#endif // defined(BOOST_ASIO_HAS_SOURCE_LOCATION)
#endif // defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
) {
if(attached_thread_->entry_point()->throw_if_cancelled_) {
if(!!attached_thread_->get_cancellation_state().cancelled()) {
throw_error(boost::asio::error::operation_aborted, "co_await");
};
};
return awaitable_async_op<
completion_signature_of_t<Op>, decay_t<Op>, Executor>{
std::forward<Op>(op), this
return awaitable_async_op<completion_signature_of_t<Op>, decay_t<Op>, Executor>{std::forward<Op>(op),
this
#if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
# if defined(BOOST_ASIO_HAS_SOURCE_LOCATION)
, location
# endif // defined(BOOST_ASIO_HAS_SOURCE_LOCATION)
#endif // defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
};
#if defined(BOOST_ASIO_HAS_SOURCE_LOCATION)
,
location
#endif // defined(BOOST_ASIO_HAS_SOURCE_LOCATION)
#endif // defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
};
};
// This await transformation obtains the associated executor of the thread of
@ -168,7 +168,7 @@ public:
return true;
};
inline constexpr auto await_suspend(coroutine_handle<void>) noexcept -> void {};
inline constexpr auto await_suspend(coroutine_handle<void>) noexcept -> void{};
inline auto await_resume() const noexcept {
return this_->attached_thread_->get_executor();
@ -188,7 +188,7 @@ public:
return true;
};
inline constexpr auto await_suspend(coroutine_handle<void>) noexcept -> void {};
inline constexpr auto await_suspend(coroutine_handle<void>) noexcept -> void{};
auto await_resume() const noexcept {
return this_->attached_thread_->get_cancellation_state();
@ -207,7 +207,7 @@ public:
return true;
};
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() const {
return this_->attached_thread_->reset_cancellation_state();
@ -219,8 +219,7 @@ public:
// This await transformation resets the associated cancellation state.
template <typename Filter>
inline constexpr auto await_transform(
this_coro::reset_cancellation_state_1_t<Filter> reset) noexcept {
inline constexpr auto await_transform(this_coro::reset_cancellation_state_1_t<Filter> reset) noexcept {
struct Result {
awaitable_frame_base* this_;
Filter filter_;
@ -229,11 +228,10 @@ public:
return true;
};
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() {
return this_->attached_thread_->reset_cancellation_state(
static_cast<Filter&&>(filter_));
return this_->attached_thread_->reset_cancellation_state(static_cast<Filter&&>(filter_));
};
};
@ -246,8 +244,7 @@ public:
// This await transformation resets the associated cancellation state.
template <typename InFilter, typename OutFilter>
inline constexpr auto await_transform(
this_coro::reset_cancellation_state_2_t<InFilter, OutFilter> reset) noexcept {
inline constexpr auto await_transform(this_coro::reset_cancellation_state_2_t<InFilter, OutFilter> reset) noexcept {
struct Result {
awaitable_frame_base* this_;
InFilter in_filter_;
@ -256,19 +253,16 @@ public:
inline constexpr auto await_ready() const noexcept -> bool {
return true;
};
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() {
return this_->attached_thread_->reset_cancellation_state(
static_cast<InFilter&&>(in_filter_),
static_cast<OutFilter&&>(out_filter_));
return this_->attached_thread_->reset_cancellation_state(static_cast<InFilter&&>(in_filter_),
static_cast<OutFilter&&>(out_filter_));
};
};
return Result{this,
static_cast<InFilter&&>(reset.in_filter),
static_cast<OutFilter&&>(reset.out_filter)};
return Result{this, static_cast<InFilter&&>(reset.in_filter), static_cast<OutFilter&&>(reset.out_filter)};
};
// This await transformation determines whether cancellation is propagated as
@ -281,7 +275,7 @@ public:
return true;
};
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() {
return this_->attached_thread_->throw_if_cancelled();
@ -302,7 +296,7 @@ public:
return true;
};
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() {
this_->attached_thread_->throw_if_cancelled(value_);
@ -317,13 +311,9 @@ public:
// immediate resumption of the coroutine in another thread does not cause a
// race condition.
template <typename Function>
inline constexpr auto await_transform(Function f,
enable_if_t<
is_convertible<
result_of_t<Function(awaitable_frame_base*)>,
awaitable_thread<Executor>*
>::value
>* = nullptr) {
inline constexpr auto await_transform(
Function f,
enable_if_t<is_convertible<result_of_t<Function(awaitable_frame_base*)>, awaitable_thread<Executor>*>::value>* = nullptr) {
struct Result {
Function function_;
awaitable_frame_base* this_;
@ -334,11 +324,11 @@ public:
inline constexpr auto await_suspend(coroutine_handle<void>) noexcept -> void {
this_->after_suspend(
[](void* arg)
{
[](void* arg) {
Result* r = static_cast<Result*>(arg);
r->function_(r->this_);
}, this);
},
this);
};
inline constexpr auto await_resume() const noexcept -> void {};
@ -356,7 +346,7 @@ public:
return true;
};
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() const noexcept -> bool& {
return this_->attached_thread_->entry_point()->has_context_switched_;
@ -370,7 +360,7 @@ public:
attached_thread_ = handler;
};
inline constexpr auto detach_thread() noexcept ->awaitable_thread<Executor>* {
inline constexpr auto detach_thread() noexcept -> awaitable_thread<Executor>* {
attached_thread_->entry_point()->has_context_switched_ = true;
return std::exchange(attached_thread_, nullptr);
};
@ -384,7 +374,7 @@ public:
};
inline constexpr auto pop_frame() noexcept -> void {
if (caller_) {
if(caller_) {
caller_->attached_thread_ = attached_thread_;
};
attached_thread_->entry_point()->top_of_stack_ = caller_;
@ -394,14 +384,14 @@ public:
struct resume_context {
void (*after_suspend_fn_)(void*) = nullptr;
void *after_suspend_arg_ = nullptr;
void* after_suspend_arg_ = nullptr;
};
inline constexpr auto resume() -> void {
resume_context context;
resume_context_ = &context;
coro_.resume();
if (context.after_suspend_fn_) {
if(context.after_suspend_fn_) {
context.after_suspend_fn_(context.after_suspend_arg_);
};
};
@ -424,7 +414,8 @@ public:
return awaitable;
};
std::size_t threadId{};
protected:
protected:
coroutine_handle<void> coro_ = nullptr;
awaitable_thread<Executor>* attached_thread_ = nullptr;
awaitable_frame_base<Executor>* caller_ = nullptr;
@ -432,4 +423,4 @@ protected:
resume_context* resume_context_ = nullptr;
};
} // namespace boost::asio::detail
} // namespace boost::asio::detail

View file

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

View file

@ -8,4 +8,4 @@ struct NotImplemented : std::runtime_error {
using std::runtime_error::runtime_error;
};
} // namespace cserver::engine
} // namespace cserver::engine

View file

@ -6,4 +6,4 @@ struct UseStreaming {};
inline constexpr auto kUseStreaming = UseStreaming{};
} // namespace cserver
} // namespace cserver

View file

@ -1,14 +1,13 @@
#pragma once
#include <cserver/components/loggable_component_base.hpp>
#include <cserver/engine/components.hpp>
#include <cserver/engine/coroutine.hpp>
#include <cserver/server/http/http_request.hpp>
#include <cserver/server/http/http_response.hpp>
#include <cserver/server/http/http_stream.hpp>
#include <cserver/engine/coroutine.hpp>
#include <cserver/components/loggable_component_base.hpp>
namespace cserver::server::handlers {
struct HttpHandlerBase : ComponentBase {
template <typename T, utempl::ConstexprString name, Options>
static consteval auto HttpHandlerAdder(const auto& context) {
@ -17,25 +16,31 @@ struct HttpHandlerBase : ComponentBase {
});
};
template <typename Self>
inline auto HandleRequest(this Self&& self, http::HttpRequest&& request
) -> Task<http::HttpResponse> requires requires{self.HandleRequestThrow(std::move(request));} {
inline auto HandleRequest(this Self&& self, http::HttpRequest&& request) -> Task<http::HttpResponse>
requires requires { self.HandleRequestThrow(std::move(request)); }
{
using T = std::remove_cvref_t<Self>;
try {
co_return co_await std::forward<Self>(self).HandleRequestThrow(std::move(request));
} catch(const std::exception& err) {
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());
};
} catch(...) {
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);
};
};
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>
inline auto HandleRequestStream(this Self&& self, cserver::server::http::HttpRequest&& request,
cserver::server::http::HttpStream& stream) -> Task<void> requires requires{self.HandleRequestStreamThrow(std::move(request), stream);} {
inline auto HandleRequestStream(this Self&& self,
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>;
try {
co_await std::forward<Self>(self).HandleRequestStreamThrow(std::move(request), stream);
@ -46,8 +51,7 @@ struct HttpHandlerBase : ComponentBase {
};
co_await stream.Close();
};
inline constexpr HttpHandlerBase(auto& context) :
ComponentBase(context) {};
explicit constexpr HttpHandlerBase(auto& context) : ComponentBase(context) {};
};
template <typename T>
struct HttpHandlerAdderType {
@ -58,9 +62,7 @@ struct HttpHandlerAdderType {
};
template <typename T>
struct HttpHandlerBaseWithAdder : HttpHandlerBase, HttpHandlerAdderType<T> {
inline constexpr HttpHandlerBaseWithAdder(auto& context) :
HttpHandlerBase(context),
HttpHandlerAdderType<T>{} {};
explicit constexpr HttpHandlerBaseWithAdder(auto& context) : HttpHandlerBase(context), HttpHandlerAdderType<T>{} {};
};
} // namespace cserver::server::handlers
} // namespace cserver::server::handlers

View file

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

View file

@ -1,7 +1,8 @@
#pragma once
#include <cserver/server/http/http_request.hpp>
#include <llhttp.h>
#include <cserver/server/http/http_request.hpp>
namespace cserver::server::http {
struct HttpRequestParser : private llhttp_t, public HttpRequest {
@ -10,7 +11,7 @@ struct HttpRequestParser : private llhttp_t, public HttpRequest {
std::string headerField = {};
std::string headerValue = {};
std::string urlString = {};
inline HttpRequestParser(std::string_view data) {
inline HttpRequestParser(std::string_view data) { // NOLINT
llhttp_settings_t settings;
llhttp_settings_init(&settings);
settings.on_method = HttpRequestParser::OnMethod;
@ -25,44 +26,44 @@ struct HttpRequestParser : private llhttp_t, public HttpRequest {
llhttp_execute(this, data.data(), data.size());
};
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);
return 0;
};
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);
return 0;
};
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);
return 0;
};
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);
return 0;
};
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);
return 0;
};
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->headerValue.clear();
self->headerField.clear();
return 0;
};
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);
return 0;
};
static inline auto OnMessageComplete(llhttp_t* parser) -> int {
auto* self = static_cast<HttpRequestParser*>(parser);
auto* self = static_cast<HttpRequestParser*>(parser); // NOLINT
self->done = true;
return 0;
};

View file

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

View file

@ -1,7 +1,8 @@
#pragma once
#include <cserver/engine/coroutine.hpp>
#include <fmt/format.h>
#include <boost/asio.hpp>
#include <cserver/engine/coroutine.hpp>
namespace cserver::server::http {
@ -10,13 +11,15 @@ struct HttpStream {
std::stringstream stream = {};
inline auto SetMethod(std::string method) -> Task<void> {
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));
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> {
this->stream << fmt::format("{}: {}\r\n", std::move(first), std::move(second));
co_return;
@ -24,10 +27,12 @@ struct HttpStream {
inline auto SetEndOfHeaders() -> Task<void> {
this->stream << "\r\n";
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> {
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> {
this->socket.close();
@ -35,4 +40,4 @@ struct HttpStream {
};
};
} // namespace cserver::server::http
} // namespace cserver::server::http

View file

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