diff options
| -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) {  | 
