#include "Webserv.hpp" #define MAX_EVENTS 42 // arbitrary #define TIMEOUT 3000 #define BUFSIZE 8192 // (8Ko) #define STATUS_500 std::string("Status: 500" CRLF CRLF); void Webserv::_read_cgi_output(Client *client) { char buf[BUFSIZE]; ssize_t ret; std::cerr << "_read_cgi_output()" << "\n"; std::cerr << "cgi_pid = " << client->cgi_pid << "\n"; std::cerr << "client fd = " << client->get_cl_fd() << "\n"; std::cerr << "cgi fd = " << client->cgi_pipe_rfd << "\n"; ret = ::read(client->cgi_pipe_rfd, buf, BUFSIZE); std::cerr << "cgi read ret = " << ret << "\n"; if (ret == -1) { std::perror("err read(cgi_fd)"); client->cgi_output = STATUS_500; } else if (ret == 0) std::cerr << "Madame s'il vous plait, du Ketchup pour mon hamburger" << " (AKA:ret=0)" << "\n"; else { std::cerr << "NORMAL BEHAVIOR I THINK!\n"; // debug client->cgi_output.append(buf, ret); } } void Webserv::_handle_epoll_error_cgi_fd(uint32_t events, Client *client) { (void)events; std::cerr << "cgi EPOLLERR" << "\n"; std::cerr << "cgi_pid = " << client->cgi_pid << "\n"; std::cerr << "client fd = " << client->get_cl_fd() << "\n"; std::cerr << "cgi fd = " << client->cgi_pipe_rfd << "\n"; client->cgi_output = STATUS_500; pid_t wait_ret; wait_ret = ::waitpid(client->cgi_pid, NULL, WNOHANG); std::cerr << "cgi EPOLLERR waitpid ret = " << wait_ret << "\n"; if (wait_ret == client->cgi_pid) { if (client->cgi_pipe_rfd) { if (::close(client->cgi_pipe_rfd) == -1) std::perror("err close()"); } client->cgi_pipe_rfd = 0; _epoll_update(client->get_cl_fd(), EPOLLOUT, EPOLL_CTL_ADD); } } void Webserv::_cgi_epollhup(uint32_t events, Client *client) { (void)events; (void)client; /* std::cerr << "cgi EPOLLHUP" << "\n"; std::cerr << "cgi_pid = " << client->cgi_pid << "\n"; std::cerr << "client fd = " << client->get_cl_fd() << "\n"; std::cerr << "cgi fd = " << client->cgi_pipe_rfd << "\n"; */ pid_t wait_ret; wait_ret = ::waitpid(client->cgi_pid, NULL, WNOHANG); // std::cerr << "cgi EPOLLHUP waitpid ret = " << wait_ret << "\n"; if (wait_ret == client->cgi_pid) { if (client->cgi_pipe_rfd) { if (::close(client->cgi_pipe_rfd) == -1) std::perror("err close()"); } client->cgi_pipe_rfd = 0; _epoll_update(client->get_cl_fd(), EPOLLOUT, EPOLL_CTL_ADD); } } Client *Webserv::_find_cgi_fd(int cgi_fd) { std::vector::iterator it = _clients.begin(); std::vector::iterator it_end = _clients.end(); while (it != it_end) { if (it->cgi_pipe_rfd == cgi_fd) return (&(*it)); ++it; } return (NULL); } 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; Client *client_cgi = NULL; g_run = true; while (g_run) { std::cerr << ++count_loop << "----loop epoll() "; nfds = ::epoll_wait(_epfd, events, MAX_EVENTS, TIMEOUT); std::cerr << "(nfds=" << nfds << ")\n"; 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); client_cgi = _find_cgi_fd(events[i].data.fd); 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 (client_cgi) { if (events[i].events & EPOLLERR) _handle_epoll_error_cgi_fd(events[i].events, client_cgi); else if (events[i].events & EPOLLIN) _read_cgi_output(client_cgi); else if ( (events[i].events & EPOLLHUP) && !(events[i].events & EPOLLIN) ) _cgi_epollhup(events[i].events, client_cgi); } else if (std::find(_clients.begin(), _clients.end(), events[i].data.fd) != _clients.end()) // TODO: save the it in var to avoid multiples find() { 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; } } } }