cpp_server/server.hpp
2025-06-12 19:01:53 -05:00

150 lines
3.9 KiB
C++

#pragma once
#include "sockets/epoll.hpp"
#include "router.hpp"
#include "http_parser.hpp"
#include <iostream>
#include <string.h>
#include <string_view>
#include <array>
#include <string>
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<char, BUFFER_SIZE> 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);
}
};