#include "Webserv.hpp" Webserv::Webserv() { int on = 1; std::cout << "Server init\n"; //_socket_fd = ::socket(AF_INET, SOCK_STREAM, 0); _socket_fd = ::socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); if (_socket_fd == -1) { ::perror("err socket()"); throw std::runtime_error("Socket init"); } // allow socket descriptor to be reuseable if (setsockopt(_socket_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0) { ::perror("setsockopt() failed"); throw std::runtime_error("Socket init"); } } //Webserv::Webserv(Webserv const &src) //{ // //} Webserv::~Webserv() { std::cout << "Server destroyed\n"; } //Webserv & Webserv::operator=(Webserv const &rhs) //{ // //} /////////////// // Functions // void Webserv::bind(in_port_t port) { struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = ::htons(port); addr.sin_addr.s_addr = ::htonl(INADDR_ANY); if (::bind(_socket_fd, (const sockaddr*)&addr, sizeof addr) == -1) { ::perror("err bind(): "); throw std::runtime_error("Socket bind"); } } void Webserv::listen(unsigned int max_connections) { if (::listen(_socket_fd, max_connections) == -1) { ::perror("err listen(): "); throw std::runtime_error("Socket listen"); } } #define BUFSIZE 8192 #define MSG_TEST "Le Webserv / 20 =D\n" #define MSG_BOUNCE "bounced properly ;)\n" // placeholder #define TRUE 1 #define FALSE 0 void Webserv::start() { int len, rc; int listen_sd = -1, new_sd = -1; int end_server = FALSE, compress_array = FALSE; int close_conn; char buffer[80]; // struct sockaddr_in addr; struct pollfd fds[200]; int nfds = 1, current_size = 0, i, j; memset(fds, 0 , sizeof(fds)); fds[0].fd = listen_sd; fds[0].events = POLLIN; std::cout << "Server started\n"; while (end_server == FALSE) { // *********************************************************** // * Call poll() * // *********************************************************** poll(fds, nfds, -1); // *********************************************************** // * One or more descriptors are readable. Need to * // * determine which ones they are. * // *********************************************************** current_size = nfds; for (i = 0; i < current_size; i++) { // ********************************************************* // * Loop through to find the descriptors that returned * // * POLLIN and determine whether it's the listening * // * or the active connection. * // ********************************************************* if(fds[i].revents == 0) continue; // ********************************************************* // * If revents is not POLLIN, it's an unexpected result, * // * log and end the server. * // ********************************************************* if(fds[i].revents != POLLIN) { printf(" Error! revents = %d\n", fds[i].revents); end_server = TRUE; break; } if (fds[i].fd == listen_sd) { // ******************************************************* // * Listening descriptor is readable. * // ******************************************************* printf(" Listening socket is readable\n"); // ******************************************************* // * Accept all incoming connections that are * // * queued up on the listening socket before we * // * loop back and call poll again. * // ******************************************************* do { // ***************************************************** // * Accept each incoming connection. If * // * accept fails with EWOULDBLOCK, then we * // * have accepted all of them. Any other * // * failure on accept will cause us to end the * // * server. * // ***************************************************** new_sd = accept(listen_sd, NULL, NULL); if (new_sd < 0) { if (errno != EWOULDBLOCK) { perror(" accept() failed"); end_server = TRUE; } break; } // ***************************************************** // * Add the new incoming connection to the * // * pollfd structure * // ***************************************************** printf(" New incoming connection - %d\n", new_sd); fds[nfds].fd = new_sd; fds[nfds].events = POLLIN; nfds++; // ***************************************************** // * Loop back up and accept another incoming * // * connection * // ***************************************************** } while (new_sd != -1); } // ********************************************************* // * This is not the listening socket, therefore an * // * existing connection must be readable * // ********************************************************* else { printf(" Descriptor %d is readable\n", fds[i].fd); close_conn = FALSE; // ******************************************************* // * Receive all incoming data on this socket * // * before we loop back and call poll again. * // ******************************************************* do { // ***************************************************** // * Receive data on this connection until the * // * recv fails with EWOULDBLOCK. If any other * // * failure occurs, we will close the * // * connection. * // ***************************************************** rc = recv(fds[i].fd, buffer, sizeof(buffer), 0); if (rc < 0) { if (errno != EWOULDBLOCK) { perror(" recv() failed"); close_conn = TRUE; } break; } // ***************************************************** // * Check to see if the connection has been * // * closed by the client * // ***************************************************** if (rc == 0) { printf(" Connection closed\n"); close_conn = TRUE; break; } // ***************************************************** // * Data was received * // ***************************************************** len = rc; printf(" %d bytes received\n", len); // ***************************************************** // * Echo the data back to the client * // ***************************************************** rc = send(fds[i].fd, buffer, len, 0); if (rc < 0) { perror(" send() failed"); close_conn = TRUE; break; } } while(TRUE); // ******************************************************* // * If the close_conn flag was turned on, we need * // * to clean up this active connection. This * // * clean up process includes removing the * // * descriptor. * // ******************************************************* if (close_conn) { close(fds[i].fd); fds[i].fd = -1; compress_array = TRUE; } } // End of existing connection is readable } // End of loop through pollable descriptors // *********************************************************** // * If the compress_array flag was turned on, we need * // * to squeeze together the array and decrement the number * // * of file descriptors. We do not need to move back the * // * events and revents fields because the events will always* // * be POLLIN in this case, and revents is output. * // *********************************************************** if (compress_array) { compress_array = FALSE; for (i = 0; i < nfds; i++) { if (fds[i].fd == -1) { for(j = i; j < nfds; j++) { fds[j].fd = fds[j+1].fd; } i--; nfds--; } } } } /* struct sockaddr_in addr; socklen_t addr_len; int accepted_fd; struct pollfd poll_s; char buf[BUFSIZE]; // WIP buffer. need to try with std::vector or std::string. int ret; std::cout << "----------\n"; std::cout << "accept()\n"; addr_len = sizeof addr; accepted_fd = ::accept(_socket_fd, (sockaddr*)&addr, &addr_len); if (accepted_fd == -1) { ::perror("err accept(): "); continue; } // "Your server must never block and the client can be bounced properly if necessary". // NO-Block OK, but how to handle it ? Miss the bouncing part. ::fcntl(accepted_fd, F_SETFL, O_NONBLOCK); std::cout << "poll()\n"; // poll (or equivalent) poll_s.fd = accepted_fd; poll_s.events = POLLIN; // We need a way to valid POLLOUT and POLLIN at the same time (both, not one of them) ::poll(&poll_s, 1, -1); std::cout << "recv()\n"; ret = ::recv(accepted_fd, buf, BUFSIZE, 0); if (ret == -1) { ::perror("err recv(): "); if (::send(accepted_fd, MSG_BOUNCE, sizeof MSG_BOUNCE - 1, 0) == -1) ::perror("err send(): "); ::close(accepted_fd); continue; } buf[ret] = '\0'; std::cout << "send()\n"; if (::send(accepted_fd, buf, ret, 0) == -1) // echo the read ::perror("err send(): "); if (::send(accepted_fd, MSG_TEST, sizeof MSG_TEST - 1, 0) == -1) ::perror("err send(): "); ::close(accepted_fd); */ } }