diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/EventManager.cpp | 101 | ||||
-rw-r--r-- | src/EventManager.hpp | 60 | ||||
-rw-r--r-- | src/main.cpp | 126 |
3 files changed, 227 insertions, 60 deletions
diff --git a/src/EventManager.cpp b/src/EventManager.cpp new file mode 100644 index 0000000..b946b5f --- /dev/null +++ b/src/EventManager.cpp @@ -0,0 +1,101 @@ +#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 EvCallbackInterceptor(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); + } +} + +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); +} +} + +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(), EvCallbackInterceptor, + evcb) != 0) { + return false; + } + } + evhttp_set_gencb(m_EvHttp, EvCallbackInterceptor, &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})); +} diff --git a/src/EventManager.hpp b/src/EventManager.hpp new file mode 100644 index 0000000..2e4184e --- /dev/null +++ b/src/EventManager.hpp @@ -0,0 +1,60 @@ +#ifndef EVENT_MANAGER_H +#define EVENT_MANAGER_H 1 + +#include <event2/buffer.h> +#include <event2/event.h> +#include <event2/http.h> + +#include <functional> +#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; + +struct ev_callback { + EvFunction cb; + EvUserData ud; +}; + +typedef std::tuple<std::string, struct ev_callback> EvUrlCallback; + +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); + } +} + +class EventManager { +public: + EventManager() : m_DefaultCallback({default_evhttp_callback, nullptr}) {} + ~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); + +private: + 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/main.cpp b/src/main.cpp index 3bcd0dd..9b38c38 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,71 +1,77 @@ -#include <iostream> - -#include <httplib.h> -#include <inja/inja.hpp> +#include "EventManager.hpp" +#include <event2/buffer.h> -void example_inja_render(const httplib::Request& req, httplib::Response& res) -{ - 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}; +#include <inja/inja.hpp> +#include <iostream> - res.set_content( - 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 {} %}a{% endfor %}<br>\n" - "</body></html>\n", - data), "text/html"); -} +static void example_inja_render(struct evhttp_request *const req, + EvUserData ud) { + (void)ud; -bool setup_httplib(const char * const addr, uint16_t port) -{ - using namespace httplib; + 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}; - Server svr; + 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); - svr.Get("/", [](const Request& req, Response& res) { - example_inja_render(req, res); - }); + evhttp_add_header(evhttp_request_get_output_headers(req), "Content-Type", + "text/html"); - svr.Get(R"(/numbers/(\d+))", [&](const Request& req, Response& res) { - auto numbers = req.matches[1]; - res.set_content(numbers, "text/plain"); - }); + 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); + } +} - svr.Get("/stop", [&](const Request& req, Response& res) { - svr.stop(); - }); +int main(int argc, char **argv) { + char const *host = "127.0.0.1"; + uint16_t port = 9000; - return svr.listen(addr, port); -} + if (argc > 1) { + host = argv[1]; + } + if (argc > 2) { + port = atoi(argv[2]); + } -int main(int argc, char **argv) -{ - setup_httplib("127.0.0.1", 8080); + EventManager evmgr; + evmgr.setDefaultCallback(example_inja_render, {}); + evmgr.Init(host, port); } |