Router: Uses Tree and Use named Parameters

This commit is contained in:
Maurice Grönwoldt 2024-04-16 20:09:22 +02:00
commit 2bd34c2bb1
8 changed files with 139 additions and 122 deletions

View file

@ -49,10 +49,9 @@ void ParseParameterString(Request &req, const std::string &toParse) {
}
}
std::string GetPostBody(const std::string& originalBody)
{
std::string GetPostBody(const std::string &originalBody) {
auto body = String::Split(originalBody, "\r\n\r\n", 1);
if (body.size() > 1 && ! body[body.size() - 1].empty())
if (body.size() > 1 && !body[body.size() - 1].empty())
return String::TrimCopy(String::UrlDecode(body[body.size() - 1]));
return {};
}
@ -62,7 +61,7 @@ void ParseParameters(Request &request, RequestHandler &requestHandler) {
size_t hasURLParameters = uri.find('?');
if (hasURLParameters != std::string::npos) {
ParseParameterString(request, uri.substr(hasURLParameters + 1));
request.URI = uri.substr (0, hasURLParameters);
request.URI = uri.substr(0, hasURLParameters);
}
if (request.Method == HttpMethod::HEAD || request.Method == HttpMethod::GET ||

View file

@ -1,6 +1,7 @@
#include "Includes/VWeb.h"
#include "StringUtils.h"
#include <iostream>
#include <type_traits>
#include <utility>
@ -30,14 +31,59 @@ public:
}
};
Router::Router() { Register<ErrorRoute>("@"); }
void Router::DeleteRoute(const std::string &name) {
if (m_Routes.contains(name)) {
m_Routes.erase(name);
void RouteTree::Add(const std::string &path, uint32_t allowedMethods,
RouteInstaniateFunction instaniate) {
auto segments = String::Split(path, "/");
auto node = &Root;
for (const auto &segment : segments) {
if (segment.empty())
continue;
if (!node->Children.contains(segment)) {
node->Children[segment] = std::make_unique<Node>(m_NodeID++);
}
node = node->Children.at(segment).get();
}
m_Routes[node->ID] = {.AllowedMethods = allowedMethods,
.Instaniate = std::move(instaniate)};
}
Ref<Route> RouteTree::Find(const std::string &path, Request &request) {
auto segments = String::Split(path, "/");
auto node = &Root;
for (const auto &segment : segments) {
if (segment.empty())
continue;
if (auto it = node->Children.find(segment); it != node->Children.end()) {
node = it->second.get();
} else {
// Arguments...
bool foundParameter = false;
for (auto &[key, child] : node->Children) {
if (key[0] == ':') {
node = child.get();
foundParameter = true;
request.URLParameters[key.substr(1)] = segment;
break;
}
}
if (!foundParameter) {
request.URLParameters = {};
return nullptr;
}
}
}
if (m_Routes.contains(node->ID)) {
const auto &instance = m_Routes[node->ID];
auto ref = instance.Instaniate();
ref->SetAllowedMethods(instance.AllowedMethods);
return ref;
}
request.URLParameters = {};
return nullptr;
}
Router::Router() { Register<ErrorRoute>("@"); }
static void HandleOptions(Ref<Response> &response, uint32_t allowedMethods) {
std::stringstream str{};
bool isFirst = true;
@ -60,21 +106,18 @@ Ref<Response> Router::HandleRoute(Ref<Request> &request) {
response->Method = request->Method;
if (!route) {
// 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);
route = m_Tree.Find(s_HttpMethodToString[request->Method] + request->URI,
*request);
if (!route) {
response->SetStatus(HttpStatusCode::NotFound);
m_Tree.Find("@", *request)->Execute(*request, *response);
return response;
}
response->SetStatus(HttpStatusCode::NotFound);
m_Routes["@"].Instaniate()->Execute(*request, *response);
return response;
}
if (!route->IsAllowed(*request)) {
response->SetStatus(HttpStatusCode::Forbidden);
m_Routes["@"].Instaniate()->Execute(*request, *response);
m_Tree.Find("@", *request)->Execute(*request, *response);
return response;
}
@ -85,77 +128,56 @@ Ref<Response> Router::HandleRoute(Ref<Request> &request) {
if (!route->Execute(*request, *response)) {
std::string rKey = "@" + std::to_string(to_underlying(response->Status));
m_Routes.contains(rKey)
? m_Routes[rKey].Instaniate()->Execute(*request, *response)
: m_Routes["@"].Instaniate()->Execute(*request, *response);
auto r = m_Tree.Find(rKey, *request);
if (r) {
r->Execute(*request, *response);
} else {
m_Tree.Find("@", *request)->Execute(*request, *response);
}
}
return response;
}
static Ref<Route> Instaniate(const RouteInstaniateFunction &func,
uint32_t allowedMethods) {
auto ref = func();
ref->SetAllowedMethods(allowedMethods);
return ref;
}
Ref<Route> Router::FindRoute(Ref<Request> &request) {
const auto &url = request->URI;
if (url.starts_with("@"))
return nullptr;
return m_Tree.Find(url, *request);
}
{
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);
}
struct InlineRoute : Route {
explicit InlineRoute(RouteFunction function) : Func(std::move(function)) {}
RouteFunction Func;
bool Execute(Request &request, Response &response) override {
Func(request, response);
return true;
}
bool IsAllowed(Request &request) override { return true; }
};
auto split = String::Split(url, "/");
if (split.size() > 1) {
AddToArgs(request, split);
while (split.size() > 1) {
std::string nUrl = String::Join(split, "/");
if (auto it = m_Routes.find(url);
it != m_Routes.end() && it->second.AllowedMethods & request->Method) {
return Instaniate(it->second.Instaniate, it->second.AllowedMethods);
}
AddToArgs(request, split);
}
}
{
if (const auto it = m_Routes.find("/");
it != m_Routes.end() && it->second.AllowedMethods & request->Method) {
return Instaniate(it->second.Instaniate, it->second.AllowedMethods);
}
}
return nullptr;
void Router::Get(const std::string &path, const RouteFunction &func) {
m_Tree.Add(s_HttpMethodToString[HttpMethod::GET] + path,
(uint32_t)HttpMethod::GET,
[func] { return std::make_shared<InlineRoute>(func); });
}
void Router::AddToArgs(Ref<Request> &request, std::vector<std::string> &items) {
request->URLParameters.push_back(items[items.size() - 1]);
items.pop_back();
void Router::Post(const std::string &path, const RouteFunction &func) {
m_Tree.Add(s_HttpMethodToString[HttpMethod::POST] + path,
(uint32_t)HttpMethod::POST,
[func] { return std::make_shared<InlineRoute>(func); });
}
void Router::Get(const std::string &path, RouteFunction func) {
m_FunctionRoutes[s_HttpMethodToString[HttpMethod::GET] + path] =
std::move(func);
void Router::Put(const std::string &path, const RouteFunction &func) {
m_Tree.Add(s_HttpMethodToString[HttpMethod::PUT] + path,
(uint32_t)HttpMethod::PUT,
[func] { return std::make_shared<InlineRoute>(func); });
}
void Router::Post(const std::string &path, RouteFunction func) {
m_FunctionRoutes[s_HttpMethodToString[HttpMethod::POST] + path] =
std::move(func);
void Router::Patch(const std::string &path, const RouteFunction &func) {
m_Tree.Add(s_HttpMethodToString[HttpMethod::PATCH] + path,
(uint32_t)HttpMethod::PATCH,
[func] { return std::make_shared<InlineRoute>(func); });
}
void Router::Put(const std::string &path, RouteFunction func) {
m_FunctionRoutes[s_HttpMethodToString[HttpMethod::PUT] + path] =
std::move(func);
}
void Router::Patch(const std::string &path, RouteFunction func) {
m_FunctionRoutes[s_HttpMethodToString[HttpMethod::PATCH] + path] =
std::move(func);
}
void Router::Delete(const std::string &path, RouteFunction func) {
m_FunctionRoutes[s_HttpMethodToString[HttpMethod::DELETE] + path] =
std::move(func);
void Router::Delete(const std::string &path, const RouteFunction &func) {
m_Tree.Add(s_HttpMethodToString[HttpMethod::DELETE] + path,
(uint32_t)HttpMethod::DELETE,
[func] { return std::make_shared<InlineRoute>(func); });
}
} // namespace VWeb

View file

@ -29,9 +29,6 @@ void Server::Start() {
fprintf(stdout, "[VWeb] Running Server On: 0.0.0.0:%d\n",
m_ServerConfig->Port);
}
void Server::RemoveRoute(const std::string &path) const {
m_Router->DeleteRoute(path);
}
void Server::Execute() {
constexpr size_t MAX_EVENTS = 5000;
struct epoll_event events[MAX_EVENTS];