From a2db0659c6ab8ce2d4490d865ddc03832510e0f6 Mon Sep 17 00:00:00 2001 From: Quinten Mennen Date: Tue, 16 Sep 2025 17:08:10 +0200 Subject: [PATCH] Refactor server configuration and implementation: update listen ports in default.conf, enhance Server class with connection handling, and improve socket management in Server.cpp. --- .clang-tidy | 1 + config/default.conf | 4 +- webserv/config/ServerConfig.hpp | 3 + webserv/main.cpp | 2 +- webserv/server/Server.cpp | 192 ++++++++++++++++++++++++++++++-- webserv/server/Server.hpp | 10 +- 6 files changed, 198 insertions(+), 14 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index 5ceb7b9..26cf53c 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -9,6 +9,7 @@ Checks: > -modernize-avoid-c-arrays, -cppcoreguidelines-avoid-magic-numbers, -readability-magic-numbers + -cppcoreguidelines-pro-type-vararg CheckOptions: - key: modernize-use-auto.MinTypeNameLength diff --git a/config/default.conf b/config/default.conf index 6488c39..0de2a12 100644 --- a/config/default.conf +++ b/config/default.conf @@ -1,5 +1,5 @@ server { - listen 80; + listen 8080; host 0.0.0.0; server_name localhost; @@ -41,7 +41,7 @@ server { } asdfasdfasdf server { - listen 80; + listen 8081; host 127.0.0.1; server_name mylocal; diff --git a/webserv/config/ServerConfig.hpp b/webserv/config/ServerConfig.hpp index dfefbe7..a1cdec4 100644 --- a/webserv/config/ServerConfig.hpp +++ b/webserv/config/ServerConfig.hpp @@ -20,10 +20,13 @@ class ServerConfig [[nodiscard]] const std::vector &getIndexFiles() const { return index_files; } [[nodiscard]] const LocationConfig &getLocation(const std::string &path) const; [[nodiscard]] std::vector getLocationPaths() const; + void setServerFD(int fd) { server_fd = fd; } + [[nodiscard]] int getServerFD() const { return server_fd; } private: std::string host; int port; + int server_fd; std::string root; std::string cgi_pass; std::string cgi_ext; diff --git a/webserv/main.cpp b/webserv/main.cpp index 48ecfa1..a8f97da 100644 --- a/webserv/main.cpp +++ b/webserv/main.cpp @@ -28,7 +28,7 @@ int main(int argc, char **argv) } Server server(ConfigManager::getInstance()); - // server.start(); + server.start(); return 0; } \ No newline at end of file diff --git a/webserv/server/Server.cpp b/webserv/server/Server.cpp index 1b42cfe..c0820df 100644 --- a/webserv/server/Server.cpp +++ b/webserv/server/Server.cpp @@ -1,16 +1,188 @@ -#include +#include // For inet_addr +#include // For exit() +#include // For memset +#include // For fcntl()" #include +#include // For sockaddr_in +#include +#include // For socket functions +#include // For close() +#include -Server::Server(const ConfigManager &configManager) +Server::Server(const ConfigManager &configManager) : epoll_fd(-1) { - const auto &serverConfigs = configManager.getServerConfigs(); - if (serverConfigs.empty()) + const auto &serverConfigs = configManager.getServerConfigs(); + if (serverConfigs.empty()) + { + throw std::runtime_error("No server configurations available."); + } + + // for(auto serverConfig : serverConfigs) { + // setupServerSocket(serverConfig); + // } + // For simplicity, just take the first server config + const ServerConfig &config = serverConfigs[0]; + host = config.getHost(); + port = config.getPort(); + std::cout << "Server initialized on " << host << ":" << port << '\n'; +} + + +void Server::start() +{ + int server_fd = -1; + + + // 1. Create socket (IPv4, TCP) + server_fd = socket(AF_INET, SOCK_STREAM, 0); + if (server_fd == -1) + { + perror("Socket creation failed"); + exit(EXIT_FAILURE); + } + + // 2. Set socket options (to reuse address and port) + int opt = 1; + if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) + { + perror("setsockopt"); + close(server_fd); + exit(EXIT_FAILURE); + } + + // 3. Bind socket to an IP/Port + struct sockaddr_in address{}; + // std::memset(&address, 0, sizeof(address)); + + address.sin_family = AF_INET; + address.sin_addr.s_addr = INADDR_ANY; // Listen on all interfaces + address.sin_port = htons(port); // Host-to-network byte order + + if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) // NOLINT + { + perror("Bind failed"); + close(server_fd); + exit(EXIT_FAILURE); + } + + // 4. Listen for incoming connections + if (listen(server_fd, SOMAXCONN) < 0) + { + perror("Listen failed"); + close(server_fd); + exit(EXIT_FAILURE); + } + + fcntl(server_fd, F_SETFL, O_NONBLOCK); + std::cout << "Server listening on port " << port << "...\n"; + + // 5. Set up epoll + int epoll_fd = epoll_create1(0); + if (epoll_fd == -1) + { + perror("epoll_create1 failed"); + close(server_fd); + // throw exception + } + + struct epoll_event event; + event.events = EPOLLIN; // Interested in read events + event.data.fd = server_fd; + if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &event) == -1) + { + perror("epoll_ctl failed"); + close(server_fd); + close(epoll_fd); + // throw exception + } + const int MAX_EVENTS = 10; + struct epoll_event events[MAX_EVENTS]; // NOLINT + while (true) + { + int nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1); // NOLINT + if (nfds == -1) + { + perror("epoll_wait failed"); + break; + } + for (int i = 0; i < nfds; ++i) + { + epoll_event &event = events[i]; + if ((event.events & EPOLLERR) > 0 || (event.events & EPOLLHUP) > 0) + { + std::cerr << "Epoll error on fd " << event.data.fd << '\n'; + close(event.data.fd); + } + if ((event.events & EPOLLIN) > 0 && event.data.fd == server_fd) + { + handleConnection(epoll_fd, &event); + } + else if ((event.events & EPOLLIN) > 0) + { + handleRequest(epoll_fd, &event); + } else if ((event.events & EPOLLOUT) > 0) { + std::cout << "Attempting to send data to fd: " << event.data.fd << '\n'; + const char *httpResponse = "HTTP/1.1 200 OK\r\nContent-Length: 13\r\n\r\nHello, World!"; + ssize_t bytesSent = send(event.data.fd, httpResponse, strlen(httpResponse), 0); + if (bytesSent < 0) + { + perror("Send error"); + } + close(event.data.fd); // Close after sending response + } + } + } +} + +void Server::handleRequest(int epoll_fd, struct epoll_event *event) +{ + std::cout << "Handling request...\n"; + + int client_fd = event->data.fd; + std::cout << "client fd: " << client_fd << '\n'; + char buffer[1024] = {}; + ssize_t bytesRead = read(client_fd, buffer, sizeof(buffer) - 1); + if (bytesRead < 0) { - throw std::runtime_error("No server configurations available."); + perror("Read error"); + close(client_fd); + return; } - // For simplicity, just take the first server config - const ServerConfig &config = serverConfigs[0]; - host = config.getHost(); - port = config.getPort(); - std::cout << "Server initialized on " << host << ":" << port << '\n'; + if (bytesRead == 0) + { + std::cout << "Client disconnected, fd: " << client_fd << '\n'; + close(client_fd); + return; + } + + buffer[bytesRead] = '\0'; // Null-terminate the buffer + std::cout << "Received request:\n" << buffer << '\n'; + + struct epoll_event ev; + ev.events = EPOLLOUT | EPOLLET; + ev.data.fd = client_fd; + epoll_ctl(epoll_fd, EPOLL_CTL_MOD, client_fd, &ev); +} + +void Server::handleConnection(int epoll_fd, struct epoll_event *event) +{ + int client_fd = accept(event->data.fd, nullptr, nullptr); + fcntl(client_fd, F_SETFL, O_NONBLOCK); + if (client_fd == -1) + { + perror("Accept failed"); + return; + } + std::cout << "New connection accepted, fd: " << client_fd << '\n'; + struct epoll_event client_event; + client_event.events = EPOLLIN | EPOLLET; + client_event.data.fd = client_fd; + if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &client_event) == -1) + { + perror("epoll_ctl failed"); + close(client_fd); + close(epoll_fd); + // throw exception + } + // close(client_fd); // Close the client socket for now } \ No newline at end of file diff --git a/webserv/server/Server.hpp b/webserv/server/Server.hpp index 5fd1a7a..a758176 100644 --- a/webserv/server/Server.hpp +++ b/webserv/server/Server.hpp @@ -5,9 +5,17 @@ class Server { public: - Server(const ConfigManager &configManager); + Server(const ConfigManager &configManager); + + void start(); + void setupServerSocket(ServerConfig &config); + void handleConnection(int epoll_fd, struct epoll_event *event); + void handleRequest(int epoll_fd, struct epoll_event *event); private: + + int epoll_fd; std::string host; int port; + };