Add clang-tidy, clang-format and fixes
This commit is contained in:
parent
c6a1198ed2
commit
7c00dc1860
22 changed files with 459 additions and 499 deletions
13
.clang-format
Normal file
13
.clang-format
Normal 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
1
.clang-tidy
Normal file
|
@ -0,0 +1 @@
|
|||
Checks: '-*,google-*,cppcoreguidelines-*,-cppcoreguidelines-c-copy-assignment-signature,-cppcoreguidelines-special-member-functions,-cppcoreguidelines-avoid-const-or-ref-data-members,modernize-*'
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -8,4 +8,4 @@ struct NotImplemented : std::runtime_error {
|
|||
using std::runtime_error::runtime_error;
|
||||
};
|
||||
|
||||
} // namespace cserver::engine
|
||||
} // namespace cserver::engine
|
||||
|
|
|
@ -6,4 +6,4 @@ struct UseStreaming {};
|
|||
|
||||
inline constexpr auto kUseStreaming = UseStreaming{};
|
||||
|
||||
} // namespace cserver
|
||||
} // namespace cserver
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue