feat(socket): implement callbacks for asynchronous handling of requests and responses

This commit is contained in:
Quinten 2025-10-15 17:29:22 +02:00
parent 0887acd81a
commit e1be8d8aa8
6 changed files with 61 additions and 24 deletions

View File

@ -1,3 +1,4 @@
#include "webserv/socket/ASocket.hpp"
#include "webserv/socket/CgiSocket.hpp"
#include <webserv/client/Client.hpp>
@ -24,6 +25,7 @@ Client::Client(std::unique_ptr<ClientSocket> socket, Server &server)
{
Log::trace(LOCATION);
Log::info("New client connected, fd: " + std::to_string(client_socket_->getFd()));
client_socket_->setCallback([this]() { request(); });
}
Client::~Client()
@ -43,6 +45,20 @@ void Client::setStatusCode(int code)
statusCode_ = code;
}
ASocket &Client::getSocket(int fd) const
{
if (fd == -1 || client_socket_->getFd() == fd)
{
return *client_socket_;
}
if (cgi_socket_ && cgi_socket_->getFd() == fd)
{
return *client_socket_; // TODO return cgi socket
}
Log::error("Socket not found for fd: " + std::to_string(fd));
throw std::runtime_error("Socket not found for fd: " + std::to_string(fd));
}
void Client::request()
{
Log::trace(LOCATION);
@ -101,14 +117,23 @@ void Client::poll() const
if (httpResponse_->isComplete())
{
Log::info("Response is ready to be sent to client, fd: " + std::to_string(client_socket_->getFd()));
client_socket_->setCallback([this]() { respond(); });
server_.responseReady(client_socket_->getFd());
}
}
std::vector<uint8_t> Client::getResponse() const
void Client::respond() const
{
return httpResponse_->toBytes();
auto payload = httpResponse_->toBytes();
ssize_t bytesSent = send(client_socket_->getFd(), payload.data(), payload.size(), 0);
if (bytesSent < 0)
{
Log::error("Send failed for fd: " + std::to_string(client_socket_->getFd()));
}
else
{
Log::debug("Sent " + std::to_string(bytesSent) + " bytes to fd: " + std::to_string(client_socket_->getFd()));
}
}
HttpRequest &Client::getHttpRequest() const

View File

@ -35,12 +35,12 @@ class Client
~Client();
void request();
void respond() const;
void poll() const;
[[nodiscard]] std::vector<uint8_t> getResponse() const;
[[nodiscard]] int getStatusCode() const;
[[nodiscard]] ClientSocket &getSocket() const { return *client_socket_; }
[[nodiscard]] ASocket &getSocket(int fd = -1) const;
void setStatusCode(int code);
void setCgiSocket(std::unique_ptr<CgiSocket> cgiSocket);

View File

@ -156,16 +156,16 @@ bool URI::isCgi() const
std::string URI::getCgiPath() const
{
Log::debug("BaseName: " + baseName_ + ", FullPath: " + fullPath_ + ", Dir: " + dir_ + ", PathInfo: " + pathInfo_ +
", Extension: " + getExtension());
// Log::debug("BaseName: " + baseName_ + ", FullPath: " + fullPath_ + ", Dir: " + dir_ + ", PathInfo: " + pathInfo_ +
// ", Extension: " + getExtension());
if (!isFile() || getExtension().empty() || !config_->get<bool>("cgi_enabled").has_value()
|| !config_->get<bool>("cgi_enabled").value())
{
Log::debug("CGI not enabled or not a file or no extension",
{{"isFile", isFile() ? "true" : "false"},
{"extension", getExtension()},
{"cgi_enabled", config_->get<bool>("cgi_enabled").has_value() ? "true" : "false"},
{"cgi_enabled_value", config_->get<bool>("cgi_enabled").value() ? "true" : "false"}});
// Log::debug("CGI not enabled or not a file or no extension",
// {{"isFile", isFile() ? "true" : "false"},
// {"extension", getExtension()},
// {"cgi_enabled", config_->get<bool>("cgi_enabled").has_value() ? "true" : "false"},
// {"cgi_enabled_value", config_->get<bool>("cgi_enabled").value() ? "true" : "false"}});
return "";
}
auto cgiPath = config_->getCGIPath(getExtension());

View File

@ -163,8 +163,10 @@ void Server::handleRequest(struct epoll_event *event) const
Log::trace(LOCATION);
int client_fd = event->data.fd;
Client &client = getClient(client_fd);
client.request();
client.getSocket().callback();
}
void Server::responseReady(int client_fd) const
@ -185,16 +187,7 @@ void Server::handleResponse(struct epoll_event *event)
{
Log::debug("Attempting to send data to fd: " + std::to_string(event->data.fd));
Client &client = getClient(event->data.fd);
auto payload = client.getResponse();
ssize_t bytesSent = send(event->data.fd, payload.data(), payload.size(), 0);
if (bytesSent < 0)
{
Log::error("Send failed for fd: " + std::to_string(event->data.fd) + " with error: " + std::strerror(errno));
}
else
{
Log::debug("Sent " + std::to_string(bytesSent) + " bytes to fd: " + std::to_string(event->data.fd));
}
client.getSocket().callback();
disconnect(client);
}

View File

@ -68,4 +68,17 @@ int ASocket::getFd() const
void ASocket::setFd(int fd)
{
fd_ = fd;
}
}
void ASocket::callback() const
{
if (callback_ != nullptr)
{
callback_();
}
}
void ASocket::setCallback(std::function<void()> callback)
{
callback_ = std::move(callback);
}

View File

@ -1,5 +1,7 @@
#pragma once
#include <functional> // for function
#include <cstddef> // for size_t
#include <cstdint>
@ -27,6 +29,9 @@ class ASocket
[[nodiscard]] virtual Type getType() const = 0;
[[nodiscard]] int getFd() const;
void callback() const;
void setCallback(std::function<void()> callback);
ssize_t read(void *buf, size_t len) const;
ssize_t write(const void *buf, size_t len) const;
@ -37,4 +42,5 @@ class ASocket
private:
int fd_;
std::function<void()> callback_ = nullptr;
};