aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/EventManager.cpp101
-rw-r--r--src/EventManager.hpp60
-rw-r--r--src/main.cpp126
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);
}