Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Compiler and flags
CXX := c++
CXXFLAGS := -Wall -Werror -Wextra -std=c++17
CXXFLAGS := -Wall -Werror -Wextra -std=c++17 -O3
DEPFLAGS := -MMD -MP

# Target name
Expand Down Expand Up @@ -76,6 +76,7 @@ HDRS := webserv.hpp \
PollFdManager.hpp \
mimetypes.hpp \
ft_toString.hpp \
globals.hpp \

OBJS := $(addprefix $(OBJ_DIR)/, $(SRCS:.cpp=.o))
DEPS := $(OBJS:.o=.d)
Expand Down
9 changes: 3 additions & 6 deletions include/Server/ClientConnection.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,7 @@

class ClientConnection {
public:
enum class Status {
HEADER,
BODY,
READY_TO_SEND,
};
enum class Status { HEADER, BODY, READY_TO_SEND, SENDING_RESPONSE };

explicit ClientConnection(int clientFd, sockaddr_in clientAddr, std::vector<ServerConfig> configs);
~ClientConnection();
Expand Down Expand Up @@ -41,6 +37,7 @@ class ClientConnection {

bool _readingChunkSize = true;
size_t _chunkSizeRemaining = 0;
size_t _bytesSendToClient = 0;

void _handleCompleteChunkedBodyRead();
bool _readChunkData();
Expand All @@ -55,7 +52,7 @@ class ClientConnection {
void _readRequestBodyIfContentLength();
void _handleCompleteBodyRead();
bool _parseHttpRequestHeader(const std::string& header);
bool _sendDataToClient(const std::string& data);
bool _sendDataToClient(const std::string& data, size_t offset, size_t length);
static std::optional<size_t> _findHeaderEnd(const std::vector<char>& buffer);
[[nodiscard]] std::string _log(const std::string& msg) const;
};
1 change: 1 addition & 0 deletions include/Server/PollFdManager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class PollFdManager {
pollfd* data();
[[nodiscard]] size_t size() const;
[[nodiscard]] std::vector<pollfd> getPolls() const;
[[nodiscard]] std::vector<pollfd> getShuffledPolls() const;

private:
PollFdManager() = default;
Expand Down
3 changes: 2 additions & 1 deletion include/webserv.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@
#include "ServerConfig.hpp"

#define SERVER_NAME "webserv"
#define DEFAULT_POLL_TIMEOUT 5000
#define DEFAULT_POLL_TIMEOUT 5000
#define SIZE_BYTES_TO_SEND_BACK size_t(1024 * 1024)
58 changes: 40 additions & 18 deletions src/Server/ClientConnection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <algorithm> // For std::search
#include <array>
#include <cstring> // For strerror
#include <webserv.hpp>

#include "HttpRequest.hpp"
#include "HttpResponse.hpp"
Expand All @@ -24,6 +25,8 @@ std::string statusToString(const ClientConnection::Status status) {
return "BODY";
case ClientConnection::Status::READY_TO_SEND:
return "READY_TO_SEND";
case ClientConnection::Status::SENDING_RESPONSE:
return "SENDING_RESPONSE";
default:
return "UNKNOWN";
}
Expand Down Expand Up @@ -70,6 +73,7 @@ void ClientConnection::handleClient() {
_receiveBody();
break;
case Status::READY_TO_SEND:
case Status::SENDING_RESPONSE:
// Nothing to do here
break;
}
Expand All @@ -80,6 +84,7 @@ bool ClientConnection::_receiveHeader() {

// Attempt to read data into the header buffer
size_t remainingHeaderSize = _requestHandler.getConfig().getClientHeaderBufferSize() - _headerBuffer.size();
LOG_TRACE(_log("Remaining header size: " + std::to_string(remainingHeaderSize)));
if (!_readData(_clientFd, _headerBuffer, remainingHeaderSize)) {
return false;
}
Expand Down Expand Up @@ -112,7 +117,7 @@ bool ClientConnection::_receiveHeader() {
if (_request.getBodyType() == HttpRequest::BodyType::CHUNKED ||
_request.getBodyType() == HttpRequest::BodyType::CONTENT_LENGTH) {
LOG_DEBUG(_log("Request has body"));
_bodyBuffer.reserve(_currentConfig.getClientBodyBufferSize());
_bodyBuffer.reserve(_currentConfig.getClientMaxBodySize());
_bodyBuffer.clear();
if (_headerBuffer.empty()) {
LOG_DEBUG(_log("No additional data in header buffer"));
Expand All @@ -133,7 +138,7 @@ ClientConnection::Status ClientConnection::getStatus() const { return _status; }
void ClientConnection::_handleCompleteChunkedBodyRead() {
LOG_DEBUG(_log("Finished reading chunked request body"));

LOG_DEBUG(_log("Request body: \n" + _request.getBody()));
// LOG_DEBUG(_log("Request body: \n" + _request.getBody()));
// _readingChunkSize = true;
// Set the status to ready to send
_status = Status::READY_TO_SEND;
Expand Down Expand Up @@ -353,9 +358,6 @@ void ClientConnection::_handleCompleteBodyRead() {
// Set the complete body in the request
_request.setBody(std::string(_bodyBuffer.begin(), _bodyBuffer.end()));

// Log the completed header and body info
_logHeader();

// Update status to ready
_status = Status::READY_TO_SEND;
}
Expand Down Expand Up @@ -434,26 +436,46 @@ bool ClientConnection::_parseHttpRequestHeader(const std::string& header) {
}

void ClientConnection::sendResponse() {
if (_status != Status::READY_TO_SEND) {
if (_status != Status::READY_TO_SEND && _status != Status::SENDING_RESPONSE) {
return;
}
if (!_response.getStatus()) {
LOG_DEBUG(_log("Building response for request"));
_response = _requestHandler.handleRequest(_request);
}

if (_status == Status::READY_TO_SEND) {
_status = Status::SENDING_RESPONSE;
_bytesSendToClient = 0;
}

LOG_INFO(_log("Sending response with status code: " + std::to_string(_response.getStatus())));
LOG_TRACE(_log("Response: \n" + _response.toString()));
if (!_sendDataToClient(_response.toString())) {
const size_t remainingBytes = _response.toString().size() - _bytesSendToClient;
const size_t bytesToSend = std::min(SIZE_BYTES_TO_SEND_BACK, remainingBytes);

LOG_DEBUG(_log("Preparing to send chunk. Total size: " + std::to_string(_response.toString().size()) +
", Bytes sent so far: " + std::to_string(_bytesSendToClient) +
", Chunk size: " + std::to_string(bytesToSend)));

if (!_sendDataToClient(_response.toString(), _bytesSendToClient, bytesToSend)) {
LOG_ERROR(_log("Failed to send chunk. Bytes sent so far: " + std::to_string(_bytesSendToClient)));
return;
}
if (_response.getHeader("Connection") == "keep-alive") {
LOG_INFO(_log("Connection is keep-alive"));
_status = Status::HEADER;
_disconnected = false;
_response = HttpResponse();
} else {
LOG_INFO(_log("Closing connection after response"));
_disconnected = true;

LOG_DEBUG(_log("Chunk sent successfully. Bytes sent in this chunk: " + std::to_string(bytesToSend) +
", Total bytes sent: " + std::to_string(_bytesSendToClient)));

if (_bytesSendToClient == _response.toString().size()) {
if (_response.getHeader("Connection") == "keep-alive") {
LOG_INFO(_log("Connection is keep-alive"));
_status = Status::HEADER;
_disconnected = false;
_response = HttpResponse();
} else {
LOG_INFO(_log("Closing connection after response"));
_disconnected = true;
}
}
}

Expand Down Expand Up @@ -483,15 +505,15 @@ bool ClientConnection::_readData(const int fd, std::vector<char>& buffer, const
return true;
}

bool ClientConnection::_sendDataToClient(const std::string& data) {
// TODO: Implement chunked transfer encoding if necessary
ssize_t bytesSent = send(_clientFd, data.data(), data.size(), 0);
bool ClientConnection::_sendDataToClient(const std::string& data, size_t offset, size_t length) {
ssize_t bytesSent = send(_clientFd, data.data() + offset, length, 0);
if (bytesSent == -1) {
LOG_ERROR(_log("Failed to send data: " + std::string(strerror(errno))));
_disconnected = true;
return false;
}
LOG_DEBUG(_log("Sent " + std::to_string(bytesSent) + " bytes to client"));
_bytesSendToClient += bytesSent;
return true;
}

Expand Down
36 changes: 24 additions & 12 deletions src/Server/MultiSocketWebserver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

#include <arpa/inet.h>
#include <sys/poll.h>
#include <sys/sysctl.h>
#include <unistd.h>

#include <cstddef>
#include <random>

#include "ClientConnection.hpp"
#include "Logger.hpp"
Expand Down Expand Up @@ -53,35 +55,44 @@ MultiSocketWebserver::~MultiSocketWebserver() {

void MultiSocketWebserver::run() {
while (stopServer == false) {
// TODO timeout
if (const int eventCount = poll(_polls.data(), _polls.size(), 5000); eventCount == -1 && !stopServer) {
LOG_ERROR("Poll failed: " + std::string(strerror(errno)));
break;
}

for (auto& [fd, events, revents] : _polls.getPolls()) {
if (stopServer) {
break;
}
// Check if there are any events to process
if (revents & POLLIN) {
if (isServerFd(fd)) {
_acceptConnection(fd);
break;
}
if (_handleClientData(fd)) {
break;
} else {
_handleClientData(fd);
}
}
if (revents & POLLOUT) {
if (_handleClientWrite(fd)) {
break;
}
_handleClientWrite(fd);
}
if (revents & (POLLERR | POLLHUP | POLLNVAL)) {
LOG_ERROR("Error on socket " + std::to_string(fd));
if (revents & (POLLERR | POLLHUP | POLLNVAL | POLLPRI)) {
if (revents & POLLHUP) {
LOG_INFO("Client disconnected from socket " + std::to_string(fd));
} else {
LOG_ERROR("Error on socket " + std::to_string(fd));
}
if (isServerFd(fd)) {
_sockets.erase(fd);
} else {
_clients.erase(fd);
}
if (fd != -1) {
close(fd);
}
_polls.removeFd(fd);
}
}
}
// TODO: Poll failed, handle error
}

void MultiSocketWebserver::_acceptConnection(const int server_fd) {
Expand Down Expand Up @@ -144,7 +155,8 @@ bool MultiSocketWebserver::_handleClientWrite(int fd) {
}

ClientConnection& client = *it->second;
if (client.getStatus() != ClientConnection::Status::READY_TO_SEND) {
if (client.getStatus() != ClientConnection::Status::READY_TO_SEND &&
client.getStatus() != ClientConnection::Status::SENDING_RESPONSE) {
return false;
}

Expand Down
9 changes: 9 additions & 0 deletions src/Server/PollFdManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include "PollFdManager.hpp"

#include <algorithm>
#include <random>

#include "Logger.hpp"

Expand All @@ -22,3 +23,11 @@ pollfd* PollFdManager::data() { return _pollFds.data(); }
size_t PollFdManager::size() const { return _pollFds.size(); }

std::vector<pollfd> PollFdManager::getPolls() const { return _pollFds; }

std::vector<pollfd> PollFdManager::getShuffledPolls() const {
std::vector<pollfd> shuffledFds = _pollFds;
std::random_device rd;
std::mt19937 g(rd());
std::shuffle(shuffledFds.begin(), shuffledFds.end(), g);
return shuffledFds;
}
1 change: 1 addition & 0 deletions src/Server/Socket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ Socket::Socket(std::vector<ServerConfig> configs)
}

Socket::~Socket() {
LOG_INFO("Closing socket");
if (_socketFd != -1) {
close(_socketFd);
}
Expand Down
9 changes: 9 additions & 0 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,17 @@ void printServerConfigs(const std::vector<std::vector<ServerConfig>> &server_con

// Signal handler function
void signalHandler(const int signum) {
if (stopServer) {
LOG_INFO("Server is already stopping...");
return;
}
// Clear the line using ANSI escape code
std::cout << "\033[2K\r";
LOG_INFO("Interrupt signal (" + std::to_string(signum) + ") received. Stopping server...");
stopServer = true;

// Ignore further SIGINT signals to prevent ^C from being printed
signal(SIGINT, SIG_IGN);
}

int main(const int argc, const char *argv[]) {
Expand Down
Loading