Stuff
This commit is contained in:
parent
792abbee93
commit
638276d8e0
12 changed files with 299 additions and 27 deletions
|
@ -17,7 +17,7 @@ set(SOURCE_FILES
|
|||
Source/StringUtils.cpp
|
||||
Source/Cookie.cpp
|
||||
Source/Session.cpp
|
||||
Source/Response.cpp)
|
||||
Source/Response.cpp Source/InbuildMiddleWare.cpp)
|
||||
include_directories(${CMAKE_SOURCE_DIR}/)
|
||||
add_library(VWeb ${SOURCE_FILES})
|
||||
|
||||
|
|
121
Source/InbuildMiddleWare.cpp
Normal file
121
Source/InbuildMiddleWare.cpp
Normal 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
|
|
@ -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,
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
75
VWeb.h
75
VWeb.h
|
@ -14,7 +14,9 @@
|
|||
#include <sys/socket.h>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
namespace VWeb {
|
||||
|
||||
|
@ -253,6 +255,7 @@ protected:
|
|||
bool m_IsErrored{false};
|
||||
};
|
||||
|
||||
class MiddleWareHandler;
|
||||
class RequestHandler;
|
||||
class Response;
|
||||
class Router;
|
||||
|
@ -272,6 +275,7 @@ public:
|
|||
void AddRoute(const std::string &path, const Ref<Route> &route);
|
||||
void RemoveRoute(const std::string &path);
|
||||
|
||||
Ref<MiddleWareHandler>& Middleware();
|
||||
protected:
|
||||
void Execute();
|
||||
void OutgoingExecute(epoll_event &event);
|
||||
|
@ -305,6 +309,8 @@ struct Session {
|
|||
void Remove(const std::string &key);
|
||||
bool Has(const std::string &key);
|
||||
Ref<SessionData> &operator[](const std::string &key) { return m_Data[key]; }
|
||||
void SetSessionData(const std::string& key, const Ref<SessionData>& data) { m_Data[key] = data; }
|
||||
bool ContainsData() { return !m_Data.empty(); }
|
||||
|
||||
protected:
|
||||
std::chrono::time_point<std::chrono::system_clock, ms> m_LastCall =
|
||||
|
@ -439,6 +445,9 @@ public:
|
|||
bool HasParameter(const std::string &key) const {
|
||||
return Parameters.contains(key);
|
||||
}
|
||||
bool HasHeader(const std::string &key) const {
|
||||
return Headers.contains(key);
|
||||
}
|
||||
std::string &FirstOf(const std::string &key) {
|
||||
return Parameters[key].GetFirst();
|
||||
}
|
||||
|
@ -448,6 +457,8 @@ public:
|
|||
Vector<std::string> URLParameters;
|
||||
};
|
||||
class Response {
|
||||
public:
|
||||
static Ref<Response> FromCode(HttpStatusCode code);
|
||||
public:
|
||||
size_t Length{0};
|
||||
Ref<Cookies> CookieData{nullptr};
|
||||
|
@ -498,6 +509,7 @@ protected:
|
|||
friend Server;
|
||||
};
|
||||
|
||||
typedef std::function<bool(const Request&,Response&)> RouteFunction;
|
||||
class Route {
|
||||
public:
|
||||
Route() = default;
|
||||
|
@ -514,6 +526,8 @@ public:
|
|||
bool SupportsMethod(const Request &request);
|
||||
virtual bool IsAllowed(const Request &request);
|
||||
|
||||
void AllowMethod(HttpMethod method);
|
||||
|
||||
protected:
|
||||
bool m_AllowAll{true};
|
||||
Vector<HttpMethod> m_AllowedMethods;
|
||||
|
@ -531,43 +545,86 @@ public:
|
|||
Ref<Route> FindRoute(Ref<Request> &request);
|
||||
static void AddToArgs(Ref<Request> &request, Vector<std::string> &items);
|
||||
|
||||
protected:
|
||||
public:
|
||||
void Get(const std::string& path, RouteFunction);
|
||||
void Post(const std::string& path, RouteFunction);
|
||||
void Put(const std::string& path, RouteFunction);
|
||||
void Patch(const std::string& path, RouteFunction);
|
||||
void Delete(const std::string& path, RouteFunction);
|
||||
std::unordered_map<std::string, Ref<Route>> m_Routes;
|
||||
};
|
||||
|
||||
typedef std::optional<Ref<Response>> PreMiddleWareReturn;
|
||||
struct MiddleWare {
|
||||
int Pos{0};
|
||||
virtual void PreHandle(Request &){};
|
||||
virtual void PostHandle(Request &, Response &){};
|
||||
virtual void Shutdown(Request &, Response &){};
|
||||
virtual PreMiddleWareReturn PreHandle(Request &){ return {}; }
|
||||
virtual bool PostHandle(const Request &, Response &){ return true; }
|
||||
virtual void Shutdown(const Request &, const Response &){};
|
||||
bool operator<(const MiddleWare *rhs) const { return Pos < rhs->Pos; }
|
||||
};
|
||||
|
||||
class MiddleWareHandler {
|
||||
public:
|
||||
void HandlePre(Ref<Request> &);
|
||||
PreMiddleWareReturn HandlePre(Ref<Request> &);
|
||||
void HandlePost(Ref<Request> &, Ref<Response> &);
|
||||
void Shutdown(Ref<Request> &, Ref<Response> &);
|
||||
|
||||
public:
|
||||
template <class T> Ref<MiddleWare> Get() { return GetById(typeid(T).name()); }
|
||||
template <class T> Ref<MiddleWare> GetRef() { return GetById(typeid(T).name()); }
|
||||
template <class T> T& Get() { return static_cast<T&>(*GetById(typeid(T).name())); }
|
||||
template <class T> void Set(Ref<MiddleWare> &instance) {
|
||||
auto &type = typeid(T);
|
||||
if (type.before(typeid(MiddleWare)))
|
||||
SetById(type.name(), instance);
|
||||
}
|
||||
template <class T> Ref<MiddleWare> Create() {
|
||||
return SetById(typeid(T).name(), CreateRef<T>());
|
||||
template <class T> T& Create() {
|
||||
return static_cast<T&>(*CreateMiddleWare<T>());
|
||||
}
|
||||
template <class T> void Remove() { RemoveById(typeid(T).name()); }
|
||||
|
||||
protected:
|
||||
template <class T> Ref<MiddleWare> CreateMiddleWare() {
|
||||
return SetById(typeid(T).name(), CreateRef<T>());
|
||||
}
|
||||
Ref<MiddleWare> GetById(const char *id);
|
||||
Ref<MiddleWare> SetById(const char *id, const Ref<MiddleWare> &);
|
||||
void RemoveById(const char *id);
|
||||
std::map<std::string, Ref<MiddleWare>> m_MiddleWares;
|
||||
};
|
||||
|
||||
typedef std::function<PreMiddleWareReturn(Request& req)> AuthFunction;
|
||||
class AuthWare : public MiddleWare {
|
||||
public:
|
||||
AuthWare() = default;
|
||||
~AuthWare() = default;
|
||||
PreMiddleWareReturn PreHandle(Request &request) override;
|
||||
void SetAuthMethod(AuthFunction function) { m_AuthFunction = std::move(function); }
|
||||
protected:
|
||||
AuthFunction m_AuthFunction{nullptr};
|
||||
};
|
||||
|
||||
class SessionManager : public MiddleWare {
|
||||
public:
|
||||
SessionManager();
|
||||
~SessionManager();
|
||||
PreMiddleWareReturn PreHandle(Request& request) override;
|
||||
bool PostHandle(const Request& request, Response& response) override;
|
||||
protected:
|
||||
void GC();
|
||||
protected:
|
||||
Ref<std::thread> m_GCThread;
|
||||
std::mutex m_Mutex;
|
||||
std::unordered_map<std::string, Ref<Session>> m_Sessions;
|
||||
int m_Counter{-1};
|
||||
bool m_IsRunning{true};
|
||||
};
|
||||
|
||||
class CookieManager : public MiddleWare {
|
||||
public:
|
||||
CookieManager() = default;
|
||||
~CookieManager() = default;
|
||||
PreMiddleWareReturn PreHandle(Request&) override;
|
||||
};
|
||||
|
||||
#pragma endregion VWEB_ROUTING
|
||||
#pragma endregion VWEB
|
||||
} // namespace VWeb
|
BIN
dist/libVWeb.debug.a
vendored
BIN
dist/libVWeb.debug.a
vendored
Binary file not shown.
BIN
dist/libVWeb.release.a
vendored
BIN
dist/libVWeb.release.a
vendored
Binary file not shown.
|
@ -1,19 +1,18 @@
|
|||
#include <iostream>
|
||||
#include <VWeb.h>
|
||||
|
||||
class BigDataRoute : public VWeb::Route {
|
||||
public:
|
||||
bool Execute(const VWeb::Request &request, VWeb::Response &response) override {
|
||||
response << "<pre>\n";
|
||||
for (int i = 0; i < 100; ++i)
|
||||
response << "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.\n";
|
||||
response << "</pre>";
|
||||
return true;
|
||||
}
|
||||
};
|
||||
bool Ping(const VWeb::Request&, VWeb::Response& response) {
|
||||
response << "Pong";
|
||||
return true;
|
||||
}
|
||||
int main() {
|
||||
using namespace VWeb;
|
||||
VWeb::Server server;
|
||||
server.AddRoute("/test", VWeb::CreateRef<BigDataRoute>());
|
||||
auto& router = server.GetRouter();
|
||||
router->Get("/test", [](const Request&, Response& response) {
|
||||
response << "NICE";
|
||||
return true;
|
||||
});
|
||||
router->Get("/ping", &Ping);
|
||||
server.Start();
|
||||
server.Join();
|
||||
return 0;
|
||||
|
|
Loading…
Reference in a new issue