2022-08-23 14:13:21 +02:00
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include <cassert>
|
|
|
|
#include <condition_variable>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <map>
|
|
|
|
#include <memory>
|
|
|
|
#include <mutex>
|
|
|
|
#include <netinet/in.h>
|
|
|
|
#include <optional>
|
|
|
|
#include <queue>
|
|
|
|
#include <sstream>
|
|
|
|
#include <sys/epoll.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <thread>
|
|
|
|
#include <unordered_map>
|
2022-08-23 16:40:57 +02:00
|
|
|
#include <utility>
|
2022-08-23 14:13:21 +02:00
|
|
|
#include <vector>
|
2022-08-23 16:40:57 +02:00
|
|
|
#include <functional>
|
2022-08-23 14:13:21 +02:00
|
|
|
|
|
|
|
namespace VWeb {
|
|
|
|
|
|
|
|
template <typename T> using Ref = std::shared_ptr<T>;
|
|
|
|
template <typename T> using Vector = std::vector<T>;
|
|
|
|
template <typename T, typename... Args>
|
|
|
|
constexpr Ref<T> CreateRef(Args &&...args) {
|
|
|
|
return std::make_shared<T>(std::forward<Args>(args)...);
|
|
|
|
}
|
|
|
|
typedef std::mutex Mutex;
|
|
|
|
|
|
|
|
#pragma region SAFESTRUCTS
|
|
|
|
template <typename T, typename S> struct SafeMap {
|
|
|
|
explicit SafeMap(size_t maxSize = -1UL) : m_MaxSize(maxSize), m_End(false){};
|
|
|
|
bool Add(const T &t, S &x) {
|
|
|
|
std::unique_lock<std::mutex> lck(m_Mutex);
|
|
|
|
while (m_Data.size() == m_MaxSize && !m_End)
|
|
|
|
return false;
|
|
|
|
assert(!m_End);
|
|
|
|
m_Data.emplace(t, std::move(x));
|
|
|
|
return true;
|
|
|
|
};
|
|
|
|
bool Add(T &&t, S &&x) {
|
|
|
|
std::unique_lock<std::mutex> lck(m_Mutex);
|
|
|
|
while (m_Data.size() == m_MaxSize && !m_End)
|
|
|
|
return false;
|
|
|
|
assert(!m_End);
|
|
|
|
m_Data.push(std::move(t));
|
|
|
|
return true;
|
|
|
|
};
|
|
|
|
void Remove(T t) {
|
|
|
|
std::unique_lock<std::mutex> lck(m_Mutex);
|
|
|
|
if (m_Data.empty() || m_End)
|
|
|
|
return;
|
|
|
|
if (m_Data.contains(t))
|
|
|
|
m_Data.erase(t);
|
|
|
|
m_CVFull.notify_one();
|
|
|
|
};
|
|
|
|
void Clear() {
|
|
|
|
std::unique_lock<std::mutex> lck(m_Mutex);
|
|
|
|
std::unordered_map<T, S> empty;
|
|
|
|
std::swap(m_Data, empty);
|
|
|
|
m_CVFull.notify_all();
|
|
|
|
};
|
|
|
|
bool Has(T t) {
|
|
|
|
std::unique_lock<std::mutex> lck(m_Mutex);
|
|
|
|
return m_Data.contains(t);
|
|
|
|
};
|
|
|
|
S &Get(T t) {
|
|
|
|
std::unique_lock<std::mutex> lck(m_Mutex);
|
|
|
|
return m_Data[t];
|
|
|
|
};
|
|
|
|
int Size() { return m_Data.size(); };
|
|
|
|
|
|
|
|
private:
|
|
|
|
std::unordered_map<T, S> m_Data{};
|
|
|
|
std::mutex m_Mutex{};
|
|
|
|
std::condition_variable m_CVFull{};
|
|
|
|
const size_t m_MaxSize{};
|
|
|
|
std::atomic<bool> m_End{};
|
|
|
|
};
|
|
|
|
|
|
|
|
template <typename T> struct SafeQueue {
|
|
|
|
explicit SafeQueue(size_t maxSize = -1UL)
|
|
|
|
: m_MaxSize(maxSize),
|
|
|
|
m_End(false){};
|
|
|
|
void Push(const T &t) {
|
|
|
|
std::unique_lock<std::mutex> lck(m_Mutex);
|
|
|
|
while (m_Queue.size() == m_MaxSize && !m_End)
|
|
|
|
m_CVFull.wait(lck);
|
|
|
|
assert(!m_End);
|
|
|
|
m_Queue.push(std::move(t));
|
|
|
|
m_CVEmpty.notify_one();
|
|
|
|
};
|
|
|
|
void Push(T &&t) {
|
|
|
|
std::unique_lock<std::mutex> lck(m_Mutex);
|
|
|
|
while (m_Queue.size() == m_MaxSize && !m_End)
|
|
|
|
m_CVFull.wait(lck);
|
|
|
|
assert(!m_End);
|
|
|
|
m_Queue.push(std::move(t));
|
|
|
|
m_CVEmpty.notify_one();
|
|
|
|
};
|
|
|
|
void Open() {
|
|
|
|
m_End = false;
|
|
|
|
std::lock_guard<std::mutex> lck(m_Mutex);
|
|
|
|
m_CVEmpty.notify_all();
|
|
|
|
m_CVFull.notify_all();
|
|
|
|
};
|
|
|
|
void Close() {
|
|
|
|
m_End = true;
|
|
|
|
std::lock_guard<std::mutex> lck(m_Mutex);
|
|
|
|
m_CVEmpty.notify_all();
|
|
|
|
m_CVFull.notify_all();
|
|
|
|
};
|
|
|
|
void Clear() {
|
|
|
|
std::unique_lock<std::mutex> lck(m_Mutex);
|
|
|
|
std::queue<T> empty;
|
|
|
|
std::swap(m_Queue, empty);
|
|
|
|
m_CVEmpty.notify_all();
|
|
|
|
m_CVFull.notify_all();
|
|
|
|
};
|
|
|
|
std::optional<T> Pop() {
|
|
|
|
std::unique_lock<std::mutex> lck(m_Mutex);
|
|
|
|
if (m_Queue.empty() || m_End)
|
|
|
|
return {};
|
|
|
|
T t = std::move(m_Queue.front());
|
|
|
|
m_Queue.pop();
|
|
|
|
m_CVFull.notify_one();
|
|
|
|
return t;
|
|
|
|
};
|
|
|
|
std::optional<T> WaitAndPop() {
|
|
|
|
std::unique_lock<std::mutex> lck(m_Mutex);
|
|
|
|
while (m_Queue.empty() && !m_End)
|
|
|
|
m_CVEmpty.wait(lck);
|
|
|
|
if (m_Queue.empty() || m_End)
|
|
|
|
return {};
|
|
|
|
T t = std::move(m_Queue.front());
|
|
|
|
m_Queue.pop();
|
|
|
|
m_CVFull.notify_one();
|
|
|
|
return t;
|
|
|
|
};
|
|
|
|
bool IsClosed() { return m_End; }
|
|
|
|
int Size() { return m_Queue.size(); }
|
|
|
|
std::queue<T> &GetQueue() { return m_Queue; }
|
|
|
|
void Flush() {
|
|
|
|
while (!m_Queue.empty())
|
|
|
|
m_CVEmpty.notify_one();
|
|
|
|
m_End = true;
|
|
|
|
m_CVEmpty.notify_all();
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
std::queue<T> m_Queue;
|
|
|
|
std::mutex m_Mutex;
|
|
|
|
std::condition_variable m_CVEmpty, m_CVFull;
|
|
|
|
const size_t m_MaxSize;
|
|
|
|
std::atomic<bool> m_End;
|
|
|
|
};
|
|
|
|
#pragma endregion SAFESTRUCTS
|
|
|
|
#pragma region THREADING
|
|
|
|
struct WorkerJob {
|
|
|
|
virtual ~WorkerJob() = default;
|
|
|
|
virtual void Execute() = 0;
|
|
|
|
};
|
|
|
|
class ThreadPool {
|
|
|
|
public:
|
|
|
|
explicit ThreadPool(const std::string &name);
|
|
|
|
void SetThreadCount(int);
|
|
|
|
void Dispatch(const Ref<WorkerJob> &);
|
|
|
|
void Create();
|
|
|
|
void Stop();
|
|
|
|
void Execute();
|
|
|
|
|
|
|
|
protected:
|
|
|
|
std::string m_Name;
|
|
|
|
int m_ThreadCount{1};
|
|
|
|
bool m_IsCreated{false};
|
|
|
|
Vector<std::thread> m_Threads{};
|
|
|
|
SafeQueue<Ref<WorkerJob>> m_Queue;
|
|
|
|
bool m_IsDone{false};
|
|
|
|
};
|
|
|
|
#pragma endregion THREADING
|
|
|
|
#pragma region VWEB
|
|
|
|
enum class EPollReturns { OK = 0, BREAK, FAILURE };
|
|
|
|
|
|
|
|
class EPollManager;
|
|
|
|
class SocketManager;
|
|
|
|
struct ServerConfig {
|
|
|
|
int Port{9020};
|
|
|
|
int MaxBufferSize{-1};
|
|
|
|
int BufferSize{16384};
|
|
|
|
int WorkerThreads{-1};
|
|
|
|
int MaxEvents{5000};
|
|
|
|
bool AllowSharedLibs{true};
|
|
|
|
Ref<EPollManager> EPoll{nullptr};
|
|
|
|
Ref<SocketManager> Socket{nullptr};
|
|
|
|
std::string ErrorDir;
|
|
|
|
std::string MimeFiles;
|
|
|
|
};
|
|
|
|
struct Accept {
|
|
|
|
EPollReturns ReturnValue{EPollReturns::OK};
|
|
|
|
std::stringstream Data{};
|
|
|
|
ssize_t CurrentBytes;
|
|
|
|
int SockId{-1};
|
|
|
|
};
|
|
|
|
class EPollManager {
|
|
|
|
public:
|
|
|
|
EPollManager();
|
|
|
|
~EPollManager();
|
|
|
|
[[nodiscard]] bool Dispatch(int sock, uint32_t eventType = EPOLLIN) const;
|
|
|
|
[[nodiscard]] bool UpdateEvents(int sock, uint32_t eventType = EPOLLIN) const;
|
|
|
|
int Wait(int idx, epoll_event *events) const;
|
|
|
|
|
|
|
|
protected:
|
|
|
|
int m_EpollID{-1};
|
|
|
|
};
|
|
|
|
|
|
|
|
struct SendData {
|
|
|
|
ssize_t Offset{0};
|
|
|
|
ssize_t Size{0};
|
|
|
|
int SocketID{0};
|
|
|
|
std::string Content;
|
|
|
|
};
|
|
|
|
|
|
|
|
enum class WriteState { OK = 0, EPOLL, ERRORED };
|
|
|
|
|
|
|
|
class SocketUtils {
|
|
|
|
public:
|
|
|
|
static bool MakeAsync(int socketId);
|
|
|
|
static bool Add(int socketId);
|
|
|
|
static bool Close(int socketId);
|
|
|
|
static bool WriteDone(int socketId);
|
|
|
|
static WriteState Write(SendData &);
|
|
|
|
};
|
|
|
|
class SocketManager {
|
|
|
|
public:
|
|
|
|
explicit SocketManager(const Ref<ServerConfig> &);
|
|
|
|
~SocketManager();
|
|
|
|
Accept Handle();
|
|
|
|
bool SetSendListen(int socketID);
|
|
|
|
bool SetReadListen(int socketID);
|
|
|
|
Ref<ServerConfig> &GetServerConfig() { return m_ServerConfig; }
|
|
|
|
[[nodiscard]] int ID() const { return m_SocketID; }
|
|
|
|
[[nodiscard]] bool IsErrored() const { return m_IsErrored; }
|
|
|
|
void Init();
|
|
|
|
|
|
|
|
protected:
|
|
|
|
void Errored(const std::string &data);
|
|
|
|
|
|
|
|
int m_SocketID{};
|
|
|
|
Ref<ServerConfig> m_ServerConfig{};
|
|
|
|
struct sockaddr_in m_Address {
|
|
|
|
0
|
|
|
|
};
|
|
|
|
int m_AddressLength{sizeof(m_Address)};
|
|
|
|
bool m_IsErrored{false};
|
|
|
|
};
|
|
|
|
|
2022-08-23 16:40:57 +02:00
|
|
|
class MiddleWareHandler;
|
2022-08-23 14:13:21 +02:00
|
|
|
class RequestHandler;
|
|
|
|
class Response;
|
|
|
|
class Router;
|
|
|
|
class Route;
|
|
|
|
class Server {
|
|
|
|
public:
|
|
|
|
Server();
|
|
|
|
// Will Load SharedLibs from subdirectory
|
|
|
|
// This will allow that VWeb will run as Standalone and will load .so files
|
|
|
|
// without recompiling itself
|
|
|
|
void LoadSharedLibs();
|
|
|
|
void Start();
|
|
|
|
void Join() { m_WorkerThread->join(); }
|
|
|
|
void Stop() { m_IsExit = true; }
|
|
|
|
Ref<Router> &GetRouter() { return m_Router; }
|
|
|
|
Ref<ServerConfig> &GetServerConfig() { return m_ServerConfig; }
|
|
|
|
void AddRoute(const std::string &path, const Ref<Route> &route);
|
|
|
|
void RemoveRoute(const std::string &path);
|
|
|
|
|
2022-08-23 16:40:57 +02:00
|
|
|
Ref<MiddleWareHandler>& Middleware();
|
2022-08-23 14:13:21 +02:00
|
|
|
protected:
|
|
|
|
void Execute();
|
|
|
|
void OutgoingExecute(epoll_event &event);
|
|
|
|
void IncomingExecute(epoll_event &event);
|
|
|
|
void HandleRequestReading(epoll_event &event);
|
|
|
|
void CreateRequest(int sockID);
|
|
|
|
|
|
|
|
protected:
|
|
|
|
Ref<Router> m_Router;
|
|
|
|
Ref<ServerConfig> m_ServerConfig;
|
|
|
|
Ref<RequestHandler> m_RequestHandler;
|
|
|
|
SafeMap<int, Accept> m_RawRequest{60000};
|
|
|
|
SafeMap<int, SendData> m_OutRequest{60000};
|
|
|
|
Ref<std::thread> m_WorkerThread;
|
|
|
|
Mutex m_Mutex;
|
|
|
|
bool m_IsExit{false};
|
|
|
|
|
|
|
|
protected:
|
|
|
|
friend RequestHandler;
|
|
|
|
};
|
|
|
|
#pragma region VWEB_ROUTING
|
|
|
|
using ms = std::chrono::duration<double, std::milli>;
|
|
|
|
struct SessionData {
|
|
|
|
virtual ~SessionData() = default;
|
|
|
|
};
|
|
|
|
struct Session {
|
|
|
|
std::string Id;
|
|
|
|
long TTLSeconds = 1440; // 24 minutes 1440 seconds
|
|
|
|
bool IsValid();
|
|
|
|
void Update();
|
|
|
|
void Remove(const std::string &key);
|
|
|
|
bool Has(const std::string &key);
|
|
|
|
Ref<SessionData> &operator[](const std::string &key) { return m_Data[key]; }
|
2022-08-23 16:40:57 +02:00
|
|
|
void SetSessionData(const std::string& key, const Ref<SessionData>& data) { m_Data[key] = data; }
|
|
|
|
bool ContainsData() { return !m_Data.empty(); }
|
2022-08-23 14:13:21 +02:00
|
|
|
|
|
|
|
protected:
|
|
|
|
std::chrono::time_point<std::chrono::system_clock, ms> m_LastCall =
|
|
|
|
std::chrono::system_clock::now();
|
|
|
|
std::unordered_map<std::string, Ref<SessionData>> m_Data;
|
|
|
|
};
|
|
|
|
enum class HttpStatusCode : int {
|
|
|
|
Continue = 100,
|
|
|
|
SwitchingProtocols = 101,
|
|
|
|
Processing = 102,
|
|
|
|
EarlyHints = 103,
|
|
|
|
OK = 200,
|
|
|
|
Created = 201,
|
|
|
|
Accepted = 202,
|
|
|
|
NonAuthoritativeInformation = 203,
|
|
|
|
NoContent = 204,
|
|
|
|
ResetContent = 205,
|
|
|
|
PartialContent = 206,
|
|
|
|
MultiStatus = 207,
|
|
|
|
AlreadyReported = 208,
|
|
|
|
IMUsed = 226,
|
|
|
|
MultipleChoices = 300,
|
|
|
|
MovedPermanently = 301,
|
|
|
|
Found = 302,
|
|
|
|
SeeOther = 303,
|
|
|
|
NotModified = 304,
|
|
|
|
UseProxy = 305,
|
|
|
|
TemporaryRedirect = 307,
|
|
|
|
PermanentRedirect = 308,
|
|
|
|
BadRequest = 400,
|
|
|
|
Unauthorized = 401,
|
|
|
|
PaymentRequired = 402,
|
|
|
|
Forbidden = 403,
|
|
|
|
NotFound = 404,
|
|
|
|
MethodNotAllowed = 405,
|
|
|
|
NotAcceptable = 406,
|
|
|
|
ProxyAuthenticationRequired = 407,
|
|
|
|
RequestTimeout = 408,
|
|
|
|
Conflict = 409,
|
|
|
|
Gone = 410,
|
|
|
|
LengthRequired = 411,
|
|
|
|
PreconditionFailed = 412,
|
|
|
|
PayloadTooLarge = 413,
|
|
|
|
URITooLong = 414,
|
|
|
|
UnsupportedMediaType = 415,
|
|
|
|
RangeNotSatisfiable = 416,
|
|
|
|
ExpectationFailed = 417,
|
|
|
|
ImATeapot = 418,
|
|
|
|
UnprocessableEntity = 422,
|
|
|
|
Locked = 423,
|
|
|
|
FailedDependency = 424,
|
|
|
|
UpgradeRequired = 426,
|
|
|
|
PreconditionRequired = 428,
|
|
|
|
TooManyRequests = 429,
|
|
|
|
RequestHeaderFieldsTooLarge = 431,
|
|
|
|
UnavailableForLegalReasons = 451,
|
|
|
|
InternalServerError = 500,
|
|
|
|
NotImplemented = 501,
|
|
|
|
BadGateway = 502,
|
|
|
|
ServiceUnavailable = 503,
|
|
|
|
GatewayTimeout = 504,
|
|
|
|
HTTPVersionNotSupported = 505,
|
|
|
|
VariantAlsoNegotiates = 506,
|
|
|
|
InsufficientStorage = 507,
|
|
|
|
LoopDetected = 508,
|
|
|
|
NotExtended = 510,
|
|
|
|
NetworkAuthenticationRequired = 511
|
|
|
|
};
|
|
|
|
enum class HttpMethod {
|
|
|
|
GET = 0,
|
|
|
|
HEAD,
|
|
|
|
OPTIONS,
|
|
|
|
POST,
|
|
|
|
PUT,
|
|
|
|
PATCH,
|
|
|
|
DELETE,
|
|
|
|
FALLBACK
|
|
|
|
};
|
|
|
|
enum class CookieSameSite { Invalid = 0, Strict, Lax, None };
|
|
|
|
struct Cookie {
|
|
|
|
std::string Name;
|
|
|
|
std::string Value;
|
|
|
|
std::string Expires;
|
|
|
|
int MaxAge{};
|
|
|
|
std::string Domain;
|
|
|
|
std::string Path;
|
|
|
|
bool Secure{};
|
|
|
|
bool HttpOnly{};
|
|
|
|
CookieSameSite SameSite{CookieSameSite::Invalid};
|
|
|
|
bool IsOld = false;
|
|
|
|
};
|
|
|
|
struct Cookies {
|
|
|
|
public:
|
|
|
|
void Remove(const std::string &name);
|
|
|
|
bool Has(const std::string &name) const;
|
|
|
|
Cookie &Get(const std::string &name) { return Data[name]; };
|
|
|
|
std::string TransformToHeader();
|
|
|
|
void CreateCookieFromString(const std::string &data);
|
|
|
|
void CreateOld(const std::string &name, const std::string &value);
|
|
|
|
|
|
|
|
std::unordered_map<std::string, Cookie> Data;
|
|
|
|
Cookie &operator[](const std::string &name) { return Data[name]; }
|
|
|
|
|
|
|
|
protected:
|
|
|
|
static void CookieToString(std::stringstream &stream, Cookie &cookie);
|
|
|
|
};
|
|
|
|
struct ParameterValue {
|
|
|
|
void Add(const std::string &item) { m_Data.push_back(item); }
|
|
|
|
void Set(const std::string &item) {
|
|
|
|
m_Data.clear();
|
|
|
|
m_Data.push_back(item);
|
|
|
|
}
|
|
|
|
std::string &Get(int item) { return m_Data[item]; }
|
|
|
|
std::string &GetFirst() { return Get(0); }
|
|
|
|
size_t Size() { return m_Data.size(); }
|
|
|
|
Vector<std::string> &Values() { return m_Data; }
|
|
|
|
|
|
|
|
protected:
|
|
|
|
Vector<std::string> m_Data{};
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Request {
|
|
|
|
public:
|
|
|
|
int ID;
|
|
|
|
std::string Body;
|
|
|
|
HttpMethod Method{HttpMethod::GET};
|
|
|
|
std::string URI;
|
|
|
|
Ref<Session> SessionData;
|
|
|
|
Ref<Cookies> CookieData;
|
|
|
|
Cookie &GetCookie(const std::string &key) { return CookieData->Get(key); };
|
|
|
|
ParameterValue &Parameter(const std::string &key) { return Parameters[key]; }
|
2022-09-10 15:09:18 +02:00
|
|
|
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(); }
|
|
|
|
ParameterValue &Header(const std::string &key) { return Headers[key]; }
|
2022-08-23 14:13:21 +02:00
|
|
|
std::unordered_map<std::string, ParameterValue> Parameters;
|
|
|
|
std::unordered_map<std::string, ParameterValue> Headers;
|
|
|
|
Vector<std::string> URLParameters;
|
|
|
|
};
|
|
|
|
class Response {
|
2022-08-23 16:40:57 +02:00
|
|
|
public:
|
|
|
|
static Ref<Response> FromCode(HttpStatusCode code);
|
2022-08-23 14:13:21 +02:00
|
|
|
public:
|
|
|
|
size_t Length{0};
|
|
|
|
Ref<Cookies> CookieData{nullptr};
|
|
|
|
Ref<Session> SessionData{nullptr};
|
|
|
|
HttpStatusCode Status{HttpStatusCode::OK};
|
|
|
|
HttpMethod Method{HttpMethod::GET};
|
|
|
|
|
|
|
|
public:
|
|
|
|
std::string GetResponse();
|
|
|
|
void SetHeader(const std::string &key, ParameterValue &value);
|
|
|
|
void SetHeader(const std::string &key, const std::string &value);
|
|
|
|
void AddHeaders(const std::string &key, const Vector<std::string> &values);
|
|
|
|
void AddHeader(const std::string &key, const std::string &value);
|
|
|
|
void SetType(const std::string &type);
|
|
|
|
void AddContent(const std::string &data);
|
|
|
|
void SetStatus(HttpStatusCode);
|
|
|
|
void SetMethod(HttpMethod);
|
|
|
|
void Reset();
|
|
|
|
Response &operator<<(const std::string &data) {
|
|
|
|
m_Content << data;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
|
|
|
std::string TransformHeaders(const std::string &content);
|
|
|
|
std::string m_Type{"text/html"};
|
|
|
|
std::stringstream m_Content;
|
|
|
|
std::unordered_map<std::string, ParameterValue> m_Headers;
|
|
|
|
};
|
|
|
|
class MiddleWareHandler;
|
|
|
|
class RequestHandler {
|
|
|
|
public:
|
|
|
|
explicit RequestHandler(const Ref<SocketManager> &manager);
|
|
|
|
void InitThreads(int count);
|
|
|
|
void AddRequest(Ref<Request> &request);
|
|
|
|
void Stop();
|
|
|
|
void SetRouter(Ref<Router> &router);
|
|
|
|
void AddSendResponse(SendData);
|
|
|
|
Ref<MiddleWareHandler> &Middleware() { return m_MiddleWare; }
|
|
|
|
|
|
|
|
protected:
|
|
|
|
Ref<Router> m_Router{nullptr};
|
|
|
|
Ref<SocketManager> m_SocketManager{nullptr};
|
|
|
|
Server *m_Server{nullptr};
|
|
|
|
SafeQueue<Ref<Request>> m_Requests{};
|
|
|
|
ThreadPool m_Pool{"RequestHandler"};
|
|
|
|
Ref<MiddleWareHandler> m_MiddleWare{nullptr};
|
|
|
|
friend Server;
|
|
|
|
};
|
|
|
|
|
2022-09-10 15:09:18 +02:00
|
|
|
typedef std::function<bool(Request&,Response&)> RouteFunction;
|
2022-08-23 14:13:21 +02:00
|
|
|
class Route {
|
|
|
|
public:
|
|
|
|
Route() = default;
|
|
|
|
virtual ~Route() = default;
|
|
|
|
Route(std::initializer_list<HttpMethod>);
|
2022-09-10 15:09:18 +02:00
|
|
|
virtual bool Execute(Request &request, Response &response);
|
|
|
|
virtual bool Get(Request &request, Response &response);
|
|
|
|
virtual bool Post(Request &request, Response &response);
|
|
|
|
virtual bool Put(Request &request, Response &response);
|
|
|
|
virtual bool Patch(Request &request, Response &response);
|
|
|
|
virtual bool Delete(Request &request, Response &response);
|
|
|
|
bool Options(Request &request, Response &response);
|
|
|
|
virtual bool Fallback(Request &request, Response &response);
|
|
|
|
bool SupportsMethod(Request &request);
|
|
|
|
virtual bool IsAllowed(Request &request);
|
2022-08-23 14:13:21 +02:00
|
|
|
|
2022-08-23 16:40:57 +02:00
|
|
|
void AllowMethod(HttpMethod method);
|
|
|
|
|
2022-08-23 14:13:21 +02:00
|
|
|
protected:
|
|
|
|
bool m_AllowAll{true};
|
|
|
|
Vector<HttpMethod> m_AllowedMethods;
|
|
|
|
friend Router;
|
|
|
|
};
|
|
|
|
|
|
|
|
class Router {
|
|
|
|
public:
|
|
|
|
Router();
|
|
|
|
void AddRoute(const std::string &name, const Ref<Route> &route);
|
|
|
|
Ref<Route> &GetRoute(const std::string &name);
|
|
|
|
void DeleteRoute(const std::string &name);
|
|
|
|
|
|
|
|
Ref<Response> HandleRoute(Ref<Request> &request);
|
|
|
|
Ref<Route> FindRoute(Ref<Request> &request);
|
|
|
|
static void AddToArgs(Ref<Request> &request, Vector<std::string> &items);
|
|
|
|
|
2022-08-23 16:40:57 +02:00
|
|
|
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);
|
2022-08-23 14:13:21 +02:00
|
|
|
std::unordered_map<std::string, Ref<Route>> m_Routes;
|
|
|
|
};
|
|
|
|
|
2022-08-23 16:40:57 +02:00
|
|
|
typedef std::optional<Ref<Response>> PreMiddleWareReturn;
|
2022-08-23 14:13:21 +02:00
|
|
|
struct MiddleWare {
|
|
|
|
int Pos{0};
|
2022-08-23 16:40:57 +02:00
|
|
|
virtual PreMiddleWareReturn PreHandle(Request &){ return {}; }
|
|
|
|
virtual bool PostHandle(const Request &, Response &){ return true; }
|
|
|
|
virtual void Shutdown(const Request &, const Response &){};
|
2022-08-23 14:13:21 +02:00
|
|
|
bool operator<(const MiddleWare *rhs) const { return Pos < rhs->Pos; }
|
|
|
|
};
|
|
|
|
|
|
|
|
class MiddleWareHandler {
|
|
|
|
public:
|
2022-08-23 16:40:57 +02:00
|
|
|
PreMiddleWareReturn HandlePre(Ref<Request> &);
|
2022-08-23 14:13:21 +02:00
|
|
|
void HandlePost(Ref<Request> &, Ref<Response> &);
|
|
|
|
void Shutdown(Ref<Request> &, Ref<Response> &);
|
|
|
|
|
|
|
|
public:
|
2022-08-23 16:40:57 +02:00
|
|
|
template <class T> Ref<MiddleWare> GetRef() { return GetById(typeid(T).name()); }
|
|
|
|
template <class T> T& Get() { return static_cast<T&>(*GetById(typeid(T).name())); }
|
2022-08-23 14:13:21 +02:00
|
|
|
template <class T> void Set(Ref<MiddleWare> &instance) {
|
|
|
|
auto &type = typeid(T);
|
|
|
|
if (type.before(typeid(MiddleWare)))
|
|
|
|
SetById(type.name(), instance);
|
|
|
|
}
|
2022-08-23 16:40:57 +02:00
|
|
|
template <class T> T& Create() {
|
|
|
|
return static_cast<T&>(*CreateMiddleWare<T>());
|
2022-08-23 14:13:21 +02:00
|
|
|
}
|
|
|
|
template <class T> void Remove() { RemoveById(typeid(T).name()); }
|
|
|
|
protected:
|
2022-08-23 16:40:57 +02:00
|
|
|
template <class T> Ref<MiddleWare> CreateMiddleWare() {
|
|
|
|
return SetById(typeid(T).name(), CreateRef<T>());
|
|
|
|
}
|
2022-08-23 14:13:21 +02:00
|
|
|
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;
|
|
|
|
};
|
|
|
|
|
2022-08-23 16:40:57 +02:00
|
|
|
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;
|
|
|
|
};
|
|
|
|
|
2022-08-23 14:13:21 +02:00
|
|
|
#pragma endregion VWEB_ROUTING
|
|
|
|
#pragma endregion VWEB
|
2022-09-10 15:09:18 +02:00
|
|
|
} // namespace VWeb
|