diff options
| author | garder500 <65552225+garder500@users.noreply.github.com> | 2025-05-01 14:35:24 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-05-01 14:35:24 +0200 |
| commit | 3d758ad615e09c18befacad5c6eb0a6ae7ad0d23 (patch) | |
| tree | 0c94147f2d9f94a7503269359103f7098a96bc35 | |
| parent | 23cba7cd6b32b7c5db98aa5a3f9206d4bce07902 (diff) | |
| parent | 5c50f36cca528c586e72d389c35774ecb64091be (diff) | |
Merge pull request #2 from ketsuna-org/main
Refactor le serveur webhook HTTP pour améliorer la gestion des événem…
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | .vscode/settings.json | 2 | ||||
| -rw-r--r-- | bot/include/http_webhook_server.hpp | 29 | ||||
| -rw-r--r-- | bot/src/http_webhook_server.cpp | 140 |
4 files changed, 137 insertions, 35 deletions
@@ -41,3 +41,4 @@ devenv.local.nix .pre-commit-config.yaml build discord-bot +.cache/* diff --git a/.vscode/settings.json b/.vscode/settings.json index 42e44f8..798eedd 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,5 @@ { - "cmake.sourceDirectory": "/home/exa/perso/bot-creator-api/bot", + "cmake.sourceDirectory": "/Users/jeremy/Documents/bot-creator-api/bot", "files.associations": { "map": "cpp", "__bit_reference": "cpp", diff --git a/bot/include/http_webhook_server.hpp b/bot/include/http_webhook_server.hpp index a42829f..8ba6841 100644 --- a/bot/include/http_webhook_server.hpp +++ b/bot/include/http_webhook_server.hpp @@ -1,12 +1,18 @@ #pragma once -#include <sys/epoll.h> -#include <netinet/in.h> #include <functional> #include <string> #include <unordered_map> #include <system_error> #include <sstream> +#include <string> +#include <netinet/in.h> + +#ifdef __linux__ + #include <sys/epoll.h> +#elif defined(__APPLE__) + #include <sys/event.h> +#endif class HttpWebhookServer { public: @@ -27,7 +33,8 @@ public: HttpWebhookServer(uint16_t port, Handler handler); ~HttpWebhookServer(); - + void setupEpoll(); + void handleEvents(); void start(); void stop(); @@ -39,17 +46,29 @@ private: }; void setupSocket(); - void setupEpoll(); - void handleEvent(struct epoll_event* event); + void setupEventLoop(); + void handleEvent(int fd, uint32_t events); void handleClient(int fd); void closeClient(int fd); + void registerFdForRead(int fd); + void modifyFdToWrite(int fd); + void sendToClient(int fd); void parseHttpRequest(ClientContext& ctx, HttpRequest& req); void buildHttpResponse(const HttpResponse& res, std::string& output); int server_fd = -1; + int event_fd = -1; int epoll_fd = -1; bool running = false; uint16_t port; Handler request_handler; std::unordered_map<int, ClientContext> clients; + +#ifdef __linux__ + static constexpr int MAX_EVENTS = 64; + struct epoll_event events[MAX_EVENTS]; +#elif defined(__APPLE__) + static constexpr int MAX_EVENTS = 64; + struct kevent events[MAX_EVENTS]; +#endif }; 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) { |
