This commit is contained in:
sha512sum 2024-03-13 13:09:35 +00:00
commit af684cc837
12 changed files with 667 additions and 0 deletions

54
CMakeLists.txt Normal file
View file

@ -0,0 +1,54 @@
cmake_minimum_required(VERSION 3.27)
project(cserver VERSION 1)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CXX_EXTENSIONS NO)
set(Boost_USE_MULTITHREADED ON)
find_package(fmt REQUIRED)
find_package(Boost 1.84.0 REQUIRED COMPONENTS url)
find_package(utempl REQUIRED)
find_package(llhttp REQUIRED)
find_package(OpenSSL REQUIRED)
cmake_policy(SET CMP0079 NEW)
include(GNUInstallDirs)
add_library(cserver INTERFACE)
target_include_directories(
cserver
INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
target_link_libraries(cserver INTERFACE utempl::utempl ${Boost_LIBRARIES} llhttp uring ${OPENSSL_LIBRARIES} )
target_compile_features(cserver INTERFACE cxx_std_20)
install(TARGETS cserver
EXPORT cserverTargets
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
include(CMakePackageConfigHelpers)
write_basic_package_version_file("cserverConfigVersion.cmake"
VERSION ${PROJECT_VERSION}
COMPATIBILITY SameMajorVersion)
configure_package_config_file(
"${PROJECT_SOURCE_DIR}/cmake/cserverConfig.cmake.in"
"${PROJECT_BINARY_DIR}/cserverConfig.cmake"
INSTALL_DESTINATION
${CMAKE_INSTALL_DATAROOTDIR}/cserver/cmake)
install(EXPORT cserverTargets
FILE cserverTargets.cmake
NAMESPACE cserver::
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/cserver/cmake)
install(FILES "${PROJECT_BINARY_DIR}/cserverConfig.cmake"
"${PROJECT_BINARY_DIR}/cserverConfigVersion.cmake"
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/cserver/cmake)
install(DIRECTORY ${PROJECT_SOURCE_DIR}/include/cserver DESTINATION include)

View file

@ -0,0 +1,4 @@
@PACKAGE_INIT@
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")
check_required_components("@PROJECT_NAME@")

View file

@ -0,0 +1,86 @@
#pragma once
#include <cserver/clients/http/request.hpp>
#include <cserver/server/http/http_response.hpp>
#include <cserver/engine/components.hpp>
#include <cserver/engine/coroutine.hpp>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/asio/error.hpp>
#include <utempl/constexpr_string.hpp>
namespace cserver::server::http {
inline constexpr auto ParseHttpHeader(std::string header) -> std::pair<std::string, std::string> {
size_t pos = header.find(":");
std::string key = header.substr(0, pos);
std::string value = header.substr(pos + 2);
return std::make_pair(key, value);
};
} // namespace cserver::server::http
namespace cserver::components {
template <typename TaskProcessor = int>
struct HttpClient {
TaskProcessor& taskProcessor;
boost::asio::ssl::context ctx;
boost::asio::ip::tcp::resolver resolver;
static constexpr utempl::ConstexprString kName = "httpClient";
inline constexpr auto CreateRequest() {
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) {}
template <utempl::ConstexprString name, typename T>
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<>>&) -> ComponentConfig<name, HttpClient<Type>> {
return {};
});
};
template <typename T>
inline auto PerformRequest(T&& request) -> cserver::Task<server::http::HTTPResponse> {
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);
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::use_awaitable);
std::string serverResponse;
co_await boost::asio::async_read_until(socket, boost::asio::dynamic_buffer(serverResponse), "\r\n\r\n", boost::asio::use_awaitable);
server::http::HTTPResponse response;
std::istringstream responseStream(std::move(serverResponse));
std::string httpVersion;
responseStream >> httpVersion >> response.statusCode;
std::getline(responseStream, response.statusMessage);
std::string header;
while(std::getline(responseStream, header) && header.find(":") != std::string::npos) {
response.headers.insert(server::http::ParseHttpHeader(std::move(header)));
};
if(response.statusCode != 200) {
co_return response;
};
if(response.headers.contains("Content-Length")) {
auto size = std::stoi(response.headers["Content-Length"]);
response.body.reserve(size);
co_await boost::asio::async_read(socket, boost::asio::dynamic_buffer(response.body), boost::asio::transfer_at_least(size), boost::asio::use_awaitable);
co_return response;
};
for(;;) {
auto [ec, n] = co_await boost::asio::async_read(socket, boost::asio::dynamic_buffer(response.body), boost::asio::transfer_at_least(1), boost::asio::as_tuple(boost::asio::use_awaitable));
if(ec && ec == boost::asio::error::eof) {
break;
};
if(ec) {
throw ec;
};
};
co_return response;
};
};
} // namespace cserver::clients::http

View file

@ -0,0 +1,92 @@
#pragma once
#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) {
this->AddHeader("User-Agent", "cserver/1");
};
inline constexpr auto Post() && -> Request<HttpClient>&& {
this->request.method = "POST";
return std::move(*this);
};
inline constexpr auto Post() & -> Request<HttpClient>& {
this->request.method = "POST";
return *this;
};
inline constexpr auto Get() && -> Request<HttpClient>&& {
this->request.method = "GET";
return std::move(*this);
};
inline constexpr auto Get() & -> Request<HttpClient>& {
this->request.method = "GET";
return *this;
};
inline constexpr auto Put() && -> Request<HttpClient>&& {
this->request.method = "PUT";
return std::move(*this);
};
inline constexpr auto Put() & -> Request<HttpClient>& {
this->request.method = "PUT";
return *this;
};
inline constexpr auto SetCustomMethod(std::string method) & -> Request<HttpClient>& {
this->request.method = std::move(method);
return *this;
};
inline constexpr auto SetCustomMethod(std::string method) && -> Request<HttpClient>&& {
this->request.method = std::move(method);
return std::move(*this);
};
inline constexpr auto AddHeader(std::string first, std::string second) && -> Request<HttpClient>&& {
this->request.headers.emplace(std::move(first), std::move(second));
return std::move(*this);
};
inline constexpr auto AddHeader(std::string first, std::string second) & -> Request<HttpClient>& {
this->request.headers.emplace(std::move(first), std::move(second));
return *this;
};
inline constexpr auto AddHeaderIfNotExists(std::string check, std::string first, std::string second) & -> Request<HttpClient>& {
if(!this->request.headers.contains(std::move(check))) {
return this->AddHeader(std::move(first), std::move(second));
};
return *this;
};
inline constexpr auto AddHeaderIfNotExists(std::string check, std::string first, std::string second) && -> Request<HttpClient>&& {
if(!this->request.headers.contains(std::move(check))) {
return std::move(*this).AddHeader(std::move(first), std::move(second));
};
return std::move(*this);
};
inline constexpr auto Url(std::string url) & -> Request<HttpClient>& {
this->request.url = boost::urls::url{std::move(url)};
auto authority = this->request.url.authority();
return this->AddHeader("Host", std::string{authority.data(), authority.size()});
};
inline constexpr auto Url(std::string url) && -> Request<HttpClient>&& {
this->request.url = boost::urls::url{std::move(url)};
auto authority = this->request.url.authority();
return std::move(*this).AddHeader("Host", std::string{authority.data(), authority.size()});
};
inline constexpr auto Data(std::string data) && -> Request<HttpClient>&& {
this->request.body = std::move(data);
return std::move(*this);
};
inline constexpr auto ToString() const -> std::string {
return this->request.ToString();
};
inline auto Perform() && -> cserver::Task<server::http::HTTPResponse> {
co_return co_await client.PerformRequest(std::move(*this).AddHeaderIfNotExists("Transfer-Encoding", "Content-Length", std::to_string(this->request.body.size())));
};
inline auto Perform() & -> cserver::Task<server::http::HTTPResponse> {
co_return co_await client.PerformRequest(this->AddHeaderIfNotExists("Transfer-Encoding", "Content-Length", std::to_string(this->request.body.size())));
};
};
};

View file

@ -0,0 +1,37 @@
#pragma once
#include <cserver/engine/components.hpp>
#include <cserver/engine/coroutine.hpp>
#include <boost/asio.hpp>
namespace cserver::engine::basic {
template <std::size_t Size = 0>
struct TaskProcessor {
boost::asio::io_context ioContext;
std::array<std::jthread, Size> pool;
static constexpr utempl::ConstexprString kName = "basicTaskProcessor";
inline constexpr TaskProcessor(auto, auto&) :
ioContext{},
pool{} {
};
template <utempl::ConstexprString name, typename T>
static consteval auto Adder(const T& context) {
constexpr std::size_t Count = T::kConfig.template Get<name>().template Get<"threadPoolSize">();
return context.TransformComponents(
[&](const ComponentConfig<name, TaskProcessor<>>&) -> ComponentConfig<name, TaskProcessor<Count>> {
return {};
});
};
inline auto Run() {
for(auto& thread : this->pool) {
thread = std::jthread([&]{
boost::asio::executor_work_guard<decltype(this->ioContext.get_executor())> guard{this->ioContext.get_executor()};
this->ioContext.run();
});
};
};
};
}

View file

@ -0,0 +1,131 @@
#pragma once
#include <utempl/utils.hpp>
#include <thread>
namespace cserver {
template <utempl::ConstexprString name, typename T>
struct NamedValue {
T value;
};
template <utempl::ConstexprString name, typename T>
struct ComponentConfig {};
template <typename T>
inline constexpr auto TransformIfOk(T&& value, auto&& f) {
if constexpr(requires{f(std::forward<T>(value));}) {
return f(std::forward<T>(value));
} else {
return value;
};
};
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<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)) {
return utempl::Get<I>(this->data).value;
};
}(*this);
};
template <utempl::ConstexprString Key, typename T>
inline constexpr auto Append(T&& value) const -> ConstexprConfig<Ts..., NamedValue<Key, std::remove_cvref_t<T>>> {
return {.data = TupleCat(this->data, utempl::MakeTuple(NamedValue<Key, std::remove_cvref_t<T>>{std::forward<T>(value)}))};
};
};
template <ConstexprConfig config, auto names, typename... Ts>
struct ServiceContext {
utempl::Tuple<Ts...> storage;
static constexpr auto kConfig = config;
static constexpr auto kList = utempl::TypeList<Ts...>{};
inline constexpr ServiceContext() :
storage{
[&]<auto... Is>(std::index_sequence<Is...>) -> utempl::Tuple<Ts...> {
return {[&]<auto I>(utempl::Wrapper<I>) {
return [&]() -> std::remove_cvref_t<decltype(Get<I>(storage))> {
return {decltype(Get<I>(names)){}, *this};
};
}(utempl::Wrapper<Is>{})...};
}(std::index_sequence_for<Ts...>())
} {};
inline constexpr auto Run() {
[&]<auto... Is>(std::index_sequence<Is...>) {
([](auto& component){
if constexpr(requires{component.Run();}) {
component.Run();
};
}(Get<Is>(this->storage)), ...);
}(std::index_sequence_for<Ts...>());
};
template <utempl::ConstexprString name>
inline constexpr auto FindComponent() -> auto& {
constexpr auto I = utempl::Find<utempl::Wrapper<name>>(names);
return Get<I>(this->storage);
};
template <typename T>
inline constexpr auto FindComponent() -> T& {
constexpr auto I = utempl::Find<std::remove_cvref_t<T>>(utempl::kTypeList<Ts...>);
return Get<I>(this->storage);
};
};
template <ConstexprConfig config = {}, typename... Ts>
struct ServiceContextBuilder {
static constexpr auto kList = utempl::kTypeList<Ts...>;
static constexpr auto kConfig = config;
template <typename T, utempl::ConstexprString name = T::kName>
static consteval auto Append() -> decltype(T::template Adder<name>(ServiceContextBuilder<config, Ts..., ComponentConfig<name, T>>{})) {
return {};
};
template <typename T, utempl::ConstexprString name = T::kName>
static consteval auto Append() -> ServiceContextBuilder<config, Ts..., ComponentConfig<name, T>>
requires (!requires(ServiceContextBuilder<config, Ts..., ComponentConfig<name, T>> builder) {T::template Adder<name>(builder);}) {
return {};
};
template <utempl::ConstexprString Key, auto Value>
static consteval auto AppendConfigParam() -> ServiceContextBuilder<config.template Append<Key>(Value), Ts...> {
return {};
};
template <typename F>
static consteval auto TransformComponents(F&& f) -> ServiceContextBuilder<config, decltype(TransformIfOk(Ts{}, f))...> {
return {};
};
template <utempl::ConstexprString name>
static consteval auto FindComponent() {
return []<typename... TTs, utempl::ConstexprString... names>
(const ServiceContextBuilder<config,ComponentConfig<names, TTs>...>&)
-> decltype(utempl::Get<Find<utempl::Wrapper<name>>(utempl::TypeList<utempl::Wrapper<names>...>{})>(utempl::TypeList<TTs...>{})) {
std::unreachable();
}(ServiceContextBuilder<config, Ts...>{});
};
static consteval auto Config() {
return config;
};
static constexpr auto Run() -> void {
[]<utempl::ConstexprString... names, typename... TTs>(utempl::TypeList<ComponentConfig<names, TTs>...>) {
ServiceContext<config, utempl::kTypeList<utempl::Wrapper<names>...>, TTs...> context;
context.Run();
for(;;) {
std::this_thread::sleep_for(std::chrono::minutes(1));
};
}(utempl::TypeList<Ts...>{});
};
};
} // namespace cserver

View file

@ -0,0 +1,9 @@
#pragma once
#include <boost/asio.hpp>
namespace cserver {
template <typename T>
using Task = boost::asio::awaitable<T>;
} // namespace cserver

View file

@ -0,0 +1,34 @@
#pragma once
#include <cserver/engine/components.hpp>
#include <cserver/server/http/http_request.hpp>
#include <cserver/server/http/http_response.hpp>
#include <cserver/engine/coroutine.hpp>
namespace cserver::server::handlers {
template <typename T>
struct HTTPHandlerBase {
template <utempl::ConstexprString name>
static consteval auto Adder(const auto& context) {
return context.TransformComponents([]<typename TT>(const ComponentConfig<T::kHandlerManagerName, TT>) {
return ComponentConfig<T::kHandlerManagerName, typename TT::template AddHandler<ComponentConfig<name, T>>>{};
});
};
inline auto HandleRequest(cserver::server::http::HTTPRequest&& request,
cserver::server::http::HTTPResponse& response) -> cserver::Task<std::string> {
try {
co_return co_await static_cast<T&>(*this).HandleRequestThrow(std::move(request), response);
} catch(const std::exception& err) {
fmt::println("Error in {}: {}", __PRETTY_FUNCTION__, err.what());
} catch(...) {
fmt::println("Error in {}: Unknown Error", __PRETTY_FUNCTION__);
};
response.statusCode = 500;
response.statusMessage = "Internal Server Error";
co_return "Internal Server Error";
};
inline constexpr HTTPHandlerBase(auto, auto&) {};
};
} // namespace cserver::server::handlers

View file

@ -0,0 +1,25 @@
#pragma once
#include <fmt/format.h>
#include <unordered_map>
#include <sstream>
#include <boost/url.hpp>
namespace cserver::server::http {
struct HTTPRequest {
std::string method = {};
boost::urls::url url = {};
std::unordered_map<std::string, std::string> headers = {};
std::string body = {};
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) {
stream << fmt::format("{}: {}\r\n", header.first, header.second);
};
stream << fmt::format("\r\n{}\r\n", this->body);
return stream.str();
};
};
} // namespace cserver::server::http

View file

@ -0,0 +1,71 @@
#pragma once
#include <cserver/server/http/http_request.hpp>
#include <llhttp.h>
namespace cserver::server::http {
struct HTTPRequestParser : private llhttp_t, public HTTPRequest {
bool err = false;
bool done = false;
std::string headerField = {};
std::string headerValue = {};
std::string urlString = {};
inline HTTPRequestParser(std::string_view data) {
llhttp_settings_t settings;
llhttp_settings_init(&settings);
settings.on_method = HTTPRequestParser::OnMethod;
settings.on_url = HTTPRequestParser::OnUrl;
settings.on_url_complete = HTTPRequestParser::OnUrlComplete;
settings.on_header_field = HTTPRequestParser::OnHeaderField;
settings.on_header_value = HTTPRequestParser::OnHeaderValue;
settings.on_headers_complete = HTTPRequestParser::OnHeaderComplete;
settings.on_body = HTTPRequestParser::OnBody;
settings.on_message_complete = HTTPRequestParser::OnMessageComplete;
llhttp_init(this, HTTP_BOTH, &settings);
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));
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);
self->urlString.append(data, size);
return 0;
};
static inline auto OnUrlComplete(llhttp_t* parser) -> int {
auto* self = static_cast<HTTPRequestParser*>(parser);
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);
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);
self->headerValue.append(data, size);
return 0;
};
static inline auto OnHeaderComplete(llhttp_t* parser) -> int {
auto* self = static_cast<HTTPRequestParser*>(parser);
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);
self->body.append(data, size);
return 0;
};
static inline auto OnMessageComplete(llhttp_t* parser) -> int {
auto* self = static_cast<HTTPRequestParser*>(parser);
self->done = true;
return 0;
};
};
} // namespace cserver::server::http

View file

@ -0,0 +1,26 @@
#pragma once
#include <unordered_map>
#include <fmt/format.h>
#include <sstream>
namespace cserver::server::http {
struct HTTPResponse {
unsigned short statusCode = 200;
std::string statusMessage = "OK";
std::unordered_map<std::string, std::string> headers = {};
std::string body = {};
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) {
stream << fmt::format("{}: {}\r\n", header.first, header.second);
};
if(!this->body.empty()) {
stream << fmt::format("\r\n{}\r\n", this->body);
};
return stream.str();
};
};
} // namespace cserver::server::http

View file

@ -0,0 +1,98 @@
#pragma once
#include <cserver/engine/components.hpp>
#include <cserver/server/http/http_request_parser.hpp>
#include <cserver/server/http/http_response.hpp>
#include <cserver/engine/coroutine.hpp>
#include <boost/asio.hpp>
namespace cserver::server::server {
namespace impl {
template <typename T>
using GetTypeFromComponentConfig = decltype([]<utempl::ConstexprString name, typename TT>(const ComponentConfig<name, TT>&) -> TT {}(std::declval<T>()));
template <typename T>
inline constexpr utempl::ConstexprString kNameFromComponentConfig =
decltype([]<utempl::ConstexprString name, typename TT>(const ComponentConfig<name, TT>&) {
return utempl::Wrapper<name>{};
} (std::declval<T>()))::kValue;
} // namespace impl
template <utempl::ConstexprString TPName = "basicTaskProcessor", typename TaskProcessor = int, typename... Ts>
struct Server {
TaskProcessor& taskProcessor;
utempl::Tuple<impl::GetTypeFromComponentConfig<Ts>&...> handlers;
static constexpr utempl::ConstexprString kName = "server";
unsigned short port;
static constexpr utempl::Tuple kNames = {impl::kNameFromComponentConfig<Ts>...};
static constexpr utempl::Tuple kPaths = {impl::GetTypeFromComponentConfig<Ts>::kPath...};
template <utempl::ConstexprString name, typename T>
static consteval auto Adder(const T& context) {
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...>>&) -> ComponentConfig<name, Server<tpName, TP, Ts...>> {
return {};
});
};
template <utempl::ConstexprString name, typename T>
inline constexpr Server(utempl::Wrapper<name>, T& context) :
taskProcessor(context.template FindComponent<TPName>()),
handlers{
[&]<auto... Is>(std::index_sequence<Is...>) -> utempl::Tuple<impl::GetTypeFromComponentConfig<Ts>&...> {
return {context.template FindComponent<Get<Is>(kNames)>()...};
}(std::index_sequence_for<Ts...>())
},
port(T::kConfig.template Get<name>().template Get<"port">()) {
};
auto Reader(boost::asio::ip::tcp::socket socket) -> Task<void> {
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};
bool flag = false;
co_await [&]<auto... Is>(std::index_sequence<Is...>) -> cserver::Task<void> {
(co_await [&]<auto I>(utempl::Wrapper<I>) -> cserver::Task<void> {
if(request.url.path().substr(0, Get<I>(kPaths).size()) == Get<I>(kPaths)) {
flag = true;
http::HTTPResponse response{};
response.body = co_await Get<I>(this->handlers).HandleRequest(std::move(request), response);
response.headers["Content-Length"] = std::to_string(response.body.size());
response.headers["Server"] = "cserver/1";
auto data = response.ToString();
co_await boost::asio::async_write(socket, boost::asio::buffer(data.data(), data.size()), boost::asio::use_awaitable);
socket.close();
};
}(utempl::Wrapper<Is>{}), ...);
}(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";
if(!flag) {
co_await boost::asio::async_write(socket, boost::asio::buffer(error404.data(), error404.size()), boost::asio::use_awaitable);
};
};
auto Task() -> cserver::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(;;) {
auto socket = co_await acceptor.async_accept(boost::asio::use_awaitable);
boost::asio::co_spawn(executor, this->Reader(std::move(socket)), boost::asio::detached);
};
};
inline constexpr auto Run() -> void {
boost::asio::co_spawn(this->taskProcessor.ioContext, this->Task(), boost::asio::detached);
};
inline ~Server() {
};
template <typename Handler>
using AddHandler = Server<TPName, TaskProcessor, Ts..., Handler>;
};
} // namespace cserver::server::server