#include "Webserv.hpp" #define MAX_EVENTS 42 // arbitrary #define TIMEOUT 3000 // TODO: temp to move in own file bool operator==(const cgi_pipe_rfd& lhs, int fd) { return lhs.fd == fd; } bool operator==(int fd, const cgi_pipe_rfd& rhs) { return fd == rhs.fd; } #define BUFSIZE 8192 // (8Ko) #define STATUS_500 std::string("Status: 500" CRLF CRLF); void Webserv::_read_cgi_output(cgi_pipe_rfd &cgi_fd) { char buf[BUFSIZE]; ssize_t ret; pid_t wait_ret; ret = ::read(cgi_fd.fd, buf, BUFSIZE); std::cerr << "cgi read ret = " << ret << "\n"; if (ret == -1) { std::perror("err read(cgi_fd)"); cgi_fd.client->cgi_output = STATUS_500; } else if (ret == 0) { (void)0; } else { cgi_fd.client->cgi_output.append(buf, ret); } wait_ret = ::waitpid(cgi_fd.cgi_pid, NULL, WNOHANG); std::cerr << "cgi waitpid ret = " << wait_ret << "\n"; if (wait_ret == 0 && ret == -1) { _epoll_update(cgi_fd.client->get_cl_fd(), EPOLLOUT, EPOLL_CTL_ADD); _close_cgi_pipe_rfd(cgi_fd.fd); // TODO: kill the child :) } else if (wait_ret == cgi_fd.cgi_pid) { _epoll_update(cgi_fd.client->get_cl_fd(), EPOLLOUT, EPOLL_CTL_ADD); _close_cgi_pipe_rfd(cgi_fd.fd); } return; } void Webserv::_handle_epoll_error_cgi_fd(uint32_t events, std::vector::iterator it) { (void)events; std::cerr << "cgi EPOLLERR" << "\n"; pid_t wait_ret; wait_ret = ::waitpid(it->cgi_pid, NULL, WNOHANG); std::cerr << "cgi EPOLLHUP waitpid ret = " << wait_ret << "\n"; if (wait_ret == 0) { _epoll_update(it->client->get_cl_fd(), EPOLLOUT, EPOLL_CTL_ADD); _close_cgi_pipe_rfd(it->fd); // TODO: kill the child :) } else if (wait_ret == it->cgi_pid) { _epoll_update(it->client->get_cl_fd(), EPOLLOUT, EPOLL_CTL_ADD); _close_cgi_pipe_rfd(it->fd); } } void Webserv::_cgi_epollhup(uint32_t events, std::vector::iterator it) { (void)events; std::cerr << "cgi EPOLLHUP" << "\n"; pid_t wait_ret; wait_ret = ::waitpid(it->cgi_pid, NULL, WNOHANG); std::cerr << "cgi EPOLLHUP waitpid ret = " << wait_ret << "\n"; if (wait_ret == 0) { _epoll_update(it->client->get_cl_fd(), EPOLLOUT, EPOLL_CTL_ADD); _close_cgi_pipe_rfd(it->fd); // TODO: kill the child :) } else if (wait_ret == it->cgi_pid) { _epoll_update(it->client->get_cl_fd(), EPOLLOUT, EPOLL_CTL_ADD); _close_cgi_pipe_rfd(it->fd); } } void Webserv::run() { std::cerr << "Server started\n"; struct epoll_event events[MAX_EVENTS]; int nfds; int i; int count_loop = 0; std::vector::iterator it_lsocket; std::vector::iterator it_cgi_fd; g_run = true; while (g_run) { std::cerr << ++count_loop << "----loop epoll()\n"; nfds = ::epoll_wait(_epfd, events, MAX_EVENTS, TIMEOUT); if (nfds == -1) { int errno_copy = errno; std::perror("err epoll_wait()"); if (errno_copy == EINTR) g_run = false; else throw std::runtime_error("Epoll wait"); } else if (nfds == 0 && !_clients.empty()) _timeout(); i = 0; while (i < nfds) { try { it_lsocket = std::find(_listen_sockets.begin(), _listen_sockets.end(), events[i].data.fd); it_cgi_fd = std::find(_cgi_pipe_rfds.begin(), _cgi_pipe_rfds.end(), events[i].data.fd); // Could be moved in the loop to avoid useless find() call if (it_lsocket != _listen_sockets.end()) { if (events[i].events & EPOLLERR || events[i].events & EPOLLHUP) _handle_epoll_error_lsocket(events[i].events, it_lsocket); else if (events[i].events & EPOLLIN) _accept_connection(*it_lsocket); } else if (it_cgi_fd != _cgi_pipe_rfds.end()) { if (events[i].events & EPOLLERR) _handle_epoll_error_cgi_fd(events[i].events, it_cgi_fd); else if (events[i].events & EPOLLHUP) _cgi_epollhup(events[i].events, it_cgi_fd); else if (events[i].events & EPOLLIN) _read_cgi_output(*it_cgi_fd); } else { if (events[i].events & EPOLLERR || events[i].events & EPOLLHUP) _handle_epoll_error_client(events[i].events, events[i].data.fd); else if (events[i].events & EPOLLIN) _request( &(*std::find(_clients.begin(), _clients.end(), events[i].data.fd)) ); else if (events[i].events & EPOLLOUT) _response( &(*std::find(_clients.begin(), _clients.end(), events[i].data.fd)) ); } ++i; if (!g_run) break; } catch (const std::bad_alloc& e) { std::cerr << e.what() << '\n'; _close_all_clients(); /* Swap to free the memory From : http://www.uml.org.cn/c%2B%2B/pdf/EffectiveSTL.pdf#page=66 */ std::vector().swap(_clients); break; } catch (const Webserv::ExecFail& e) { std::cerr << e.what() << '\n'; throw; } catch (const std::exception& e) { std::cerr << e.what() << '\n'; ++i; } } } }