Files
codam-webserver-team/src/net/FileDescriptor.cpp
T
2026-09-07 00:45:35 +02:00

97 lines
3.4 KiB
C++

/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* FileDescriptor.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: acossari <acossari@student.codam.nl> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2026/08/24 16:23:52 by acossari #+# #+# */
/* Updated: 2026/08/28 16:13:23 by acossari ### ########.fr */
/* */
/* ************************************************************************** */
#include "net/FileDescriptor.hpp"
#include <fcntl.h>
#include <unistd.h>
#include <cerrno>
#include <cstring>
#include <format>
#include <utility>
namespace webserv {
// "Sink parameter": consumes the local parameter by moving its value into msg_
// lvalue -> copy into message -> move into msg_ (1 copy, worst case scenario)
// rvalue -> move into message -> move into msg_ (0 copies)
FileDescriptor::Error::Error(std::string message) : msg_(std::move(message)) {}
const char* FileDescriptor::Error::what() const noexcept {
return msg_.c_str();
}
// A valid fd is either made close-on-exec or closed before construction fails.
FileDescriptor::FileDescriptor(int fd) : fd_(fd) {
if (fd_ < 0) {
return;
}
// fork() inherits this fd, but a successful execve() closes it before the
// new program starts, preventing CGI processes from retaining server fds.
if (::fcntl(fd_, F_SETFD, FD_CLOEXEC) == -1) {
// The subject forbids errno only after read/recv/write/send.
// Save it before close(), which may overwrite the original fcntl error.
const int errnoCode = errno;
::close(fd_);
fd_ = -1;
throw Error(std::format("fcntl(F_SETFD, FD_CLOEXEC) on fd {}: {}", fd,
std::strerror(errnoCode)));
}
}
FileDescriptor::~FileDescriptor() {
reset();
}
// The FileDescriptor&& parameter is an rvalue reference and a sink. It accepts
// only a temporary or an object passed with std::move(). The constructor takes
// ownership of that object's fd, so only the new FileDescriptor will close it.
FileDescriptor::FileDescriptor(FileDescriptor&& other) noexcept
: fd_(std::exchange(other.fd_, -1)) {}
FileDescriptor& FileDescriptor::operator=(FileDescriptor&& other) noexcept {
if (this != &other) {
reset();
fd_ = std::exchange(other.fd_, -1);
}
return *this;
}
void FileDescriptor::setNonBlocking() {
if (!valid()) {
throw Error("setNonBlocking() on an invalid file descriptor");
}
// Read, modify, write pattern: F_SETFL replaces the whole status flags word,
// so passing O_NONBLOCK alone would clear whatever else is set.
const int flags = ::fcntl(fd_, F_GETFL);
if (flags == -1) {
throw Error(
std::format("fcntl(F_GETFL) on fd {}: {}", fd_, std::strerror(errno)));
}
if (::fcntl(fd_, F_SETFL, flags | O_NONBLOCK) == -1) {
throw Error(std::format("fcntl(F_SETFL, O_NONBLOCK) on fd {}: {}", fd_,
std::strerror(errno)));
}
}
void FileDescriptor::reset() noexcept {
if (fd_ >= 0) {
::close(fd_);
fd_ = -1;
}
}
} // namespace webserv