Maurice Grönwoldt
5bb68a7d02
We have no make install support... so we don't need to have everything as a single-header and lib file.
161 lines
4.6 KiB
C++
161 lines
4.6 KiB
C++
#include "Includes/VWeb.h"
|
|
#include "StringUtils.h"
|
|
|
|
#include <type_traits>
|
|
#include <utility>
|
|
|
|
namespace VWeb {
|
|
|
|
template <typename E> constexpr auto to_underlying(E e) noexcept {
|
|
return static_cast<std::underlying_type_t<E>>(e);
|
|
}
|
|
|
|
class ErrorRoute : public Route {
|
|
public:
|
|
bool Execute(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(Request &request, Response &response) override {
|
|
return GetFunc && GetFunc(request, response);
|
|
}
|
|
bool Post(Request &request, Response &response) override {
|
|
return PostFunc && PostFunc(request, response);
|
|
}
|
|
bool Put(Request &request, Response &response) override {
|
|
return PutFunc && PutFunc(request, response);
|
|
}
|
|
bool Patch(Request &request, Response &response) override {
|
|
return PatchFunc && PatchFunc(request, response);
|
|
}
|
|
bool Delete(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<ErrorRoute>(); }
|
|
|
|
void Router::AddRoute(const std::string &name, const Ref<Route> &route) {
|
|
m_Routes[name] = route;
|
|
}
|
|
|
|
Ref<Route> &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<Response> Router::HandleRoute(Ref<Request> &request) {
|
|
auto response = CreateRef<Response>();
|
|
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<Route> Router::FindRoute(Ref<Request> &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> &request, std::vector<std::string> &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<InstanceHandleRoute>());
|
|
}
|
|
route = dynamic_cast<InstanceHandleRoute*>(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
|