From 5c50f36cca528c586e72d389c35774ecb64091be Mon Sep 17 00:00:00 2001 From: garder500 Date: Thu, 1 May 2025 14:32:40 +0200 Subject: 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. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bot/src/http_webhook_server.cpp | 140 +++++++++++++++++++++++++++++++--------- 1 file changed, 111 insertions(+), 29 deletions(-) (limited to 'bot/src') 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 #include #include +#include +#include +#include + +#ifdef __linux__ +#include +#elif defined(__APPLE__) +#include +#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(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) { -- cgit v1.2.3