diff options
author | Toni Uhlig <matzeton@googlemail.com> | 2021-09-30 21:12:57 +0200 |
---|---|---|
committer | Toni Uhlig <matzeton@googlemail.com> | 2021-09-30 21:12:57 +0200 |
commit | 6c04dfe2caff1e03ba5c898b591327439452f616 (patch) | |
tree | 11c6f6955de188c48015641c1ae2e63b0d0e50d6 | |
parent | ec7cfa85530082127703278cf1ae5167990c0f45 (diff) |
CMS functionality works just find..minimal-working-example
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
-rw-r--r-- | .clang-format | 77 | ||||
-rw-r--r-- | src/Content.cpp | 28 | ||||
-rw-r--r-- | src/Content.hpp | 30 | ||||
-rw-r--r-- | src/ContentManager.cpp | 89 | ||||
-rw-r--r-- | src/ContentManager.hpp | 32 | ||||
-rw-r--r-- | src/EventManager.cpp | 314 | ||||
-rw-r--r-- | src/EventManager.hpp | 48 | ||||
-rw-r--r-- | src/Filesystem.cpp | 165 | ||||
-rw-r--r-- | src/Filesystem.hpp | 30 | ||||
-rw-r--r-- | src/RequestResponse.cpp | 32 | ||||
-rw-r--r-- | src/RequestResponse.hpp | 25 | ||||
-rw-r--r-- | src/TemplateManager.cpp | 57 | ||||
-rw-r--r-- | src/TemplateManager.hpp | 28 | ||||
-rw-r--r-- | src/content/blog/Blog.cpp | 36 | ||||
-rw-r--r-- | src/content/blog/Blog.hpp | 20 | ||||
-rw-r--r-- | src/main.cpp | 202 | ||||
-rw-r--r-- | wwwroot/index.html | 35 | ||||
-rw-r--r-- | wwwroot/index.tmpl | 1 |
18 files changed, 751 insertions, 498 deletions
diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..aeb942a --- /dev/null +++ b/.clang-format @@ -0,0 +1,77 @@ +Language: Cpp +BasedOnStyle : LLVM +Standard : Cpp03 +# BasedOnStyle: LLVM +BraceWrapping: + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false + IndentBraces: true +ConstructorInitializerIndentWidth: 4 +AlignEscapedNewlinesLeft: false +AlignTrailingComments: true +AllowShortBlocksOnASingleLine: true +AllowShortCaseLabelsOnASingleLine: false +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: false +AlwaysBreakTemplateDeclarations: true +AlwaysBreakBeforeMultilineStrings: true +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +ColumnLimit: 120 +ConstructorInitializerAllOnOneLineOrOnePerLine: true +DerivePointerAlignment: false +ExperimentalAutoDetectBinPacking: false +IndentCaseLabels: true +IndentWrappedFunctionNames: false +IndentFunctionDeclarationAfterType: false +MaxEmptyLinesToKeep: 1 +KeepEmptyLinesAtTheStartOfBlocks: true +NamespaceIndentation: None +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: false + +PenaltyExcessCharacter : 500 +PenaltyReturnTypeOnItsOwnLine : 120 +PenaltyBreakBeforeFirstCallParameter : 100 +PenaltyBreakString : 20 +PenaltyBreakComment : 10 +PenaltyBreakFirstLessLess : 0 + +SpacesBeforeTrailingComments: 1 +Cpp11BracedListStyle: true +IndentWidth: 4 +TabWidth: 4 +UseTab: Never +BreakBeforeBraces: Allman +SpacesInParentheses: false +SpacesInSquareBrackets: false +SpacesInAngles : false +SpaceInEmptyParentheses : false +SpacesInCStyleCastParentheses : false +SpaceAfterCStyleCast : false +SpacesInContainerLiterals : true +SpaceBeforeAssignmentOperators : true +ContinuationIndentWidth : 4 +SpaceBeforeParens : ControlStatements +DisableFormat : false +AccessModifierOffset : -4 +PointerAlignment : Middle +AlignAfterOpenBracket : Align +AllowAllParametersOfDeclarationOnNextLine : true +BinPackArguments : false +BinPackParameters : false +AlignOperands : true +AlignConsecutiveAssignments : false +AllowShortFunctionsOnASingleLine : None +BreakBeforeBinaryOperators : None +AlwaysBreakAfterReturnType : None +SortIncludes : false diff --git a/src/Content.cpp b/src/Content.cpp deleted file mode 100644 index b2977bb..0000000 --- a/src/Content.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#include "Content.hpp" - -TemplatedContent::TemplatedContent(std::string filesystemPath) : m_FilesystemPath(filesystemPath) -{ -} - -bool TemplatedContent::Init() -{ - return false; -} - -void TemplatedContent::Shutdown() -{ -} - -bool TemplatedContent::Render(RequestResponse & rr, std::string & out) -{ - (void)rr; - - out = "tmpl"; - - return false; -} - -void TemplatedContent::GetRequiredFiles(std::vector<std::string> & requiredFiles) const -{ - requiredFiles.push_back(m_FilesystemPath); -} diff --git a/src/Content.hpp b/src/Content.hpp index c379522..82eb35e 100644 --- a/src/Content.hpp +++ b/src/Content.hpp @@ -8,30 +8,20 @@ #include <tuple> #include <vector> -typedef std::vector<std::string> Redirections; +using Redirections = std::vector<std::string>; +using RenderData = std::unordered_map<std::string, std::string>; -class Content { +class Content +{ public: - virtual bool Init() = 0; - virtual void Shutdown() = 0; - virtual bool Render(RequestResponse & rr, std::string & out) = 0; + virtual bool Init() = 0; + virtual void Shutdown() = 0; - virtual std::string const & GetBaseUri() const = 0; - virtual Redirections const & GetRedirections() const = 0; - virtual void GetRequiredFiles(std::vector<std::string> & requiredFiles) const = 0; -}; - -class TemplatedContent : public Content { -public: - explicit TemplatedContent(std::string mainTemplate); - - virtual bool Init(); - virtual void Shutdown(); - virtual bool Render(RequestResponse & rr, std::string & out); - virtual void GetRequiredFiles(std::vector<std::string> & requiredFiles) const; + virtual bool Render(RequestResponse & rr, RenderData & rd) = 0; -private: - std::string m_FilesystemPath; + virtual std::string const & GetUriBasePath() const = 0; + virtual std::string const & GetMainTemplate() const = 0; + virtual Redirections const & GetRedirections() const = 0; }; #endif diff --git a/src/ContentManager.cpp b/src/ContentManager.cpp index a1787bd..6beef5a 100644 --- a/src/ContentManager.cpp +++ b/src/ContentManager.cpp @@ -1,61 +1,90 @@ #include "ContentManager.hpp" +void ContentManager::SetStaticFilesystem(std::shared_ptr<Filesystem> & static_fs) +{ + m_StaticFilesystem = static_fs; +} + +void ContentManager::SetTemplateSystem(std::shared_ptr<TemplateManager> & tmgr) +{ + m_TemplateManager = tmgr; +} + bool ContentManager::RegisterModule(std::shared_ptr<Content> ctnt) { - std::string const & basePath = ctnt->GetBaseUri(); - Redirections const & rs = ctnt->GetRedirections(); + std::string const & basePath = ctnt->GetUriBasePath(); + Redirections const & rs = ctnt->GetRedirections(); - m_ContentModules[basePath] = ctnt; - for (auto & redirect : rs) - { - m_ContentModules[redirect] = ctnt; - } + m_ContentModules[basePath] = ctnt; + for (auto & redirect : rs) + { + m_ContentModules[redirect] = ctnt; + } - return false; + return false; } bool ContentManager::InitAll(void) { - bool ret = true; + bool ret = true; - for (auto & content : m_ContentModules) - { - if (content.second->Init() == false) + for (auto & content : m_ContentModules) { - ret = false; + if (content.second->Init() == false) + { + ret = false; + } } - } - return ret; + return ret; } void ContentManager::ShutdownAll(void) { - std::unordered_map<std::shared_ptr<Content>, bool> shutdownModules; + std::unordered_map<std::shared_ptr<Content>, bool> shutdownModules; - for (auto & content : m_ContentModules) - { - auto const & search = shutdownModules.find(content.second); - if (search != shutdownModules.end()) + for (auto & content : m_ContentModules) { - continue; - } else { - content.second->Shutdown(); - shutdownModules[content.second] = true; + auto const & search = shutdownModules.find(content.second); + if (search != shutdownModules.end()) + { + continue; + } + else + { + content.second->Shutdown(); + shutdownModules[content.second] = true; + } } - } - m_ContentModules.clear(); + m_ContentModules.clear(); } -bool ContentManager::Render(std::string & basePath) +bool ContentManager::Render(char const * basePath, RequestResponse & rr, std::string & out) { - (void)basePath; + if (m_ContentModules.find(basePath) == m_ContentModules.end()) + { + return false; + } + + RenderData rd; + auto & cntm = m_ContentModules[basePath]; + auto & main = cntm->GetMainTemplate(); + + if (m_ContentModules[basePath]->Render(rr, rd) == false) + { + return false; + } + + if (m_TemplateManager->RenderTemplate(main, rd, out) == false) + { + return false; + } - return false; + return true; } ContentModules const & ContentManager::GetAllModules() const { - return m_ContentModules; + return m_ContentModules; } diff --git a/src/ContentManager.hpp b/src/ContentManager.hpp index 033be59..83f4bc9 100644 --- a/src/ContentManager.hpp +++ b/src/ContentManager.hpp @@ -2,25 +2,37 @@ #define CONTENTMANAGER_H 1 #include "Content.hpp" +#include "Filesystem.hpp" +#include "TemplateManager.hpp" #include <memory> #include <unordered_map> -typedef std::unordered_map<std::string, std::shared_ptr<Content>> ContentModules; +using ContentModules = std::unordered_map<std::string, std::shared_ptr<Content> >; -class ContentManager { +class ContentManager +{ public: - ContentManager() {} - ~ContentManager() { ShutdownAll(); } + ContentManager() + { + } + ~ContentManager() + { + ShutdownAll(); + } - bool RegisterModule(std::shared_ptr<Content> ctnt); - bool InitAll(void); - void ShutdownAll(void); - bool Render(std::string & basePath); - ContentModules const & GetAllModules() const; + void SetStaticFilesystem(std::shared_ptr<Filesystem> & static_fs); + void SetTemplateSystem(std::shared_ptr<TemplateManager> & tmgr); + bool RegisterModule(std::shared_ptr<Content> ctnt); + bool InitAll(void); + void ShutdownAll(void); + bool Render(char const * basePath, RequestResponse & rr, std::string & out); + ContentModules const & GetAllModules() const; private: - ContentModules m_ContentModules; + std::shared_ptr<Filesystem> m_StaticFilesystem; + std::shared_ptr<TemplateManager> m_TemplateManager; + ContentModules m_ContentModules; }; #endif diff --git a/src/EventManager.cpp b/src/EventManager.cpp index ad480bc..3270297 100644 --- a/src/EventManager.cpp +++ b/src/EventManager.cpp @@ -8,171 +8,199 @@ #include <event2/util.h> #include <signal.h> -extern "C" { -static void GenerateInternalErrorPage(struct evhttp_request * const req, - std::string text) +extern "C" { - evhttp_add_header(evhttp_request_get_output_headers(req), "Content-Type", - "text/html"); - - struct evbuffer *const output = evbuffer_new(); - if (output != nullptr) { - evbuffer_add(output, text.c_str(), text.size()); - evhttp_send_reply(req, 500, "Internal Error", output); - evbuffer_free(output); - } -} + static void GenerateInternalErrorPage(struct evhttp_request * const req, std::string text) + { + evhttp_add_header(evhttp_request_get_output_headers(req), "Content-Type", "text/html"); + + struct evbuffer * const output = evbuffer_new(); + if (output != nullptr) + { + evbuffer_add(output, text.c_str(), text.size()); + evhttp_send_reply(req, 500, "Internal Error", output); + evbuffer_free(output); + } + } -static void EvGenericInterceptor(struct evhttp_request *const req, - void *ev_c_callback) { - if (ev_c_callback != nullptr) { - struct ev_callback *const evcb = (struct ev_callback *)ev_c_callback; - evcb->cb(req, evcb->ud); - } else { - GenerateInternalErrorPage(req, "EvGenericInterceptor: ev_c_callback == nullptr"); - } -} + static void EvGenericInterceptor(struct evhttp_request * const req, void * ev_c_callback) + { + if (ev_c_callback != nullptr) + { + struct ev_callback * const evcb = (struct ev_callback *)ev_c_callback; + evcb->cb(req, evcb->ud); + } + else + { + GenerateInternalErrorPage(req, "EvGenericInterceptor: ev_c_callback == nullptr"); + } + } -static void EvContentManagerInterceptor(struct evhttp_request * const req, - void * ev_c_callback) { - if (ev_c_callback != nullptr) { - Content * const cntnt = (Content *)ev_c_callback; - RequestResponse rr(req); - std::string out; - if (cntnt->Render(rr, out) == false) { - std::string text; - text = "ContentModule(\"" + cntnt->GetBaseUri() + "\")->Render() failed.\n"; - GenerateInternalErrorPage(req, text); - } else { - evhttp_add_header(evhttp_request_get_output_headers(req), "Content-Type", - "text/html"); - - struct evbuffer *const output = evbuffer_new(); - if (output != nullptr) { - evbuffer_add(output, out.c_str(), out.size()); - evhttp_send_reply(req, HTTP_OK, "OK", output); - evbuffer_free(output); - } + static void EvContentManagerInterceptor(struct evhttp_request * const req, void * ev_c_callback) + { + if (ev_c_callback != nullptr) + { + struct evhttp_uri const * const uri = evhttp_request_get_evhttp_uri(req); + if (uri == nullptr) + { + GenerateInternalErrorPage(req, "EvContentManagerInterceptor: uri == nullptr"); + return; + } + + char const * const path = evhttp_uri_get_path(uri); + if (path == nullptr) + { + GenerateInternalErrorPage(req, "EvContentManagerInterceptor: path == nullptr"); + return; + } + + std::shared_ptr<ContentManager> const cmgr = *(std::shared_ptr<ContentManager> const *)ev_c_callback; + RequestResponse rr(req); + std::string out; + + if (cmgr->Render(path, rr, out) == false) + { + std::string text; + text = "ContentModule(\"" + std::string(path) + "\")->Render() failed.\n"; + GenerateInternalErrorPage(req, text); + } + else + { + evhttp_add_header(evhttp_request_get_output_headers(req), "Content-Type", "text/html"); + + struct evbuffer * const output = evbuffer_new(); + if (output != nullptr) + { + evbuffer_add(output, out.c_str(), out.size()); + evhttp_send_reply(req, HTTP_OK, "OK", output); + evbuffer_free(output); + } + } + } + else + { + GenerateInternalErrorPage(req, "EvContentManagerInterceptor: ev_c_callback == nullptr"); + } } - } else { - GenerateInternalErrorPage(req, "EvContentManagerInterceptor: ev_c_callback == nullptr"); - } -} -static void do_term(int sig, short events, void *arg) { - (void)events; - struct event_base *base = (struct event_base *)arg; - event_base_loopbreak(base); - fprintf(stderr, "Got %i, Terminating\n", sig); -} + static void do_term(int sig, short events, void * arg) + { + (void)events; + struct event_base * base = (struct event_base *)arg; + event_base_loopbreak(base); + fprintf(stderr, "Got %i, Terminating\n", sig); + } } -static inline void default_evhttp_callback(struct evhttp_request * const req, - EvUserData ud) { - (void)ud; +static inline void default_evhttp_callback(struct evhttp_request * const req, EvUserData ud) +{ + (void)ud; - evhttp_add_header(evhttp_request_get_output_headers(req), "Content-Type", - "text/html"); + evhttp_add_header(evhttp_request_get_output_headers(req), "Content-Type", "text/html"); - struct evbuffer *const output = evbuffer_new(); - if (output != nullptr) { - evbuffer_add_printf(output, "%s\n", - "<html><body><b>default page</b></body></html>"); - evhttp_send_reply(req, 200, "OK", output); - evbuffer_free(output); - } + struct evbuffer * const output = evbuffer_new(); + if (output != nullptr) + { + evbuffer_add_printf(output, "%s\n", "<html><body><b>default page</b></body></html>"); + evhttp_send_reply(req, 200, "OK", output); + evbuffer_free(output); + } } -EventManager::EventManager() : m_DefaultCallback({default_evhttp_callback, nullptr}) +EventManager::EventManager(std::shared_ptr<ContentManager> & cmgr) + : m_ContentManager(cmgr), m_DefaultCallback({default_evhttp_callback, nullptr}) { } -EventManager::~EventManager() { - if (m_EvConfig != nullptr) - event_config_free(m_EvConfig); - if (m_EvHttp != nullptr) - evhttp_free(m_EvHttp); - if (m_EvTermEvent != nullptr) - event_free(m_EvTermEvent); - if (m_EvBase != nullptr) - event_base_free(m_EvBase); +EventManager::~EventManager() +{ + if (m_EvConfig != nullptr) + event_config_free(m_EvConfig); + if (m_EvHttp != nullptr) + evhttp_free(m_EvHttp); + if (m_EvTermEvent != nullptr) + event_free(m_EvTermEvent); + if (m_EvBase != nullptr) + event_base_free(m_EvBase); } -bool EventManager::Init(std::string host, uint16_t port) { - struct event_config *cfg = nullptr; - - if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) { - return false; - } - - event_enable_debug_logging(EVENT_DBG_ALL); - - cfg = event_config_new(); - event_config_set_flag(cfg, EVENT_BASE_FLAG_STARTUP_IOCP); - - m_EvBase = event_base_new_with_config(cfg); - if (m_EvBase == nullptr) { - fprintf(stderr, "Couldn't create an event_base: exiting\n"); - return false; - } - event_config_free(cfg); - cfg = nullptr; - - /* Create a new evhttp object to handle requests. */ - m_EvHttp = evhttp_new(m_EvBase); - if (m_EvHttp == nullptr) { - fprintf(stderr, "couldn't create evhttp. Exiting.\n"); - return false; - } - - for (auto & uc : m_UrlCallbacks) { - struct ev_callback * const evcb = &std::get<1>(uc); - if (evhttp_set_cb(m_EvHttp, std::get<0>(uc).c_str(), EvGenericInterceptor, - evcb) != 0) { - return false; +bool EventManager::Init(std::string host, uint16_t port) +{ + struct event_config * cfg = nullptr; + + if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) + { + return false; + } + + event_enable_debug_logging(EVENT_DBG_ALL); + + cfg = event_config_new(); + event_config_set_flag(cfg, EVENT_BASE_FLAG_STARTUP_IOCP); + + m_EvBase = event_base_new_with_config(cfg); + if (m_EvBase == nullptr) + { + fprintf(stderr, "Couldn't create an event_base: exiting\n"); + return false; } - } - for (auto & cm : m_ContentModules) { - std::shared_ptr<Content> content = cm.second; - if (evhttp_set_cb(m_EvHttp, cm.first.c_str(), EvContentManagerInterceptor, - content.get()) != 0) { - return false; + event_config_free(cfg); + cfg = nullptr; + + /* Create a new evhttp object to handle requests. */ + m_EvHttp = evhttp_new(m_EvBase); + if (m_EvHttp == nullptr) + { + fprintf(stderr, "couldn't create evhttp. Exiting.\n"); + return false; } - } - evhttp_set_gencb(m_EvHttp, EvGenericInterceptor, &m_DefaultCallback); - - m_EvSocket = evhttp_bind_socket_with_handle(m_EvHttp, host.c_str(), port); - if (m_EvSocket == nullptr) { - fprintf(stderr, "couldn't bind to %s:%d. Exiting.\n", host.c_str(), port); - return false; - } - - m_EvTermEvent = evsignal_new(m_EvBase, SIGINT, do_term, m_EvBase); - if (m_EvTermEvent == nullptr) { - return false; - } - if (event_add(m_EvTermEvent, NULL) != 0) { - return false; - } - - event_base_dispatch(m_EvBase); - - return true; -} -void EventManager::SetDefaultCallback(EvFunction fn, EvUserData dat) { - m_DefaultCallback.cb = fn; - m_DefaultCallback.ud = dat; -} + for (auto & uc : m_UrlCallbacks) + { + struct ev_callback * const evcb = &std::get<1>(uc); + if (evhttp_set_cb(m_EvHttp, std::get<0>(uc).c_str(), EvGenericInterceptor, evcb) != 0) + { + return false; + } + } + for (auto & cm : m_ContentManager->GetAllModules()) + { + if (evhttp_set_cb(m_EvHttp, cm.first.c_str(), EvContentManagerInterceptor, &m_ContentManager) != 0) + { + return false; + } + } + evhttp_set_gencb(m_EvHttp, EvGenericInterceptor, &m_DefaultCallback); -void EventManager::AddCallback(std::string url, EvFunction fn, EvUserData dat) { - m_UrlCallbacks.push_back(EvUrlCallback(url, {fn, dat})); + m_EvSocket = evhttp_bind_socket_with_handle(m_EvHttp, host.c_str(), port); + if (m_EvSocket == nullptr) + { + fprintf(stderr, "couldn't bind to %s:%d. Exiting.\n", host.c_str(), port); + return false; + } + + m_EvTermEvent = evsignal_new(m_EvBase, SIGINT, do_term, m_EvBase); + if (m_EvTermEvent == nullptr) + { + return false; + } + if (event_add(m_EvTermEvent, NULL) != 0) + { + return false; + } + + event_base_dispatch(m_EvBase); + + return true; } -void EventManager::AddContentManager(ContentManager const & cmgr) +void EventManager::SetDefaultCallback(EvFunction fn, EvUserData dat) { - ContentModules new_mods = m_ContentModules; + m_DefaultCallback.cb = fn; + m_DefaultCallback.ud = dat; +} - new_mods.insert(cmgr.GetAllModules().begin(), cmgr.GetAllModules().end()); - m_ContentModules = new_mods; +void EventManager::AddCallback(std::string url, EvFunction fn, EvUserData dat) +{ + m_UrlCallbacks.push_back(EvUrlCallback(url, {fn, dat})); } diff --git a/src/EventManager.hpp b/src/EventManager.hpp index db9d461..cf09e14 100644 --- a/src/EventManager.hpp +++ b/src/EventManager.hpp @@ -12,38 +12,38 @@ #include <string> #include <vector> -typedef void *EvUserData; -typedef void ev_c_callback(struct evhttp_request *, EvUserData); -typedef std::function<void(struct evhttp_request *, EvUserData)> EvFunction; +using EvUserData = void *; +using EvFunction = std::function<void(struct evhttp_request *, EvUserData)>; -struct ev_callback { - EvFunction cb; - EvUserData ud; +struct ev_callback +{ + EvFunction cb; + EvUserData ud; }; -typedef std::tuple<std::string, struct ev_callback> EvUrlCallback; +using EvUrlCallback = std::tuple<std::string, struct ev_callback>; -class EventManager { +class EventManager +{ public: - EventManager(); - ~EventManager(); + EventManager(std::shared_ptr<ContentManager> & cmgr); + ~EventManager(); - bool Init(std::string = "127.0.0.1", uint16_t port = 9000); - void SetDefaultCallback(EvFunction fn, EvUserData dat); - void AddCallback(std::string url, EvFunction fn, EvUserData dat); - void AddContentManager(ContentManager const & cmgr); + bool Init(std::string = "127.0.0.1", uint16_t port = 9000); + void SetDefaultCallback(EvFunction fn, EvUserData dat); + void AddCallback(std::string url, EvFunction fn, EvUserData dat); private: - struct ev_callback m_DefaultCallback; - std::vector<EvUrlCallback> m_UrlCallbacks; - ContentModules m_ContentModules; - - struct event_config *m_EvConfig = nullptr; - struct event_base *m_EvBase = nullptr; - struct evhttp *m_EvHttp = nullptr; - struct evhttp_bound_socket *m_EvSocket = nullptr; - struct evconnlistener *m_EvListener = nullptr; - struct event *m_EvTermEvent = nullptr; + std::shared_ptr<ContentManager> m_ContentManager; + struct ev_callback m_DefaultCallback; + std::vector<EvUrlCallback> m_UrlCallbacks; + + struct event_config * m_EvConfig = nullptr; + struct event_base * m_EvBase = nullptr; + struct evhttp * m_EvHttp = nullptr; + struct evhttp_bound_socket * m_EvSocket = nullptr; + struct evconnlistener * m_EvListener = nullptr; + struct event * m_EvTermEvent = nullptr; }; #endif diff --git a/src/Filesystem.cpp b/src/Filesystem.cpp index 022c1a5..09e5146 100644 --- a/src/Filesystem.cpp +++ b/src/Filesystem.cpp @@ -5,76 +5,121 @@ #include <fstream> #include <iostream> -static std::string make_path_relative(std::string &path, std::string &root) { - return std::filesystem::relative(path, root); +static std::string make_path_relative(std::string & path, std::string & root) +{ + return std::filesystem::relative(path, root); } -bool Filesystem::AddSingleFile(std::string path, std::string root) { - std::ifstream ifs(path, std::ios::binary | std::ios::ate); - - if (!ifs) { - return false; - } - - auto end = ifs.tellg(); - if (end <= 0) { - return false; - } - if (!ifs.seekg(0, std::ios::beg)) { - return false; - } - - auto size = std::size_t(end - ifs.tellg()); - - if (size == 0) { - return false; - } - - struct file_data fd = {}; - try { - fd.data.reserve(size); - } catch (const std::exception &e) { - return false; - } - - if (!ifs.read((char *)fd.data.data(), fd.data.size())) { - return false; - } - - std::string relpath = make_path_relative(path, root); - if (m_Files.count(relpath) > 0) { - std::cout << "Adding file: " << path << " and overwriting " << relpath - << std::endl; - } else { - std::cout << "Adding file: " << path << " as " << relpath << std::endl; - } - - std::string ext = std::filesystem::path(relpath).extension(); - if (ext == ".html" || ext == ".tmpl") - { - std::string tmpl(fd.data.data(), fd.data.data() + fd.data.size()); - m_Templates[relpath] = inja::Template(tmpl); - std::cout << "File: " << relpath << " may contain a renderable template." << std::endl; - } else { +bool Filesystem::AddSingleFile(std::string path, std::string root) +{ + std::ifstream ifs(path, std::ios::binary | std::ios::ate); + + if (!ifs) + { + return false; + } + + auto end = ifs.tellg(); + if (end <= 0) + { + return false; + } + if (!ifs.seekg(0, std::ios::beg)) + { + return false; + } + + auto size = std::size_t(end - ifs.tellg()); + + if (size == 0) + { + return false; + } + + struct file_data fd = {}; + try + { + fd.data.resize(size); + } + catch (const std::exception & e) + { + return false; + } + + if (!ifs.read((char *)fd.data.data(), fd.data.size())) + { + return false; + } + + std::string relpath = make_path_relative(path, root); + if (m_Files.count(relpath) > 0) + { + std::cout << "Adding file: " << path << " (" << size << " bytes) and overwriting " << relpath << " to " << this + << std::endl; + } + else + { + std::cout << "Adding file: " << path << " (" << size << " bytes) as " << relpath << " to " << this << std::endl; + } + m_Files[relpath] = fd; - } - return true; + return true; } -bool Filesystem::Scan(std::string root) { - for (const auto &entry : std::filesystem::directory_iterator(root)) { - AddSingleFile(entry.path(), root); - } - return true; +bool Filesystem::Scan(std::string root) +{ + bool retval = true; + + for (const auto & entry : std::filesystem::directory_iterator(root)) + { + if (AddSingleFile(entry.path(), root) == false) + { + retval = true; + } + } + + return retval; } -void Filesystem::AddInjaCallback(std::string functionName, std::size_t numberOfArgs, inja::CallbackFunction function) +bool Filesystem::Scan(std::string root, std::vector<std::string> extensions, bool exclude_extensions) { - m_Inja.add_callback(functionName, numberOfArgs, function); + bool retval = true; + + for (const auto & entry : std::filesystem::directory_iterator(root)) + { + std::string ext = std::filesystem::path(entry).extension(); + bool found_extension = false; + + for (const auto & extension : extensions) + { + if (ext == "." + extension) + { + found_extension = true; + break; + } + } + + if (found_extension == true && exclude_extensions == false) + { + if (AddSingleFile(entry.path(), root) == false) + { + retval = false; + } + } + if (found_extension == false && exclude_extensions == true) + { + if (AddSingleFile(entry.path(), root) == false) + { + retval = false; + } + } + } + + return retval; } -void Filesystem::AddVoidInjaCallback(std::string functionName, std::size_t numberOfArgs, inja::VoidCallbackFunction function) +const std::unordered_map<std::string, struct file_data> & Filesystem::GetFiles() const { - m_Inja.add_void_callback(functionName, numberOfArgs, function); + return m_Files; } diff --git a/src/Filesystem.hpp b/src/Filesystem.hpp index 7ebfb84..13733c9 100644 --- a/src/Filesystem.hpp +++ b/src/Filesystem.hpp @@ -2,28 +2,32 @@ #define FILESYSTEM_H 1 #include <inja/inja.hpp> -#include <unordered_map> #include <string> +#include <unordered_map> #include <vector> -struct file_data { - std::vector<unsigned char> data; +struct file_data +{ + std::vector<unsigned char> data; }; -class Filesystem { +class Filesystem +{ public: - Filesystem() {} - ~Filesystem() {} + Filesystem() + { + } + ~Filesystem() + { + } - bool AddSingleFile(std::string path, std::string root); - bool Scan(std::string root = "./wwwroot"); - void AddInjaCallback(std::string functionName, std::size_t numberOfArgs, inja::CallbackFunction function); - void AddVoidInjaCallback(std::string functionName, std::size_t numberOfArgs, inja::VoidCallbackFunction function); + bool AddSingleFile(std::string path, std::string root); + bool Scan(std::string root = "./wwwroot"); + bool Scan(std::string root, std::vector<std::string> extensions, bool exclude_extensions = false); + const std::unordered_map<std::string, struct file_data> & GetFiles() const; private: - std::unordered_map<std::string, struct file_data> m_Files; - inja::TemplateStorage m_Templates; - inja::Environment m_Inja; + std::unordered_map<std::string, struct file_data> m_Files; }; #endif diff --git a/src/RequestResponse.cpp b/src/RequestResponse.cpp index be8a0d0..2567253 100644 --- a/src/RequestResponse.cpp +++ b/src/RequestResponse.cpp @@ -10,38 +10,38 @@ RequestResponse::~RequestResponse() void RequestResponse::UseInputHeader() { - m_InputHeader = evhttp_request_get_input_headers(m_Request); + m_InputHeader = evhttp_request_get_input_headers(m_Request); } void RequestResponse::UseOutputHeader() { - m_OutputHeader = evhttp_request_get_output_headers(m_Request); + m_OutputHeader = evhttp_request_get_output_headers(m_Request); } -bool RequestResponse::AddOutputHeader(std::string & key, std::string & value) +bool RequestResponse::AddOutputHeaderByRef(std::string & key, std::string & value) { - return evhttp_add_header(m_OutputHeader, key.c_str(), value.c_str()); + return evhttp_add_header(m_OutputHeader, key.c_str(), value.c_str()); } -bool RequestResponse::AddOutputHeader2(std::string key, std::string value) +bool RequestResponse::AddOutputHeader(std::string key, std::string value) { - return AddOutputHeader(key, value); + return AddOutputHeaderByRef(key, value); } -bool RequestResponse::GetInputHeader(std::string & key, std::string & value) +bool RequestResponse::GetInputHeaderByRef(std::string & key, std::string & value) { - char const * const v = evhttp_find_header(m_InputHeader, key.c_str()); + char const * const v = evhttp_find_header(m_InputHeader, key.c_str()); - if (v == nullptr) - { - return false; - } + if (v == nullptr) + { + return false; + } - value = v; - return true; + value = v; + return true; } -bool RequestResponse::GetInputHeader2(std::string key, std::string value) +bool RequestResponse::GetInputHeader(std::string key, std::string value) { - return GetInputHeader(key, value); + return GetInputHeader(key, value); } diff --git a/src/RequestResponse.hpp b/src/RequestResponse.hpp index 24577aa..8a7e1d3 100644 --- a/src/RequestResponse.hpp +++ b/src/RequestResponse.hpp @@ -5,24 +5,25 @@ #include <string> -class RequestResponse { +class RequestResponse +{ public: - RequestResponse(struct evhttp_request * const req); - ~RequestResponse(); + RequestResponse(struct evhttp_request * const req); + ~RequestResponse(); - void UseInputHeader(); - void UseOutputHeader(); + void UseInputHeader(); + void UseOutputHeader(); - bool AddOutputHeader(std::string & key, std::string & value); - bool AddOutputHeader2(std::string key, std::string value); + bool AddOutputHeaderByRef(std::string & key, std::string & value); + bool AddOutputHeader(std::string key, std::string value); - bool GetInputHeader(std::string & key, std::string & value); - bool GetInputHeader2(std::string key, std::string value); + bool GetInputHeaderByRef(std::string & key, std::string & value); + bool GetInputHeader(std::string key, std::string value); private: - struct evhttp_request * const m_Request; - struct evkeyvalq * m_InputHeader; - struct evkeyvalq * m_OutputHeader; + struct evhttp_request * const m_Request; + struct evkeyvalq * m_InputHeader; + struct evkeyvalq * m_OutputHeader; }; #endif diff --git a/src/TemplateManager.cpp b/src/TemplateManager.cpp new file mode 100644 index 0000000..06771ea --- /dev/null +++ b/src/TemplateManager.cpp @@ -0,0 +1,57 @@ +#include "TemplateManager.hpp" + +#include <filesystem> + +TemplateManager::TemplateManager() +{ + AddInjaCallback("test_fn", 0, [](inja::Arguments & args) { + (void)args; + return "Just a test fn."; + }); + AddInjaCallback("test_return_true", 0, [](inja::Arguments & args) { + (void)args; + return true; + }); +} + +void TemplateManager::ParseTemplates(Filesystem const & fs) +{ + for (auto & tpl : fs.GetFiles()) + { + std::string tmpl(tpl.second.data.data(), tpl.second.data.data() + tpl.second.data.size()); + m_Templates[tpl.first] = m_Inja.parse(tmpl); + std::cout << "File: " << tpl.first << " may contain a renderable template." << std::endl; + } +} + +void TemplateManager::AddInjaCallback(std::string functionName, + std::size_t numberOfArgs, + inja::CallbackFunction function) +{ + m_Inja.add_callback(functionName, numberOfArgs, function); +} + +void TemplateManager::AddVoidInjaCallback(std::string functionName, + std::size_t numberOfArgs, + inja::VoidCallbackFunction function) +{ + m_Inja.add_void_callback(functionName, numberOfArgs, function); +} + +bool TemplateManager::TemplateExists(std::string const & templatePath) +{ + return m_Templates.find(templatePath) != m_Templates.end(); +} + +bool TemplateManager::RenderTemplate(std::string const & templatePath, RenderData const & rd, std::string & out) +{ + if (TemplateExists(templatePath) == false) + { + return false; + } + + inja::json ij(rd); + out = m_Inja.render(m_Templates[templatePath].content, ij); + + return true; +} diff --git a/src/TemplateManager.hpp b/src/TemplateManager.hpp new file mode 100644 index 0000000..7b89d71 --- /dev/null +++ b/src/TemplateManager.hpp @@ -0,0 +1,28 @@ +#ifndef TEMPLATEMANAGER_H +#define TEMPLATEMANAGER_H 1 + +#include "Content.hpp" +#include "Filesystem.hpp" + +#include <inja/inja.hpp> + +class TemplateManager +{ +public: + TemplateManager(); + ~TemplateManager() + { + } + + void ParseTemplates(Filesystem const & fs); + void AddInjaCallback(std::string functionName, std::size_t numberOfArgs, inja::CallbackFunction function); + void AddVoidInjaCallback(std::string functionName, std::size_t numberOfArgs, inja::VoidCallbackFunction function); + bool TemplateExists(std::string const & templatePath); + bool RenderTemplate(std::string const & templatePath, RenderData const & rd, std::string & out); + +private: + inja::TemplateStorage m_Templates; + inja::Environment m_Inja; +}; + +#endif diff --git a/src/content/blog/Blog.cpp b/src/content/blog/Blog.cpp index f72ef70..56a23c1 100644 --- a/src/content/blog/Blog.cpp +++ b/src/content/blog/Blog.cpp @@ -1,37 +1,41 @@ #include "Blog.hpp" -Blog::Blog(std::string baseUri, std::string templatePath) : TemplatedContent(templatePath), m_BaseUri(baseUri), m_Redirections() +Blog::Blog(std::string uriBasePath, std::string mainTemplatePath) + : Content(), m_UriBasePath(uriBasePath), m_MainTemplatePath(mainTemplatePath) { - m_Redirections.push_back(baseUri + "-data"); + m_Redirections.push_back(uriBasePath + "/"); + m_Redirections.push_back(uriBasePath + "/index.html"); } -bool Blog::Init(void) +bool Blog::Init() { - return true; + return true; } -void Blog::Shutdown(void) +void Blog::Shutdown() { } -bool Blog::Render(RequestResponse & rr, std::string & out) +bool Blog::Render(RequestResponse & rr, RenderData & rd) { - (void)rr; + rd["blah"] = "Yoh!"; + rr.UseOutputHeader(); + rr.AddOutputHeader("bla", "blubb"); - rr.UseOutputHeader(); - rr.AddOutputHeader2("bla", "blubb"); - out = "blog-bla"; + return true; +} - return true; +std::string const & Blog::GetUriBasePath() const +{ + return m_UriBasePath; } -std::string const & Blog::GetBaseUri(void) const +std::string const & Blog::GetMainTemplate() const { - return m_BaseUri; + return m_MainTemplatePath; } -Redirections const & -Blog::GetRedirections(void) const +Redirections const & Blog::GetRedirections() const { - return m_Redirections; + return m_Redirections; } diff --git a/src/content/blog/Blog.hpp b/src/content/blog/Blog.hpp index 1de6e0f..086acc7 100644 --- a/src/content/blog/Blog.hpp +++ b/src/content/blog/Blog.hpp @@ -3,21 +3,23 @@ #include "../../Content.hpp" -class Blog : public TemplatedContent +class Blog : public Content { public: - explicit Blog(std::string baseUri, std::string templatePath); + explicit Blog(std::string uriBasePath, std::string mainTemplatePath); - bool Init(void); - void Shutdown(void); - bool Render(RequestResponse & rr, std::string & out); + bool Init(); + void Shutdown(); + bool Render(RequestResponse & rr, RenderData & rd); - std::string const & GetBaseUri() const; - Redirections const & GetRedirections() const; + std::string const & GetUriBasePath() const; + std::string const & GetMainTemplate() const; + Redirections const & GetRedirections() const; private: - std::string m_BaseUri; - Redirections m_Redirections; + std::string m_UriBasePath; + std::string m_MainTemplatePath; + Redirections m_Redirections; }; #endif diff --git a/src/main.cpp b/src/main.cpp index 198f065..98f19c5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,105 +1,131 @@ #include "ContentManager.hpp" -#include "content/blog/Blog.hpp" #include "EventManager.hpp" #include "Filesystem.hpp" +#include "TemplateManager.hpp" + +#include "content/blog/Blog.hpp" #include <event2/buffer.h> #include <inja/inja.hpp> #include <iostream> -static void example_inja_render(struct evhttp_request *const req, - EvUserData ud) { - (void)ud; - - inja::Environment env; - nlohmann::json data; - data["name"] = "Peter"; - data["city"] = "Brunswick"; - data["age"] = 29; - data["names"] = {"Jeff", "Seb"}; - data["brother"]["name"] = "Chris"; - data["brother"]["daughters"] = {"Maria", "Helen"}; - data["brother"]["daughter0"] = {{"name", "Maria"}}; - data["is_happy"] = true; - data["is_sad"] = false; - data["relatives"]["mother"] = "Maria"; - data["relatives"]["brother"] = "Chris"; - data["relatives"]["sister"] = "Jenny"; - data["vars"] = {2, 3, 4, 0, -1, -2, -3}; - - auto reply = env.render( - "<html><body>\n" - "Hello {{ name }}! I come from {{ city }}.<br>\n" - "Hello {{ names.1 }}!<br>\n" - "Hello {{ brother.name }}!<br>\n" - "Hello {{ brother.daughter0.name }}!<br>\n" - "{{ \"{{ no_value }}\" }}<br>\n" - "Hello{# This is a comment #}!<br>\n" - "{# --- #Todo --- #}<br>\n" - "{% for name in names %}a{% endfor %}<br>\n" - "Hello {% for name in names %}{{ name }} {% endfor %}!<br>\n" - "Hello {% for name in names %}{{ loop.index }}: {{ name }}, {% " - "endfor %}!<br>\n" - "{% for type, name in relatives %}{{ type }}: {{ name }}, {% endfor " - "%}<br>\n" - "{% for v in vars %}{% if v > 0 %}+{% endif %}{% endfor %}<br>\n" - "{% for name in names %}{{ loop.index }}: {{ name }}{% if not " - "loop.is_last %}, {% endif %}{% endfor %}!<br>\n" - "{% for name in names %}{{ loop.index }}: {{ name }}{% if " - "loop.is_last == false %}, {% endif %}{% endfor %}!<br>\n" - "{% for name in names %}a{% endfor %}<br>\n" - "</body></html>\n", - data); - - evhttp_add_header(evhttp_request_get_output_headers(req), "Content-Type", - "text/html"); - - struct evbuffer *const output = evbuffer_new(); - if (output != nullptr) { - evbuffer_add(output, reply.c_str(), reply.size()); - evhttp_send_reply(req, 200, "OK", output); - evbuffer_free(output); - } +static void example_inja_render(struct evhttp_request * const req, EvUserData ud) +{ + (void)ud; + + inja::Environment env; + nlohmann::json data; + data["name"] = "Peter"; + data["city"] = "Brunswick"; + data["age"] = 29; + data["names"] = {"Jeff", "Seb"}; + data["brother"]["name"] = "Chris"; + data["brother"]["daughters"] = {"Maria", "Helen"}; + data["brother"]["daughter0"] = {{"name", "Maria"}}; + data["is_happy"] = true; + data["is_sad"] = false; + data["relatives"]["mother"] = "Maria"; + data["relatives"]["brother"] = "Chris"; + data["relatives"]["sister"] = "Jenny"; + data["vars"] = {2, 3, 4, 0, -1, -2, -3}; + + auto reply = env.render( + "<html><body>\n" + "Hello {{ name }}! I come from {{ city }}.<br>\n" + "Hello {{ names.1 }}!<br>\n" + "Hello {{ brother.name }}!<br>\n" + "Hello {{ brother.daughter0.name }}!<br>\n" + "{{ \"{{ no_value }}\" }}<br>\n" + "Hello{# This is a comment #}!<br>\n" + "{# --- #Todo --- #}<br>\n" + "{% for name in names %}a{% endfor %}<br>\n" + "Hello {% for name in names %}{{ name }} {% endfor %}!<br>\n" + "Hello {% for name in names %}{{ loop.index }}: {{ name }}, {% " + "endfor %}!<br>\n" + "{% for type, name in relatives %}{{ type }}: {{ name }}, {% endfor " + "%}<br>\n" + "{% for v in vars %}{% if v > 0 %}+{% endif %}{% endfor %}<br>\n" + "{% for name in names %}{{ loop.index }}: {{ name }}{% if not " + "loop.is_last %}, {% endif %}{% endfor %}!<br>\n" + "{% for name in names %}{{ loop.index }}: {{ name }}{% if " + "loop.is_last == false %}, {% endif %}{% endfor %}!<br>\n" + "{% for name in names %}a{% endfor %}<br>\n" + "</body></html>\n", + data); + + evhttp_add_header(evhttp_request_get_output_headers(req), "Content-Type", "text/html"); + + struct evbuffer * const output = evbuffer_new(); + if (output != nullptr) + { + evbuffer_add(output, reply.c_str(), reply.size()); + evhttp_send_reply(req, 200, "OK", output); + evbuffer_free(output); + } } -int main(int argc, char **argv) { - char const *host = "127.0.0.1"; - uint16_t port = 9000; - - if (argc <= 1) { - std::cout << "usage: cpp-web [HOST] [PORT] [WWWROOTs..]" << std::endl; - } - - if (argc > 1) { - host = argv[1]; - } - if (argc > 2) { - port = atoi(argv[2]); - } - - Filesystem fs; - for (auto i = 3; i < argc; ++i) { - if (fs.Scan(argv[i]) != true) { - return 1; +int main(int argc, char ** argv) +{ + char const * host = "127.0.0.1"; + uint16_t port = 9000; + + if (argc <= 1 || argc > 5) + { + std::cout << "usage: cpp-web [HOST] [PORT] [STATIC-WWWROOT] [DYNAMIC-WWWROOT]" << std::endl; + if (argc > 5) + { + return 1; + } } - } - fs.Scan(); - ContentManager ctmgr; - ctmgr.RegisterModule(std::make_shared<Blog>("/blog", "index.html")); + if (argc > 1) + { + host = argv[1]; + } + if (argc > 2) + { + port = atoi(argv[2]); + } - if (ctmgr.InitAll() == false) - { - std::cout << "InitAll() failed." << std::endl; - return 1; - } + std::shared_ptr<Filesystem> static_fs = std::make_shared<Filesystem>(); + if (argc > 3) + { + if (static_fs->Scan(argv[3]) != true) + { + return 1; + } + } + static_fs->Scan("./wwwroot", {"html", "tmpl"}, true); + + Filesystem dynamic_fs; + if (argc > 4) + { + if (dynamic_fs.Scan(argv[4]) != true) + { + return 1; + } + } + dynamic_fs.Scan("./wwwroot", {"html", "tmpl"}, false); + + std::shared_ptr<TemplateManager> tmgr = std::make_shared<TemplateManager>(); + tmgr->ParseTemplates(dynamic_fs); + + std::shared_ptr<ContentManager> ctmgr = std::make_shared<ContentManager>(); + ctmgr->SetStaticFilesystem(static_fs); + ctmgr->SetTemplateSystem(tmgr); + ctmgr->RegisterModule(std::make_shared<Blog>("/blog", "index.html")); + + if (ctmgr->InitAll() == false) + { + std::cout << "InitAll() failed." << std::endl; + return 1; + } - EventManager evmgr; - //evmgr.SetDefaultCallback(example_inja_render, {}); - evmgr.AddCallback("/bla", example_inja_render, {}); - evmgr.AddContentManager(ctmgr); - evmgr.Init(host, port); + EventManager evmgr(ctmgr); + // evmgr.SetDefaultCallback(example_inja_render, {}); + evmgr.AddCallback("/bla", example_inja_render, {}); + evmgr.Init(host, port); - //ctmgr.ShutdownAll(); + // ctmgr.ShutdownAll(); } diff --git a/wwwroot/index.html b/wwwroot/index.html index c0edd6c..002ea72 100644 --- a/wwwroot/index.html +++ b/wwwroot/index.html @@ -1,31 +1,8 @@ <html><body> - Hello {{ name }}! I come from {{ city }}.<br> - - Hello {{ names.1 }}!<br> - - Hello {{ brother.name }}!<br> - - Hello {{ brother.daughter0.name }}!<br> - - {{ \"{{ no_value }}\" }}<br> - - Hello{# This is a comment #}!<br> - - {# --- #Todo --- #}<br> - - {% for name in names %}a{% endfor %}<br> - - Hello {% for name in names %}{{ name }} {% endfor %}!<br> - - Hello {% for name in names %}{{ loop.index }}: {{ name }}, {% endfor %}!<br> - - {% for type, name in relatives %}{{ type }}: {{ name }}, {% endfor %}<br> - - {% for v in vars %}{% if v > 0 %}+{% endif %}{% endfor %}<br> - - {% for name in names %}{{ loop.index }}: {{ name }}{% if not loop.is_last %}, {% endif %}{% endfor %}!<br> - - {% for name in names %}{{ loop.index }}: {{ name }}{% if loop.is_last == false %}, {% endif %}{% endfor %}!<br> - - {% for name in names %}a{% endfor %}<br> +<p> +<b>blabla</b> +Test fn....: <b>{{ test_fn }}</b><br> +Test RetFN.: <b>{{ test_return_true }}</b><br> +Blog Render: <b>{{ blah }}</b><br> +</p> </body></html> diff --git a/wwwroot/index.tmpl b/wwwroot/index.tmpl new file mode 100644 index 0000000..b42f18c --- /dev/null +++ b/wwwroot/index.tmpl @@ -0,0 +1 @@ +<html><body><b>INDEX TMPL</b></body></html> |