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.
This commit is contained in:
parent
a2db0659c6
commit
8e40f538cf
22
webserv/server/Client.cpp
Normal file
22
webserv/server/Client.cpp
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <webserv/server/Client.hpp>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
21
webserv/server/Client.hpp
Normal file
21
webserv/server/Client.hpp
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <webserv/config/ServerConfig.hpp>
|
||||||
|
#include <webserv/server/Server.hpp>
|
||||||
|
|
||||||
|
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> server;
|
||||||
|
std::reference_wrapper<const ServerConfig> server_config;
|
||||||
|
};
|
||||||
@ -1,47 +1,36 @@
|
|||||||
|
#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 <map>
|
||||||
#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 <webserv/server/Server.hpp>
|
#include <webserv/server/Server.hpp>
|
||||||
|
|
||||||
Server::Server(const ConfigManager &configManager) : epoll_fd(-1)
|
Server::Server(const ConfigManager &configManager) : _epoll_fd(-1), _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.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int createServerSocket(const std::string &host, int port)
|
||||||
void Server::start()
|
|
||||||
{
|
{
|
||||||
int server_fd = -1;
|
int server_fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
|
||||||
|
|
||||||
// 1. Create socket (IPv4, TCP)
|
|
||||||
server_fd = socket(AF_INET, SOCK_STREAM, 0);
|
|
||||||
if (server_fd == -1)
|
if (server_fd == -1)
|
||||||
{
|
{
|
||||||
perror("Socket creation failed");
|
perror("Socket creation failed");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Set socket options (to reuse address and port)
|
|
||||||
int opt = 1;
|
int opt = 1;
|
||||||
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0)
|
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0)
|
||||||
{
|
{
|
||||||
@ -50,13 +39,10 @@ void Server::start()
|
|||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Bind socket to an IP/Port
|
|
||||||
struct sockaddr_in address{};
|
struct sockaddr_in address{};
|
||||||
// std::memset(&address, 0, sizeof(address));
|
|
||||||
|
|
||||||
address.sin_family = AF_INET;
|
address.sin_family = AF_INET;
|
||||||
address.sin_addr.s_addr = INADDR_ANY; // Listen on all interfaces
|
address.sin_addr.s_addr = inet_addr(host.c_str());
|
||||||
address.sin_port = htons(port); // Host-to-network byte order
|
address.sin_port = htons(port);
|
||||||
|
|
||||||
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) // NOLINT
|
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) // NOLINT
|
||||||
{
|
{
|
||||||
@ -65,7 +51,6 @@ void Server::start()
|
|||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. Listen for incoming connections
|
|
||||||
if (listen(server_fd, SOMAXCONN) < 0)
|
if (listen(server_fd, SOMAXCONN) < 0)
|
||||||
{
|
{
|
||||||
perror("Listen failed");
|
perror("Listen failed");
|
||||||
@ -74,32 +59,64 @@ void Server::start()
|
|||||||
}
|
}
|
||||||
|
|
||||||
fcntl(server_fd, F_SETFL, O_NONBLOCK);
|
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
|
// 5. Set up epoll
|
||||||
int epoll_fd = epoll_create1(0);
|
int epoll_fd = epoll_create1(0);
|
||||||
if (epoll_fd == -1)
|
if (epoll_fd == -1)
|
||||||
{
|
{
|
||||||
perror("epoll_create1 failed");
|
perror("epoll_create1 failed");
|
||||||
close(server_fd);
|
// TODO: Close all server FDs
|
||||||
// throw exception
|
// for (int server_fd : _server_fds)
|
||||||
|
// {
|
||||||
|
// close(server_fd);
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
struct epoll_event event;
|
for (const auto &pair : _fdToConfig)
|
||||||
|
{
|
||||||
|
int server_fd = pair.first;
|
||||||
|
struct epoll_event event{};
|
||||||
event.events = EPOLLIN; // Interested in read events
|
event.events = EPOLLIN; // Interested in read events
|
||||||
event.data.fd = server_fd;
|
event.data.fd = server_fd;
|
||||||
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &event) == -1)
|
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &event) == -1)
|
||||||
{
|
{
|
||||||
perror("epoll_ctl failed");
|
perror("epoll_ctl failed");
|
||||||
close(server_fd);
|
// TODO close fds
|
||||||
close(epoll_fd);
|
close(epoll_fd);
|
||||||
// throw exception
|
// 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;
|
const int MAX_EVENTS = 10;
|
||||||
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");
|
perror("epoll_wait failed");
|
||||||
@ -113,21 +130,25 @@ void Server::start()
|
|||||||
std::cerr << "Epoll error on fd " << event.data.fd << '\n';
|
std::cerr << "Epoll error on fd " << event.data.fd << '\n';
|
||||||
close(event.data.fd);
|
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)
|
else if ((event.events & EPOLLIN) > 0)
|
||||||
{
|
{
|
||||||
handleRequest(epoll_fd, &event);
|
handleRequest(_epoll_fd, &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';
|
||||||
const char *httpResponse = "HTTP/1.1 200 OK\r\nContent-Length: 13\r\n\r\nHello, World!";
|
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);
|
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);
|
||||||
close(event.data.fd); // Close after sending response
|
close(event.data.fd); // Close after sending response
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -158,15 +179,23 @@ void Server::handleRequest(int epoll_fd, struct epoll_event *event)
|
|||||||
buffer[bytesRead] = '\0'; // Null-terminate the buffer
|
buffer[bytesRead] = '\0'; // Null-terminate the buffer
|
||||||
std::cout << "Received request:\n" << buffer << '\n';
|
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;
|
struct epoll_event ev;
|
||||||
ev.events = EPOLLOUT | EPOLLET;
|
ev.events = EPOLLOUT | EPOLLET;
|
||||||
ev.data.fd = client_fd;
|
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)
|
void Server::handleConnection(int epoll_fd, struct epoll_event *event)
|
||||||
{
|
{
|
||||||
int client_fd = accept(event->data.fd, nullptr, nullptr);
|
int client_fd = accept(event->data.fd, nullptr, nullptr);
|
||||||
|
std::cout << "connection " << client_fd << '\n';
|
||||||
fcntl(client_fd, F_SETFL, O_NONBLOCK);
|
fcntl(client_fd, F_SETFL, O_NONBLOCK);
|
||||||
if (client_fd == -1)
|
if (client_fd == -1)
|
||||||
{
|
{
|
||||||
@ -184,5 +213,7 @@ void Server::handleConnection(int epoll_fd, struct epoll_event *event)
|
|||||||
close(epoll_fd);
|
close(epoll_fd);
|
||||||
// throw exception
|
// 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))});
|
||||||
}
|
}
|
||||||
@ -1,21 +1,30 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "webserv/config/ConfigManager.hpp"
|
#include <functional>
|
||||||
|
#include <webserv/config/ConfigManager.hpp>
|
||||||
|
#include <webserv/server/Client.hpp>
|
||||||
|
|
||||||
|
class Client;
|
||||||
|
|
||||||
class Server
|
class Server
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Server(const ConfigManager &configManager);
|
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 start();
|
||||||
void setupServerSocket(ServerConfig &config);
|
void setupServerSocket(ServerConfig &config);
|
||||||
void handleConnection(int epoll_fd, struct epoll_event *event);
|
void handleConnection(int epoll_fd, struct epoll_event *event);
|
||||||
void handleRequest(int epoll_fd, struct epoll_event *event);
|
void handleRequest(int epoll_fd, struct epoll_event *event);
|
||||||
|
void responseReady(int client_fd);
|
||||||
|
void eventLoop();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
int _epoll_fd;
|
||||||
int epoll_fd;
|
std::reference_wrapper<const ConfigManager> _configManager;
|
||||||
std::string host;
|
std::map<int, std::reference_wrapper<const ServerConfig>> _fdToConfig;
|
||||||
int port;
|
std::vector<int> _server_fds;
|
||||||
|
std::map<int, Client> clients;
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user