diff --git a/config/default.conf b/config/default.conf index 968b764..2c1260e 100644 --- a/config/default.conf +++ b/config/default.conf @@ -83,6 +83,8 @@ server { cgi_enabled yes; cgi_handler .php /usr/bin/php-cgi; + # cgi_handler .py /usr/bin/python3; + # cgi_handler .sh; client_max_body_size 10M; } diff --git a/htdocs/site-1/upload.php b/htdocs/site-1/upload.php index 7255a85..44110c7 100644 --- a/htdocs/site-1/upload.php +++ b/htdocs/site-1/upload.php @@ -2,38 +2,35 @@ $name) { + handle_upload([ + "name" => $_FILES["fileToUpload"]["name"][$i], + "tmp_name" => $_FILES["fileToUpload"]["tmp_name"][$i] + ]); + } + } else { + handle_upload($_FILES["fileToUpload"]); + } } else { - if (move_uploaded_file($_FILES["fileToUpload"]["tmp_name"], $target_file)) { - echo "The file ". htmlspecialchars( basename( $_FILES["fileToUpload"]["name"])). " has been uploaded."; - } else { - echo "Sorry, there was an error uploading your file."; - var_dump(error_get_last()); - } + echo "No files received.\n"; } ?> diff --git a/htdocs/site-1/upload.py b/htdocs/site-1/upload.py new file mode 100644 index 0000000..e594c07 --- /dev/null +++ b/htdocs/site-1/upload.py @@ -0,0 +1,35 @@ +## CGI Script to handle file uploads + +import cgi +import os +import sys + +print("Content-Type: text/plain") +print() # End of headers + +def main(): + form = cgi.FieldStorage() + + # Directory to save uploaded files + upload_dir = "/home/qmennen/Documents/webserv/htdocs/site-1/uploads" + if not os.path.exists(upload_dir): + os.makedirs(upload_dir) + + # Process each file in the form + for field in form.keys(): + field_item = form[field] + if field_item.filename: + # Get the filename and file data + filename = os.path.basename(field_item.filename) + file_data = field_item.file.read() + + # Save the file to the upload directory + with open(os.path.join(upload_dir, filename), 'wb') as f: + f.write(file_data) + + print(f"Uploaded file: {filename} ({len(file_data)} bytes) to {upload_dir}") + else: + print(f"No file uploaded for field: {field}") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/htdocs/site-1/upload.sh b/htdocs/site-1/upload.sh new file mode 100755 index 0000000..65f9651 --- /dev/null +++ b/htdocs/site-1/upload.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +cat > ./uploaded_file.txt +echo "File uploaded successfully." \ No newline at end of file diff --git a/webserv/client/Client.cpp b/webserv/client/Client.cpp index bddbcf1..fadd529 100644 --- a/webserv/client/Client.cpp +++ b/webserv/client/Client.cpp @@ -74,7 +74,7 @@ void Client::request() } if (bytesRead == 0) { - Log::info("Client closed connection, fd: " + std::to_string(clientSocket_->getFd())); + Log::info("Client closed connection: " + clientSocket_->toString()); server_.disconnect(*this); // CRITICAL: RETURN IMMEDIATELY return; } @@ -168,7 +168,7 @@ void Client::poll() const } if (httpResponse_->isComplete() && clientSocket_->getEvent() != ASocket::IoState::WRITE) { - Log::info("Response is ready to be sent to client, fd: " + std::to_string(clientSocket_->getFd())); + Log::info("Response is ready to be sent to client: " + clientSocket_->toString()); clientSocket_->setCallback([this]() { respond(); }); // server_.writable(clientSocket_->getFd()); clientSocket_->setIOState(ASocket::IoState::WRITE); @@ -181,11 +181,11 @@ void Client::respond() const ssize_t bytesSent = send(clientSocket_->getFd(), payload.data(), payload.size(), 0); if (bytesSent < 0) { - Log::error("Send failed for fd: " + std::to_string(clientSocket_->getFd())); + Log::error("Send failed for: " + clientSocket_->toString()); } else { - Log::debug("Sent " + std::to_string(bytesSent) + " bytes to fd: " + std::to_string(clientSocket_->getFd())); + Log::debug("Sent " + std::to_string(bytesSent) + " bytes to: " + clientSocket_->toString()); } server_.disconnect(*this); // ! CRITICAL: RETURN IMMEDIATELY } diff --git a/webserv/handler/CgiEnvironment.cpp b/webserv/handler/CgiEnvironment.cpp index 7f4aea6..a9ee862 100644 --- a/webserv/handler/CgiEnvironment.cpp +++ b/webserv/handler/CgiEnvironment.cpp @@ -76,6 +76,16 @@ char **CgiEnvironment::toEnvp() const return envp; } +std::string CgiEnvironment::get(const std::string &key) const +{ + auto it = env_.find(key); + if (it != env_.end()) + { + return it->second; + } + return ""; +} + void CgiEnvironment::appendCustomHeaders(const HttpHeaders &headers) { Log::trace(LOCATION); diff --git a/webserv/handler/CgiEnvironment.hpp b/webserv/handler/CgiEnvironment.hpp index 05c462f..c39bfda 100644 --- a/webserv/handler/CgiEnvironment.hpp +++ b/webserv/handler/CgiEnvironment.hpp @@ -15,6 +15,7 @@ class CgiEnvironment public: CgiEnvironment(const URI &uri, const HttpRequest &request); + std::string get(const std::string &key) const; [[nodiscard]] char **toEnvp() const; private: diff --git a/webserv/handler/CgiHandler.cpp b/webserv/handler/CgiHandler.cpp index c9a9dd6..1ea5465 100644 --- a/webserv/handler/CgiHandler.cpp +++ b/webserv/handler/CgiHandler.cpp @@ -72,34 +72,26 @@ void CgiHandler::write() return; } const std::string &body = request_.getBody(); - size_t before = writeOffset_; - while (writeOffset_ < body.size()) + + if (writeOffset_ < body.size()) { const char *data = body.data() + writeOffset_; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) size_t remaining = body.size() - writeOffset_; size_t chunk = remaining > CHUNK_SIZE ? CHUNK_SIZE : remaining; - ssize_t bytesRead = cgiStdIn_->write(data, chunk); - if (bytesRead > 0) + ssize_t bytesWritten = cgiStdIn_->write(data, chunk); + if (bytesWritten > 0) { - writeOffset_ += static_cast(bytesRead); - } - else - { - break; // would block or peer closed; try again on next EPOLLOUT + writeOffset_ += static_cast(bytesWritten); + Log::debug("Wrote " + std::to_string(bytesWritten) + " bytes, write offset " + std::to_string(writeOffset_) + + "/ " + std::to_string(body.size())); } } + if (writeOffset_ >= body.size()) { Log::debug("CGI stdin sent " + std::to_string(body.size()) + " bytes, closing write end"); request_.getClient().removeSocket(cgiStdIn_.get()); cgiStdIn_.reset(); - bodyWriteCompleted_ = true; - } - else - { - Log::debug("Wrote " + std::to_string(writeOffset_ - before) + " bytes, write offset " - + std::to_string(writeOffset_) + "/ " + std::to_string(body.size())); - // Log::debug("CGI stdin progress " + std::to_string(before) + "→" + std::to_string(writeOffset_)); } } @@ -147,7 +139,7 @@ void CgiHandler::read() responseComplete = (buffer_.size() >= contentLength_.value()); } - if (bodyWriteCompleted_ && responseComplete) + if (responseComplete) { Log::debug("Response complete: headers parsed and content received"); request_.getClient().removeSocket(cgiStdOut_.get()); @@ -161,7 +153,7 @@ void CgiHandler::read() if (bytesRead == 0) { // EOF from CGI process - Log::info("CGI process closed stdout, fd: " + std::to_string(cgiStdOut_->getFd())); + Log::info("CGI process closed stdout, fd: " + std::to_string(cgiStdOut_->getFd())); request_.getClient().removeSocket(cgiStdOut_.get()); cgiStdOut_.reset(); @@ -181,35 +173,15 @@ void CgiHandler::read() } // Only finalize if we've finished writing the request body - if (bodyWriteCompleted_) - { - finalizeCgiResponse(); - } - else - { - Log::warning("CGI process closed stdout before request body was completely written"); - // Set error response but don't finalize until write is complete - // ErrorHandler::createErrorResponse(500, response_); - } + finalizeCgiResponse(); return; } if (bytesRead < 0) { - if (errno == EAGAIN || errno == EWOULDBLOCK) - { - Log::debug("CGI stdout would block, will retry on next EPOLLIN"); - return; - } - else - { - Log::error("Error reading from CGI stdout: " + std::string(strerror(errno))); - // Only finalize if write is complete - if (bodyWriteCompleted_) - { - finalizeCgiResponse(); - } - } + + Log::error("Error reading from CGI stdout: " + std::string(strerror(errno))); + finalizeCgiResponse(); } } @@ -396,4 +368,4 @@ void CgiHandler::appendToBuffer(const char *data, size_t length) { Log::trace(LOCATION); buffer_.insert(buffer_.end(), data, data + length); -} +} \ No newline at end of file diff --git a/webserv/handler/CgiHandler.hpp b/webserv/handler/CgiHandler.hpp index bf950af..1b21d74 100644 --- a/webserv/handler/CgiHandler.hpp +++ b/webserv/handler/CgiHandler.hpp @@ -37,7 +37,7 @@ class CgiHandler : public AHandler void handleTimeout() override; private: - constexpr static size_t CHUNK_SIZE = 32768; + constexpr static size_t CHUNK_SIZE = 65536; // 64kb constexpr static size_t bufferSize_ = 8192; // TODO: remove duplicate definition and move to configmanager std::vector buffer_; @@ -54,7 +54,6 @@ class CgiHandler : public AHandler int pid_ = -1; size_t writeOffset_ = 0; bool headersParsed_ = false; - bool bodyWriteCompleted_ = false; std::optional contentLength_; void write(); diff --git a/webserv/handler/CgiHandler2.cpp2 b/webserv/handler/CgiHandler2.cpp2 deleted file mode 100644 index e9b4c1d..0000000 --- a/webserv/handler/CgiHandler2.cpp2 +++ /dev/null @@ -1,254 +0,0 @@ -CgiHandler::CgiHandler(const HttpRequest &request, HttpResponse &response) - : AHandler(request, response), cgiProcess_(nullptr), cgiStdIn_(nullptr), cgiStdOut_(nullptr) -{ - Log::debug("CgiHandler constructed"); - bodyWriteOffset_ = 0; - headersParsed_ = false; - expectedBody_.reset(); -} - -void CgiHandler::setCgiSockets(std::unique_ptr cgiStdIn, std::unique_ptr cgiStdOut, - std::unique_ptr cgiStdErr) -{ - cgiStdIn->setCallback([this]() { write(); }); - cgiStdOut->setCallback([this]() { read(); }); - cgiStdErr->setCallback([this]() { error(); }); - - cgiStdOut_ = std::move(cgiStdOut); - cgiStdIn_ = std::move(cgiStdIn); - cgiStdErr_ = std::move(cgiStdErr); - - request_.getClient().addSocket(cgiStdIn_.get()); - request_.getClient().addSocket(cgiStdOut_.get()); - request_.getClient().addSocket(cgiStdErr_.get()); - // Ensure stdout/stderr are set to READ interest; stdin to WRITE only if there's a body - cgiStdOut_->setIOState(ASocket::IoState::READ); - cgiStdOut_->markDirty(); - cgiStdErr_->setIOState(ASocket::IoState::READ); - cgiStdErr_->markDirty(); - if (!request_.getBody().empty()) - { - cgiStdIn_->setIOState(ASocket::IoState::WRITE); - cgiStdIn_->markDirty(); - } - else - { - request_.getClient().removeSocket(cgiStdIn_.get()); - cgiStdIn_.reset(); - } -} - -void CgiHandler::write() -{ - Log::trace(LOCATION); - if (!cgiStdIn_) return; - const std::string &body = request_.getBody(); - if (bodyWriteOffset_ >= body.size()) - { - request_.getClient().removeSocket(cgiStdIn_.get()); - cgiStdIn_.reset(); - return; - } - // Stream body until pipe stops accepting data; no errno checks needed (level-triggered epoll) - size_t before = bodyWriteOffset_; - while (bodyWriteOffset_ < body.size()) - { - const char *data = body.data() + bodyWriteOffset_; - size_t remain = body.size() - bodyWriteOffset_; - size_t chunk = remain > 32768 ? 32768 : remain; - ssize_t n = cgiStdIn_->write(data, chunk); - if (n > 0) - { - bodyWriteOffset_ += static_cast(n); - } - else - { - break; // would block or peer closed; try again on next EPOLLOUT - } - } - if (bodyWriteOffset_ >= body.size()) - { - Log::debug("CGI stdin sent " + std::to_string(body.size()) + " bytes, closing write end"); - request_.getClient().removeSocket(cgiStdIn_.get()); - cgiStdIn_.reset(); - } - else - { - Log::debug("CGI stdin progress " + std::to_string(before) + "→" + std::to_string(bodyWriteOffset_)); - cgiStdIn_->setIOState(ASocket::IoState::WRITE); - cgiStdIn_->markDirty(); - } -} - -static inline bool findHeaderEnd(const std::string &s, size_t &pos, long &sepSize) -{ - size_t a = s.find("\r\n\r\n"); - size_t b = s.find("\n\n"); - size_t c = s.find("\r\r"); - size_t end = std::min({a, b, c}); - if (end == std::string::npos) return false; - sepSize = (end == a) ? 4 : 2; - pos = end; - return true; -} - -void CgiHandler::read() -{ - Log::trace(LOCATION); - if (!cgiStdOut_) return; - // Drain as much as available this callback - for (;;) - { - char buffer[bufferSize_] = {}; - ssize_t n = cgiStdOut_->read(buffer, sizeof(buffer)); - if (n > 0) - { - appendToBuffer(buffer, static_cast(n)); - // Parse headers once, as soon as we have them - if (!headersParsed_) - { - size_t headerEnd = 0; - long sepSize = 0; - std::string snapshot(buffer_.begin(), buffer_.end()); - if (findHeaderEnd(snapshot, headerEnd, sepSize)) - { - std::string headers(snapshot.begin(), snapshot.begin() + static_cast(headerEnd)); - parseCgiHeaders(headers); - // After headers parsed, remove them from buffer_ so it contains only body - buffer_.erase(buffer_.begin(), buffer_.begin() + static_cast(headerEnd) + sepSize); - headersParsed_ = true; - // Capture expected body size if provided - std::string cl = response_.getHeaders().get("Content-Length"); - if (!cl.empty()) - { - expectedBody_ = static_cast(std::strtoul(cl.c_str(), nullptr, 10)); - } - else - { - expectedBody_.reset(); - } - } - } - // If we know Content-Length and already have it, finalize - if (headersParsed_ && expectedBody_.has_value() && buffer_.size() >= expectedBody_.value()) - { - request_.getClient().removeSocket(cgiStdOut_.get()); - cgiStdOut_.reset(); - finalizeCgiResponse(); - return; - } - continue; // try to read more this tick - } - else if (n == 0) - { - // EOF → finalize with whatever body we have - Log::info("CGI process closed stdout, fd: " + std::to_string(cgiStdOut_->getFd())); - request_.getClient().removeSocket(cgiStdOut_.get()); - cgiStdOut_.reset(); - // If headers not parsed yet, try once more (some scripts may end with only headers) - if (!headersParsed_) - { - size_t headerEnd = 0; - long sep = 0; - std::string snap(buffer_.begin(), buffer_.end()); - if (findHeaderEnd(snap, headerEnd, sep)) - { - std::string headers(snap.begin(), snap.begin() + static_cast(headerEnd)); - parseCgiHeaders(headers); - buffer_.erase(buffer_.begin(), buffer_.begin() + static_cast(headerEnd) + sep); - headersParsed_ = true; - } - } - finalizeCgiResponse(); - return; - } - else - { - // Would block or transient error; wait for next EPOLLIN - break; - } - } -} - -void CgiHandler::error() -{ - Log::trace(LOCATION); - if (!cgiStdErr_) return; - for (;;) - { - char buffer[bufferSize_] = {}; - ssize_t n = cgiStdErr_->read(buffer, sizeof(buffer)); - if (n > 0) - { - appendToBuffer(buffer, static_cast(n)); - Log::error("CGI stderr output (fd: " + std::to_string(cgiStdErr_->getFd()) - + "): " + std::string(buffer, static_cast(n))); - continue; - } - else if (n == 0) - { - Log::info("CGI process closed stderr, fd: " + std::to_string(cgiStdErr_->getFd())); - request_.getClient().removeSocket(cgiStdErr_.get()); - cgiStdErr_.reset(); - break; - } - else - { - break; - } - } -} - -void CgiHandler::parseCgiOutput() -{ - // Changed: only parse headers if present; do NOT finalize here - Log::trace(LOCATION); - if (headersParsed_) return; - size_t headerEnd = 0; - long sepSize = 0; - std::string header(buffer_.begin(), buffer_.end()); - if (!findHeaderEnd(header, headerEnd, sepSize)) - { - Log::debug("CGI output headers not complete yet"); - return; - } - std::string headers(header.begin(), header.begin() + static_cast(headerEnd)); - Log::debug("CGI output headers: " + headers); - parseCgiHeaders(headers); - buffer_.erase(buffer_.begin(), buffer_.begin() + static_cast(headerEnd) + sepSize); - headersParsed_ = true; - std::string cl = response_.getHeaders().get("Content-Length"); - if (!cl.empty()) - expectedBody_ = static_cast(std::strtoul(cl.c_str(), nullptr, 10)); - else - expectedBody_.reset(); -} - -void CgiHandler::parseCgiHeaders(std::string &headers) -{ - Log::trace(LOCATION); - // ...existing code... - // At end, capture Content-Length if present - std::string cl = response_.getHeaders().get("Content-Length"); - if (!cl.empty()) expectedBody_ = static_cast(std::strtoul(cl.c_str(), nullptr, 10)); - // ...existing code... -} - -void CgiHandler::finalizeCgiResponse() -{ - Log::trace(LOCATION); - auto status = response_.getHeaders().get("Status"); - wait(); - if (cgiProcess_ && cgiProcess_->getExitCode() > 0 && status.empty()) - { - response_.setStatus(500); - } - else if (!status.empty()) - { - response_.setStatus(std::atoi(status.c_str())); - } - // Append only the body (headers already stripped) - response_.appendBody(buffer_); - response_.setComplete(); - buffer_.clear(); -} \ No newline at end of file diff --git a/webserv/handler/CgiProcess.cpp b/webserv/handler/CgiProcess.cpp index e12418d..79681fc 100644 --- a/webserv/handler/CgiProcess.cpp +++ b/webserv/handler/CgiProcess.cpp @@ -98,9 +98,9 @@ void CgiProcess::spawn() else { // Parent process - auto cgiStdIn = std::make_unique(pipeStdin[1], ASocket::IoState::WRITE); - auto cgiStdOut = std::make_unique(pipeStdout[0], ASocket::IoState::READ); - auto cgiStdErr = std::make_unique(pipeStderr[0], ASocket::IoState::READ); + auto cgiStdIn = std::make_unique(pipeStdin[1], ASocket::IoState::WRITE, "stdin"); + auto cgiStdOut = std::make_unique(pipeStdout[0], ASocket::IoState::READ, "stdout"); + auto cgiStdErr = std::make_unique(pipeStderr[0], ASocket::IoState::READ, "stderr"); close(pipeStdin[0]); close(pipeStdout[1]); @@ -108,8 +108,6 @@ void CgiProcess::spawn() Log::debug("CGI process forked with PID: " + std::to_string(pid_)); - // request_.getClient().setCgiSockets(std::move(cgiStdIn), std::move(cgiStdOut)); // move the sockets to the - // client handler_.setCgiSockets(std::move(cgiStdIn), std::move(cgiStdOut), std::move(cgiStdErr)); handler_.setPid(pid_); diff --git a/webserv/log/Log.hpp b/webserv/log/Log.hpp index 4d81217..a46ab29 100644 --- a/webserv/log/Log.hpp +++ b/webserv/log/Log.hpp @@ -49,7 +49,7 @@ class Log void log(Level level, const std::string &message, const std::map &context); - static constexpr Log::Level COMPILE_TIME_LOG_LEVEL = Log::Level::Trace; + static constexpr Log::Level COMPILE_TIME_LOG_LEVEL = Log::Level::Debug; static void setFileChannel(const std::string &filename); static void setStdoutChannel(); diff --git a/webserv/main.cpp b/webserv/main.cpp index 316c13f..951bde1 100644 --- a/webserv/main.cpp +++ b/webserv/main.cpp @@ -35,6 +35,7 @@ int main(int argc, char **argv) Log::setFileChannel("logs/webserv.log"); Log::setStdoutChannel(); + // ::signal(SIGPIPE, SIG_IGN); // Ignore SIGPIPE globally printHeader(); ConfigManager &configManager = ConfigManager::getInstance(); configManager.init(configPath); // NOLINT diff --git a/webserv/server/Server.cpp b/webserv/server/Server.cpp index 4e7c18b..095974e 100644 --- a/webserv/server/Server.cpp +++ b/webserv/server/Server.cpp @@ -8,8 +8,9 @@ #include // for ServerSocket #include // for stateToEpoll -#include // for errno, EBADF, ENOENT, EINTR -#include // for SIGINT, sig_atomic_t +#include // for errno, EBADF, ENOENT, EINTR +#include // for SIGINT, sig_atomic_t +#include #include // for strerror #include // for exception #include // for unique_ptr, allocator, make_unique @@ -76,10 +77,10 @@ void Server::add(ASocket &socket, Client *client) event.data.fd = fd; if (epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, fd, &event) == -1) { - Log::error("epoll_ctl ADD failed for fd: " + std::to_string(fd) + " with error: " + std::strerror(errno)); + Log::error(socket.toString() + ": epoll_ctl ADD failed with error: " + std::strerror(errno)); throw std::runtime_error("epoll_ctl ADD failed"); } - Log::debug("Socket added to epoll, fd: " + std::to_string(fd)); + Log::debug(socket.toString() + ": added to epoll"); socketToClient_[fd] = client; sockets_.insert(&socket); } @@ -92,10 +93,10 @@ void Server::remove(ASocket &socket) { if (errno == EBADF || errno == ENOENT) { - Log::debug("Socket fd " + std::to_string(fd) + " was already closed or removed from epoll"); + Log::debug(socket.toString() + " was already closed or removed from epoll"); return; } - Log::error("epoll_ctl DEL failed for fd: " + std::to_string(fd) + " with error: " + std::strerror(errno)); + Log::error(socket.toString() + ": epoll_ctl DEL failed with error: " + std::string(std::strerror(errno))); throw std::runtime_error("epoll_ctl DEL failed"); } socketToClient_.erase(fd); @@ -181,13 +182,15 @@ void Server::handleRequest(struct epoll_event *event) const void Server::writable(int client_fd) const { Log::trace(LOCATION); - Log::debug("Response ready for client fd: " + std::to_string(client_fd)); + Client &client = getClient(client_fd); + ASocket &socket = client.getSocket(client_fd); + Log::debug(socket.toString() + ": response ready"); struct epoll_event ev{}; ev.events = EPOLLOUT; ev.data.fd = client_fd; if (epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, client_fd, &ev) == -1) { - Log::error("epoll_ctl MOD failed for fd: " + std::to_string(client_fd)); + Log::error(socket.toString() + ": epoll_ctl MOD failed with error: " + std::string(std::strerror(errno))); throw std::runtime_error("epoll_ctl MOD failed"); } } @@ -198,7 +201,7 @@ void Server::update(const ASocket &socket) const int socketFd = socket.getFd(); uint32_t events = utils::stateToEpoll(socket.getEvent()); - Log::debug("Socket (" + std::to_string(socket.getFd()) + ") is being updated"); + Log::debug(socket.toString() + ": is being updated"); struct epoll_event evt{}; evt.events = events; evt.data.fd = socketFd; @@ -206,10 +209,10 @@ void Server::update(const ASocket &socket) const { if (errno == EBADF || errno == ENOENT) { - Log::debug("Socket fd " + std::to_string(socketFd) + " was already closed or removed from epoll"); + Log::debug(socket.toString() + ": was already closed or removed from epoll"); return; } - Log::error("epoll_ctl MOD failed for fd: " + std::to_string(socketFd) + " with error: " + std::strerror(errno)); + Log::error(socket.toString() + ": epoll_ctl MOD failed with error: " + std::string(std::strerror(errno))); throw std::runtime_error("epoll_ctl MOD failed"); } } @@ -217,23 +220,24 @@ void Server::update(const ASocket &socket) const void Server::handleResponse(struct epoll_event *event) const { int socket_fd = event->data.fd; - Log::debug("Attempting to send data to fd: " + std::to_string(socket_fd)); Client &client = getClient(socket_fd); - client.getSocket(socket_fd).callback(); + ASocket &socket = client.getSocket(socket_fd); + Log::debug(socket.toString() + ": attempting to send data"); + socket.callback(); // disconnect(client); } -void Server::handleEpollHangUp(struct epoll_event *event) const +void Server::handleEpollHangUp(struct epoll_event *event) { Client &client = getClient(event->data.fd); ASocket &socket = client.getSocket(event->data.fd); if (socket.getType() == ASocket::Type::CGI_SOCKET) { - Log::info("CGI socket hang up on fd " + std::to_string(event->data.fd)); + Log::info(socket.toString() + ": CGI socket hang up"); socket.callback(); return; } - Log::warning("Epoll hang up on fd " + std::to_string(event->data.fd) + ": " + std::strerror(errno)); + Log::warning(socket.toString() + ": Epoll hang up with error: " + std::string(std::strerror(errno))); } void Server::handleEvent(struct epoll_event *event) @@ -266,17 +270,18 @@ void Server::handleEvent(struct epoll_event *event) ASocket &socket = client.getSocket(fd); if (socket.getType() == ASocket::Type::CGI_SOCKET) { - Log::info("EPOLLERR on CGI socket fd " + std::to_string(fd) + ", invoking callback"); - socket.callback(); + Log::info(socket.toString() + ": EPOLLERR invoking callback"); + remove(socket); + close(fd); } else if (socket.getType() == ASocket::Type::CLIENT_SOCKET) { - Log::warning("EPOLLERR on client socket fd " + std::to_string(fd) + ", disconnecting client"); + Log::warning(socket.toString() + ": EPOLLERR disconnecting client"); disconnect(client); } else { - Log::warning("EPOLLERR on auxiliary socket fd " + std::to_string(fd) + ", removing"); + Log::warning(socket.toString() + ": EPOLLERR removing auxiliary socket"); remove(socket); close(fd); } diff --git a/webserv/socket/ASocket.cpp b/webserv/socket/ASocket.cpp index 4c415c8..fa302d0 100644 --- a/webserv/socket/ASocket.cpp +++ b/webserv/socket/ASocket.cpp @@ -95,7 +95,7 @@ void ASocket::setNonBlocking() const } flagStr += ")"; - Log::debug("ASocket: FD " + std::to_string(fd_) + " configured. Flags: " + flagStr); + Log::debug(this->toString() + " configured. Flags: " + flagStr); } int ASocket::getFd() const noexcept @@ -127,7 +127,7 @@ void ASocket::setIOState(IoState event) void ASocket::processed() { - Log::debug("Socket " + std::to_string(fd_) + " processed"); + Log::debug(this->toString() + " processed"); dirty_ = false; } @@ -148,3 +148,8 @@ void ASocket::setCallback(std::function callback) { callback_ = std::move(callback); } + +std::string ASocket::toString() const +{ + return "ASocket(fd=" + std::to_string(fd_) + ")"; +} \ No newline at end of file diff --git a/webserv/socket/ASocket.hpp b/webserv/socket/ASocket.hpp index b0d3bb4..19b481a 100644 --- a/webserv/socket/ASocket.hpp +++ b/webserv/socket/ASocket.hpp @@ -3,6 +3,7 @@ #include // for size_t #include #include // for function +#include #include // for ssize_t @@ -48,6 +49,8 @@ class ASocket void setIOState(IoState event); void processed(); + [[nodiscard]] virtual std::string toString() const; + protected: void setNonBlocking() const; void setFd(int fd); diff --git a/webserv/socket/CgiSocket.cpp b/webserv/socket/CgiSocket.cpp index 3f79565..bf30d87 100644 --- a/webserv/socket/CgiSocket.cpp +++ b/webserv/socket/CgiSocket.cpp @@ -3,12 +3,13 @@ #include // for LOCATION, Log #include // for ASocket +#include #include // for generic_category, system_error #include // for errno #include // for read, write -CgiSocket::CgiSocket(int fd, ASocket::IoState event) : ASocket(fd, event) +CgiSocket::CgiSocket(int fd, ASocket::IoState event, std::string stream) : ASocket(fd, event), stream_(std::move(stream)) { Log::trace(LOCATION); } @@ -31,3 +32,8 @@ ssize_t CgiSocket::write(const void *buf, size_t len) const ssize_t bytesSent = ::write(getFd(), buf, len); return bytesSent; } + +std::string CgiSocket::toString() const +{ + return "CgiSocket(fd=" + std::to_string(getFd()) + "), stream (" + stream_ + ")"; +} \ No newline at end of file diff --git a/webserv/socket/CgiSocket.hpp b/webserv/socket/CgiSocket.hpp index 97276c3..872a091 100644 --- a/webserv/socket/CgiSocket.hpp +++ b/webserv/socket/CgiSocket.hpp @@ -1,6 +1,7 @@ #pragma once #include // for ASocket +#include #include // for size_t #include @@ -10,10 +11,14 @@ class CgiSocket : public ASocket { public: - explicit CgiSocket(int fd, ASocket::IoState event); + explicit CgiSocket(int fd, ASocket::IoState event, std::string stream); [[nodiscard]] ASocket::Type getType() const noexcept override; ssize_t read(void *buf, size_t len) const override; ssize_t write(const void *buf, size_t len) const override; + + [[nodiscard]] std::string toString() const override; + private: + std::string stream_; }; \ No newline at end of file diff --git a/webserv/socket/ClientSocket.cpp b/webserv/socket/ClientSocket.cpp index f7dbf95..73691b2 100644 --- a/webserv/socket/ClientSocket.cpp +++ b/webserv/socket/ClientSocket.cpp @@ -16,4 +16,9 @@ ASocket::Type ClientSocket::getType() const noexcept const struct sockaddr *ClientSocket::getAddress() const noexcept { return &address_; -} \ No newline at end of file +} + +std::string ClientSocket::toString() const +{ + return "ClientSocket(fd=" + std::to_string(getFd()) + ")"; +} diff --git a/webserv/socket/ClientSocket.hpp b/webserv/socket/ClientSocket.hpp index 78068f5..a61101d 100644 --- a/webserv/socket/ClientSocket.hpp +++ b/webserv/socket/ClientSocket.hpp @@ -13,6 +13,8 @@ class ClientSocket : public ASocket [[nodiscard]] ASocket::Type getType() const noexcept override; [[nodiscard]] const struct sockaddr *getAddress() const noexcept; + + [[nodiscard]] std::string toString() const override; private: struct sockaddr address_; }; \ No newline at end of file diff --git a/webserv/socket/ServerSocket.cpp b/webserv/socket/ServerSocket.cpp index f0756c3..2d92ec7 100644 --- a/webserv/socket/ServerSocket.cpp +++ b/webserv/socket/ServerSocket.cpp @@ -81,3 +81,8 @@ std::unique_ptr ServerSocket::accept() const } return std::make_unique(client_fd, client_address); } + +std::string ServerSocket::toString() const +{ + return "ServerSocket(fd=" + std::to_string(getFd()) + ")"; +} \ No newline at end of file diff --git a/webserv/socket/ServerSocket.hpp b/webserv/socket/ServerSocket.hpp index 92c3305..e0860c7 100644 --- a/webserv/socket/ServerSocket.hpp +++ b/webserv/socket/ServerSocket.hpp @@ -17,4 +17,6 @@ class ServerSocket : public ASocket [[nodiscard]] ASocket::Type getType() const noexcept override; [[nodiscard]] std::unique_ptr accept() const; + + [[nodiscard]] std::string toString() const override; }; \ No newline at end of file diff --git a/webserv/socket/TimerSocket.cpp b/webserv/socket/TimerSocket.cpp index 800127e..3b388a4 100644 --- a/webserv/socket/TimerSocket.cpp +++ b/webserv/socket/TimerSocket.cpp @@ -63,4 +63,9 @@ ssize_t TimerSocket::write(const void *buf, size_t len) const ssize_t bytesSent = ::write(getFd(), buf, len); return bytesSent; +} + +std::string TimerSocket::toString() const +{ + return "TimerSocket(fd=" + std::to_string(getFd()) + "), active (" + (active_ ? "true" : "false") + "), timeout (" + std::to_string(timeout_.count()) + "ms)"; } \ No newline at end of file diff --git a/webserv/socket/TimerSocket.hpp b/webserv/socket/TimerSocket.hpp index fde093f..6d18599 100644 --- a/webserv/socket/TimerSocket.hpp +++ b/webserv/socket/TimerSocket.hpp @@ -3,6 +3,7 @@ #include // for ASocket #include // for milliseconds +#include #include // for size_t #include // for ssize_t @@ -20,6 +21,8 @@ class TimerSocket : public ASocket void activate(); + [[nodiscard]] std::string toString() const override; + private: bool active_ = false; std::chrono::milliseconds timeout_;