VWeb/Source/Router.cpp

162 lines
5 KiB
C++
Raw Normal View History

#include "Includes/VWeb.h"
2022-08-23 14:13:21 +02:00
#include "StringUtils.h"
#include <type_traits>
2022-08-23 16:40:57 +02:00
#include <utility>
2022-08-23 14:13:21 +02:00
namespace VWeb {
2024-02-04 13:52:08 +01:00
#define stringify(name) \
{ name, std::string(#name).replace(0, 12, "") }
static std::unordered_map<HttpMethod, std::string> s_HttpMethodToString = {
stringify(HttpMethod::HEAD), stringify(HttpMethod::GET),
stringify(HttpMethod::OPTIONS), stringify(HttpMethod::POST),
stringify(HttpMethod::PUT), stringify(HttpMethod::PATCH),
stringify(HttpMethod::DELETE), stringify(HttpMethod::FALLBACK)};
#undef stringify
2022-08-23 14:13:21 +02:00
template <typename E> constexpr auto to_underlying(E e) noexcept {
return static_cast<std::underlying_type_t<E>>(e);
}
class ErrorRoute : public Route {
public:
2022-09-10 15:09:18 +02:00
bool Execute(Request &request, Response &response) override {
2022-08-23 14:13:21 +02:00
response.Reset();
response << "Unhandled Error: Status "
<< std::to_string(to_underlying(response.Status));
response.SetType("text/plain");
return true;
}
};
2024-02-04 13:52:08 +01:00
Router::Router() { Register<ErrorRoute>("@"); }
2022-08-23 14:13:21 +02:00
void Router::DeleteRoute(const std::string &name) {
if (m_Routes.contains(name)) {
m_Routes.erase(name);
}
}
2024-02-04 13:52:08 +01:00
static void HandleOptions(Ref<Response> &response, uint32_t allowedMethods) {
std::stringstream str{};
bool isFirst = true;
for (auto &[key, value] : s_HttpMethodToString) {
if (allowedMethods & static_cast<uint32_t>(key)) {
if (!isFirst)
str << ", ";
str << value;
isFirst = false;
}
}
response->SetHeader("Allow", str.str());
}
2022-08-23 14:13:21 +02:00
Ref<Response> Router::HandleRoute(Ref<Request> &request) {
auto response = CreateRef<Response>();
auto route = FindRoute(request);
response->CookieData = request->CookieData;
2022-08-23 16:40:57 +02:00
response->SessionData = request->SessionData;
2022-08-23 14:13:21 +02:00
response->Method = request->Method;
2024-02-04 13:52:08 +01:00
2022-08-23 14:13:21 +02:00
if (!route) {
2024-02-04 13:52:08 +01:00
// Lets check if we can run it through functions routes..
const auto it = m_FunctionRoutes.find(
s_HttpMethodToString[request->Method] + request->URI);
if (it != m_FunctionRoutes.end()) {
it->second(*request, *response);
return response;
}
2022-08-23 14:13:21 +02:00
response->SetStatus(HttpStatusCode::NotFound);
2024-02-04 13:52:08 +01:00
m_Routes["@"].Instaniate()->Execute(*request, *response);
2022-08-23 14:13:21 +02:00
return response;
}
if (!route->IsAllowed(*request)) {
response->SetStatus(HttpStatusCode::Forbidden);
2024-02-04 13:52:08 +01:00
m_Routes["@"].Instaniate()->Execute(*request, *response);
return response;
}
if (request->Method == HttpMethod::OPTIONS) {
HandleOptions(response, route->GetAllowedMethods());
2022-08-23 14:13:21 +02:00
return response;
}
if (!route->Execute(*request, *response)) {
std::string rKey = "@" + std::to_string(to_underlying(response->Status));
2024-02-04 13:52:08 +01:00
m_Routes.contains(rKey)
? m_Routes[rKey].Instaniate()->Execute(*request, *response)
: m_Routes["@"].Instaniate()->Execute(*request, *response);
2022-08-23 14:13:21 +02:00
}
return response;
}
2024-02-04 13:52:08 +01:00
static Ref<Route> Instaniate(const RouteInstaniateFunction &func,
uint32_t allowedMethods) {
auto ref = func();
ref->SetAllowedMethods(allowedMethods);
return ref;
}
2022-08-23 14:13:21 +02:00
Ref<Route> Router::FindRoute(Ref<Request> &request) {
2024-02-04 13:52:08 +01:00
const auto &url = request->URI;
2022-08-23 14:13:21 +02:00
if (url.starts_with("@"))
return nullptr;
2024-02-04 13:52:08 +01:00
{
if (const auto it = m_Routes.find(url);
it != m_Routes.end() && it->second.AllowedMethods & request->Method) {
return Instaniate(it->second.Instaniate, it->second.AllowedMethods);
}
}
auto split = String::Split(url, "/");
2022-08-23 14:13:21 +02:00
if (split.size() > 1) {
AddToArgs(request, split);
while (split.size() > 1) {
std::string nUrl = String::Join(split, "/");
2024-02-04 13:52:08 +01:00
if (auto it = m_Routes.find(url);
it != m_Routes.end() && it->second.AllowedMethods & request->Method) {
return Instaniate(it->second.Instaniate, it->second.AllowedMethods);
2022-08-23 14:13:21 +02:00
}
AddToArgs(request, split);
}
}
2024-02-04 13:52:08 +01:00
{
if (const auto it = m_Routes.find("/");
it != m_Routes.end() && it->second.AllowedMethods & request->Method) {
return Instaniate(it->second.Instaniate, it->second.AllowedMethods);
}
2022-08-23 14:13:21 +02:00
}
return nullptr;
}
void Router::AddToArgs(Ref<Request> &request, std::vector<std::string> &items) {
2022-08-23 14:13:21 +02:00
request->URLParameters.push_back(items[items.size() - 1]);
items.pop_back();
}
2022-08-23 16:40:57 +02:00
void Router::Get(const std::string &path, RouteFunction func) {
2024-02-04 13:52:08 +01:00
m_FunctionRoutes[s_HttpMethodToString[HttpMethod::GET] + path] =
std::move(func);
2022-08-23 16:40:57 +02:00
}
void Router::Post(const std::string &path, RouteFunction func) {
2024-02-04 13:52:08 +01:00
m_FunctionRoutes[s_HttpMethodToString[HttpMethod::POST] + path] =
std::move(func);
2022-08-23 16:40:57 +02:00
}
void Router::Put(const std::string &path, RouteFunction func) {
2024-02-04 13:52:08 +01:00
m_FunctionRoutes[s_HttpMethodToString[HttpMethod::PUT] + path] =
std::move(func);
2022-08-23 16:40:57 +02:00
}
void Router::Patch(const std::string &path, RouteFunction func) {
2024-02-04 13:52:08 +01:00
m_FunctionRoutes[s_HttpMethodToString[HttpMethod::PATCH] + path] =
std::move(func);
2022-08-23 16:40:57 +02:00
}
void Router::Delete(const std::string &path, RouteFunction func) {
2024-02-04 13:52:08 +01:00
m_FunctionRoutes[s_HttpMethodToString[HttpMethod::DELETE] + path] =
std::move(func);
2022-08-23 16:40:57 +02:00
}
2022-11-09 14:34:20 +01:00
} // namespace VWeb