#include "StringUtils.h" #include #include #include namespace VWeb { template constexpr auto to_underlying(E e) noexcept { return static_cast>(e); } class ErrorRoute : public Route { public: bool Execute(const Request &request, Response &response) override { response.Reset(); response << "Unhandled Error: Status " << std::to_string(to_underlying(response.Status)); response.SetType("text/plain"); return true; } }; class InstanceHandleRoute : public Route { public: bool Get(const Request &request, Response &response) override { return GetFunc && GetFunc(request, response); } bool Post(const Request &request, Response &response) override { return PostFunc && PostFunc(request, response); } bool Put(const Request &request, Response &response) override { return PutFunc && PutFunc(request, response); } bool Patch(const Request &request, Response &response) override { return PatchFunc && PatchFunc(request, response); } bool Delete(const Request &request, Response &response) override { return DeleteFunc && DeleteFunc(request, response); } protected: RouteFunction GetFunc{nullptr}; RouteFunction PostFunc{nullptr}; RouteFunction PutFunc{nullptr}; RouteFunction PatchFunc{nullptr}; RouteFunction DeleteFunc{nullptr}; friend Router; }; Router::Router() { m_Routes["@"] = CreateRef(); } void Router::AddRoute(const std::string &name, const Ref &route) { m_Routes[name] = route; } Ref &Router::GetRoute(const std::string &name) { return m_Routes[name]; } void Router::DeleteRoute(const std::string &name) { if (m_Routes.contains(name)) { m_Routes.erase(name); } } Ref Router::HandleRoute(Ref &request) { auto response = CreateRef(); auto route = FindRoute(request); response->CookieData = request->CookieData; response->SessionData = request->SessionData; response->Method = request->Method; if (!route) { response->SetStatus(HttpStatusCode::NotFound); m_Routes["@"]->Execute(*request, *response); return response; } if (!route->IsAllowed(*request)) { response->SetStatus(HttpStatusCode::Forbidden); m_Routes["@"]->Execute(*request, *response); return response; } if (!route->Execute(*request, *response)) { std::string rKey = "@" + std::to_string(to_underlying(response->Status)); m_Routes.contains(rKey) ? m_Routes[rKey]->Execute(*request, *response) : m_Routes["@"]->Execute(*request, *response); } return response; } Ref Router::FindRoute(Ref &request) { auto &url = request->URI; if (m_Routes.contains(url.data())) { auto &route = m_Routes.at(url.data()); if (route->SupportsMethod(*request)) return route; } if (url.starts_with("@")) return nullptr; auto split = String::Split(url.data(), "/"); if (split.size() > 1) { AddToArgs(request, split); while (split.size() > 1) { std::string nUrl = String::Join(split, "/"); if (m_Routes.contains(nUrl)) { auto &route = m_Routes[nUrl]; if (route->SupportsMethod(*request)) return route; } AddToArgs(request, split); } } if (m_Routes.contains("/")) { auto &route = m_Routes["/"]; if (route->SupportsMethod(*request)) return route; } return nullptr; } void Router::AddToArgs(Ref &request, Vector &items) { request->URLParameters.push_back(items[items.size() - 1]); items.pop_back(); } InstanceHandleRoute* GetOrNull(const std::string& path, Router* router) { InstanceHandleRoute* route; if (!router->m_Routes.contains(path)) { router->AddRoute(path, CreateRef()); } route = dynamic_cast(router->m_Routes[path].get()); return route; } void Router::Get(const std::string &path, RouteFunction func) { auto* route = GetOrNull(path, this); if (route) route->GetFunc = std::move(func); } void Router::Post(const std::string &path, RouteFunction func) { auto* route = GetOrNull(path, this); if (route) route->PostFunc = std::move(func); } void Router::Put(const std::string &path, RouteFunction func) { auto* route = GetOrNull(path, this); if (route) route->PutFunc = std::move(func); } void Router::Patch(const std::string &path, RouteFunction func) { auto* route = GetOrNull(path, this); if (route) route->PatchFunc = std::move(func); } void Router::Delete(const std::string &path, RouteFunction func) { auto* route = GetOrNull(path, this); if (route) route->DeleteFunc = std::move(func); } } // namespace VWeb