Refactor Client and Server classes to use unique_ptr for socket management, enhance request handling, and improve connection lifecycle management
This commit is contained in:
parent
fc5e59689f
commit
db8b078c27
@ -1,22 +1,44 @@
|
|||||||
|
#include "webserv/socket/Socket.hpp"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <webserv/client/Client.hpp>
|
#include <webserv/client/Client.hpp>
|
||||||
|
|
||||||
Client::Client(int client_fd, Server &server, const ServerConfig &server_config)
|
Client::Client(std::unique_ptr<Socket> socket, Server &server, const ServerConfig &server_config)
|
||||||
: client_fd(client_fd), server(std::ref(server)), server_config(std::cref(server_config))
|
: client_socket_(std::move(socket)), server(std::ref(server)), server_config(std::cref(server_config))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::request(const std::string &req)
|
Client::~Client(){
|
||||||
|
std::cout << "Client destructor called for fd: " << client_socket_->getFd() << '\n';
|
||||||
|
server.removeFromEpoll(*client_socket_);
|
||||||
|
};
|
||||||
|
|
||||||
|
void Client::request()
|
||||||
{
|
{
|
||||||
|
char buffer[1024] = {};
|
||||||
|
ssize_t bytesRead = client_socket_->recv(buffer, sizeof(buffer) - 1);
|
||||||
|
if (bytesRead < 0)
|
||||||
|
{
|
||||||
|
perror("Read error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (bytesRead == 0)
|
||||||
|
{
|
||||||
|
std::cout << "Client disconnected, fd: " << client_socket_->getFd() << '\n';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer[bytesRead] = '\0'; // Null-terminate the buffer
|
||||||
|
std::cout << "Received request:\n" << buffer << '\n';
|
||||||
|
|
||||||
// Handle the request (placeholder implementation)
|
// Handle the request (placeholder implementation)
|
||||||
std::cout << "Client received request: " << req << '\n';
|
server.responseReady(client_socket_->getFd());
|
||||||
server.get().responseReady(client_fd);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::string Client::getResponse() const
|
std::string Client::getResponse() const
|
||||||
{
|
{
|
||||||
std::string response = "HTTP/1.1 200 OK\r\nContent-Length: 32\r\n\r\nHello, World!";
|
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";
|
response += " Server port " + std::to_string(server_config.getPort()) + "\r\n";
|
||||||
std::cout << response << '\n';
|
std::cout << response << '\n';
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <functional>
|
#include "webserv/socket/Socket.hpp"
|
||||||
|
#include <memory>
|
||||||
#include <webserv/config/ServerConfig.hpp>
|
#include <webserv/config/ServerConfig.hpp>
|
||||||
#include <webserv/server/Server.hpp>
|
#include <webserv/server/Server.hpp>
|
||||||
|
|
||||||
@ -9,13 +10,21 @@ class Server;
|
|||||||
class Client
|
class Client
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Client(int client_fd, Server &server, const ServerConfig &server_config);
|
Client(std::unique_ptr<Socket> socket, Server &server, const ServerConfig &server_config);
|
||||||
void request(const std::string &req);
|
|
||||||
std::string getResponse() const;
|
|
||||||
|
|
||||||
|
Client(const Client &other) = delete; // Disable copy constructor
|
||||||
|
Client &operator=(const Client &other) = delete; // Disable copy assignment
|
||||||
|
Client(Client &&other) noexcept = delete; // Move constructor
|
||||||
|
Client &operator=(Client &&other) noexcept = delete; // Move assignment
|
||||||
|
|
||||||
|
~Client();
|
||||||
|
|
||||||
|
|
||||||
|
void request();
|
||||||
|
[[nodiscard]] std::string getResponse() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int client_fd;
|
std::unique_ptr<Socket> client_socket_;
|
||||||
std::reference_wrapper<Server> server;
|
const Server &server;
|
||||||
std::reference_wrapper<const ServerConfig> server_config;
|
const ServerConfig &server_config;
|
||||||
};
|
};
|
||||||
@ -1,112 +1,167 @@
|
|||||||
|
#include "webserv/socket/Socket.hpp"
|
||||||
#include <algorithm> // For std::find
|
#include <algorithm> // For std::find
|
||||||
#include <arpa/inet.h> // For inet_addr
|
#include <arpa/inet.h> // For inet_addr
|
||||||
#include <cstdlib> // For exit()
|
#include <cstdlib> // For exit()
|
||||||
#include <cstring> // For memset
|
#include <cstring> // For memset
|
||||||
#include <fcntl.h> // For fcntl()"
|
#include <fcntl.h> // For fcntl()"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
#include <netinet/in.h> // For sockaddr_in
|
#include <netinet/in.h> // For sockaddr_in
|
||||||
#include <ranges> // For std::ranges::find
|
|
||||||
#include <sys/epoll.h>
|
#include <sys/epoll.h>
|
||||||
#include <sys/socket.h> // For socket functions
|
#include <sys/socket.h> // For socket functions
|
||||||
#include <unistd.h> // For close()
|
#include <unistd.h> // For close()
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <webserv/server/Server.hpp>
|
#include <webserv/server/Server.hpp>
|
||||||
|
|
||||||
Server::Server(const ConfigManager &configManager) : _epoll_fd(-1), _configManager(configManager)
|
Server::Server(const ConfigManager &configManager) : epoll_fd_(epoll_create1(0)), configManager_(configManager)
|
||||||
{
|
{
|
||||||
const auto &serverConfigs = configManager.getServerConfigs();
|
const auto &serverConfigs = configManager.getServerConfigs();
|
||||||
if (serverConfigs.empty())
|
if (serverConfigs.empty())
|
||||||
{
|
{
|
||||||
throw std::runtime_error("No server configurations available.");
|
throw std::runtime_error("No server configurations available.");
|
||||||
}
|
}
|
||||||
|
if (epoll_fd_ == -1)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("epoll_create1 failed");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int createServerSocket(const std::string &host, int port)
|
Server::~Server()
|
||||||
{
|
{
|
||||||
int server_fd = socket(AF_INET, SOCK_STREAM, 0);
|
if (epoll_fd_ != -1)
|
||||||
if (server_fd == -1)
|
|
||||||
{
|
{
|
||||||
perror("Socket creation failed");
|
close(epoll_fd_);
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int opt = 1;
|
|
||||||
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0)
|
|
||||||
{
|
|
||||||
perror("setsockopt");
|
|
||||||
close(server_fd);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct sockaddr_in address{};
|
|
||||||
address.sin_family = AF_INET;
|
|
||||||
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
|
|
||||||
{
|
|
||||||
perror("Bind failed");
|
|
||||||
close(server_fd);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 " << host << ":" << port << "...\n";
|
|
||||||
return server_fd;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server::start()
|
void Server::start()
|
||||||
{
|
{
|
||||||
std::cout << "Starting servers...\n";
|
std::cout << "Starting servers...\n";
|
||||||
// 1. Load server configurations
|
// 1. Load server configurations
|
||||||
|
|
||||||
for (const auto &config : _configManager.get().getServerConfigs())
|
for (const auto &config : configManager_.getServerConfigs())
|
||||||
{
|
{
|
||||||
int server_fd = createServerSocket(config.getHost(), config.getPort());
|
setupServerSocket(config);
|
||||||
_fdToConfig.insert({server_fd, std::cref(config)});
|
|
||||||
}
|
}
|
||||||
if (_fdToConfig.empty())
|
if (fdToConfig_.empty())
|
||||||
{
|
{
|
||||||
throw std::runtime_error("No server sockets created.");
|
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");
|
|
||||||
// TODO: Close all server FDs
|
|
||||||
// for (int server_fd : _server_fds)
|
|
||||||
// {
|
|
||||||
// close(server_fd);
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto &pair : _fdToConfig)
|
|
||||||
{
|
|
||||||
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();
|
eventLoop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Server::addToEpoll(const Socket &socket, uint32_t events) const
|
||||||
|
{
|
||||||
|
int fd = socket.getFd();
|
||||||
|
struct epoll_event event{};
|
||||||
|
event.events = events;
|
||||||
|
event.data.fd = fd;
|
||||||
|
if (epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, fd, &event) == -1)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("epoll_ctl ADD failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Server::removeFromEpoll(const Socket &socket) const
|
||||||
|
{
|
||||||
|
int filedes = socket.getFd();
|
||||||
|
if (epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, filedes, nullptr) == -1)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("epoll_ctl DEL failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Server::setupServerSocket(const ServerConfig &config)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
std::unique_ptr<Socket> serverSocket = std::make_unique<Socket>();
|
||||||
|
serverSocket->bind(config.getHost(), config.getPort());
|
||||||
|
serverSocket->listen(SOMAXCONN);
|
||||||
|
int server_fd = serverSocket->getFd();
|
||||||
|
|
||||||
|
addToEpoll(*serverSocket, EPOLLIN);
|
||||||
|
|
||||||
|
listeners_.push_back(std::move(serverSocket));
|
||||||
|
fdToConfig_.insert({server_fd, std::cref(config)});
|
||||||
|
std::cout << "Server listening on " << config.getHost() << ":" << config.getPort() << "...\n";
|
||||||
|
}
|
||||||
|
catch (const std::exception &e)
|
||||||
|
{
|
||||||
|
std::cerr << "Error setting up server socket: " << e.what() << '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Server::handleConnection(struct epoll_event *event)
|
||||||
|
{
|
||||||
|
Socket &listener = getListener(event->data.fd);
|
||||||
|
std::unique_ptr<Socket> clientSocket = listener.accept();
|
||||||
|
addToEpoll(*clientSocket, EPOLLIN | EPOLLET);
|
||||||
|
clients_.insert(
|
||||||
|
{clientSocket->getFd(), std::make_unique<Client>(std::move(clientSocket), *this, getConfig(listener))});
|
||||||
|
// clients_.insert({clientSocket->getFd(), Client{std::move(clientSocket), *this, getConfig(listener)}});
|
||||||
|
}
|
||||||
|
|
||||||
|
Socket &Server::getListener(int fd) const
|
||||||
|
{
|
||||||
|
for (const auto &listener : listeners_)
|
||||||
|
{
|
||||||
|
if (listener->getFd() == fd)
|
||||||
|
{
|
||||||
|
return *listener;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw std::runtime_error("Listener not found for fd: " + std::to_string(fd));
|
||||||
|
}
|
||||||
|
|
||||||
|
Client &Server::getClient(int fd) const
|
||||||
|
{
|
||||||
|
auto it = clients_.find(fd);
|
||||||
|
if (it != clients_.end())
|
||||||
|
{
|
||||||
|
return *(it->second);
|
||||||
|
}
|
||||||
|
throw std::runtime_error("Client not found for fd: " + std::to_string(fd));
|
||||||
|
}
|
||||||
|
|
||||||
|
const ServerConfig &Server::getConfig(const Socket &socket) const
|
||||||
|
{
|
||||||
|
return getConfig(socket.getFd());
|
||||||
|
}
|
||||||
|
|
||||||
|
const ServerConfig &Server::getConfig(int fd) const
|
||||||
|
{
|
||||||
|
auto it = fdToConfig_.find(fd);
|
||||||
|
if (it != fdToConfig_.end())
|
||||||
|
{
|
||||||
|
return (it->second.get());
|
||||||
|
}
|
||||||
|
throw std::runtime_error("Config not found for fd: " + std::to_string(fd));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Server::handleRequest(struct epoll_event *event)
|
||||||
|
{
|
||||||
|
std::cout << "Handling request...\n";
|
||||||
|
|
||||||
|
int client_fd = event->data.fd;
|
||||||
|
|
||||||
|
Client &client = getClient(client_fd);
|
||||||
|
client.request();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Server::responseReady(int client_fd) const
|
||||||
|
{
|
||||||
|
std::cout << "Response ready for client fd: " << client_fd << '\n';
|
||||||
|
struct epoll_event ev{};
|
||||||
|
ev.events = EPOLLOUT | EPOLLET;
|
||||||
|
ev.data.fd = client_fd;
|
||||||
|
if (epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, client_fd, &ev) == -1)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("epoll_ctl MOD failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Server::eventLoop()
|
void Server::eventLoop()
|
||||||
{
|
{
|
||||||
// Placeholder for the event loop logic
|
// Placeholder for the event loop logic
|
||||||
@ -115,11 +170,10 @@ void Server::eventLoop()
|
|||||||
struct epoll_event events[MAX_EVENTS]; // NOLINT
|
struct epoll_event events[MAX_EVENTS]; // NOLINT
|
||||||
while (true)
|
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)
|
if (nfds == -1)
|
||||||
{
|
{
|
||||||
perror("epoll_wait failed");
|
throw std::runtime_error("epoll_wait failed");
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
for (int i = 0; i < nfds; ++i)
|
for (int i = 0; i < nfds; ++i)
|
||||||
{
|
{
|
||||||
@ -127,92 +181,235 @@ void Server::eventLoop()
|
|||||||
if ((event.events & EPOLLERR) > 0 || (event.events & EPOLLHUP) > 0)
|
if ((event.events & EPOLLERR) > 0 || (event.events & EPOLLHUP) > 0)
|
||||||
{
|
{
|
||||||
std::cerr << "Epoll error on fd " << event.data.fd << '\n';
|
std::cerr << "Epoll error on fd " << event.data.fd << '\n';
|
||||||
|
removeFromEpoll(getListener(event.data.fd));
|
||||||
close(event.data.fd);
|
close(event.data.fd);
|
||||||
}
|
}
|
||||||
else if (_fdToConfig.contains(event.data.fd))
|
else if (fdToConfig_.contains(event.data.fd))
|
||||||
{
|
{
|
||||||
handleConnection(_epoll_fd, &event);
|
handleConnection(&event);
|
||||||
}
|
}
|
||||||
else if ((event.events & EPOLLIN) > 0)
|
else if ((event.events & EPOLLIN) > 0)
|
||||||
{
|
{
|
||||||
handleRequest(_epoll_fd, &event);
|
handleRequest(&event);
|
||||||
}
|
}
|
||||||
else if ((event.events & EPOLLOUT) > 0)
|
else if ((event.events & EPOLLOUT) > 0)
|
||||||
{
|
{
|
||||||
std::cout << "Attempting to send data to fd: " << event.data.fd << '\n';
|
std::cout << "Attempting to send data to fd: " << event.data.fd << '\n';
|
||||||
std::string response = clients.at(event.data.fd).getResponse();
|
Client &client = getClient(event.data.fd);
|
||||||
|
std::string response = client.getResponse();
|
||||||
const char *httpResponse = response.c_str();
|
const char *httpResponse = response.c_str();
|
||||||
ssize_t bytesSent = send(event.data.fd, httpResponse, strlen(httpResponse), 0);
|
ssize_t bytesSent = send(event.data.fd, httpResponse, strlen(httpResponse), 0);
|
||||||
if (bytesSent < 0)
|
if (bytesSent < 0)
|
||||||
{
|
{
|
||||||
perror("Send error");
|
perror("Send error");
|
||||||
}
|
}
|
||||||
clients.erase(event.data.fd);
|
clients_.erase(event.data.fd);
|
||||||
close(event.data.fd); // Close after sending response
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server::handleRequest(int epoll_fd, struct epoll_event *event)
|
// Server::Server(const ConfigManager &configManager) : _epoll_fd(-1), _configManager(configManager)
|
||||||
{
|
// {
|
||||||
std::cout << "Handling request...\n";
|
// const auto &serverConfigs = configManager.getServerConfigs();
|
||||||
|
// if (serverConfigs.empty())
|
||||||
|
// {
|
||||||
|
// throw std::runtime_error("No server configurations available.");
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
int client_fd = event->data.fd;
|
// int createServerSocket(const std::string &host, int port)
|
||||||
std::cout << "client fd: " << client_fd << '\n';
|
// {
|
||||||
char buffer[1024] = {};
|
// int server_fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
ssize_t bytesRead = read(client_fd, buffer, sizeof(buffer) - 1);
|
// if (server_fd == -1)
|
||||||
if (bytesRead < 0)
|
// {
|
||||||
{
|
// perror("Socket creation failed");
|
||||||
perror("Read error");
|
// exit(EXIT_FAILURE);
|
||||||
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
|
// int opt = 1;
|
||||||
std::cout << "Received request:\n" << buffer << '\n';
|
// if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0)
|
||||||
|
// {
|
||||||
|
// perror("setsockopt");
|
||||||
|
// close(server_fd);
|
||||||
|
// exit(EXIT_FAILURE);
|
||||||
|
// }
|
||||||
|
|
||||||
// clients[client_fd] = Client(client_fd, *this, ServerConfig);
|
// struct sockaddr_in address{};
|
||||||
clients.at(client_fd).request(buffer);
|
// address.sin_family = AF_INET;
|
||||||
}
|
// address.sin_addr.s_addr = inet_addr(host.c_str());
|
||||||
|
// address.sin_port = htons(port);
|
||||||
|
|
||||||
void Server::responseReady(int client_fd)
|
// if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) // NOLINT
|
||||||
{
|
// {
|
||||||
std::cout << "Response ready for client fd: " << client_fd << '\n';
|
// perror("Bind failed");
|
||||||
struct epoll_event ev;
|
// close(server_fd);
|
||||||
ev.events = EPOLLOUT | EPOLLET;
|
// exit(EXIT_FAILURE);
|
||||||
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)
|
// if (listen(server_fd, SOMAXCONN) < 0)
|
||||||
{
|
// {
|
||||||
int client_fd = accept(event->data.fd, nullptr, nullptr);
|
// perror("Listen failed");
|
||||||
std::cout << "connection " << client_fd << '\n';
|
// close(server_fd);
|
||||||
fcntl(client_fd, F_SETFL, O_NONBLOCK);
|
// exit(EXIT_FAILURE);
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << "client fd " << client_fd << ", server fd " << event->data.fd << '\n';
|
// fcntl(server_fd, F_SETFL, O_NONBLOCK);
|
||||||
clients.insert({client_fd, Client(client_fd, *this, _fdToConfig.at(event->data.fd))});
|
// 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");
|
||||||
|
// // TODO: Close all server FDs
|
||||||
|
// // for (int server_fd : _server_fds)
|
||||||
|
// // {
|
||||||
|
// // close(server_fd);
|
||||||
|
// // }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// for (const auto &pair : _fdToConfig)
|
||||||
|
// {
|
||||||
|
// 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
|
||||||
|
// 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);
|
||||||
|
// }
|
||||||
|
// else if (_fdToConfig.contains(event.data.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';
|
||||||
|
// 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
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
// {
|
||||||
|
// 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
|
||||||
|
// }
|
||||||
|
|
||||||
|
// 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))});
|
||||||
|
// }
|
||||||
@ -1,30 +1,49 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "webserv/config/ServerConfig.hpp"
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <webserv/config/ConfigManager.hpp>
|
#include <memory>
|
||||||
|
#include <unordered_map>
|
||||||
#include <webserv/client/Client.hpp>
|
#include <webserv/client/Client.hpp>
|
||||||
|
#include <webserv/config/ConfigManager.hpp>
|
||||||
|
#include <webserv/socket/Socket.hpp>
|
||||||
|
|
||||||
class Client;
|
class Client;
|
||||||
|
|
||||||
class Server
|
class Server
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
Server() = delete;
|
||||||
Server(const ConfigManager &configManager);
|
Server(const ConfigManager &configManager);
|
||||||
|
|
||||||
|
Server(const Server &other) = delete; // Disable copy constructor
|
||||||
|
Server &operator=(const Server &other) = delete; // Disable copy assignment
|
||||||
|
Server(Server &&other) noexcept = delete; // Move constructor
|
||||||
|
Server &operator=(Server &&other) noexcept = delete; // Move assignment
|
||||||
|
|
||||||
// The constructor must initialize the reference member 'configManager'
|
// The constructor must initialize the reference member 'configManager'
|
||||||
// Implementation should be in the .cpp file using an initializer list
|
// Implementation should be in the .cpp file using an initializer list
|
||||||
|
|
||||||
|
~Server();
|
||||||
|
|
||||||
void start();
|
void start();
|
||||||
void setupServerSocket(ServerConfig &config);
|
void addToEpoll(const Socket &socket, uint32_t events) const;
|
||||||
void handleConnection(int epoll_fd, struct epoll_event *event);
|
void removeFromEpoll(const Socket &socket) const ;
|
||||||
void handleRequest(int epoll_fd, struct epoll_event *event);
|
void setupServerSocket(const ServerConfig &config);
|
||||||
void responseReady(int client_fd);
|
void handleConnection(struct epoll_event *event);
|
||||||
|
void handleRequest(struct epoll_event *event);
|
||||||
|
void responseReady(int client_fd) const;
|
||||||
void eventLoop();
|
void eventLoop();
|
||||||
|
Socket &getListener(int fd) const;
|
||||||
|
Client &getClient(int fd) const;
|
||||||
|
const ServerConfig &getConfig(int fd) const;
|
||||||
|
const ServerConfig &getConfig(const Socket &socket) const;
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int _epoll_fd;
|
int epoll_fd_;
|
||||||
std::reference_wrapper<const ConfigManager> _configManager;
|
const ConfigManager &configManager_;
|
||||||
std::map<int, std::reference_wrapper<const ServerConfig>> _fdToConfig;
|
std::vector<std::unique_ptr<Socket>> listeners_;
|
||||||
std::vector<int> _server_fds;
|
std::unordered_map<int, std::reference_wrapper<const ServerConfig>> fdToConfig_;
|
||||||
std::map<int, Client> clients;
|
std::unordered_map<int, std::unique_ptr<Client>> clients_;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
#include <memory>
|
||||||
#include <webserv/socket/Socket.hpp>
|
#include <webserv/socket/Socket.hpp>
|
||||||
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
@ -21,6 +22,7 @@ Socket::Socket() : _fd(socket(AF_INET, SOCK_STREAM, 0))
|
|||||||
_fd = -1;
|
_fd = -1;
|
||||||
throw std::runtime_error("setsockopt failed");
|
throw std::runtime_error("setsockopt failed");
|
||||||
}
|
}
|
||||||
|
setNonBlocking();
|
||||||
}
|
}
|
||||||
|
|
||||||
Socket::Socket(int fd) : _fd(fd) // NOLINT readability-identifier-naming
|
Socket::Socket(int fd) : _fd(fd) // NOLINT readability-identifier-naming
|
||||||
@ -29,6 +31,7 @@ Socket::Socket(int fd) : _fd(fd) // NOLINT readability-identifier-naming
|
|||||||
{
|
{
|
||||||
throw std::runtime_error("Invalid file descriptor");
|
throw std::runtime_error("Invalid file descriptor");
|
||||||
}
|
}
|
||||||
|
setNonBlocking();
|
||||||
}
|
}
|
||||||
|
|
||||||
Socket::~Socket()
|
Socket::~Socket()
|
||||||
@ -54,20 +57,20 @@ void Socket::bind(const std::string &host, const int port) const
|
|||||||
address.sin_addr.s_addr = inet_addr(host.c_str());
|
address.sin_addr.s_addr = inet_addr(host.c_str());
|
||||||
address.sin_port = htons(port);
|
address.sin_port = htons(port);
|
||||||
|
|
||||||
if (::bind(_fd, (struct sockaddr *)&address, sizeof(address)) < 0) // NOLINT cppcoreguidelines-pro-type-cstyle-cast
|
if (::bind(_fd, (struct sockaddr *)&address, sizeof(address)) < 0) // NOLINT(cppcoreguidelines-pro-type-cstyle-cast
|
||||||
{
|
{
|
||||||
throw std::runtime_error("Bind failed");
|
throw std::runtime_error("Bind failed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Socket Socket::accept() const
|
std::unique_ptr<Socket> Socket::accept() const
|
||||||
{
|
{
|
||||||
int client_fd = ::accept(_fd, nullptr, nullptr);
|
int client_fd = ::accept(_fd, nullptr, nullptr);
|
||||||
if (client_fd < 0)
|
if (client_fd < 0)
|
||||||
{
|
{
|
||||||
throw std::runtime_error("Accept failed");
|
throw std::runtime_error("Accept failed");
|
||||||
}
|
}
|
||||||
return {client_fd};
|
return std::make_unique<Socket>(client_fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t Socket::recv(void *buf, size_t len) const
|
ssize_t Socket::recv(void *buf, size_t len) const
|
||||||
@ -87,3 +90,8 @@ void Socket::setNonBlocking() const
|
|||||||
throw std::runtime_error("Failed to set non-blocking mode");
|
throw std::runtime_error("Failed to set non-blocking mode");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Socket::getFd() const
|
||||||
|
{
|
||||||
|
return _fd;
|
||||||
|
}
|
||||||
@ -1,7 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
class Socket
|
class Socket
|
||||||
{
|
{
|
||||||
@ -11,14 +12,14 @@ class Socket
|
|||||||
|
|
||||||
Socket(const Socket &other) = delete; // Disable copy constructor
|
Socket(const Socket &other) = delete; // Disable copy constructor
|
||||||
Socket &operator=(const Socket &other) = delete; // Disable copy assignment
|
Socket &operator=(const Socket &other) = delete; // Disable copy assignment
|
||||||
Socket(Socket &&other) noexcept; // Move constructor
|
Socket(Socket &&other) noexcept = default; // Move constructor
|
||||||
Socket &operator=(Socket &&other) noexcept; // Move assignment
|
Socket &operator=(Socket &&other) noexcept = default; // Move assignment
|
||||||
|
|
||||||
~Socket();
|
~Socket();
|
||||||
|
|
||||||
void listen(int backlog) const;
|
void listen(int backlog) const;
|
||||||
void bind(const std::string &host, const int port) const;
|
void bind(const std::string &host, const int port) const;
|
||||||
[[nodiscard]] Socket accept() const;
|
[[nodiscard]] std::unique_ptr<Socket> accept() const;
|
||||||
ssize_t recv(void *buf, size_t len) const;
|
ssize_t recv(void *buf, size_t len) const;
|
||||||
ssize_t send(const void *buf, size_t len) const;
|
ssize_t send(const void *buf, size_t len) const;
|
||||||
void setNonBlocking() const;
|
void setNonBlocking() const;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user