#pragma once #include #include #include #include #include #include #include #include #include #include #include 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 newHttpResponse() { return std::make_shared(); } 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; class HttpRequest { public: std::string_view getBody() const noexcept { return body; } 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 parameters; private: inline static const std::string empty_parameter_; }; using HttpRequestPtr = std::shared_ptr; class HttpAppFramework { public: using Callback = std::function; using Handler = std::function; using Parameter_Handler = std::function; template void registerHandler(const std::string& path, Function&& function, const std::vector& constraints) { HttpMethod method = Get; std::vector 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) { route.parameter_handler = Parameter_Handler(std::forward(function)); } else { route.handler = Handler(std::forward(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& 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 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 routes_; }; }