diff --git a/headers/Webserv.hpp b/headers/Webserv.hpp new file mode 100644 index 0000000..fda9751 --- /dev/null +++ b/headers/Webserv.hpp @@ -0,0 +1,57 @@ + +#ifndef WEBSERV_HPP +# define WEBSERV_HPP + +# include +# include +# include // errno +# include // perror +# include +# include +# include // close +# include // cout, cin +# include // memset + +# include // socket, accept, listen, send, recv, bind, connect, setsockopt, getsockname +# include // sockaddr_in +// # include // usefull for what ? +# include // htonl, htons, ntohl, ntohs, inet_addr + +# include // epoll +# include // fcntl + +#define BUFSIZE 8192 +#define TIMEOUT 3 * 60 * 1000 +#define MAX_EVENTS 42 // arbitrary +#define MSG_TEST "Le Webserv / 20 =D\n" +#define MSG_BOUNCE "bounced properly ;)\n" // placeholder + +class Webserv +{ + public: + Webserv(); + // Webserv(Webserv const &src); + ~Webserv(); + // Webserv &operator=(Webserv const &rhs); + + void init_virtual_servers(); // ADD config param + void start(); + + private: + int _socket_fd; // TODO: replace with vector of "Server" struct + int _epfd; + + // WIP global buffer. Need one variable set per "Client" + char _buf[BUFSIZE+1]; + ssize_t _read_ret; + std::map _request; + std::map _response; + + void _bind(int socket_fd, in_port_t port); + void _listen(int socket_fd, unsigned int max_connections); + void _accept_connection(int fd); + void _read_request(int fd); + void _send_response(int fd); +}; + +#endif diff --git a/srcs/Webserv.cpp b/srcs/Webserv.cpp new file mode 100644 index 0000000..b0bd37f --- /dev/null +++ b/srcs/Webserv.cpp @@ -0,0 +1,186 @@ + +#include "Webserv.hpp" + +Webserv::Webserv() +{ + std::cout << "Server init\n"; + + _epfd = ::epoll_create1(0); // (EPOLL_CLOEXEC) for CGI fork ? + if (_epfd == -1) + { + std::perror("err epoll_create1(): "); + throw std::runtime_error("Epoll init"); + } +} + +/* Webserv::Webserv(Webserv const &src) +{ + +} */ + +Webserv::~Webserv() +{ + close(_socket_fd); + close(_epfd); + std::cout << "Server destroyed\n"; +} + +/* Webserv & Webserv::operator=(Webserv const &rhs) +{ + +} */ + + /////////////// + // Functions // + +void Webserv::init_virtual_servers() // ADD config param +{ + _socket_fd = ::socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); // (SOCK_CLOEXEC) for CGI fork ? + if (_socket_fd == -1) + { + std::perror("err socket(): "); + throw std::runtime_error("Socket init"); + } + + _bind(_socket_fd, 4040); + _listen(_socket_fd, 512); // 512 arbitrary + + struct epoll_event ev; + std::memset(&ev, 0, sizeof ev); + ev.events = EPOLLIN; + ev.data.fd = _socket_fd; + if (::epoll_ctl(_epfd, EPOLL_CTL_ADD, _socket_fd, &ev) == -1) + { + std::perror("err epoll_ctl(): "); + throw std::runtime_error("Socket init"); + } +} + +void Webserv::start() +{ + std::cout << "Server started\n"; + struct epoll_event events[MAX_EVENTS]; + int nfds; + int i; + int count_loop = 0; + std::cout << ++count_loop << "----loop epoll()\n"; + + while ( (nfds = ::epoll_wait(_epfd, events, MAX_EVENTS, TIMEOUT)) != -1) + { + if (nfds == 0) + { + (void)0; + // TODO : parcourir les "Clients" encore ouvert et les close() tous + } + i = 0; + while (i < nfds) + { + if ((events[i].data.fd == _socket_fd) && (events[i].events & EPOLLIN)) + _accept_connection(events[i].data.fd); + else if (events[i].events & EPOLLIN) + _read_request(events[i].data.fd); + else if (events[i].events & EPOLLOUT) + _send_response(events[i].data.fd); + ++i; + } + std::cout << ++count_loop << "----loop epoll()\n"; + } + + if (nfds == -1) + { + std::perror("err epoll_wait(): "); + throw std::runtime_error("Epoll wait"); + } +} + + + /////////////////////// + // Private Functions // + +void Webserv::_bind(int socket_fd, in_port_t port) +{ + // cast invalid ? how to ? + // const struct sockaddr* cast_test = static_cast(addr); + + struct sockaddr_in addr; + std::memset(&addr, 0, sizeof addr); + addr.sin_family = AF_INET; + addr.sin_port = ::htons(port); + addr.sin_addr.s_addr = ::htonl(INADDR_ANY); // htonl useless with 0 value (INADDR_ANY) ? + + if (::bind(socket_fd, (const sockaddr*)&addr, sizeof addr) == -1) + { + std::perror("err bind(): "); + throw std::runtime_error("Socket bind"); + } +} + +void Webserv::_listen(int socket_fd, unsigned int max_connections) +{ + if (::listen(socket_fd, max_connections) == -1) + { + std::perror("err listen(): "); + throw std::runtime_error("Socket listen"); + } +} + +void Webserv::_accept_connection(int fd) +{ + struct sockaddr_in addr; + socklen_t addr_len; + int accepted_fd; + + std::cout << "accept()\n"; + addr_len = sizeof addr; + accepted_fd = ::accept(fd, (sockaddr*)&addr, &addr_len); + if (accepted_fd == -1) + { + std::perror("err accept(): "); + return; + } + ::fcntl(accepted_fd, F_SETFL, O_NONBLOCK); + + struct epoll_event ev; + std::memset(&ev, 0, sizeof ev); + ev.events = EPOLLIN; + ev.data.fd = accepted_fd; + if (::epoll_ctl(_epfd, EPOLL_CTL_ADD, accepted_fd, &ev) == -1) + std::perror("err accept() epoll_ctl(): "); +} + +void Webserv::_read_request(int fd) +{ + std::cout << "recv()\n"; + _read_ret = ::recv(fd, _buf, BUFSIZE, 0); + if (_read_ret == -1) + { + std::perror("err recv(): "); + if (::send(fd, MSG_BOUNCE, sizeof MSG_BOUNCE - 1, 0) == -1) + std::perror("err send(): "); + + ::close(fd); + return; + } + /* + if (_read_ret == BUFSIZE) + // send error like "request too long" to client + */ + _buf[_read_ret] = '\0'; + + struct epoll_event ev; + std::memset(&ev, 0, sizeof ev); + ev.events = EPOLLOUT; + ev.data.fd = fd; + if (::epoll_ctl(_epfd, EPOLL_CTL_MOD, fd, &ev) == -1) + std::perror("err accept() epoll_ctl(): "); +} + +void Webserv::_send_response(int fd) +{ + std::cout << "send()\n"; + if (::send(fd, _buf, _read_ret, 0) == -1) + std::perror("err send(): "); + if (::send(fd, MSG_TEST, sizeof MSG_TEST - 1, 0) == -1) + std::perror("err send(): "); + ::close(fd); +} diff --git a/srcs/main.cpp b/srcs/main.cpp new file mode 100644 index 0000000..5121ac7 --- /dev/null +++ b/srcs/main.cpp @@ -0,0 +1,21 @@ + +#include +#include +#include +#include + +int main(void) +{ + try + { + Webserv serv; + + serv.init_virtual_servers(); + serv.start(); + } + catch (std::exception& e) + { + std::cout << e.what() << '\n'; + } + return (0); +}