Files
Adminive/backend/service/tests/fake_drogon/drogon/drogon.h
T
2026-08-08 21:45:29 +08:00

170 lines
5.6 KiB
C++

#pragma once
#include <charconv>
#include <cstdint>
#include <functional>
#include <map>
#include <memory>
#include <stdexcept>
#include <string>
#include <string_view>
#include <type_traits>
#include <utility>
#include <vector>
namespace drogon {
enum HttpStatusCode {
k200OK = 200,
k400BadRequest = 400,
k404NotFound = 404,
k422UnprocessableEntity = 422,
k500InternalServerError = 500
};
enum ContentType {
CT_APPLICATION_JSON
};
enum HttpMethod {
Get,
Post,
Put,
Patch,
Delete
};
namespace internal {
class HttpConstraint {
public:
HttpConstraint(HttpMethod method) : method_(method), is_method_(true) {}
HttpConstraint(std::string filter) : filter_(std::move(filter)) {}
bool is_method() const noexcept {
return is_method_;
}
HttpMethod method() const noexcept {
return method_;
}
const std::string& filter() const noexcept {
return filter_;
}
private:
HttpMethod method_{Get};
bool is_method_{};
std::string filter_;
};
}
class HttpResponse {
public:
static std::shared_ptr<HttpResponse> newHttpResponse() {
return std::make_shared<HttpResponse>();
}
void setStatusCode(HttpStatusCode value) {
status = value;
}
void setContentTypeCode(ContentType value) {
content_type = value;
}
void setBody(std::string value) {
body = std::move(value);
}
HttpStatusCode status{k200OK};
ContentType content_type{CT_APPLICATION_JSON};
std::string body;
};
using HttpResponsePtr = std::shared_ptr<HttpResponse>;
class HttpRequest {
public:
std::string_view getBody() const noexcept {
return body;
}
const std::map<std::string, std::string>& getParameters() const noexcept {
return parameters;
}
const std::string& getParameter(const std::string& name) const {
const auto iterator = parameters.find(name);
return iterator == parameters.end() ? empty_parameter_ : iterator->second;
}
std::string body;
std::string user;
std::map<std::string, std::string> parameters;
private:
inline static const std::string empty_parameter_;
};
using HttpRequestPtr = std::shared_ptr<HttpRequest>;
class HttpAppFramework {
public:
using Callback = std::function<void(const HttpResponsePtr&)>;
using Handler = std::function<void(const HttpRequestPtr&, Callback&&)>;
using Parameter_Handler = std::function<void(const HttpRequestPtr&, Callback&&, std::uint64_t)>;
template <class Function>
void registerHandler(const std::string& path, Function&& function, const std::vector<internal::HttpConstraint>& constraints) {
HttpMethod method = Get;
std::vector<std::string> filters;
for(const auto& constraint : constraints) {
if(constraint.is_method()) {
method = constraint.method();
} else {
filters.push_back(constraint.filter());
}
}
Route route;
route.path = path;
route.method = method;
route.filters = std::move(filters);
if constexpr(std::is_invocable_v<Function, const HttpRequestPtr&, Callback&&, std::uint64_t>) {
route.parameter_handler = Parameter_Handler(std::forward<Function>(function));
} else {
route.handler = Handler(std::forward<Function>(function));
}
routes_.push_back(std::move(route));
}
void handle(const std::string& path, HttpMethod method, const HttpRequestPtr& request, Callback callback) {
for(auto& route : routes_) {
if(route.method != method) {
continue;
}
if(route.handler && route.path == path) {
route.handler(request, std::move(callback));
return;
}
std::uint64_t parameter{};
if(route.parameter_handler && match_parameter_route(route.path, path, parameter)) {
route.parameter_handler(request, std::move(callback), parameter);
return;
}
}
throw std::runtime_error("route is not registered");
}
const std::vector<std::string>& filters(const std::string& path, HttpMethod method) const {
for(const auto& route : routes_) {
if(route.path == path && route.method == method) {
return route.filters;
}
}
throw std::runtime_error("route is not registered");
}
private:
struct Route {
std::string path;
HttpMethod method{Get};
Handler handler;
Parameter_Handler parameter_handler;
std::vector<std::string> filters;
};
static bool match_parameter_route(const std::string& pattern, const std::string& path, std::uint64_t& parameter) {
const auto begin = pattern.find('{');
if(begin == std::string::npos) {
return false;
}
const auto end = pattern.find('}', begin);
if(end == std::string::npos) {
return false;
}
const std::string_view prefix(pattern.data(), begin);
const std::string_view suffix(pattern.data() + end + 1, pattern.size() - end - 1);
if(path.size() < prefix.size() + suffix.size() || !std::string_view(path).starts_with(prefix) || !std::string_view(path).ends_with(suffix)) {
return false;
}
const std::string_view value(path.data() + prefix.size(), path.size() - prefix.size() - suffix.size());
const auto [parsed_end, error] = std::from_chars(value.data(), value.data() + value.size(), parameter);
return error == std::errc{} && parsed_end == value.data() + value.size();
}
std::vector<Route> routes_;
};
}