diff options
Diffstat (limited to 'src/EventManager.cpp')
-rw-r--r-- | src/EventManager.cpp | 206 |
1 files changed, 206 insertions, 0 deletions
diff --git a/src/EventManager.cpp b/src/EventManager.cpp new file mode 100644 index 0000000..3270297 --- /dev/null +++ b/src/EventManager.cpp @@ -0,0 +1,206 @@ +#include "EventManager.hpp" + +#include <event2/buffer.h> +#include <event2/event.h> +#include <event2/http.h> +#include <event2/keyvalq_struct.h> +#include <event2/listener.h> +#include <event2/util.h> +#include <signal.h> + +extern "C" +{ + 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 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"); + } + } + + 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; + + 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); + } +} + +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); +} + +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; + } + } + 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); + + 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; +} + +void EventManager::AddCallback(std::string url, EvFunction fn, EvUserData dat) +{ + m_UrlCallbacks.push_back(EvUrlCallback(url, {fn, dat})); +} |