summaryrefslogtreecommitdiff
path: root/bot/src/http_webhook_server.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'bot/src/http_webhook_server.cpp')
-rw-r--r--bot/src/http_webhook_server.cpp140
1 files changed, 111 insertions, 29 deletions
diff --git a/bot/src/http_webhook_server.cpp b/bot/src/http_webhook_server.cpp
index a6daa63..fd01a1f 100644
--- a/bot/src/http_webhook_server.cpp
+++ b/bot/src/http_webhook_server.cpp
@@ -3,6 +3,15 @@
#include <unistd.h>
#include <cstring>
#include <algorithm>
+#include <sstream>
+#include <stdexcept>
+#include <system_error>
+
+#ifdef __linux__
+#include <sys/epoll.h>
+#elif defined(__APPLE__)
+#include <sys/event.h>
+#endif
HttpWebhookServer::HttpWebhookServer(uint16_t port, Handler handler)
: port(port), request_handler(handler) {
@@ -17,12 +26,16 @@ HttpWebhookServer::~HttpWebhookServer() {
}
void HttpWebhookServer::setupSocket() {
- server_fd = ::socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
- if (server_fd == -1) throw std::system_error(errno, std::generic_category());
+ server_fd = ::socket(AF_INET, SOCK_STREAM, 0);
+ if (server_fd == -1)
+ throw std::system_error(errno, std::generic_category());
int opt = 1;
setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
+ if (::fcntl(server_fd, F_SETFL, O_NONBLOCK) == -1)
+ throw std::system_error(errno, std::generic_category());
+
sockaddr_in addr{};
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
@@ -36,6 +49,7 @@ void HttpWebhookServer::setupSocket() {
}
void HttpWebhookServer::setupEpoll() {
+#ifdef __linux__
epoll_fd = ::epoll_create1(0);
if (epoll_fd == -1)
throw std::system_error(errno, std::generic_category());
@@ -46,33 +60,96 @@ void HttpWebhookServer::setupEpoll() {
if (::epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &event) == -1)
throw std::system_error(errno, std::generic_category());
+
+#elif defined(__APPLE__)
+ epoll_fd = ::kqueue();
+ if (epoll_fd == -1)
+ throw std::system_error(errno, std::generic_category());
+
+ struct kevent ev_set;
+ EV_SET(&ev_set, server_fd, EVFILT_READ, EV_ADD | EV_CLEAR, 0, 0, nullptr);
+ if (kevent(epoll_fd, &ev_set, 1, nullptr, 0, nullptr) == -1)
+ throw std::system_error(errno, std::generic_category());
+#endif
+}
+
+void HttpWebhookServer::registerFdForRead(int fd) {
+#ifdef __linux__
+ epoll_event event{};
+ event.events = EPOLLIN | EPOLLET | EPOLLRDHUP;
+ event.data.fd = fd;
+ ::epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &event);
+#elif defined(__APPLE__)
+ struct kevent ev_set;
+ EV_SET(&ev_set, fd, EVFILT_READ, EV_ADD | EV_CLEAR, 0, 0, nullptr);
+ kevent(epoll_fd, &ev_set, 1, nullptr, 0, nullptr);
+#endif
+}
+
+void HttpWebhookServer::modifyFdToWrite(int fd) {
+#ifdef __linux__
+ epoll_event event{};
+ event.events = EPOLLOUT | EPOLLET | EPOLLRDHUP;
+ event.data.fd = fd;
+ ::epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fd, &event);
+#elif defined(__APPLE__)
+ struct kevent ev_set[2];
+ EV_SET(&ev_set[0], fd, EVFILT_READ, EV_DELETE, 0, 0, nullptr);
+ EV_SET(&ev_set[1], fd, EVFILT_WRITE, EV_ADD | EV_CLEAR, 0, 0, nullptr);
+ kevent(epoll_fd, ev_set, 2, nullptr, 0, nullptr);
+#endif
}
void HttpWebhookServer::start() {
running = true;
+
+#ifdef __linux__
epoll_event events[64];
+#elif defined(__APPLE__)
+ struct kevent events[64];
+#endif
while (running) {
+#ifdef __linux__
int nfds = ::epoll_wait(epoll_fd, events, 64, -1);
+#elif defined(__APPLE__)
+ int nfds = ::kevent(epoll_fd, nullptr, 0, events, 64, nullptr);
+#endif
if (nfds == -1 && errno != EINTR)
throw std::system_error(errno, std::generic_category());
for (int i = 0; i < nfds; ++i) {
- if (events[i].data.fd == server_fd) {
+#ifdef __linux__
+ int fd = events[i].data.fd;
+ bool isWritable = events[i].events & EPOLLOUT;
+#elif defined(__APPLE__)
+ int fd = static_cast<int>(events[i].ident);
+ bool isWritable = events[i].filter == EVFILT_WRITE;
+#endif
+
+ if (fd == server_fd) {
while (true) {
sockaddr_in client_addr{};
socklen_t client_len = sizeof(client_addr);
+
+#ifdef __linux__
int client_fd = ::accept4(server_fd, (sockaddr*)&client_addr, &client_len, SOCK_NONBLOCK);
+#elif defined(__APPLE__)
+ int client_fd = ::accept(server_fd, (sockaddr*)&client_addr, &client_len);
+ if (client_fd != -1) {
+ int flags = fcntl(client_fd, F_GETFL, 0);
+ fcntl(client_fd, F_SETFL, flags | O_NONBLOCK);
+ }
+#endif
if (client_fd == -1) break;
- epoll_event event{};
- event.events = EPOLLIN | EPOLLET | EPOLLRDHUP;
- event.data.fd = client_fd;
- ::epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &event);
clients[client_fd] = ClientContext{};
+ registerFdForRead(client_fd);
}
+ } else if (isWritable) {
+ sendToClient(fd);
} else {
- handleClient(events[i].data.fd);
+ handleClient(fd);
}
}
}
@@ -83,11 +160,35 @@ void HttpWebhookServer::stop() {
}
void HttpWebhookServer::closeClient(int fd) {
+#ifdef __linux__
::epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, nullptr);
+#elif defined(__APPLE__)
+ struct kevent ev_del[2];
+ EV_SET(&ev_del[0], fd, EVFILT_READ, EV_DELETE, 0, 0, nullptr);
+ EV_SET(&ev_del[1], fd, EVFILT_WRITE, EV_DELETE, 0, 0, nullptr);
+ kevent(epoll_fd, ev_del, 2, nullptr, 0, nullptr);
+#endif
::close(fd);
clients.erase(fd);
}
+void HttpWebhookServer::sendToClient(int fd) {
+ if (!clients.count(fd)) return;
+
+ auto& ctx = clients[fd];
+ if (!ctx.output_buffer.empty()) {
+ ssize_t sent = ::send(fd,
+ ctx.output_buffer.data() + ctx.bytes_written,
+ ctx.output_buffer.size() - ctx.bytes_written,
+ MSG_DONTWAIT);
+
+ if (sent > 0) ctx.bytes_written += sent;
+ if (ctx.bytes_written == ctx.output_buffer.size()) {
+ closeClient(fd);
+ }
+ }
+}
+
void HttpWebhookServer::handleClient(int fd) {
char buffer[4096];
ssize_t count = ::recv(fd, buffer, sizeof(buffer), MSG_DONTWAIT);
@@ -103,31 +204,12 @@ void HttpWebhookServer::handleClient(int fd) {
HttpResponse res = request_handler(req);
buildHttpResponse(res, ctx.output_buffer);
- epoll_event event{};
- event.events = EPOLLOUT | EPOLLET | EPOLLRDHUP;
- event.data.fd = fd;
- ::epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fd, &event);
+ modifyFdToWrite(fd);
}
- }
- else if (count == 0 || (count == -1 && errno != EAGAIN)) {
+ } else if (count == 0 || (count == -1 && errno != EAGAIN)) {
closeClient(fd);
return;
}
-
- if (!clients.count(fd)) return; // Client déjà fermé plus haut
-
- auto& ctx = clients[fd];
- if (!ctx.output_buffer.empty()) {
- ssize_t sent = ::send(fd,
- ctx.output_buffer.data() + ctx.bytes_written,
- ctx.output_buffer.size() - ctx.bytes_written,
- MSG_DONTWAIT);
-
- if (sent > 0) ctx.bytes_written += sent;
- if (ctx.bytes_written == ctx.output_buffer.size()) {
- closeClient(fd);
- }
- }
}
void HttpWebhookServer::parseHttpRequest(ClientContext& ctx, HttpRequest& req) {