#pragma once #include "sockets/epoll.hpp" #include "router.hpp" #include "http_parser.hpp" #include #include #include #include #include class HttpServer { public: explicit HttpServer(uint16_t port, Router& router) : socket_(port), router_(router) { socket_.on_connection([this](int fd) { handle_connection(fd); }); socket_.on_data([this](int fd) { handle_data(fd); }); socket_.on_disconnect([this](int fd) { handle_disconnect(fd); }); } bool start() { return socket_.start(); } void run() { socket_.run(); } void stop() { socket_.stop(); } // Utility function to extract path parameters static std::string get_path_param(string_view path, size_t segment_index = 0) { size_t start = 0; size_t current_segment = 0; while (start < path.length()) { if (path[start] == '/') { start++; continue; } size_t end = path.find('/', start); if (end == std::string_view::npos) end = path.length(); if (current_segment == segment_index) { return std::string(path.substr(start, end - start)); } current_segment++; start = end; } return ""; } private: static constexpr int BUFFER_SIZE = 8192; EpollSocket socket_; Router& router_; std::array buffer_; void handle_connection(int client_fd) { // Client connected } void handle_data(int client_fd) { while (true) { ssize_t bytes_read = read(client_fd, buffer_.data(), buffer_.size()); if (bytes_read == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) break; return; } if (bytes_read == 0) return; std::string request_data(buffer_.data(), bytes_read); if (request_data.find("\r\n\r\n") != std::string::npos) { process_request(client_fd, request_data); } } } void handle_disconnect(int client_fd) { // Client disconnected } void process_request(int client_fd, std::string_view request_data) { HttpRequest req = HttpParser::parse(request_data); if (!req.valid) { send_error_response(client_fd, "Bad Request", 400); return; } HttpResponse response; // Try to route the request (router will populate req.params) if (router_.handle(req, response)) { send_http_response(client_fd, response); } else { response.status = 404; response.set_text("Not Found"); send_http_response(client_fd, response); } } void send_http_response(int client_fd, const HttpResponse& response) { std::string http_response = "HTTP/1.1 " + std::to_string(response.status); switch (response.status) { case 200: http_response += " OK"; break; case 201: http_response += " Created"; break; case 400: http_response += " Bad Request"; break; case 404: http_response += " Not Found"; break; case 500: http_response += " Internal Server Error"; break; default: http_response += " Unknown"; break; } http_response += "\r\n"; http_response += "Content-Type: " + response.content_type + "\r\n"; http_response += "Content-Length: " + std::to_string(response.body.size()) + "\r\n"; // Add custom headers for (const auto& [key, value] : response.headers) { http_response += key + ": " + value + "\r\n"; } http_response += "Connection: keep-alive\r\n\r\n"; http_response += response.body; send_raw_response(client_fd, http_response); } void send_raw_response(int client_fd, const std::string& response) { ssize_t total_sent = 0; ssize_t response_len = response.size(); while (total_sent < response_len) { ssize_t sent = write(client_fd, response.data() + total_sent, response_len - total_sent); if (sent == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) continue; break; } total_sent += sent; } } void send_error_response(int client_fd, const std::string& message, int status) { HttpResponse response; response.status = status; response.set_text(message); response.headers["Connection"] = "close"; send_http_response(client_fd, response); } };