1#include "../includes/ServerManager.hpp"
2#include "../includes/FatalExceptions.hpp"
3#include "../includes/CGIManager.hpp"
21 throw FatalException(std::string(
"epoll_create(): ") + strerror(errno));
29 throw FatalException(std::string(
"epoll_create(): ") + strerror(errno));
34 for (
size_t i = 0; i < confsCopy.size(); ++i)
51 : _interfacePortPairs(other._interfacePortPairs),
53 _eventBuffer(other._eventBuffer),
55 _listenFds(other._listenFds),
56 _listenFdToServerConf(other._listenFdToServerConf)
60 throw FatalException(std::string(
"epoll_create(): ") + strerror(errno));
63 for (std::map<int, uint32_t>::const_iterator it = other.
_fdEvents.begin();
87 throw FatalException(std::string(
"epoll_create(): ") + strerror(errno));
88 for (std::map<int, uint32_t>::const_iterator it = other.
_fdEvents.begin();
117 std::cout <<
"Server "<< conf->
getServerName() <<
" Listening on "
119 << ntohs(addr.sin_port) << std::endl;
128 struct sockaddr_in addr;
129 std::memset(&addr, 0,
sizeof(addr));
130 addr.sin_family = AF_INET;
131 addr.sin_addr.s_addr = htonl(INADDR_ANY);
132 addr.sin_port = htons(port);
140 std::cout <<
"Listening on 0.0.0.0:" << port << std::endl;
157 if (ready == 0 || errno == EINTR)
159 throw FatalException(std::string(
"epoll_wait(): ") + strerror(errno));
162 for (
int i = 0; i < ready; ++i)
172 if (events & (EPOLLHUP | EPOLLERR))
183 std::map<int, Connection*>::iterator it =
_connections.find(fd);
189 std::cout <<
"\nServer shut down.\n";
194 int fd = conn->
getFd();
198 if (events & EPOLLIN)
212 std::cerr <<
"client runtime error on fd " << fd <<
": " << e.
what() << std::endl;
219 catch (
const std::exception& e)
221 std::cerr <<
"unexpected runtime error on fd " << fd <<
": " << e.
what() << std::endl;
249 struct epoll_event ev;
250 std::memset(&ev, 0,
sizeof(ev));
254 std::map<int, uint32_t>::iterator it =
_fdEvents.find(fd);
258 if (epoll_ctl(
_epollFd, EPOLL_CTL_MOD, fd, &ev) < 0)
259 throw FatalException(std::string(
"epoll_ctl(MOD): ") + strerror(errno));
263 if (epoll_ctl(
_epollFd, EPOLL_CTL_ADD, fd, &ev) < 0)
264 throw FatalException(std::string(
"epoll_ctl(ADD): ") + strerror(errno));
270 struct sockaddr_in localAddr;
271 socklen_t len =
sizeof(localAddr);
272 if (getsockname(clientFd,
reinterpret_cast<struct sockaddr*
>(&localAddr), &len) < 0)
275 std::map<struct sockaddr_in, const ServerConf*, SockAddrCompare>::const_iterator it;
286 int fd = socket(AF_INET, SOCK_STREAM, 0);
288 throw FatalException(std::string(
"socket(): ") + strerror(errno));
291 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes,
sizeof yes) < 0)
294 throw FatalException(std::string(
"setsockopt(): ") + strerror(errno));
297 if (bind(fd,
reinterpret_cast<const struct sockaddr*
>(&addr),
sizeof(addr)) < 0)
306 throw FatalException(std::string(
"listen(): ") + strerror(errno));
309 if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
322 struct sockaddr_in clientAddr;
323 socklen_t clientLen =
sizeof(clientAddr);
324 int clientFd = accept(listenFd,
325 reinterpret_cast<struct sockaddr*
>(&clientAddr), &clientLen);
328 if (errno == EAGAIN || errno == EWOULDBLOCK)
330 std::cerr <<
"accept(): " << strerror(errno) << std::endl;
334 if (fcntl(clientFd, F_SETFL, O_NONBLOCK) < 0)
336 std::cerr <<
"fcntl(client): " << strerror(errno) << std::endl;
344 conf = confIt->second;
349 std::cout <<
"New connection from "
351 << ntohs(clientAddr.sin_port)
352 <<
" [fd " << clientFd <<
"]" << std::endl;
359 ssize_t n = recv(fd, buf,
sizeof(buf), 0);
362 std::cout <<
"\n--- Received " << n <<
" bytes from fd " << fd <<
" ---\n";
363 std::cout.write(buf, n);
364 std::cout <<
"\n--- End fd " << fd <<
" ---" << std::endl;
368 std::cout <<
"Client [fd " << fd <<
"] disconnected." << std::endl;
370 std::cerr <<
"recv error on fd " << fd << std::endl;
376 std::map<int, Connection*>::iterator it =
_connections.find(fd);
380 int pipeFd = it->second->getCgiPipeFd();
389 epoll_ctl(
_epollFd, EPOLL_CTL_DEL, fd, NULL);
408 size_t processed = 0;
425 std::cerr <<
"client runtime error on fd " << conn->
getFd() <<
": " << e.
what() << std::endl;
432 catch (
const std::exception& e)
434 std::cerr <<
"unexpected runtime error on fd " << conn->
getFd() <<
": " << e.
what() << std::endl;
451 int fd = conn->
getFd();
485 epoll_ctl(
_epollFd, EPOLL_CTL_DEL, pipeFd, NULL);
493 std::map<int, Connection*>::iterator it =
_cgiPipeToConn.find(pipeFd);
503 if (events & (EPOLLHUP | EPOLLERR))
505 else if (events & EPOLLIN)
510 std::cerr <<
"client CGI runtime error on fd " << conn->
getFd() <<
": " << e.
what() << std::endl;
520 catch (
const std::exception& e)
522 std::cerr <<
"unexpected CGI runtime error on fd " << conn->
getFd() <<
": " << e.
what() << std::endl;
543 time_t now = time(NULL);
544 std::vector<int> toKill;
550 toKill.push_back(it->first);
553 for (
size_t i = 0; i < toKill.size(); ++i)
555 int pipeFd = toKill[i];
556 std::map<int, Connection*>::iterator it =
_cgiPipeToConn.find(pipeFd);
580 static time_t lastSweep = 0;
581 time_t now = time(NULL);
582 if (now - lastSweep < 5)
586 std::vector<int> toDrop;
587 for (std::map<int, Connection*>::iterator it =
_connections.begin();
591 toDrop.push_back(it->first);
593 for (
size_t i = 0; i < toDrop.size(); ++i)
595 std::map<int, Connection*>::iterator it =
_connections.find(toDrop[i]);
598 it->second->triggerError(408);
613 for(std::map<int, Connection*>::iterator it =
_connections.begin();
620 for (std::map<int, uint32_t>::iterator it =
_fdEvents.begin();
volatile sig_atomic_t g_running
#define CONNECTION_TIMEOUT_S
static void cleanupAllProcesses()
Cleans up all active CGI processes on server shutdown. Sends SIGTERM to all processes,...
Exception type for failures scoped to a single client request.
virtual const char * what() const
int getStatusCode() const
void handleWrite()
Writes data from the _writeBuffer (or Response) to the client socket using send()....
ConnectionState getState() const
Response * getResponse() const
void setState(ConnectionState state)
Request * getRequest() const
void process()
Executes routing logic, instantiates the Response, and prepares data for sending. Transitions state t...
void triggerError(int statusCode)
Forces the connection into an error state, bypassing normal processing.
const ServerConf * getServerConf() const
int getCgiPipeFd() const
Returns the CGI output pipe fd, or -1 if no CGI is active.
void handleRead()
Reads data from the client socket using recv() into _readBuffer. Transitions state to PROCESSING if t...
void resetReadPosition()
Resets the internal read position to the beginning of the data.
DataStore & getBodyStore()
Returns a reference to the DataStore to allow direct writing from recv().
void setResponsePhrase(const std::string &phrase)
void finalizeCgiResponse()
Finalizes the CGI response after all output has been read. Parses CGI output headers,...
bool readCgiOutput()
Called by ServerManager when the CGI pipe is readable. Reads available data into _responseDataStore.
void cgiTimeout(const ServerConf &config)
Called by ServerManager when the CGI process times out. Kills the process and builds a 504 error page...
void setStatusCode(const std::string &code)
const struct sockaddr_in & getInterfacePortPair() const
const std::string & getServerName() const
void _enqueueProcessing(Connection *conn)
Adds a connection to the processing queue if not already queued.
void _handleCgiPipeEvent(int pipeFd, uint32_t events)
Handles an epoll event on a CGI pipe fd.
void _closeAllFds()
Closes all fds (listeners + clients) during shutdown.
std::set< int > _listenFds
void addServer(const ServerConf *conf)
Creates a listening socket for the given ServerConf's IP:Port and registers the mapping.
ServerManager & operator=(const ServerManager &other)
void _runRoundRobin()
Runs a budgeted round-robin pass over _processingQueue. Calls process() once per connection,...
const ServerConf * getServerConfForFd(int clientFd) const
Uses getsockname() to find the local address of a client fd, then returns the matching ServerConf.
void _finalizeProcessed(Connection *conn)
Handles state transition for a connection that just left PROCESSING.
void _unregisterCgiPipe(int pipeFd)
Unregisters a CGI pipe fd from epoll and removes the mapping.
std::map< struct sockaddr_in, const ServerConf *, SockAddrCompare > _interfacePortPairs
std::set< Connection * > _processingSet
void _registerCgiPipe(Connection *conn)
Registers a CGI pipe fd in epoll and maps it to its Connection.
std::deque< Connection * > _processingQueue
void addPollFd(int fd, uint32_t events)
Adds or updates an fd in the epoll list (e.g., CGI output pipe).
std::vector< ServerConf * > _serverConfs
bool _readAndPrint(int fd)
Reads from a client fd and prints the raw data.
void _handleConnection(Connection *conn, uint32_t events)
Dispatches epoll events to the correct Connection handler and drives state transitions....
void _sweepTimeouts()
Scans all connections and drops any that have been idle too long.
void addListenPort(int port)
Temporary overload for early development: creates a listening socket on the given port bound to INADD...
std::map< int, time_t > _cgiStartTimes
std::map< int, uint32_t > _fdEvents
std::map< int, const ServerConf * > _listenFdToServerConf
void _acceptNewConnections(int listenFd)
Accepts all pending connections on a listening fd. Sets each client fd to O_NONBLOCK and adds it to _...
void _dequeueProcessing(Connection *conn)
Removes a connection from the processing set (queue cleanup is lazy).
std::map< int, Connection * > _cgiPipeToConn
std::vector< struct epoll_event > _eventBuffer
int _createListeningSocket(const struct sockaddr_in &addr)
Creates, binds, listens, and sets O_NONBLOCK on a socket.
void _dropConnection(int fd)
Closes a client fd and removes it from epoll and _connections.
void run()
Enters the main epoll() event loop. Blocks until g_running becomes false.
void _sweepCgiTimeouts()
Sweeps CGI processes for timeout.
std::map< int, Connection * > _connections
volatile sig_atomic_t g_running
std::string ipv4ToString(const struct ::sockaddr_in &addr)