rsgit /cpp-webA hyper-hyperfast web frontend for git repositories written in Rust.
tree/src/EventManager.cpp
EventManager.cpp227 lines · 6.8 KiB
1#include "EventManager.hpp"
2
3#include <event2/buffer.h>
4#include <event2/event.h>
5#include <event2/http.h>
6#include <event2/keyvalq_struct.h>
7#include <event2/listener.h>
8#include <event2/util.h>
9#include <signal.h>
10
11extern "C"
12{
13 static void GenerateInternalErrorPage(struct evhttp_request * const req, std::string text)
14 {
15 evhttp_clear_headers(evhttp_request_get_output_headers(req));
16 evhttp_add_header(evhttp_request_get_output_headers(req), "Content-Type", "text/html");
17
18 struct evbuffer * const output = evbuffer_new();
19 if (output != nullptr)
20 {
21 evbuffer_add(output, text.c_str(), text.size());
22 evhttp_send_reply(req, 500, "Internal Error", output);
23 evbuffer_free(output);
24 }
25 }
26
27 static void EvGenericInterceptor(struct evhttp_request * const req, void * ev_c_callback)
28 {
29 if (ev_c_callback != nullptr)
30 {
31 struct ev_callback * const evcb = (struct ev_callback *)ev_c_callback;
32 evcb->cb(req, evcb->ud);
33 }
34 else
35 {
36 GenerateInternalErrorPage(req, "EvGenericInterceptor: ev_c_callback == nullptr");
37 }
38 }
39
40 static void EvContentManagerInterceptor(struct evhttp_request * const req, void * ev_c_callback)
41 {
42 if (ev_c_callback != nullptr)
43 {
44 struct evhttp_uri const * const uri = evhttp_request_get_evhttp_uri(req);
45 if (uri == nullptr)
46 {
47 GenerateInternalErrorPage(req, "EvContentManagerInterceptor: uri == nullptr");
48 return;
49 }
50
51 char const * const path = evhttp_uri_get_path(uri);
52 if (path == nullptr)
53 {
54 GenerateInternalErrorPage(req, "EvContentManagerInterceptor: path == nullptr");
55 return;
56 }
57#if 0
58 std::cout << "URI Path: " << path << std::endl;
59#endif
60
61 std::shared_ptr<ContentManager> const cmgr = *(std::shared_ptr<ContentManager> const *)ev_c_callback;
62 RequestResponse rr(path, req);
63 std::string out;
64
65 if (cmgr->Render(path, rr, out) == false)
66 {
67 std::string text;
68 text = "ContentModule(\"" + std::string(path) + "\")->Render() failed.\n";
69 GenerateInternalErrorPage(req, text);
70 }
71 else
72 {
73 struct evbuffer * const output = evbuffer_new();
74 if (output != nullptr)
75 {
76 evbuffer_add(output, out.c_str(), out.size());
77 evhttp_send_reply(req, HTTP_OK, "OK", output);
78 evbuffer_free(output);
79 }
80 }
81 }
82 else
83 {
84 GenerateInternalErrorPage(req, "EvContentManagerInterceptor: ev_c_callback == nullptr");
85 }
86 }
87
88 static void do_term(int sig, short events, void * arg)
89 {
90 (void)events;
91 struct event_base * base = (struct event_base *)arg;
92 event_base_loopbreak(base);
93 fprintf(stderr, "Got %i, Terminating\n", sig);
94 }
95}
96
97static inline void default_evhttp_callback(struct evhttp_request * const req, EvUserData ud)
98{
99 (void)ud;
100
101 evhttp_add_header(evhttp_request_get_output_headers(req), "Content-Type", "text/html");
102
103 struct evbuffer * const output = evbuffer_new();
104 if (output != nullptr)
105 {
106 static char const * const page_404 =
107 "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n"
108 "<html>\n"
109 "\t<head><title>404 Not Found</title></head>\n"
110 "\t<body><h1>Not Found</h1>The requested URI was not found.</body>\n"
111 "</html>\n";
112 evbuffer_add_printf(output, "%s\n", page_404);
113 evhttp_send_reply(req, 404, "Not found", output);
114 evbuffer_free(output);
115 }
116}
117
118EventManager::EventManager(std::shared_ptr<ContentManager> & cmgr)
119 : m_ContentManager(cmgr), m_DefaultCallback({default_evhttp_callback, nullptr})
120{
121}
122
123EventManager::~EventManager()
124{
125 if (m_EvConfig != nullptr)
126 event_config_free(m_EvConfig);
127 if (m_EvHttp != nullptr)
128 evhttp_free(m_EvHttp);
129 if (m_EvTermEvent != nullptr)
130 event_free(m_EvTermEvent);
131 if (m_EvBase != nullptr)
132 event_base_free(m_EvBase);
133}
134
135bool EventManager::Init(std::string host, uint16_t port)
136{
137 struct event_config * cfg = nullptr;
138
139 if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
140 {
141 return false;
142 }
143
144#if 0
145 event_enable_debug_logging(EVENT_DBG_ALL);
146#endif
147
148 cfg = event_config_new();
149 event_config_set_flag(cfg, EVENT_BASE_FLAG_STARTUP_IOCP);
150
151 m_EvBase = event_base_new_with_config(cfg);
152 if (m_EvBase == nullptr)
153 {
154 fprintf(stderr, "Couldn't create an event_base: exiting\n");
155 return false;
156 }
157 event_config_free(cfg);
158 cfg = nullptr;
159
160 /* Create a new evhttp object to handle requests. */
161 m_EvHttp = evhttp_new(m_EvBase);
162 if (m_EvHttp == nullptr)
163 {
164 fprintf(stderr, "couldn't create evhttp. Exiting.\n");
165 return false;
166 }
167
168 for (auto & uc : m_UrlCallbacks)
169 {
170 struct ev_callback * const evcb = &std::get<1>(uc);
171 if (evhttp_set_cb(m_EvHttp, std::get<0>(uc).c_str(), EvGenericInterceptor, evcb) != 0)
172 {
173 return false;
174 }
175 }
176 for (auto & cm : m_ContentManager->GetAllModules())
177 {
178 if (evhttp_set_cb(m_EvHttp, cm.first.c_str(), EvContentManagerInterceptor, &m_ContentManager) != 0)
179 {
180 fprintf(stderr, "Failed to add module callback: %s\n", cm.first.c_str());
181 }
182 }
183 for (auto & cm : m_ContentManager->GetAllModulesRoutes())
184 {
185 if (evhttp_set_cb(m_EvHttp, cm.first.c_str(), EvContentManagerInterceptor, &m_ContentManager) != 0 &&
186 cm.first != "/")
187 {
188 fprintf(stderr, "Failed to add route callback: %s\n", cm.first.c_str());
189 }
190 }
191 evhttp_set_gencb(m_EvHttp, EvGenericInterceptor, &m_DefaultCallback);
192
193 if (evhttp_bind_socket(m_EvHttp, host.c_str(), port) != 0)
194 {
195 fprintf(stderr, "Couldn't bind to %s:%d. Exiting.\n", host.c_str(), port);
196 return false;
197 }
198
199 evhttp_set_allowed_methods(m_EvHttp, EVHTTP_REQ_GET | EVHTTP_REQ_POST | EVHTTP_REQ_HEAD);
200 evhttp_set_default_content_type(m_EvHttp, NULL);
201
202 m_EvTermEvent = evsignal_new(m_EvBase, SIGINT, do_term, m_EvBase);
203 if (m_EvTermEvent == nullptr)
204 {
205 return false;
206 }
207 if (event_add(m_EvTermEvent, NULL) != 0)
208 {
209 return false;
210 }
211
212 event_base_dispatch(m_EvBase);
213
214 return true;
215}
216
217void EventManager::SetDefaultCallback(EvFunction fn, EvUserData dat)
218{
219 m_DefaultCallback.cb = fn;
220 m_DefaultCallback.ud = dat;
221}
222
223void EventManager::AddCallback(std::string url, EvFunction fn, EvUserData dat)
224{
225 m_UrlCallbacks.push_back(EvUrlCallback(url, {fn, dat}));
226}
227