refactor: implement socket structure and server refactor
This commit is contained in:
parent
970ab847fc
commit
f3bdf28eed
@ -1,4 +1,4 @@
|
||||
#include <webserv/socket/Socket.hpp>
|
||||
#include <webserv/socket/ServerSocket.hpp>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
@ -23,7 +23,7 @@ class SocketTest : public ::testing::Test
|
||||
|
||||
TEST_F(SocketTest, DefaultConstructor)
|
||||
{
|
||||
Socket socket;
|
||||
ServerSocket socket;
|
||||
// Socket should be created successfully
|
||||
// We can't test much without actually creating network resources
|
||||
SUCCEED();
|
||||
@ -32,7 +32,7 @@ TEST_F(SocketTest, DefaultConstructor)
|
||||
TEST_F(SocketTest, ConstructorWithFd)
|
||||
{
|
||||
// Socket constructor with invalid fd throws an exception
|
||||
EXPECT_THROW(Socket socket(-1), std::runtime_error);
|
||||
EXPECT_THROW(ServerSocket socket(-1), std::runtime_error);
|
||||
}
|
||||
|
||||
TEST_F(SocketTest, MoveConstructor)
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
#include <webserv/log/Log.hpp> // for Log, LOCATION
|
||||
#include <webserv/router/Router.hpp> // for Router
|
||||
#include <webserv/server/Server.hpp> // for Server
|
||||
#include <webserv/socket/Socket.hpp> // for Socket
|
||||
#include <webserv/socket/ClientSocket.hpp> // for Socket
|
||||
|
||||
#include <cstdint> // for uint8_t
|
||||
#include <functional> // for ref, reference_wrapper
|
||||
@ -13,7 +13,7 @@
|
||||
|
||||
#include <sys/types.h> // for ssize_t
|
||||
|
||||
Client::Client(std::unique_ptr<Socket> socket, Server &server)
|
||||
Client::Client(std::unique_ptr<ClientSocket> socket, Server &server)
|
||||
: httpRequest_(std::make_unique<HttpRequest>(this)), httpResponse_(std::make_unique<HttpResponse>()),
|
||||
client_socket_(std::move(socket)), server_(std::ref(server))
|
||||
{
|
||||
@ -44,7 +44,7 @@ void Client::request()
|
||||
{
|
||||
Log::trace(LOCATION);
|
||||
char buffer[bufferSize_] = {}; // NOLINT(cppcoreguidelines-avoid-c-arrays)
|
||||
ssize_t bytesRead = client_socket_->recv(
|
||||
ssize_t bytesRead = client_socket_->read(
|
||||
buffer, sizeof(buffer) - 1); // NOLINT(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
|
||||
if (bytesRead < 0)
|
||||
{
|
||||
|
||||
@ -2,12 +2,14 @@
|
||||
|
||||
// #include <webserv/http/HttpResponse.hpp>
|
||||
|
||||
#include "webserv/socket/ClientSocket.hpp"
|
||||
|
||||
#include <webserv/config/ServerConfig.hpp> // for ServerConfig
|
||||
#include <webserv/http/HttpConstants.hpp> // for OK
|
||||
#include <webserv/http/HttpRequest.hpp> // for HttpRequest
|
||||
#include <webserv/http/HttpResponse.hpp> // for HttpResponse
|
||||
#include <webserv/server/Server.hpp>
|
||||
#include <webserv/socket/Socket.hpp> // for Socket
|
||||
#include <webserv/socket/ClientSocket.hpp> // for Socket
|
||||
|
||||
#include <cstddef> // for size_t
|
||||
#include <cstdint> // for uint8_t
|
||||
@ -15,14 +17,14 @@
|
||||
#include <vector> // for vector
|
||||
|
||||
class Server;
|
||||
class Socket;
|
||||
class ClientSocket;
|
||||
class ServerConfig;
|
||||
class HttpResponse;
|
||||
|
||||
class Client
|
||||
{
|
||||
public:
|
||||
Client(std::unique_ptr<Socket> socket, Server &server);
|
||||
Client(std::unique_ptr<ClientSocket> socket, Server &server);
|
||||
|
||||
Client(const Client &other) = delete; // Disable copy constructor
|
||||
Client &operator=(const Client &other) = delete; // Disable copy assignment
|
||||
@ -37,7 +39,7 @@ class Client
|
||||
[[nodiscard]] bool isResponseReady() const;
|
||||
[[nodiscard]] int getStatusCode() const;
|
||||
|
||||
[[nodiscard]] Socket &getSocket() const { return *client_socket_; }
|
||||
[[nodiscard]] ClientSocket &getSocket() const { return *client_socket_; }
|
||||
|
||||
// void setError(int statusCode);
|
||||
|
||||
@ -48,7 +50,7 @@ class Client
|
||||
constexpr static size_t bufferSize_ = 4096;
|
||||
std::unique_ptr<HttpRequest> httpRequest_ = nullptr;
|
||||
std::unique_ptr<HttpResponse> httpResponse_ = nullptr;
|
||||
std::unique_ptr<Socket> client_socket_;
|
||||
std::unique_ptr<ClientSocket> client_socket_;
|
||||
Server &server_;
|
||||
// mutable const ServerConfig *server_config_ = nullptr;
|
||||
};
|
||||
@ -49,7 +49,7 @@ class Log
|
||||
|
||||
void log(Level level, const std::string &message, const std::map<std::string, std::string> &context);
|
||||
|
||||
static constexpr Log::Level COMPILE_TIME_LOG_LEVEL = Log::Level::Info;
|
||||
static constexpr Log::Level COMPILE_TIME_LOG_LEVEL = Log::Level::Trace;
|
||||
|
||||
static void setFileChannel(const std::string &filename, std::ios_base::openmode mode = std::ios_base::app);
|
||||
static void setStdoutChannel();
|
||||
|
||||
@ -6,7 +6,6 @@
|
||||
|
||||
#include <iostream> // for ios_base
|
||||
#include <string> // for allocator, basic_string, char_traits, operator+, string
|
||||
#include <vector> // for vector
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
@ -37,6 +36,6 @@ int main(int argc, char **argv)
|
||||
Log::debug("ConfigManager initialized successfully.");
|
||||
Server server(configManager);
|
||||
|
||||
server.start();
|
||||
server.run();
|
||||
return 0;
|
||||
}
|
||||
@ -1,9 +1,12 @@
|
||||
#include "webserv/socket/ASocket.hpp"
|
||||
|
||||
#include <webserv/client/Client.hpp> // for Client
|
||||
#include <webserv/config/ConfigManager.hpp> // for ConfigManager
|
||||
#include <webserv/config/ServerConfig.hpp> // for ServerConfig
|
||||
#include <webserv/log/Log.hpp> // for Log, LOCATION
|
||||
#include <webserv/server/Server.hpp>
|
||||
#include <webserv/socket/Socket.hpp> // for Socket
|
||||
#include <webserv/socket/ClientSocket.hpp> // for ClientSocket
|
||||
#include <webserv/socket/ServerSocket.hpp> // for ServerSocket
|
||||
|
||||
#include <cerrno> // for errno
|
||||
#include <cstring> // for strerror
|
||||
@ -38,6 +41,15 @@ Server::Server(const ConfigManager &configManager)
|
||||
Log::fatal("epoll_create1 failed");
|
||||
throw std::runtime_error("epoll_create1 failed");
|
||||
}
|
||||
for (const auto &config : configManager_.getServerConfigs())
|
||||
{
|
||||
setupServerSocket(*config);
|
||||
}
|
||||
if (listener_fds_.empty())
|
||||
{
|
||||
Log::fatal("No server sockets created.");
|
||||
throw std::runtime_error("No server sockets created.");
|
||||
}
|
||||
}
|
||||
|
||||
Server::~Server()
|
||||
@ -49,27 +61,13 @@ Server::~Server()
|
||||
}
|
||||
}
|
||||
|
||||
void Server::start()
|
||||
void Server::add(const ASocket &socket, uint32_t events, Client *client)
|
||||
{
|
||||
Log::trace(LOCATION);
|
||||
Log::info("Starting servers...");
|
||||
// 1. Load server configurations
|
||||
|
||||
for (const auto &config : configManager_.getServerConfigs())
|
||||
if (socket.getType() != ASocket::Type::SERVER_SOCKET && client == nullptr)
|
||||
{
|
||||
setupServerSocket(*config);
|
||||
Log::error("Client pointer must be provided for non-server sockets");
|
||||
throw std::invalid_argument("Client pointer must be provided for non-server sockets");
|
||||
}
|
||||
if (listener_fds_.empty())
|
||||
{
|
||||
Log::fatal("No server sockets created.");
|
||||
throw std::runtime_error("No server sockets created.");
|
||||
}
|
||||
|
||||
eventLoop();
|
||||
}
|
||||
|
||||
void Server::add(const Socket &socket, uint32_t events) const
|
||||
{
|
||||
Log::trace(LOCATION);
|
||||
int fd = socket.getFd();
|
||||
struct epoll_event event{};
|
||||
@ -80,24 +78,27 @@ void Server::add(const Socket &socket, uint32_t events) const
|
||||
Log::error("epoll_ctl ADD failed for fd: " + std::to_string(fd));
|
||||
throw std::runtime_error("epoll_ctl ADD failed");
|
||||
}
|
||||
socketToClient_[fd] = client;
|
||||
}
|
||||
|
||||
void Server::remove(const ASocket &socket)
|
||||
{
|
||||
Log::trace(LOCATION);
|
||||
int fd = socket.getFd();
|
||||
if (epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, fd, nullptr) == -1)
|
||||
{
|
||||
Log::error("epoll_ctl DEL failed for fd: " + std::to_string(fd));
|
||||
throw std::runtime_error("epoll_ctl DEL failed");
|
||||
}
|
||||
socketToClient_.erase(fd);
|
||||
}
|
||||
|
||||
void Server::disconnect(const Client &client)
|
||||
{
|
||||
Log::trace(LOCATION);
|
||||
int client_fd = client.getSocket().getFd();
|
||||
clients_.erase(client_fd);
|
||||
}
|
||||
|
||||
void Server::remove(const Socket &socket) const
|
||||
{
|
||||
Log::trace(LOCATION);
|
||||
int filedes = socket.getFd();
|
||||
if (epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, filedes, nullptr) == -1)
|
||||
{
|
||||
Log::error("epoll_ctl DEL failed for fd: " + std::to_string(filedes));
|
||||
throw std::runtime_error("epoll_ctl DEL failed");
|
||||
}
|
||||
std::erase_if(clients_, [&](const std::unique_ptr<Client> &c) { return c->getSocket().getFd() == client_fd; });
|
||||
}
|
||||
|
||||
void Server::setupServerSocket(const ServerConfig &config)
|
||||
@ -106,9 +107,8 @@ void Server::setupServerSocket(const ServerConfig &config)
|
||||
try
|
||||
{
|
||||
auto host = config.get<std::string>("host").value_or(std::string()); // TODO should not be a default host
|
||||
|
||||
auto port = config.get<int>("listen").value_or(0); // TODO should not be a default port
|
||||
std::unique_ptr<Socket> serverSocket = std::make_unique<Socket>();
|
||||
std::unique_ptr<ServerSocket> serverSocket = std::make_unique<ServerSocket>();
|
||||
serverSocket->bind(host, port);
|
||||
serverSocket->listen(SOMAXCONN);
|
||||
int server_fd = serverSocket->getFd();
|
||||
@ -118,7 +118,6 @@ void Server::setupServerSocket(const ServerConfig &config)
|
||||
listeners_.push_back(std::move(serverSocket));
|
||||
listener_fds_.insert(server_fd);
|
||||
Log::info("Server listening on " + host + ":" + std::to_string(port) + "...");
|
||||
// static_cast<std::string>(config["listen"]) + "...");
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
@ -129,13 +128,15 @@ void Server::setupServerSocket(const ServerConfig &config)
|
||||
void Server::handleConnection(struct epoll_event *event)
|
||||
{
|
||||
Log::trace(LOCATION);
|
||||
Socket &listener = getListener(event->data.fd);
|
||||
std::unique_ptr<Socket> clientSocket = listener.accept();
|
||||
add(*clientSocket, EPOLLIN);
|
||||
clients_.insert({clientSocket->getFd(), std::make_unique<Client>(std::move(clientSocket), *this)});
|
||||
ServerSocket &listener = getListener(event->data.fd);
|
||||
std::unique_ptr<ClientSocket> clientSocket = listener.accept();
|
||||
|
||||
auto client = std::make_unique<Client>(std::move(clientSocket), *this);
|
||||
add(client->getSocket(), EPOLLIN, client.get());
|
||||
clients_.emplace_back(std::move(client));
|
||||
}
|
||||
|
||||
Socket &Server::getListener(int fd) const
|
||||
ServerSocket &Server::getListener(int fd) const
|
||||
{
|
||||
Log::trace(LOCATION);
|
||||
for (const auto &listener : listeners_)
|
||||
@ -152,10 +153,9 @@ Socket &Server::getListener(int fd) const
|
||||
Client &Server::getClient(int fd) const
|
||||
{
|
||||
Log::trace(LOCATION);
|
||||
auto it = clients_.find(fd);
|
||||
if (it != clients_.end())
|
||||
if (socketToClient_.contains(fd))
|
||||
{
|
||||
return *(it->second);
|
||||
return *(socketToClient_.at(fd));
|
||||
}
|
||||
Log::error("Client not found for fd: " + std::to_string(fd));
|
||||
throw std::runtime_error("Client not found for fd: " + std::to_string(fd));
|
||||
@ -198,7 +198,7 @@ void Server::handleResponse(struct epoll_event *event)
|
||||
{
|
||||
Log::debug("Sent " + std::to_string(bytesSent) + " bytes to fd: " + std::to_string(event->data.fd));
|
||||
}
|
||||
clients_.erase(event->data.fd);
|
||||
disconnect(client);
|
||||
}
|
||||
|
||||
void Server::handleEvent(struct epoll_event *event)
|
||||
@ -224,7 +224,7 @@ void Server::handleEvent(struct epoll_event *event)
|
||||
}
|
||||
}
|
||||
|
||||
void Server::eventLoop()
|
||||
void Server::run()
|
||||
{
|
||||
Log::trace(LOCATION);
|
||||
Log::info("Listening...");
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "webserv/socket/ASocket.hpp"
|
||||
|
||||
#include <webserv/client/Client.hpp>
|
||||
#include <webserv/config/ConfigManager.hpp>
|
||||
#include <webserv/config/ServerConfig.hpp> // for ServerConfig
|
||||
#include <webserv/router/Router.hpp> // for Router
|
||||
#include <webserv/socket/Socket.hpp> // for Socket
|
||||
#include <webserv/socket/ServerSocket.hpp> // for ServerSocket
|
||||
|
||||
#include <cstdint> // for uint32_t
|
||||
#include <memory> // for unique_ptr
|
||||
@ -32,13 +34,13 @@ class Server
|
||||
|
||||
~Server();
|
||||
|
||||
void start();
|
||||
void add(const Socket &socket, uint32_t events) const;
|
||||
void remove(const Socket &socket) const;
|
||||
void run();
|
||||
void add(const ASocket &socket, uint32_t events, Client *client = nullptr);
|
||||
void remove(const ASocket &socket);
|
||||
void disconnect(const Client &client);
|
||||
void responseReady(int client_fd) const;
|
||||
|
||||
Socket &getListener(int fd) const;
|
||||
ServerSocket &getListener(int fd) const;
|
||||
Client &getClient(int fd) const;
|
||||
const Router &getRouter() const;
|
||||
|
||||
@ -46,9 +48,11 @@ class Server
|
||||
int epoll_fd_;
|
||||
const ConfigManager &configManager_;
|
||||
const Router router_;
|
||||
std::vector<std::unique_ptr<Socket>> listeners_;
|
||||
std::vector<std::unique_ptr<ServerSocket>> listeners_;
|
||||
std::set<int> listener_fds_;
|
||||
std::unordered_map<int, std::unique_ptr<Client>> clients_;
|
||||
// std::unordered_map<int, std::unique_ptr<Client>> clients_;
|
||||
std::vector<std::unique_ptr<Client>> clients_;
|
||||
std::unordered_map<int, Client *> socketToClient_;
|
||||
|
||||
void handleEvent(struct epoll_event *event);
|
||||
void handleConnection(struct epoll_event *event);
|
||||
@ -56,5 +60,4 @@ class Server
|
||||
void handleResponse(struct epoll_event *event);
|
||||
|
||||
void setupServerSocket(const ServerConfig &config);
|
||||
void eventLoop();
|
||||
};
|
||||
};
|
||||
|
||||
66
webserv/socket/ASocket.cpp
Normal file
66
webserv/socket/ASocket.cpp
Normal file
@ -0,0 +1,66 @@
|
||||
#include <webserv/log/Log.hpp> // for Log, LOCATION
|
||||
#include <webserv/socket/ASocket.hpp>
|
||||
|
||||
#include <fcntl.h> // For fcntl()
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h> // For close()
|
||||
|
||||
ASocket::ASocket(int fd) : fd_(fd)
|
||||
{
|
||||
Log::trace(LOCATION);
|
||||
if (fd_ == -1)
|
||||
{
|
||||
Log::error("Invalid file descriptor");
|
||||
throw std::runtime_error("Invalid file descriptor");
|
||||
}
|
||||
setNonBlocking();
|
||||
}
|
||||
|
||||
ASocket::~ASocket()
|
||||
{
|
||||
Log::trace(LOCATION);
|
||||
if (fd_ != -1)
|
||||
{
|
||||
close(fd_);
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t ASocket::read(void *buf, size_t len) const
|
||||
{
|
||||
ssize_t bytesRead = ::recv(fd_, buf, len, 0);
|
||||
if (bytesRead == -1)
|
||||
{
|
||||
throw std::system_error(errno, std::generic_category(), "Socket: Read error");
|
||||
}
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
ssize_t ASocket::write(const void *buf, size_t len) const
|
||||
{
|
||||
Log::trace(LOCATION);
|
||||
ssize_t bytesSent = ::send(fd_, buf, len, 0);
|
||||
if (bytesSent == -1)
|
||||
{
|
||||
throw std::system_error(errno, std::generic_category(), "Socket: Write error");
|
||||
}
|
||||
return bytesSent;
|
||||
}
|
||||
|
||||
void ASocket::setNonBlocking() const
|
||||
{
|
||||
if (fcntl(fd_, F_SETFL, O_NONBLOCK) == -1)
|
||||
{
|
||||
throw std::system_error(errno, std::generic_category(), "ASocket: Failed to set FD non-blocking");
|
||||
}
|
||||
}
|
||||
|
||||
int ASocket::getFd() const
|
||||
{
|
||||
Log::trace(LOCATION);
|
||||
return fd_;
|
||||
}
|
||||
|
||||
void ASocket::setFd(int fd)
|
||||
{
|
||||
fd_ = fd;
|
||||
}
|
||||
40
webserv/socket/ASocket.hpp
Normal file
40
webserv/socket/ASocket.hpp
Normal file
@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef> // for size_t
|
||||
#include <cstdint>
|
||||
|
||||
#include <sys/types.h> // for ssize_t
|
||||
|
||||
class ASocket
|
||||
{
|
||||
public:
|
||||
enum class Type : uint8_t
|
||||
{
|
||||
CLIENT_SOCKET,
|
||||
SERVER_SOCKET,
|
||||
CGI_SOCKET
|
||||
};
|
||||
|
||||
ASocket() = delete;
|
||||
explicit ASocket(int fd);
|
||||
|
||||
ASocket(const ASocket &other) = delete;
|
||||
ASocket &operator=(const ASocket &other) = delete;
|
||||
ASocket(ASocket &&other) noexcept = default;
|
||||
ASocket &operator=(ASocket &&other) noexcept = default;
|
||||
|
||||
virtual ~ASocket();
|
||||
|
||||
[[nodiscard]] virtual Type getType() const = 0;
|
||||
[[nodiscard]] int getFd() const;
|
||||
|
||||
ssize_t read(void *buf, size_t len) const;
|
||||
ssize_t write(const void *buf, size_t len) const;
|
||||
|
||||
protected:
|
||||
void setNonBlocking() const;
|
||||
void setFd(int fd);
|
||||
|
||||
private:
|
||||
int fd_;
|
||||
};
|
||||
@ -1,74 +1,12 @@
|
||||
#include <webserv/log/Log.hpp>
|
||||
#include <webserv/socket/CGISocket.hpp>
|
||||
|
||||
#include <cstring> // for strerror
|
||||
#include <stdexcept> // for runtime_error
|
||||
#include <string> // for string, operator+
|
||||
#include <system_error> // for system_error, error_code
|
||||
|
||||
#include <fcntl.h> // for fcntl, O_NONBLOCK
|
||||
#include <unistd.h> // for close, read, write
|
||||
|
||||
CGISocket::CGISocket(int readFd, int writeFd) : _readFd(readFd), _writeFd(writeFd)
|
||||
CGISocket::CGISocket(int fd) : ASocket(fd)
|
||||
{
|
||||
if (_readFd == -1 || _writeFd == -1)
|
||||
{
|
||||
throw std::runtime_error("CGISocket: Invalid file descriptors");
|
||||
}
|
||||
setNonBlocking();
|
||||
Log::trace(LOCATION);
|
||||
}
|
||||
|
||||
CGISocket::~CGISocket()
|
||||
ASocket::Type CGISocket::getType() const
|
||||
{
|
||||
if (_readFd != -1)
|
||||
{
|
||||
close(_readFd);
|
||||
}
|
||||
if (_writeFd != -1)
|
||||
{
|
||||
close(_writeFd);
|
||||
}
|
||||
|
||||
return ASocket::Type::CGI_SOCKET;
|
||||
}
|
||||
|
||||
int CGISocket::getReadFd() const
|
||||
{
|
||||
return _readFd;
|
||||
}
|
||||
|
||||
int CGISocket::getWriteFd() const
|
||||
{
|
||||
return _writeFd;
|
||||
}
|
||||
|
||||
ssize_t CGISocket::read(void *buffer, size_t size) const
|
||||
{
|
||||
ssize_t bytesRead = ::read(_readFd, buffer, size);
|
||||
if (bytesRead == -1)
|
||||
{
|
||||
throw std::system_error(errno, std::generic_category(), "CGISocket: Read error");
|
||||
}
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
ssize_t CGISocket::write(const void *buffer, size_t size) const
|
||||
{
|
||||
ssize_t bytesWritten = ::write(_writeFd, buffer, size);
|
||||
if (bytesWritten == -1)
|
||||
{
|
||||
throw std::system_error(errno, std::generic_category(), "CGISocket: Write error");
|
||||
}
|
||||
return bytesWritten;
|
||||
}
|
||||
|
||||
void CGISocket::setNonBlocking() const
|
||||
{
|
||||
if (fcntl(_readFd, F_SETFL, O_NONBLOCK) == -1)
|
||||
{
|
||||
throw std::system_error(errno, std::generic_category(), "CGISocket: Failed to set read FD non-blocking");
|
||||
}
|
||||
if (fcntl(_writeFd, F_SETFL, O_NONBLOCK) == -1)
|
||||
{
|
||||
throw std::system_error(errno, std::generic_category(), "CGISocket: Failed to set write FD non-blocking");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,32 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <webserv/socket/ASocket.hpp>
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
class CGISocket
|
||||
class CGISocket : public ASocket
|
||||
{
|
||||
public:
|
||||
CGISocket(int readFd, int writeFd);
|
||||
|
||||
CGISocket(const CGISocket &) = delete;
|
||||
CGISocket &operator=(const CGISocket &) = delete;
|
||||
CGISocket(CGISocket &&other) noexcept = default;
|
||||
CGISocket &operator=(CGISocket &&other) noexcept = default;
|
||||
|
||||
~CGISocket();
|
||||
|
||||
// Get FDs for server epoll registration
|
||||
[[nodiscard]] int getReadFd() const; // Server reads CGI output
|
||||
[[nodiscard]] int getWriteFd() const; // Server writes to CGI input
|
||||
|
||||
// Handler interface
|
||||
ssize_t read(void *buffer, size_t size) const;
|
||||
ssize_t write(const void *buffer, size_t size) const;
|
||||
void setNonBlocking() const;
|
||||
|
||||
private:
|
||||
int _readFd = -1; // Server side of output pipe
|
||||
int _writeFd = -1; // Server side of input pipe
|
||||
[[nodiscard]] ASocket::Type getType() const override;
|
||||
|
||||
explicit CGISocket(int fd);
|
||||
};
|
||||
13
webserv/socket/ClientSocket.cpp
Normal file
13
webserv/socket/ClientSocket.cpp
Normal file
@ -0,0 +1,13 @@
|
||||
#include <webserv/log/Log.hpp>
|
||||
#include <webserv/socket/ClientSocket.hpp>
|
||||
|
||||
ClientSocket::ClientSocket(int fd) : ASocket(fd)
|
||||
{
|
||||
Log::trace(LOCATION);
|
||||
}
|
||||
|
||||
ASocket::Type ClientSocket::getType() const
|
||||
{
|
||||
Log::trace(LOCATION);
|
||||
return ASocket::Type::CLIENT_SOCKET;
|
||||
}
|
||||
15
webserv/socket/ClientSocket.hpp
Normal file
15
webserv/socket/ClientSocket.hpp
Normal file
@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <webserv/socket/ASocket.hpp>
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
class ClientSocket : public ASocket
|
||||
{
|
||||
public:
|
||||
[[nodiscard]] ASocket::Type getType() const override;
|
||||
|
||||
explicit ClientSocket(int fd);
|
||||
};
|
||||
83
webserv/socket/ServerSocket.cpp
Normal file
83
webserv/socket/ServerSocket.cpp
Normal file
@ -0,0 +1,83 @@
|
||||
#include "webserv/socket/ASocket.hpp"
|
||||
#include "webserv/socket/ClientSocket.hpp"
|
||||
|
||||
#include <webserv/log/Log.hpp>
|
||||
#include <webserv/socket/ServerSocket.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <arpa/inet.h> // For inet_addr
|
||||
#include <fcntl.h> // For fcntl()"
|
||||
#include <netinet/in.h> // For sockaddr_in
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h> // For close()
|
||||
|
||||
ServerSocket::ServerSocket() : ASocket(socket(AF_INET, SOCK_STREAM, 0))
|
||||
{
|
||||
Log::trace(LOCATION);
|
||||
if (getFd() == -1)
|
||||
{
|
||||
Log::error("Socket creation failed");
|
||||
throw std::runtime_error("Socket creation failed");
|
||||
}
|
||||
int opt = 1;
|
||||
if (setsockopt(getFd(), SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0)
|
||||
{
|
||||
close(getFd());
|
||||
setFd(-1);
|
||||
Log::error("setsockopt failed");
|
||||
throw std::runtime_error("setsockopt failed");
|
||||
}
|
||||
setNonBlocking();
|
||||
}
|
||||
|
||||
ServerSocket::ServerSocket(int fd) : ASocket(fd)
|
||||
{
|
||||
Log::trace(LOCATION);
|
||||
}
|
||||
|
||||
void ServerSocket::listen(int backlog) const
|
||||
{
|
||||
Log::trace(LOCATION);
|
||||
if (::listen(getFd(), backlog) < 0)
|
||||
{
|
||||
Log::error("Listen failed");
|
||||
throw std::runtime_error("Listen failed");
|
||||
}
|
||||
}
|
||||
|
||||
void ServerSocket::bind(const std::string &host, const int port) const
|
||||
{
|
||||
Log::trace(LOCATION);
|
||||
struct sockaddr_in address{};
|
||||
address.sin_family = AF_INET;
|
||||
address.sin_addr.s_addr = inet_addr(host.c_str());
|
||||
address.sin_port = htons(port);
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
|
||||
if (::bind(getFd(), reinterpret_cast<struct sockaddr *>(&address), sizeof(address)) < 0)
|
||||
{
|
||||
Log::fatal("Cannot bind to " + host + ":" + std::to_string(port)
|
||||
+ " - address already in use or permission denied");
|
||||
throw std::runtime_error("Bind failed");
|
||||
}
|
||||
}
|
||||
|
||||
ASocket::Type ServerSocket::getType() const
|
||||
{
|
||||
Log::trace(LOCATION);
|
||||
return ASocket::Type::SERVER_SOCKET;
|
||||
}
|
||||
|
||||
std::unique_ptr<ClientSocket> ServerSocket::accept() const
|
||||
{
|
||||
Log::trace(LOCATION);
|
||||
int client_fd = ::accept(getFd(), nullptr, nullptr);
|
||||
if (client_fd < 0)
|
||||
{
|
||||
Log::error("Accept failed");
|
||||
throw std::runtime_error("Accept failed");
|
||||
}
|
||||
return std::make_unique<ClientSocket>(client_fd);
|
||||
}
|
||||
22
webserv/socket/ServerSocket.hpp
Normal file
22
webserv/socket/ServerSocket.hpp
Normal file
@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "webserv/socket/ClientSocket.hpp"
|
||||
#include <webserv/socket/ASocket.hpp>
|
||||
|
||||
#include <memory> // for unique_ptr
|
||||
#include <string> // for string
|
||||
|
||||
class ServerSocket : public ASocket
|
||||
{
|
||||
public:
|
||||
ServerSocket();
|
||||
|
||||
ServerSocket(int fd);
|
||||
|
||||
void listen(int backlog) const;
|
||||
void bind(const std::string &host, int port) const;
|
||||
|
||||
[[nodiscard]] ASocket::Type getType() const override;
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ClientSocket> accept() const;
|
||||
};
|
||||
@ -1,118 +0,0 @@
|
||||
#include <webserv/socket/Socket.hpp>
|
||||
|
||||
#include <webserv/log/Log.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <arpa/inet.h> // For inet_addr
|
||||
#include <fcntl.h> // For fcntl()"
|
||||
#include <netinet/in.h> // For sockaddr_in
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h> // For close()
|
||||
|
||||
Socket::Socket() : fd_(socket(AF_INET, SOCK_STREAM, 0))
|
||||
{
|
||||
Log::trace(LOCATION);
|
||||
if (fd_ == -1)
|
||||
{
|
||||
Log::error("Socket creation failed");
|
||||
throw std::runtime_error("Socket creation failed");
|
||||
}
|
||||
int opt = 1;
|
||||
if (setsockopt(fd_, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0)
|
||||
{
|
||||
close(fd_);
|
||||
fd_ = -1;
|
||||
Log::error("setsockopt failed");
|
||||
throw std::runtime_error("setsockopt failed");
|
||||
}
|
||||
setNonBlocking();
|
||||
}
|
||||
|
||||
Socket::Socket(int fd) : fd_(fd) // NOLINT(readability-identifier-naming)
|
||||
{
|
||||
Log::trace(LOCATION);
|
||||
if (fd_ == -1)
|
||||
{
|
||||
Log::error("Invalid file descriptor");
|
||||
throw std::runtime_error("Invalid file descriptor");
|
||||
}
|
||||
setNonBlocking();
|
||||
}
|
||||
|
||||
Socket::~Socket()
|
||||
{
|
||||
Log::trace(LOCATION);
|
||||
if (fd_ != -1)
|
||||
{
|
||||
close(fd_);
|
||||
}
|
||||
}
|
||||
|
||||
void Socket::listen(int backlog) const
|
||||
{
|
||||
Log::trace(LOCATION);
|
||||
if (::listen(fd_, backlog) < 0)
|
||||
{
|
||||
Log::error("Listen failed");
|
||||
throw std::runtime_error("Listen failed");
|
||||
}
|
||||
}
|
||||
|
||||
void Socket::bind(const std::string &host, const int port) const
|
||||
{
|
||||
Log::trace(LOCATION);
|
||||
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(fd_, reinterpret_cast<struct sockaddr *>(&address), sizeof(address))
|
||||
< 0) // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
|
||||
{
|
||||
Log::fatal("Cannot bind to " + host + ":" + std::to_string(port)
|
||||
+ " - address already in use or permission denied");
|
||||
throw std::runtime_error("Bind failed");
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<Socket> Socket::accept() const
|
||||
{
|
||||
Log::trace(LOCATION);
|
||||
int client_fd = ::accept(fd_, nullptr, nullptr);
|
||||
if (client_fd < 0)
|
||||
{
|
||||
Log::error("Accept failed");
|
||||
throw std::runtime_error("Accept failed");
|
||||
}
|
||||
return std::make_unique<Socket>(client_fd);
|
||||
}
|
||||
|
||||
ssize_t Socket::recv(void *buf, size_t len) const
|
||||
{
|
||||
Log::trace(LOCATION);
|
||||
return ::recv(fd_, buf, len, 0);
|
||||
}
|
||||
|
||||
ssize_t Socket::send(const void *buf, size_t len) const
|
||||
{
|
||||
Log::trace(LOCATION);
|
||||
return ::send(fd_, buf, len, 0);
|
||||
}
|
||||
|
||||
void Socket::setNonBlocking() const
|
||||
{
|
||||
Log::trace(LOCATION);
|
||||
if (fcntl(fd_, F_SETFL, O_NONBLOCK) < 0)
|
||||
{
|
||||
Log::error("Failed to set non-blocking mode");
|
||||
throw std::runtime_error("Failed to set non-blocking mode");
|
||||
}
|
||||
}
|
||||
|
||||
int Socket::getFd() const
|
||||
{
|
||||
Log::trace(LOCATION);
|
||||
return fd_;
|
||||
}
|
||||
@ -1,32 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef> // for size_t
|
||||
#include <memory> // for unique_ptr
|
||||
#include <string> // for string
|
||||
|
||||
#include <sys/types.h> // for ssize_t
|
||||
|
||||
class Socket
|
||||
{
|
||||
public:
|
||||
Socket();
|
||||
Socket(int fd); // NOLINT readability-identifier-naming
|
||||
|
||||
Socket(const Socket &other) = delete; // Disable copy constructor
|
||||
Socket &operator=(const Socket &other) = delete; // Disable copy assignment
|
||||
Socket(Socket &&other) noexcept = default; // Move constructor
|
||||
Socket &operator=(Socket &&other) noexcept = default; // Move assignment
|
||||
|
||||
~Socket();
|
||||
|
||||
void listen(int backlog) const;
|
||||
void bind(const std::string &host, int port) const;
|
||||
[[nodiscard]] std::unique_ptr<Socket> accept() const;
|
||||
ssize_t recv(void *buf, size_t len) const;
|
||||
ssize_t send(const void *buf, size_t len) const;
|
||||
void setNonBlocking() const;
|
||||
[[nodiscard]] int getFd() const;
|
||||
|
||||
private:
|
||||
int fd_;
|
||||
};
|
||||
Loading…
Reference in New Issue
Block a user