Split VWeb into smaller headers
We have no make install support... so we don't need to have everything as a single-header and lib file.
This commit is contained in:
parent
4367534a33
commit
5bb68a7d02
43 changed files with 902 additions and 685 deletions
5
.gitattributes
vendored
Normal file
5
.gitattributes
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
* text=auto
|
||||||
|
*.txt text
|
||||||
|
*.cpp text
|
||||||
|
*.h text
|
||||||
|
*.hpp text
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -4,6 +4,7 @@ build-debug/
|
||||||
release-build/
|
release-build/
|
||||||
build-release/
|
build-release/
|
||||||
cmake-build-debug/
|
cmake-build-debug/
|
||||||
|
cmake-build-debug-wsl/
|
||||||
.idea/
|
.idea/
|
||||||
|
|
||||||
example/build
|
example/build
|
||||||
|
|
|
@ -8,21 +8,15 @@ set(CMAKE_CXX_STANDARD 20)
|
||||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUGSOFT")
|
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUGSOFT")
|
||||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||||
find_package(Threads REQUIRED)
|
find_package(Threads REQUIRED)
|
||||||
set(SOURCE_FILES
|
|
||||||
Source/EPollManager.cpp
|
include(CMakeMacros.txt)
|
||||||
Source/RequestHandler.cpp
|
|
||||||
Source/Server.cpp
|
add_subdirectory(Source)
|
||||||
Source/SocketManager.cpp
|
add_subdirectory(Includes)
|
||||||
Source/ThreadPool.cpp
|
|
||||||
Source/Router.cpp
|
|
||||||
Source/MiddleWare.cpp
|
|
||||||
Source/Route.cpp
|
|
||||||
Source/StringUtils.cpp
|
|
||||||
Source/Cookie.cpp
|
|
||||||
Source/Session.cpp
|
|
||||||
Source/Response.cpp
|
|
||||||
Source/InbuildMiddleWare.cpp)
|
|
||||||
include_directories(${CMAKE_SOURCE_DIR}/)
|
include_directories(${CMAKE_SOURCE_DIR}/)
|
||||||
|
get_property(source_files GLOBAL PROPERTY SRCS)
|
||||||
|
set(SOURCE_FILES ${source_files})
|
||||||
add_library(VWeb ${SOURCE_FILES})
|
add_library(VWeb ${SOURCE_FILES})
|
||||||
|
|
||||||
include(GNUInstallDirs)
|
include(GNUInstallDirs)
|
||||||
|
@ -31,7 +25,9 @@ set(generated_dir "${CMAKE_CURRENT_BINARY_DIR}/generated")
|
||||||
set(version_config "${generated_dir}/${PROJECT_NAME}ConfigVersion.cmake")
|
set(version_config "${generated_dir}/${PROJECT_NAME}ConfigVersion.cmake")
|
||||||
set(project_config "${generated_dir}/${PROJECT_NAME}Config.cmake")
|
set(project_config "${generated_dir}/${PROJECT_NAME}Config.cmake")
|
||||||
|
|
||||||
install(FILES VWeb.h DESTINATION include/VWeb-${version})
|
get_property(header_files GLOBAL PROPERTY Headers)
|
||||||
|
|
||||||
|
install(FILES ${header_files} DESTINATION include/VWeb-${version})
|
||||||
install(TARGETS ${PROJECT_NAME}
|
install(TARGETS ${PROJECT_NAME}
|
||||||
EXPORT ${PROJECT_NAME}-targets
|
EXPORT ${PROJECT_NAME}-targets
|
||||||
LIBRARY DESTINATION lib/$<CONFIG>
|
LIBRARY DESTINATION lib/$<CONFIG>
|
||||||
|
|
25
CMakeMacros.txt
Normal file
25
CMakeMacros.txt
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
macro(add_sources)
|
||||||
|
get_property(tmp GLOBAL PROPERTY SRCS)
|
||||||
|
file(RELATIVE_PATH _relPath "${PROJECT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||||
|
foreach (_src ${ARGN})
|
||||||
|
if (_relPath)
|
||||||
|
list(APPEND tmp "${_relPath}/${_src}")
|
||||||
|
else ()
|
||||||
|
list(APPEND tmp "${_src}")
|
||||||
|
endif ()
|
||||||
|
endforeach ()
|
||||||
|
set_property(GLOBAL PROPERTY SRCS ${tmp})
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
macro(add_headers)
|
||||||
|
get_property(tmp GLOBAL PROPERTY Headers)
|
||||||
|
file(RELATIVE_PATH _relPath "${PROJECT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||||
|
foreach (_src ${ARGN})
|
||||||
|
if (_relPath)
|
||||||
|
list(APPEND tmp "${_relPath}/${_src}")
|
||||||
|
else ()
|
||||||
|
list(APPEND tmp "${_src}")
|
||||||
|
endif ()
|
||||||
|
endforeach ()
|
||||||
|
set_property(GLOBAL PROPERTY Headers ${tmp})
|
||||||
|
endmacro()
|
24
Includes/CMakeLists.txt
Normal file
24
Includes/CMakeLists.txt
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
add_headers(
|
||||||
|
Cookie.h
|
||||||
|
EPollManager.h
|
||||||
|
Http.h
|
||||||
|
Map.h
|
||||||
|
MiddleWare.h
|
||||||
|
MiddleWareHandler.h
|
||||||
|
ParameterValue.h
|
||||||
|
PreMiddleWare.h
|
||||||
|
Queue.h
|
||||||
|
Request.h
|
||||||
|
RequestHandler.h
|
||||||
|
Response.h
|
||||||
|
Route.h
|
||||||
|
Router.h
|
||||||
|
Server.h
|
||||||
|
ServerConfig.h
|
||||||
|
Session.h
|
||||||
|
Socket.h
|
||||||
|
SocketManager.h
|
||||||
|
ThreadPool.h
|
||||||
|
Types.h
|
||||||
|
VWeb.h
|
||||||
|
)
|
35
Includes/Cookie.h
Normal file
35
Includes/Cookie.h
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
namespace VWeb {
|
||||||
|
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::Strict};
|
||||||
|
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);
|
||||||
|
};
|
||||||
|
} // namespace VWeb
|
17
Includes/EPollManager.h
Normal file
17
Includes/EPollManager.h
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <sys/epoll.h>
|
||||||
|
|
||||||
|
namespace VWeb {
|
||||||
|
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};
|
||||||
|
};
|
||||||
|
}
|
76
Includes/Http.h
Normal file
76
Includes/Http.h
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace VWeb {
|
||||||
|
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
|
||||||
|
};
|
||||||
|
} // namespace VWeb
|
59
Includes/Map.h
Normal file
59
Includes/Map.h
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <mutex>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
|
namespace VWeb {
|
||||||
|
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{};
|
||||||
|
};
|
||||||
|
}
|
16
Includes/MiddleWare.h
Normal file
16
Includes/MiddleWare.h
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
#include "Response.h"
|
||||||
|
#include "Request.h"
|
||||||
|
|
||||||
|
namespace VWeb {
|
||||||
|
typedef std::optional<Ref<Response>> PreMiddleWareReturn;
|
||||||
|
struct MiddleWare {
|
||||||
|
int Pos{0};
|
||||||
|
virtual PreMiddleWareReturn PreHandle(Request &) { return {}; }
|
||||||
|
virtual bool PostHandle(Request &, Response &) { return true; }
|
||||||
|
virtual void Shutdown(Request &, const Response &){};
|
||||||
|
bool operator<(const MiddleWare *rhs) const { return Pos < rhs->Pos; }
|
||||||
|
};
|
||||||
|
}
|
43
Includes/MiddleWareHandler.h
Normal file
43
Includes/MiddleWareHandler.h
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "MiddleWare.h"
|
||||||
|
#include "Request.h"
|
||||||
|
#include "Response.h"
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
namespace VWeb {
|
||||||
|
|
||||||
|
class MiddleWareHandler {
|
||||||
|
public:
|
||||||
|
PreMiddleWareReturn HandlePre(Ref<Request> &);
|
||||||
|
void HandlePost(Ref<Request> &, Ref<Response> &);
|
||||||
|
void Shutdown(Ref<Request> &, Ref<Response> &);
|
||||||
|
|
||||||
|
public:
|
||||||
|
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> 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;
|
||||||
|
};
|
||||||
|
} // namespace VWeb
|
21
Includes/ParameterValue.h
Normal file
21
Includes/ParameterValue.h
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace VWeb {
|
||||||
|
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(); }
|
||||||
|
std::vector<std::string> &Values() { return m_Data; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::vector<std::string> m_Data{};
|
||||||
|
};
|
||||||
|
}
|
48
Includes/PreMiddleWare.h
Normal file
48
Includes/PreMiddleWare.h
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "MiddleWare.h"
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
|
||||||
|
namespace VWeb {
|
||||||
|
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(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;
|
||||||
|
};
|
||||||
|
} // namespace VWeb
|
87
Includes/Queue.h
Normal file
87
Includes/Queue.h
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
#include <queue>
|
||||||
|
#include <optional>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <atomic>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
namespace VWeb {
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
}
|
34
Includes/Request.h
Normal file
34
Includes/Request.h
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Cookie.h"
|
||||||
|
#include "Http.h"
|
||||||
|
#include "ParameterValue.h"
|
||||||
|
#include "Session.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace VWeb {
|
||||||
|
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]; }
|
||||||
|
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]; }
|
||||||
|
std::unordered_map<std::string, ParameterValue> Parameters;
|
||||||
|
std::unordered_map<std::string, ParameterValue> Headers;
|
||||||
|
std::vector<std::string> URLParameters;
|
||||||
|
};
|
||||||
|
} // namespace VWeb
|
42
Includes/RequestHandler.h
Normal file
42
Includes/RequestHandler.h
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "MiddleWareHandler.h"
|
||||||
|
#include "Queue.h"
|
||||||
|
#include "Request.h"
|
||||||
|
#include "Router.h"
|
||||||
|
#include "SocketManager.h"
|
||||||
|
#include "ThreadPool.h"
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
namespace VWeb {
|
||||||
|
class Server;
|
||||||
|
typedef std::function<const void(Request &, const std::string &body)>
|
||||||
|
ParseFunction;
|
||||||
|
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; }
|
||||||
|
bool HasBodyParser(const std::string &key) {
|
||||||
|
return m_ParseFunctions.contains(key);
|
||||||
|
}
|
||||||
|
ParseFunction &GetBodyParser(const std::string &key) {
|
||||||
|
return m_ParseFunctions.at(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::unordered_map<std::string, ParseFunction> m_ParseFunctions;
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
} // namespace VWeb
|
43
Includes/Response.h
Normal file
43
Includes/Response.h
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Cookie.h"
|
||||||
|
#include "Http.h"
|
||||||
|
#include "Session.h"
|
||||||
|
#include "Types.h"
|
||||||
|
#include "ParameterValue.h"
|
||||||
|
|
||||||
|
namespace VWeb {
|
||||||
|
class Response {
|
||||||
|
public:
|
||||||
|
static Ref<Response> FromCode(HttpStatusCode code);
|
||||||
|
|
||||||
|
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 std::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;
|
||||||
|
};
|
||||||
|
} // namespace VWeb
|
34
Includes/Route.h
Normal file
34
Includes/Route.h
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Http.h"
|
||||||
|
#include <initializer_list>
|
||||||
|
#include "Request.h"
|
||||||
|
#include "Response.h"
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace VWeb {
|
||||||
|
class Router;
|
||||||
|
class Route {
|
||||||
|
public:
|
||||||
|
Route() = default;
|
||||||
|
virtual ~Route() = default;
|
||||||
|
Route(std::initializer_list<HttpMethod>);
|
||||||
|
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);
|
||||||
|
|
||||||
|
void AllowMethod(HttpMethod method);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool m_AllowAll{true};
|
||||||
|
std::vector<HttpMethod> m_AllowedMethods;
|
||||||
|
friend Router;
|
||||||
|
};
|
||||||
|
}
|
29
Includes/Router.h
Normal file
29
Includes/Router.h
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Route.h"
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace VWeb {
|
||||||
|
typedef std::function<bool(Request &, Response &)> RouteFunction;
|
||||||
|
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, std::vector<std::string> &items);
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
} // namespace VWeb
|
49
Includes/Server.h
Normal file
49
Includes/Server.h
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "EPollManager.h"
|
||||||
|
#include "SocketManager.h"
|
||||||
|
#include "Map.h"
|
||||||
|
#include "Router.h"
|
||||||
|
#include "RequestHandler.h"
|
||||||
|
#include "MiddleWareHandler.h"
|
||||||
|
#include "Route.h"
|
||||||
|
|
||||||
|
namespace VWeb {
|
||||||
|
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);
|
||||||
|
|
||||||
|
Ref<MiddleWareHandler> &Middleware();
|
||||||
|
|
||||||
|
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;
|
||||||
|
std::mutex m_Mutex;
|
||||||
|
bool m_IsExit{false};
|
||||||
|
|
||||||
|
protected:
|
||||||
|
friend RequestHandler;
|
||||||
|
};
|
||||||
|
}
|
22
Includes/ServerConfig.h
Normal file
22
Includes/ServerConfig.h
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "EPollManager.h"
|
||||||
|
#include "SocketManager.h"
|
||||||
|
#include "Types.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace VWeb {
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
}
|
31
Includes/Session.h
Normal file
31
Includes/Session.h
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include "Types.h"
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
namespace VWeb {
|
||||||
|
using ms = std::chrono::duration<double, std::milli>;
|
||||||
|
struct SessionData {
|
||||||
|
virtual ~SessionData() = default;
|
||||||
|
template <class T> T *As() { return reinterpret_cast<T *>(this); }
|
||||||
|
};
|
||||||
|
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]; }
|
||||||
|
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 =
|
||||||
|
std::chrono::system_clock::now();
|
||||||
|
std::unordered_map<std::string, Ref<SessionData>> m_Data;
|
||||||
|
};
|
||||||
|
}
|
22
Includes/Socket.h
Normal file
22
Includes/Socket.h
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
namespace VWeb {
|
||||||
|
enum class EPollReturns { OK = 0, BREAK, FAILURE };
|
||||||
|
enum class WriteState { OK = 0, EPOLL, ERRORED };
|
||||||
|
|
||||||
|
struct Accept {
|
||||||
|
EPollReturns ReturnValue{EPollReturns::OK};
|
||||||
|
std::stringstream Data{};
|
||||||
|
ssize_t CurrentBytes;
|
||||||
|
int SockId{-1};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SendData {
|
||||||
|
ssize_t Offset{0};
|
||||||
|
ssize_t Size{0};
|
||||||
|
int SocketID{0};
|
||||||
|
std::string Content;
|
||||||
|
};
|
||||||
|
} // namespace VWeb
|
41
Includes/SocketManager.h
Normal file
41
Includes/SocketManager.h
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Socket.h"
|
||||||
|
|
||||||
|
#include <netinet/in.h>
|
||||||
|
|
||||||
|
namespace VWeb {
|
||||||
|
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 ServerConfig;
|
||||||
|
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};
|
||||||
|
};
|
||||||
|
} // namespace VWeb
|
27
Includes/ThreadPool.h
Normal file
27
Includes/ThreadPool.h
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
#pragma once
|
||||||
|
#include "Queue.h"
|
||||||
|
#include "Types.h"
|
||||||
|
|
||||||
|
namespace VWeb {
|
||||||
|
struct WorkerJob {
|
||||||
|
virtual ~WorkerJob() = default;
|
||||||
|
virtual void Execute() = 0;
|
||||||
|
};
|
||||||
|
class ThreadPool {
|
||||||
|
public:
|
||||||
|
explicit ThreadPool(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};
|
||||||
|
std::vector<std::thread> m_Threads{};
|
||||||
|
SafeQueue<Ref<WorkerJob>> m_Queue;
|
||||||
|
bool m_IsDone{false};
|
||||||
|
};
|
||||||
|
} // namespace VWeb
|
11
Includes/Types.h
Normal file
11
Includes/Types.h
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace VWeb {
|
||||||
|
template <typename T> using Ref = std::shared_ptr<T>;
|
||||||
|
template <typename T, typename... Args>
|
||||||
|
constexpr Ref<T> CreateRef(Args &&...args) {
|
||||||
|
return std::make_shared<T>(std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
} // namespace VWeb
|
7
Includes/VWeb.h
Normal file
7
Includes/VWeb.h
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Request.h"
|
||||||
|
#include "Response.h"
|
||||||
|
#include "Route.h"
|
||||||
|
#include "Server.h"
|
||||||
|
#include "ServerConfig.h"
|
15
Source/CMakeLists.txt
Normal file
15
Source/CMakeLists.txt
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
add_sources(
|
||||||
|
EPollManager.cpp
|
||||||
|
RequestHandler.cpp
|
||||||
|
Server.cpp
|
||||||
|
SocketManager.cpp
|
||||||
|
ThreadPool.cpp
|
||||||
|
Router.cpp
|
||||||
|
MiddleWare.cpp
|
||||||
|
Route.cpp
|
||||||
|
StringUtils.cpp
|
||||||
|
Cookie.cpp
|
||||||
|
Session.cpp
|
||||||
|
Response.cpp
|
||||||
|
InbuildMiddleWare.cpp
|
||||||
|
)
|
|
@ -1,7 +1,6 @@
|
||||||
|
#include "Includes/VWeb.h"
|
||||||
#include "StringUtils.h"
|
#include "StringUtils.h"
|
||||||
|
|
||||||
#include <VWeb.h>
|
|
||||||
|
|
||||||
namespace VWeb {
|
namespace VWeb {
|
||||||
void Cookies::Remove(const std::string &name) {
|
void Cookies::Remove(const std::string &name) {
|
||||||
if (Data.contains(name))
|
if (Data.contains(name))
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
#include <VWeb.h>
|
#include "Includes/VWeb.h"
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
namespace VWeb {
|
namespace VWeb {
|
||||||
EPollManager::EPollManager() {
|
EPollManager::EPollManager() {
|
||||||
m_EpollID = epoll_create1(0);
|
m_EpollID = epoll_create1(0);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
|
#include "Includes/PreMiddleWare.h"
|
||||||
#include "StringUtils.h"
|
#include "StringUtils.h"
|
||||||
#include "VWeb.h"
|
|
||||||
|
|
||||||
#include <random>
|
#include <random>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#include <VWeb.h>
|
#include "Includes/VWeb.h"
|
||||||
|
|
||||||
namespace VWeb {
|
namespace VWeb {
|
||||||
std::optional<Ref<Response>>
|
std::optional<Ref<Response>>
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
|
#include "Includes/VWeb.h"
|
||||||
#include "StringUtils.h"
|
#include "StringUtils.h"
|
||||||
|
|
||||||
#include <VWeb.h>
|
|
||||||
|
|
||||||
namespace VWeb {
|
namespace VWeb {
|
||||||
|
|
||||||
HttpMethod StringToHTTPMethod(std::string &method) {
|
HttpMethod StringToHTTPMethod(std::string &method) {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#include <VWeb.h>
|
#include "Includes/VWeb.h"
|
||||||
|
|
||||||
namespace VWeb {
|
namespace VWeb {
|
||||||
// clang-format off
|
// clang-format off
|
||||||
|
@ -87,7 +87,7 @@ void Response::SetHeader(const std::string &key, ParameterValue &value) {
|
||||||
m_Headers[key] = value;
|
m_Headers[key] = value;
|
||||||
}
|
}
|
||||||
void Response::AddHeaders(const std::string &key,
|
void Response::AddHeaders(const std::string &key,
|
||||||
const Vector<std::string> &values) {
|
const std::vector<std::string> &values) {
|
||||||
auto &element = m_Headers[key];
|
auto &element = m_Headers[key];
|
||||||
for (const auto &value : values)
|
for (const auto &value : values)
|
||||||
element.Add(value);
|
element.Add(value);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#include <VWeb.h>
|
#include "Includes/VWeb.h"
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
namespace VWeb {
|
namespace VWeb {
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
|
#include "Includes/VWeb.h"
|
||||||
#include "StringUtils.h"
|
#include "StringUtils.h"
|
||||||
|
|
||||||
#include <VWeb.h>
|
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
@ -119,7 +119,7 @@ Ref<Route> Router::FindRoute(Ref<Request> &request) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Router::AddToArgs(Ref<Request> &request, Vector<std::string> &items) {
|
void Router::AddToArgs(Ref<Request> &request, std::vector<std::string> &items) {
|
||||||
request->URLParameters.push_back(items[items.size() - 1]);
|
request->URLParameters.push_back(items[items.size() - 1]);
|
||||||
items.pop_back();
|
items.pop_back();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
#include <VWeb.h>
|
#include "Includes/PreMiddleWare.h"
|
||||||
|
#include "Includes/VWeb.h"
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
namespace VWeb {
|
namespace VWeb {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#include <VWeb.h>
|
#include "Includes/VWeb.h"
|
||||||
|
|
||||||
namespace VWeb {
|
namespace VWeb {
|
||||||
bool Session::IsValid() {
|
bool Session::IsValid() {
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
#include <VWeb.h>
|
#include "Includes/VWeb.h"
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
namespace VWeb {
|
namespace VWeb {
|
||||||
#pragma region VWebSocketUtils
|
#pragma region VWebSocketUtils
|
||||||
|
@ -89,7 +91,8 @@ void SocketManager::Init() {
|
||||||
if (listen(m_SocketID, SOMAXCONN) < 0)
|
if (listen(m_SocketID, SOMAXCONN) < 0)
|
||||||
return Errored("Socket Failed to Listen");
|
return Errored("Socket Failed to Listen");
|
||||||
|
|
||||||
if (!m_ServerConfig->EPoll->Dispatch(m_SocketID,EPOLLIN | EPOLLET | EPOLLOUT))
|
if (!m_ServerConfig->EPoll->Dispatch(m_SocketID,
|
||||||
|
EPOLLIN | EPOLLET | EPOLLOUT))
|
||||||
return Errored("Cannot Add Event");
|
return Errored("Cannot Add Event");
|
||||||
}
|
}
|
||||||
void SocketManager::Errored(const std::string &data) {
|
void SocketManager::Errored(const std::string &data) {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include <VWeb.h>
|
#include "Includes/VWeb.h"
|
||||||
|
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
|
651
VWeb.h
651
VWeb.h
|
@ -1,651 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
#include <condition_variable>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <functional>
|
|
||||||
#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>
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
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(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};
|
|
||||||
};
|
|
||||||
|
|
||||||
class MiddleWareHandler;
|
|
||||||
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);
|
|
||||||
|
|
||||||
Ref<MiddleWareHandler> &Middleware();
|
|
||||||
|
|
||||||
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;
|
|
||||||
template <class T> T *As() { return reinterpret_cast<T *>(this); }
|
|
||||||
};
|
|
||||||
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]; }
|
|
||||||
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 =
|
|
||||||
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::Strict};
|
|
||||||
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]; }
|
|
||||||
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]; }
|
|
||||||
std::unordered_map<std::string, ParameterValue> Parameters;
|
|
||||||
std::unordered_map<std::string, ParameterValue> Headers;
|
|
||||||
Vector<std::string> URLParameters;
|
|
||||||
};
|
|
||||||
class Response {
|
|
||||||
public:
|
|
||||||
static Ref<Response> FromCode(HttpStatusCode code);
|
|
||||||
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;
|
|
||||||
typedef std::function<const void(Request &, const std::string& body)> ParseFunction;
|
|
||||||
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; }
|
|
||||||
bool HasBodyParser(const std::string &key) {
|
|
||||||
return m_ParseFunctions.contains(key);
|
|
||||||
}
|
|
||||||
ParseFunction &GetBodyParser(const std::string &key) {
|
|
||||||
return m_ParseFunctions.at(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
std::unordered_map<std::string, ParseFunction> m_ParseFunctions;
|
|
||||||
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;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef std::function<bool(Request &, Response &)> RouteFunction;
|
|
||||||
class Route {
|
|
||||||
public:
|
|
||||||
Route() = default;
|
|
||||||
virtual ~Route() = default;
|
|
||||||
Route(std::initializer_list<HttpMethod>);
|
|
||||||
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);
|
|
||||||
|
|
||||||
void AllowMethod(HttpMethod method);
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
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 PreMiddleWareReturn PreHandle(Request &) { return {}; }
|
|
||||||
virtual bool PostHandle(Request &, Response &) { return true; }
|
|
||||||
virtual void Shutdown(Request &, const Response &){};
|
|
||||||
bool operator<(const MiddleWare *rhs) const { return Pos < rhs->Pos; }
|
|
||||||
};
|
|
||||||
|
|
||||||
class MiddleWareHandler {
|
|
||||||
public:
|
|
||||||
PreMiddleWareReturn HandlePre(Ref<Request> &);
|
|
||||||
void HandlePost(Ref<Request> &, Ref<Response> &);
|
|
||||||
void Shutdown(Ref<Request> &, Ref<Response> &);
|
|
||||||
|
|
||||||
public:
|
|
||||||
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> 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(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
|
|
|
@ -3,6 +3,7 @@ project(VWeb_Example)
|
||||||
set(CMAKE_CXX_STANDARD 20)
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||||
find_package(Threads REQUIRED)
|
find_package(Threads REQUIRED)
|
||||||
|
find_package(VWeb 1.0 REQUIRED)
|
||||||
add_executable(VWeb_Example main.cpp)
|
add_executable(VWeb_Example main.cpp)
|
||||||
|
|
||||||
include_directories(${CMAKE_SOURCE_DIR}/..)
|
include_directories(${CMAKE_SOURCE_DIR}/..)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#include <VWeb.h>
|
#include "VWeb-1.0/VWeb.h"
|
||||||
|
|
||||||
class MyCompleteController : public VWeb::Route {
|
class MyCompleteController : public VWeb::Route {
|
||||||
public:
|
public:
|
||||||
|
|
Loading…
Reference in a new issue