From 8e40f538cf6b773f843b4ec18517ae190607427e Mon Sep 17 00:00:00 2001 From: whaffman Date: Wed, 17 Sep 2025 17:19:10 +0200 Subject: [PATCH] Implement Client and Server classes: add Client class for handling client requests and responses, and enhance Server class with connection management and event loop functionality. --- webserv/server/Client.cpp | 22 +++++ webserv/server/Client.hpp | 21 +++++ webserv/server/Server.cpp | 171 ++++++++++++++++++++++---------------- webserv/server/Server.hpp | 23 +++-- 4 files changed, 160 insertions(+), 77 deletions(-) create mode 100644 webserv/server/Client.cpp create mode 100644 webserv/server/Client.hpp diff --git a/webserv/server/Client.cpp b/webserv/server/Client.cpp new file mode 100644 index 0000000..eec565c --- /dev/null +++ b/webserv/server/Client.cpp @@ -0,0 +1,22 @@ +#include +#include + +Client::Client(int client_fd, Server &server, const ServerConfig &server_config) + : client_fd(client_fd), server(std::ref(server)), server_config(std::cref(server_config)) +{ +} + +void Client::request(const std::string &req) +{ + // Handle the request (placeholder implementation) + std::cout << "Client received request: " << req << '\n'; + server.get().responseReady(client_fd); +} + +std::string Client::getResponse() const +{ + std::string response = "HTTP/1.1 200 OK\r\nContent-Length: 32\r\n\r\nHello, World!"; + response += " Server port " + std::to_string(server_config.get().getPort()) + "\r\n"; + std::cout << response << '\n'; + return response; +} \ No newline at end of file diff --git a/webserv/server/Client.hpp b/webserv/server/Client.hpp new file mode 100644 index 0000000..66d6414 --- /dev/null +++ b/webserv/server/Client.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include +#include +#include + +class Server; + +class Client +{ + public: + Client(int client_fd, Server &server, const ServerConfig &server_config); + void request(const std::string &req); + std::string getResponse() const; + + + private: + int client_fd; + std::reference_wrapper server; + std::reference_wrapper server_config; +}; \ No newline at end of file diff --git a/webserv/server/Server.cpp b/webserv/server/Server.cpp index c0820df..bdf4a9d 100644 --- a/webserv/server/Server.cpp +++ b/webserv/server/Server.cpp @@ -1,47 +1,36 @@ +#include // For std::find #include // For inet_addr #include // For exit() #include // For memset #include // For fcntl()" #include +#include #include // For sockaddr_in +#include // For std::ranges::find #include #include // For socket functions #include // For close() +#include #include -Server::Server(const ConfigManager &configManager) : epoll_fd(-1) +Server::Server(const ConfigManager &configManager) : _epoll_fd(-1), _configManager(configManager) { 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 createServerSocket(const std::string &host, int port) { - int server_fd = -1; - - - // 1. Create socket (IPv4, TCP) - server_fd = socket(AF_INET, SOCK_STREAM, 0); + int 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) { @@ -50,13 +39,10 @@ void Server::start() 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 + address.sin_addr.s_addr = inet_addr(host.c_str()); + address.sin_port = htons(port); if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) // NOLINT { @@ -65,7 +51,6 @@ void Server::start() exit(EXIT_FAILURE); } - // 4. Listen for incoming connections if (listen(server_fd, SOMAXCONN) < 0) { perror("Listen failed"); @@ -74,32 +59,64 @@ void Server::start() } fcntl(server_fd, F_SETFL, O_NONBLOCK); - std::cout << "Server listening on port " << port << "...\n"; + std::cout << "Server listening on " << host << ":" << port << "...\n"; + return server_fd; +} +void Server::start() +{ + std::cout << "Starting servers...\n"; + // 1. Load server configurations + + for (const auto &config : _configManager.get().getServerConfigs()) + { + int server_fd = createServerSocket(config.getHost(), config.getPort()); + _fdToConfig.insert({server_fd, std::cref(config)}); + } + if (_fdToConfig.empty()) + { + throw std::runtime_error("No server sockets created."); + } // 5. Set up epoll int epoll_fd = epoll_create1(0); if (epoll_fd == -1) { perror("epoll_create1 failed"); - close(server_fd); - // throw exception + // TODO: Close all server FDs + // for (int server_fd : _server_fds) + // { + // close(server_fd); + // } } - 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) + for (const auto &pair : _fdToConfig) { - perror("epoll_ctl failed"); - close(server_fd); - close(epoll_fd); - // throw exception + int server_fd = pair.first; + 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"); + // TODO close fds + close(epoll_fd); + // throw exception + } + // _server_fds.push_back(server_fd); } + _epoll_fd = epoll_fd; + eventLoop(); +} + +void Server::eventLoop() +{ + // Placeholder for the event loop logic + std::cout << "Entering event loop...\n"; 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 + int nfds = epoll_wait(_epoll_fd, events, MAX_EVENTS, -1); // NOLINT if (nfds == -1) { perror("epoll_wait failed"); @@ -107,29 +124,33 @@ void Server::start() } for (int i = 0; i < nfds; ++i) { - epoll_event &event = events[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) + else if (_fdToConfig.contains(event.data.fd)) { - handleConnection(epoll_fd, &event); + 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 - } + handleRequest(_epoll_fd, &event); + } + else if ((event.events & EPOLLOUT) > 0) + { + std::cout << "Attempting to send data to fd: " << event.data.fd << '\n'; + std::string response = clients.at(event.data.fd).getResponse(); + const char *httpResponse = response.c_str(); + ssize_t bytesSent = send(event.data.fd, httpResponse, strlen(httpResponse), 0); + if (bytesSent < 0) + { + perror("Send error"); + } + clients.erase(event.data.fd); + close(event.data.fd); // Close after sending response + } } } } @@ -138,35 +159,43 @@ 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) - { - perror("Read error"); - close(client_fd); - return; - } - if (bytesRead == 0) - { - std::cout << "Client disconnected, fd: " << client_fd << '\n'; - close(client_fd); - return; - } + 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) + { + perror("Read error"); + close(client_fd); + return; + } + 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'; + buffer[bytesRead] = '\0'; // Null-terminate the buffer + std::cout << "Received request:\n" << buffer << '\n'; + // clients[client_fd] = Client(client_fd, *this, ServerConfig); + clients.at(client_fd).request(buffer); +} + +void Server::responseReady(int client_fd) +{ + std::cout << "Response ready for client fd: " << client_fd << '\n'; struct epoll_event ev; ev.events = EPOLLOUT | EPOLLET; ev.data.fd = client_fd; - epoll_ctl(epoll_fd, EPOLL_CTL_MOD, client_fd, &ev); + 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); + std::cout << "connection " << client_fd << '\n'; fcntl(client_fd, F_SETFL, O_NONBLOCK); if (client_fd == -1) { @@ -184,5 +213,7 @@ void Server::handleConnection(int epoll_fd, struct epoll_event *event) close(epoll_fd); // throw exception } - // close(client_fd); // Close the client socket for now + + std::cout << "client fd " << client_fd << ", server fd " << event->data.fd << '\n'; + clients.insert({client_fd, Client(client_fd, *this, _fdToConfig.at(event->data.fd))}); } \ No newline at end of file diff --git a/webserv/server/Server.hpp b/webserv/server/Server.hpp index a758176..8760387 100644 --- a/webserv/server/Server.hpp +++ b/webserv/server/Server.hpp @@ -1,21 +1,30 @@ #pragma once -#include "webserv/config/ConfigManager.hpp" +#include +#include +#include + +class Client; class Server { public: Server(const ConfigManager &configManager); + // The constructor must initialize the reference member 'configManager' + // Implementation should be in the .cpp file using an initializer list + void start(); - void setupServerSocket(ServerConfig &config); + void setupServerSocket(ServerConfig &config); void handleConnection(int epoll_fd, struct epoll_event *event); void handleRequest(int epoll_fd, struct epoll_event *event); + void responseReady(int client_fd); + void eventLoop(); private: - - int epoll_fd; - std::string host; - int port; - + int _epoll_fd; + std::reference_wrapper _configManager; + std::map> _fdToConfig; + std::vector _server_fds; + std::map clients; };