HTTP -> Http
This commit is contained in:
parent
0d77aa7e3c
commit
61a7e85e52
10 changed files with 55 additions and 55 deletions
|
@ -2,15 +2,15 @@
|
||||||
#include <cserver/engine/basic/task_processor.hpp>
|
#include <cserver/engine/basic/task_processor.hpp>
|
||||||
#include <cserver/server/handlers/http_handler_base.hpp>
|
#include <cserver/server/handlers/http_handler_base.hpp>
|
||||||
|
|
||||||
struct SomeComponent : public cserver::server::handlers::HTTPHandlerBaseWithAdder<SomeComponent> {
|
struct SomeComponent : public cserver::server::handlers::HttpHandlerBaseWithAdder<SomeComponent> {
|
||||||
static constexpr utempl::ConstexprString kPath = "/v1/some/";
|
static constexpr utempl::ConstexprString kPath = "/v1/some/";
|
||||||
static constexpr utempl::ConstexprString kName = "name";
|
static constexpr utempl::ConstexprString kName = "name";
|
||||||
static constexpr utempl::ConstexprString kHandlerManagerName = "server";
|
static constexpr utempl::ConstexprString kHandlerManagerName = "server";
|
||||||
inline constexpr SomeComponent(auto name, auto& context) :
|
inline constexpr SomeComponent(auto name, auto& context) :
|
||||||
HTTPHandlerBaseWithAdder(name, context) {};
|
HttpHandlerBaseWithAdder(name, context) {};
|
||||||
|
|
||||||
inline auto HandleRequestThrow(const cserver::server::http::HTTPRequest& request) -> cserver::Task<cserver::server::http::HTTPResponse> {
|
inline auto HandleRequestThrow(const cserver::server::http::HttpRequest& request) -> cserver::Task<cserver::server::http::HttpResponse> {
|
||||||
co_return cserver::server::http::HTTPResponse{.body = request.url.data()};
|
co_return cserver::server::http::HttpResponse{.body = request.url.data()};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -52,16 +52,16 @@ private:
|
||||||
std::unreachable();
|
std::unreachable();
|
||||||
}();
|
}();
|
||||||
} else {
|
} else {
|
||||||
return [] -> server::http::HTTPResponse {
|
return [] -> server::http::HttpResponse {
|
||||||
std::unreachable();
|
std::unreachable();
|
||||||
}();
|
}();
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
template <typename Socket>
|
template <typename Socket>
|
||||||
inline auto ReadHeaders(Socket&& socket, auto&&...) const -> cserver::Task<server::http::HTTPResponse> {
|
inline auto ReadHeaders(Socket&& socket, auto&&...) const -> cserver::Task<server::http::HttpResponse> {
|
||||||
std::string serverResponse;
|
std::string serverResponse;
|
||||||
co_await boost::asio::async_read_until(socket, boost::asio::dynamic_buffer(serverResponse), "\r\n\r\n", boost::asio::use_awaitable);
|
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;
|
server::http::HttpResponse response;
|
||||||
std::istringstream responseStream(std::move(serverResponse));
|
std::istringstream responseStream(std::move(serverResponse));
|
||||||
std::string httpVersion;
|
std::string httpVersion;
|
||||||
responseStream >> httpVersion >> response.statusCode;
|
responseStream >> httpVersion >> response.statusCode;
|
||||||
|
|
|
@ -7,7 +7,7 @@ namespace cserver::clients::http {
|
||||||
template <typename HttpClient>
|
template <typename HttpClient>
|
||||||
struct Request {
|
struct Request {
|
||||||
HttpClient& client;
|
HttpClient& client;
|
||||||
server::http::HTTPRequest request;
|
server::http::HttpRequest request;
|
||||||
Request(HttpClient& client) :
|
Request(HttpClient& client) :
|
||||||
client(client) {
|
client(client) {
|
||||||
this->AddHeader("User-Agent", "cserver/1");
|
this->AddHeader("User-Agent", "cserver/1");
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
namespace cserver::clients::http {
|
namespace cserver::clients::http {
|
||||||
|
|
||||||
template <typename HttpClient, typename Socket>
|
template <typename HttpClient, typename Socket>
|
||||||
class Response : public server::http::HTTPResponse {
|
class Response : public server::http::HttpResponse {
|
||||||
HttpClient& client;
|
HttpClient& client;
|
||||||
Socket socket;
|
Socket socket;
|
||||||
|
|
||||||
|
@ -25,10 +25,10 @@ public:
|
||||||
};
|
};
|
||||||
inline constexpr Response(Response&&) = default;
|
inline constexpr Response(Response&&) = default;
|
||||||
inline constexpr Response(const Response&) = default;
|
inline constexpr Response(const Response&) = default;
|
||||||
inline constexpr Response(HttpClient& client, Socket socket, server::http::HTTPResponse response) :
|
inline constexpr Response(HttpClient& client, Socket socket, server::http::HttpResponse response) :
|
||||||
client(client),
|
client(client),
|
||||||
socket(std::move(socket)),
|
socket(std::move(socket)),
|
||||||
HTTPResponse(std::move(response)) {};
|
HttpResponse(std::move(response)) {};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace cserver::clients::http
|
} // namespace cserver::clients::http
|
||||||
|
|
|
@ -7,16 +7,16 @@
|
||||||
|
|
||||||
namespace cserver::server::handlers {
|
namespace cserver::server::handlers {
|
||||||
|
|
||||||
struct HTTPHandlerBase {
|
struct HttpHandlerBase {
|
||||||
template <typename T, utempl::ConstexprString name, Options Options>
|
template <typename T, utempl::ConstexprString name, Options Options>
|
||||||
static consteval auto Adder(const auto& context) {
|
static consteval auto HttpHandlerAdder(const auto& context) {
|
||||||
return context.TransformComponents([]<typename TT>(const ComponentConfig<T::kHandlerManagerName, TT, Options>) {
|
return context.TransformComponents([]<typename TT>(const ComponentConfig<T::kHandlerManagerName, TT, Options>) {
|
||||||
return ComponentConfig<T::kHandlerManagerName, typename TT::template AddHandler<ComponentConfig<name, T, Options>>, Options>{};
|
return ComponentConfig<T::kHandlerManagerName, typename TT::template AddHandler<ComponentConfig<name, T, Options>>, Options>{};
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
template <typename Self>
|
template <typename Self>
|
||||||
inline auto HandleRequest(this Self&& self, http::HTTPRequest&& request
|
inline auto HandleRequest(this Self&& self, http::HttpRequest&& request
|
||||||
) -> Task<http::HTTPResponse> requires requires{self.HandleRequestThrow(std::move(request));} {
|
) -> Task<http::HttpResponse> requires requires{self.HandleRequestThrow(std::move(request));} {
|
||||||
using T = std::remove_cvref_t<Self>;
|
using T = std::remove_cvref_t<Self>;
|
||||||
try {
|
try {
|
||||||
co_return co_await std::forward<Self>(self).HandleRequestThrow(std::move(request));
|
co_return co_await std::forward<Self>(self).HandleRequestThrow(std::move(request));
|
||||||
|
@ -25,11 +25,11 @@ struct HTTPHandlerBase {
|
||||||
} catch(...) {
|
} catch(...) {
|
||||||
fmt::println("Error in handler with default name {}: Unknown Error", T::kName);
|
fmt::println("Error in handler with default name {}: Unknown Error", T::kName);
|
||||||
};
|
};
|
||||||
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"};
|
||||||
};
|
};
|
||||||
template <typename Self>
|
template <typename Self>
|
||||||
inline auto HandleRequestStream(this Self&& self, cserver::server::http::HTTPRequest&& request,
|
inline auto HandleRequestStream(this Self&& self, cserver::server::http::HttpRequest&& request,
|
||||||
cserver::server::http::HTTPStream& stream) -> Task<void> requires requires{self.HandleRequestStreamThrow(std::move(request), stream);} {
|
cserver::server::http::HttpStream& stream) -> Task<void> requires requires{self.HandleRequestStreamThrow(std::move(request), stream);} {
|
||||||
using T = std::remove_cvref_t<Self>;
|
using T = std::remove_cvref_t<Self>;
|
||||||
try {
|
try {
|
||||||
co_await std::forward<Self>(self).HandleRequestStreamThrow(std::move(request), stream);
|
co_await std::forward<Self>(self).HandleRequestStreamThrow(std::move(request), stream);
|
||||||
|
@ -40,20 +40,20 @@ struct HTTPHandlerBase {
|
||||||
};
|
};
|
||||||
co_await stream.Close();
|
co_await stream.Close();
|
||||||
};
|
};
|
||||||
inline constexpr HTTPHandlerBase(auto, auto&) {};
|
inline constexpr HttpHandlerBase(auto, auto&) {};
|
||||||
};
|
};
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct HTTPHandlerAdder {
|
struct HttpHandlerAdderType {
|
||||||
template <utempl::ConstexprString Name, Options Options>
|
template <utempl::ConstexprString Name, Options Options>
|
||||||
static consteval auto Adder(const auto& context) {
|
static consteval auto Adder(const auto& context) {
|
||||||
return HTTPHandlerBase::template Adder<T, Name, Options>(context);
|
return HttpHandlerBase::template HttpHandlerAdder<T, Name, Options>(context);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct HTTPHandlerBaseWithAdder : HTTPHandlerBase, HTTPHandlerAdder<T> {
|
struct HttpHandlerBaseWithAdder : HttpHandlerBase, HttpHandlerAdderType<T> {
|
||||||
inline constexpr HTTPHandlerBaseWithAdder(auto name, auto& context) :
|
inline constexpr HttpHandlerBaseWithAdder(auto name, auto& context) :
|
||||||
HTTPHandlerBase(name, context),
|
HttpHandlerBase(name, context),
|
||||||
HTTPHandlerAdder<T>{} {};
|
HttpHandlerAdderType<T>{} {};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace cserver::server::handlers
|
} // namespace cserver::server::handlers
|
||||||
|
|
|
@ -6,14 +6,14 @@
|
||||||
|
|
||||||
namespace cserver::server::http {
|
namespace cserver::server::http {
|
||||||
|
|
||||||
struct HTTPRequest {
|
struct HttpRequest {
|
||||||
std::string method = {};
|
std::string method = {};
|
||||||
boost::urls::url url = {};
|
boost::urls::url url = {};
|
||||||
std::unordered_map<std::string, std::string> headers = {};
|
std::unordered_map<std::string, std::string> headers = {};
|
||||||
std::string body = {};
|
std::string body = {};
|
||||||
inline auto ToString() const -> std::string {
|
inline auto ToString() const -> std::string {
|
||||||
std::ostringstream stream;
|
std::ostringstream stream;
|
||||||
stream << fmt::format("{} {} HTTP/1.1\r\n", this->method, this->url.path());
|
stream << fmt::format("{} {} Http/1.1\r\n", this->method, this->url.path());
|
||||||
for(const auto& header : this->headers) {
|
for(const auto& header : this->headers) {
|
||||||
stream << fmt::format("{}: {}\r\n", header.first, header.second);
|
stream << fmt::format("{}: {}\r\n", header.first, header.second);
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,65 +4,65 @@
|
||||||
|
|
||||||
namespace cserver::server::http {
|
namespace cserver::server::http {
|
||||||
|
|
||||||
struct HTTPRequestParser : private llhttp_t, public HTTPRequest {
|
struct HttpRequestParser : private llhttp_t, public HttpRequest {
|
||||||
bool err = false;
|
bool err = false;
|
||||||
bool done = false;
|
bool done = false;
|
||||||
std::string headerField = {};
|
std::string headerField = {};
|
||||||
std::string headerValue = {};
|
std::string headerValue = {};
|
||||||
std::string urlString = {};
|
std::string urlString = {};
|
||||||
inline HTTPRequestParser(std::string_view data) {
|
inline HttpRequestParser(std::string_view data) {
|
||||||
llhttp_settings_t settings;
|
llhttp_settings_t settings;
|
||||||
llhttp_settings_init(&settings);
|
llhttp_settings_init(&settings);
|
||||||
settings.on_method = HTTPRequestParser::OnMethod;
|
settings.on_method = HttpRequestParser::OnMethod;
|
||||||
settings.on_url = HTTPRequestParser::OnUrl;
|
settings.on_url = HttpRequestParser::OnUrl;
|
||||||
settings.on_url_complete = HTTPRequestParser::OnUrlComplete;
|
settings.on_url_complete = HttpRequestParser::OnUrlComplete;
|
||||||
settings.on_header_field = HTTPRequestParser::OnHeaderField;
|
settings.on_header_field = HttpRequestParser::OnHeaderField;
|
||||||
settings.on_header_value = HTTPRequestParser::OnHeaderValue;
|
settings.on_header_value = HttpRequestParser::OnHeaderValue;
|
||||||
settings.on_headers_complete = HTTPRequestParser::OnHeaderComplete;
|
settings.on_headers_complete = HttpRequestParser::OnHeaderComplete;
|
||||||
settings.on_body = HTTPRequestParser::OnBody;
|
settings.on_body = HttpRequestParser::OnBody;
|
||||||
settings.on_message_complete = HTTPRequestParser::OnMessageComplete;
|
settings.on_message_complete = HttpRequestParser::OnMessageComplete;
|
||||||
llhttp_init(this, HTTP_BOTH, &settings);
|
llhttp_init(this, HTTP_BOTH, &settings);
|
||||||
llhttp_execute(this, data.data(), data.size());
|
llhttp_execute(this, data.data(), data.size());
|
||||||
};
|
};
|
||||||
static inline auto OnMethod(llhttp_t* parser, const char* data, std::size_t size) -> int {
|
static inline auto OnMethod(llhttp_t* parser, const char* data, std::size_t size) -> int {
|
||||||
auto* self = static_cast<HTTPRequest*>(static_cast<HTTPRequestParser*>(parser));
|
auto* self = static_cast<HttpRequest*>(static_cast<HttpRequestParser*>(parser));
|
||||||
self->method.append(data, size);
|
self->method.append(data, size);
|
||||||
return 0;
|
return 0;
|
||||||
};
|
};
|
||||||
static inline auto OnUrl(llhttp_t* parser, const char* data, std::size_t size) -> int {
|
static inline auto OnUrl(llhttp_t* parser, const char* data, std::size_t size) -> int {
|
||||||
auto* self = static_cast<HTTPRequestParser*>(parser);
|
auto* self = static_cast<HttpRequestParser*>(parser);
|
||||||
self->urlString.append(data, size);
|
self->urlString.append(data, size);
|
||||||
return 0;
|
return 0;
|
||||||
};
|
};
|
||||||
static inline auto OnUrlComplete(llhttp_t* parser) -> int {
|
static inline auto OnUrlComplete(llhttp_t* parser) -> int {
|
||||||
auto* self = static_cast<HTTPRequestParser*>(parser);
|
auto* self = static_cast<HttpRequestParser*>(parser);
|
||||||
self->url = boost::urls::url(self->urlString);
|
self->url = boost::urls::url(self->urlString);
|
||||||
return 0;
|
return 0;
|
||||||
};
|
};
|
||||||
static inline auto OnHeaderField(llhttp_t* parser, const char* data, std::size_t size) -> int {
|
static inline auto OnHeaderField(llhttp_t* parser, const char* data, std::size_t size) -> int {
|
||||||
auto* self = static_cast<HTTPRequestParser*>(parser);
|
auto* self = static_cast<HttpRequestParser*>(parser);
|
||||||
self->headerField.append(data, size);
|
self->headerField.append(data, size);
|
||||||
return 0;
|
return 0;
|
||||||
};
|
};
|
||||||
static inline auto OnHeaderValue(llhttp_t* parser, const char* data, std::size_t size) -> int {
|
static inline auto OnHeaderValue(llhttp_t* parser, const char* data, std::size_t size) -> int {
|
||||||
auto* self = static_cast<HTTPRequestParser*>(parser);
|
auto* self = static_cast<HttpRequestParser*>(parser);
|
||||||
self->headerValue.append(data, size);
|
self->headerValue.append(data, size);
|
||||||
return 0;
|
return 0;
|
||||||
};
|
};
|
||||||
static inline auto OnHeaderComplete(llhttp_t* parser) -> int {
|
static inline auto OnHeaderComplete(llhttp_t* parser) -> int {
|
||||||
auto* self = static_cast<HTTPRequestParser*>(parser);
|
auto* self = static_cast<HttpRequestParser*>(parser);
|
||||||
self->headers.emplace(std::move(self->headerField), std::move(self->headerValue));
|
self->headers.emplace(std::move(self->headerField), std::move(self->headerValue));
|
||||||
self->headerValue.clear();
|
self->headerValue.clear();
|
||||||
self->headerField.clear();
|
self->headerField.clear();
|
||||||
return 0;
|
return 0;
|
||||||
};
|
};
|
||||||
static inline auto OnBody(llhttp_t* parser, const char* data, std::size_t size) -> int {
|
static inline auto OnBody(llhttp_t* parser, const char* data, std::size_t size) -> int {
|
||||||
auto* self = static_cast<HTTPRequestParser*>(parser);
|
auto* self = static_cast<HttpRequestParser*>(parser);
|
||||||
self->body.append(data, size);
|
self->body.append(data, size);
|
||||||
return 0;
|
return 0;
|
||||||
};
|
};
|
||||||
static inline auto OnMessageComplete(llhttp_t* parser) -> int {
|
static inline auto OnMessageComplete(llhttp_t* parser) -> int {
|
||||||
auto* self = static_cast<HTTPRequestParser*>(parser);
|
auto* self = static_cast<HttpRequestParser*>(parser);
|
||||||
self->done = true;
|
self->done = true;
|
||||||
return 0;
|
return 0;
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,14 +5,14 @@
|
||||||
|
|
||||||
namespace cserver::server::http {
|
namespace cserver::server::http {
|
||||||
|
|
||||||
struct HTTPResponse {
|
struct HttpResponse {
|
||||||
unsigned short statusCode = 200;
|
unsigned short statusCode = 200;
|
||||||
std::string statusMessage = "OK";
|
std::string statusMessage = "OK";
|
||||||
std::unordered_map<std::string, std::string> headers = {};
|
std::unordered_map<std::string, std::string> headers = {};
|
||||||
std::string body = {};
|
std::string body = {};
|
||||||
inline auto ToString() const -> std::string {
|
inline auto ToString() const -> std::string {
|
||||||
std::ostringstream stream;
|
std::ostringstream stream;
|
||||||
stream << fmt::format("HTTP/1.1 {} {}\r\n", this->statusCode, this->statusMessage);
|
stream << fmt::format("Http/1.1 {} {}\r\n", this->statusCode, this->statusMessage);
|
||||||
for(const auto& header : this->headers) {
|
for(const auto& header : this->headers) {
|
||||||
stream << fmt::format("{}: {}\r\n", header.first, header.second);
|
stream << fmt::format("{}: {}\r\n", header.first, header.second);
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
namespace cserver::server::http {
|
namespace cserver::server::http {
|
||||||
|
|
||||||
struct HTTPStream {
|
struct HttpStream {
|
||||||
boost::asio::ip::tcp::socket socket;
|
boost::asio::ip::tcp::socket socket;
|
||||||
std::stringstream stream = {};
|
std::stringstream stream = {};
|
||||||
inline auto SetMethod(std::string method) -> Task<void> {
|
inline auto SetMethod(std::string method) -> Task<void> {
|
||||||
|
@ -13,7 +13,7 @@ struct HTTPStream {
|
||||||
co_await boost::asio::async_write(this->socket, boost::asio::buffer(method.data(), method.size()), boost::asio::transfer_all(), boost::asio::use_awaitable);
|
co_await boost::asio::async_write(this->socket, boost::asio::buffer(method.data(), method.size()), boost::asio::transfer_all(), boost::asio::use_awaitable);
|
||||||
};
|
};
|
||||||
inline auto SetStatus(std::string status) -> Task<void> {
|
inline auto SetStatus(std::string status) -> Task<void> {
|
||||||
status = fmt::format("HTTP/1.1 {}\r\n", std::move(status));
|
status = fmt::format("Http/1.1 {}\r\n", std::move(status));
|
||||||
co_await boost::asio::async_write(this->socket, boost::asio::buffer(status.data(), status.size()), boost::asio::transfer_all(), boost::asio::use_awaitable);
|
co_await boost::asio::async_write(this->socket, boost::asio::buffer(status.data(), status.size()), boost::asio::transfer_all(), boost::asio::use_awaitable);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -49,13 +49,13 @@ struct Server : StopBlocker {
|
||||||
Server(std::index_sequence_for<Ts...>{}, name, context) {
|
Server(std::index_sequence_for<Ts...>{}, name, context) {
|
||||||
};
|
};
|
||||||
template<auto I, typename Socket>
|
template<auto I, typename Socket>
|
||||||
auto ProcessHandler(Socket&& socket, http::HTTPRequest request) -> Task<void> {
|
auto ProcessHandler(Socket&& socket, http::HttpRequest request) -> Task<void> {
|
||||||
if constexpr(requires(http::HTTPStream& stream){Get<I>(this->handlers).HandleRequestStream(std::move(request), stream);}) {
|
if constexpr(requires(http::HttpStream& stream){Get<I>(this->handlers).HandleRequestStream(std::move(request), stream);}) {
|
||||||
http::HTTPStream stream{std::move(socket)};
|
http::HttpStream stream{std::move(socket)};
|
||||||
co_await Get<I>(this->handlers).HandleRequestStream(std::move(request), stream);
|
co_await Get<I>(this->handlers).HandleRequestStream(std::move(request), stream);
|
||||||
co_return;
|
co_return;
|
||||||
} else {
|
} else {
|
||||||
http::HTTPResponse response = co_await Get<I>(this->handlers).HandleRequest(std::move(request));
|
http::HttpResponse response = co_await Get<I>(this->handlers).HandleRequest(std::move(request));
|
||||||
response.headers["Content-Length"] = std::to_string(response.body.size());
|
response.headers["Content-Length"] = std::to_string(response.body.size());
|
||||||
response.headers["Server"] = "cserver/1";
|
response.headers["Server"] = "cserver/1";
|
||||||
auto data = response.ToString();
|
auto data = response.ToString();
|
||||||
|
@ -67,7 +67,7 @@ struct Server : StopBlocker {
|
||||||
std::string buffer;
|
std::string buffer;
|
||||||
buffer.reserve(socket.available());
|
buffer.reserve(socket.available());
|
||||||
co_await boost::asio::async_read_until(socket, boost::asio::dynamic_buffer(buffer), "\r\n\r\n", boost::asio::use_awaitable);
|
co_await boost::asio::async_read_until(socket, boost::asio::dynamic_buffer(buffer), "\r\n\r\n", boost::asio::use_awaitable);
|
||||||
http::HTTPRequest request = http::HTTPRequestParser{buffer};
|
http::HttpRequest request = http::HttpRequestParser{buffer};
|
||||||
bool flag = false;
|
bool flag = false;
|
||||||
co_await [&]<auto... Is>(std::index_sequence<Is...>) -> cserver::Task<void> {
|
co_await [&]<auto... Is>(std::index_sequence<Is...>) -> cserver::Task<void> {
|
||||||
(co_await [&] -> cserver::Task<void> {
|
(co_await [&] -> cserver::Task<void> {
|
||||||
|
@ -76,7 +76,7 @@ struct Server : StopBlocker {
|
||||||
};
|
};
|
||||||
}(), ...);
|
}(), ...);
|
||||||
}(std::index_sequence_for<Ts...>());
|
}(std::index_sequence_for<Ts...>());
|
||||||
constexpr std::string_view error404 = "HTTP/1.1 404 Not Found\r\n"
|
constexpr std::string_view error404 = "Http/1.1 404 Not Found\r\n"
|
||||||
"Content-Length: 0\r\n"
|
"Content-Length: 0\r\n"
|
||||||
"\r\n";
|
"\r\n";
|
||||||
if(!flag) {
|
if(!flag) {
|
||||||
|
|
Loading…
Reference in a new issue