diff options
| author | garder500 <jeremy27.clara22@gmail.com> | 2025-05-01 14:32:40 +0200 |
|---|---|---|
| committer | garder500 <jeremy27.clara22@gmail.com> | 2025-05-01 14:32:40 +0200 |
| commit | 5c50f36cca528c586e72d389c35774ecb64091be (patch) | |
| tree | 0c94147f2d9f94a7503269359103f7098a96bc35 | |
| parent | 23cba7cd6b32b7c5db98aa5a3f9206d4bce07902 (diff) | |
Refactor le serveur webhook HTTP pour améliorer la gestion des événements et des connexions, en ajoutant la prise en charge de l'architecture macOS et en optimisant la gestion des descripteurs de fichiers.
| -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) { |
