This commit is contained in:
Maurice Grönwoldt 2022-08-23 16:40:57 +02:00
commit 638276d8e0
12 changed files with 299 additions and 27 deletions

View file

@ -0,0 +1,121 @@
#include "StringUtils.h"
#include "VWeb.h"
#include <random>
#include <sstream>
namespace VWeb {
#pragma region AUTH
PreMiddleWareReturn AuthWare::PreHandle(Request &request) {
if (m_AuthFunction)
return m_AuthFunction(request);
return {};
}
#pragma endregion AUTH
#pragma region SESSION
static std::string GenerateSID() {
static std::random_device dev;
static std::mt19937 rng(dev());
std::uniform_int_distribution<int> dist(0, 15);
const char *v = "0123456789abcdef";
const bool dashArray[] = {false, false, false, false, true, false,
true, false, true, false, true, false,
false, false, false, false};
std::stringstream res;
for (bool dash : dashArray) {
if (dash)
res << "-";
res << v[dist(rng)];
res << v[dist(rng)];
}
return res.str();
}
SessionManager::SessionManager() {
m_GCThread = CreateRef<std::thread>([this]() {
while (m_IsRunning) {
if (m_Counter == 59) {
GC();
m_Counter = -1;
}
m_Counter++;
}
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
});
}
SessionManager::~SessionManager() {
m_IsRunning = false;
m_GCThread->join();
}
PreMiddleWareReturn SessionManager::PreHandle(Request &request) {
auto &cookies = request.CookieData;
if (!cookies->Has("sid"))
return {};
auto &cookie = cookies->Get("sid");
std::lock_guard<std::mutex> lck(m_Mutex);
if (!m_Sessions.contains(cookie.Value))
return {};
auto &session = m_Sessions[cookie.Value];
session->Update();
request.SessionData = session;
return {};
}
bool SessionManager::PostHandle(const Request &request, Response &response) {
if (response.SessionData->Id.empty() &&
response.SessionData->ContainsData()) {
response.SessionData->Update();
{
std::lock_guard<std::mutex> lck(m_Mutex);
response.SessionData->Id = GenerateSID();
while (m_Sessions.contains(response.SessionData->Id))
response.SessionData->Id = GenerateSID();
}
m_Sessions[response.SessionData->Id] = response.SessionData;
auto &sidCookie = response.CookieData->Get("sid");
sidCookie.HttpOnly = true;
sidCookie.Secure = true;
sidCookie.Value = response.SessionData->Id;
}
return true;
}
void SessionManager::GC() {
std::lock_guard<std::mutex> lck(m_Mutex);
std::vector<std::string_view> mark_as_delete;
for (auto &itemToCollect : m_Sessions) {
if (!itemToCollect.second->IsValid())
mark_as_delete.push_back(itemToCollect.first);
}
for (auto &i : mark_as_delete)
m_Sessions.erase(i.data());
}
#pragma endregion SESSION
#pragma region COOKIES
PreMiddleWareReturn CookieManager::PreHandle(Request &request) {
auto &cookieHeaders = request.Header("Cookie");
auto &cookies = request.CookieData;
if (cookieHeaders.Size() > 0) {
auto &values = cookieHeaders.Values();
for (auto &rawCookie : cookieHeaders.Values()) {
auto splitCookies = String::Split(rawCookie, ";");
for (auto &cookie : splitCookies) {
auto split = String::Split(cookie, "=");
String::Trim(split[0]);
String::Trim(split[1]);
cookies->CreateOld(split[0], split[1]);
}
}
}
return {};
}
#pragma endregion COOKIES
} // namespace VWeb

View file

@ -1,15 +1,21 @@
#include <VWeb.h>
namespace VWeb {
void MiddleWareHandler::HandlePre(Ref<Request> &request) {
std::optional<Ref<Response>>
MiddleWareHandler::HandlePre(Ref<Request> &request) {
for (auto &[key, middleWare] : m_MiddleWares) {
middleWare->PreHandle(*request);
auto data = middleWare->PreHandle(*request);
if (data.has_value())
return data;
}
return {};
}
void MiddleWareHandler::HandlePost(Ref<Request> &request,
Ref<Response> &response) {
for (auto &[key, middleWare] : m_MiddleWares) {
middleWare->PostHandle(*request, *response);
if (!middleWare->PostHandle(*request, *response)) {
break;
}
}
}
void MiddleWareHandler::Shutdown(Ref<Request> &request,

View file

@ -55,8 +55,15 @@ struct RequestJob : public WorkerJob {
}
MRequest->CookieData = CreateRef<Cookies>();
MRequest->SessionData = CreateRef<Session>();
MMiddleWareHandler->HandlePre(MRequest);
auto response = MRouter->HandleRoute(MRequest);
Ref<Response> response;
auto preValue = MMiddleWareHandler->HandlePre(MRequest);
if (preValue.has_value()) {
response = preValue.value();
response->SessionData = MRequest->SessionData;
response->CookieData = MRequest->CookieData;
} else {
response = MRouter->HandleRoute(MRequest);
}
MMiddleWareHandler->HandlePost(MRequest, response);
auto content = response->GetResponse();
MRequestHandler->AddSendResponse(

View file

@ -65,6 +65,13 @@ static std::unordered_map<HttpStatusCode, std::string> s_HTTPCodeToString = {
{HttpStatusCode::NetworkAuthenticationRequired,"511 Network Authentication Required"}
};
// clang-format on
Ref<Response> Response::FromCode(HttpStatusCode code) {
auto response = CreateRef<Response>();
response->SetStatus(code);
return response;
}
std::string Response::GetResponse() {
std::string content = m_Content.str();
auto headData = TransformHeaders(content);

View file

@ -80,4 +80,8 @@ bool Route::SupportsMethod(const Request &request) {
std::find(m_AllowedMethods.begin(), m_AllowedMethods.end(),
request.Method) != m_AllowedMethods.end();
}
void Route::AllowMethod(HttpMethod method) {
if (std::find(m_AllowedMethods.begin(), m_AllowedMethods.end(), method) == m_AllowedMethods.end())
m_AllowedMethods.push_back(method);
}
} // namespace VWeb

View file

@ -2,6 +2,7 @@
#include <VWeb.h>
#include <type_traits>
#include <utility>
namespace VWeb {
@ -20,6 +21,33 @@ public:
}
};
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<ErrorRoute>(); }
void Router::AddRoute(const std::string &name, const Ref<Route> &route) {
@ -38,6 +66,7 @@ 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);
@ -94,4 +123,38 @@ void Router::AddToArgs(Ref<Request> &request, 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

View file

@ -8,6 +8,10 @@ Server::Server() {
m_ServerConfig->EPoll = CreateRef<EPollManager>();
m_ServerConfig->Socket = CreateRef<SocketManager>(m_ServerConfig);
m_RequestHandler = CreateRef<RequestHandler>(m_ServerConfig->Socket);
auto& middleWare = m_RequestHandler->Middleware();
middleWare->Create<CookieManager>();
middleWare->Create<SessionManager>();
middleWare->Create<AuthWare>();
m_RequestHandler->m_Server = this;
};
void Server::LoadSharedLibs() {
@ -132,4 +136,8 @@ void Server::CreateRequest(int sockID) {
m_RawRequest.Remove(sockID);
m_RequestHandler->AddRequest(request);
}
Ref<MiddleWareHandler> &Server::Middleware() {
return m_RequestHandler->Middleware();
}
} // namespace VWeb