From 50ad8fd560070f81ef675c8cd3a5780ba4568ed7 Mon Sep 17 00:00:00 2001 From: nicolas-arnaud Date: Fri, 11 Nov 2022 13:44:12 +0100 Subject: [PATCH] missing files --- .gitignore | 1 + includes/Client.hpp | 5 +- srcs/sock/Client.cpp | 149 +++++++++++++++++++++++++++++++++++++++++++ srcs/sock/Master.cpp | 147 ++++++++++++++++++++++++++++++++++++++++++ srcs/tools.cpp | 10 +-- 5 files changed, 304 insertions(+), 8 deletions(-) create mode 100644 srcs/sock/Client.cpp create mode 100644 srcs/sock/Master.cpp diff --git a/.gitignore b/.gitignore index f3ab56d..7564a07 100644 --- a/.gitignore +++ b/.gitignore @@ -21,4 +21,5 @@ !srcs/*.cpp !srcs/load/ !srcs/json/ +!srcs/sock/ !srcs/*/*.cpp diff --git a/includes/Client.hpp b/includes/Client.hpp index c96cbeb..ce29a5f 100644 --- a/includes/Client.hpp +++ b/includes/Client.hpp @@ -16,9 +16,8 @@ class Client { bool getRequest(string paquet); bool parseHeader(); int answer(Env *env); - void call_cgi(string cgi, string path); - void send_404(); + void send_cgi(string cgi, string path); + void send_error(int error_code); void send_answer(string msg); - bool waitHeader(); friend class Master; }; diff --git a/srcs/sock/Client.cpp b/srcs/sock/Client.cpp new file mode 100644 index 0000000..911edcb --- /dev/null +++ b/srcs/sock/Client.cpp @@ -0,0 +1,149 @@ +#include "webserv.hpp" + +Client::Client(int fd, Master *parent) : _fd(fd), _parent(parent) {} +Client::~Client(void) { + close(_fd); + cout << "Destroyed client socket\n"; +} + +bool Client::getRequest(string paquet) { + cout << "|===|paquet|===>" << paquet << "|===||\n"; + if (paquet.length() < 1) // HTTPS? + return false; + std::vector< string > lines = split(paquet, '\n'); + long chunk_len = + (_content.length() > 0 && _request["Transfer-Encoding:"].size() && + _request["Transfer-Encoding:"].at(0) == "chunked") + ? std::strtol(lines.at(0).substr(1).c_str(), 0, 16) + : -1; + cout << "Chunk length: " << chunk_len << "\n"; + for (std::vector< string >::iterator it = lines.begin(); it < lines.end(); + it++) { + if (*it == "\r" && _content.length() == 0) + this->parseHeader(); + if (*it != "\r" && _content.length() == 0) + _header += *it + "\n"; + else if (chunk_len == -1 || it != lines.begin()) + _content += *it + "\n"; + } + cout << "Header: \n-|" << _header << "|-\n"; + cout << "Content: \n-|" << _content << "|-\n"; + if (_content.length() > 0) { + _content.resize(_content.length() - 1); + if (_request["Method:"].at(0) == "GET" || + (chunk_len == 0 || + std::strtoul(_request["Content-Length:"].at(0).c_str(), 0, 10) <= + _content.length())) { + cout << "Request received\n"; + return true; + } + } else + _header.resize(_header.length() - 1); + return false; +} + +bool Client::parseHeader() { + std::vector< string > lines = split(_header, '\n'); + std::vector< string > line; + if (lines.size() > 0) { + for (std::vector< string >::iterator it = lines.begin() + 1; + it < lines.end(); it++) { + line = split(*it, ' '); + cout << line.at(0) << "scraped from header\n"; + _request[line.at(0)] = + std::vector< string >(line.begin() + 1, line.end()); + } + } + std::vector< string > method = split(lines.at(0), ' '); + if (method.at(0) == "POST" && + _request.find("Content-Length:") == _request.end() && + _request.find("Transfer-Encoding:") == _request.end()) + return false; + _request["Method:"] = method; + return true; +} + +int Client::answer(Env *env) { + cout << "Method: " << _request["Method:"].at(0) << "\n"; + cout << "URI: " << _request["Method:"].at(1) << "\n"; + cout << "Host: " << _request["Host:"].at(0) << "\n"; + string ret; + + Server *server = _parent->choose_server(env, _request["Host:"].at(0)); + Route *route = server->choose_route(_request["Method:"].at(1)); + string method = _request["Method:"].at(0); + std::vector< string > allowed; + if (method != "GET" && method != "POST" && method != "DELETE") + send_error(405); + else if ((allowed = route->_headers).size() > 0) { + if (std::find(allowed.begin(), allowed.end(), method) == allowed.end()) + send_error(405); + } else if ((allowed = server->_headers).size() > 0) { + if (std::find(allowed.begin(), allowed.end(), method) == allowed.end()) + send_error(405); + } + + string path = route->correctUri(_request["Method:"].at(1)); + string cgi = route->_cgi.size() ? route->_cgi[get_extension(path)] : ""; + if (cgi == "") { + if ((ret = route->getIndex(_request["Method:"].at(1), path)) == "" && + (ret = read_file(path)) == "") + send_error(404); + else + send_answer("HTTP/1.1 200 OK\r\n" + ret); + } else + send_cgi(cgi, path); + _content = ""; + _header = ""; + return EXIT_SUCCESS; +} + +void Client::send_cgi(string cgi, string path) { + int status; + int fd[2]; + std::stringstream ss; + string ret; + + if (!std::ifstream(cgi.c_str()).good()) + send_error(404); // another error else ? + pipe(fd); + int pid = fork(); + if (pid == 0) { + const char **args = new const char *[cgi.length() + path.length() + 2]; + args[0] = cgi.c_str(); + args[1] = path.c_str(); + args[2] = NULL; + dup2(fd[1], STDOUT_FILENO); + close(fd[1]); + close(fd[0]); + execve(cgi.c_str(), (char **)args, NULL); + } + close(fd[1]); + waitpid(pid, &status, 0); + char buffer[10000]; + buffer[read(fd[0], buffer, 10000)] = 0; + ret = string(buffer); + ss << "HTTP/1.1 200 OK\r\nContent-length: " + << ret.length() - ret.find("\r\n\r\n") - 4 << "\r\n" + << ret; + send_answer(ss.str()); +} + +void Client::send_error(int error_code) { + switch (error_code) { + case 404: + return send_answer( + "HTTP/1.1 404 Not Found\r\nContent-length: 0\r\n\r\n"); + case 405: + return send_answer( + "HTTP/1.1 405 Method Not Allowed\r\nContent-length: 0\r\n\r\n"); + } +} + +void Client::send_answer(string msg) { +#ifdef __linux__ + send(_fd, msg.c_str(), msg.length(), MSG_NOSIGNAL); +#elif __APPLE__ + send(_fd, msg.c_str(), msg.length(), 0); +#endif +} diff --git a/srcs/sock/Master.cpp b/srcs/sock/Master.cpp new file mode 100644 index 0000000..98e91e5 --- /dev/null +++ b/srcs/sock/Master.cpp @@ -0,0 +1,147 @@ +#include "webserv.hpp" + +Master::Master(listen_t listen) : _listen(listen) {} +Master::~Master(void) { + close(_fd); + cout << "Destroyed master socket\n"; +} + +int Master::launch(void) { + int opt = 1; + string ip = _listen.ip; + int port = _listen.port; + + _fd = socket(AF_INET, SOCK_STREAM, 0); + if (_fd == 0) { + cout << "Socket creation: " << strerror(errno) << "\n"; + return (EXIT_FAILURE); + } + int opt_ret = + setsockopt(_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt)); + if (opt_ret < 0) { + cout << "Sockopt: " << strerror(errno) << "\n"; + return (EXIT_FAILURE); + } + + _address.sin_family = AF_INET; + _address.sin_addr.s_addr = inet_addr(ip.c_str()); + _address.sin_port = htons(port); + + if (bind(_fd, (struct sockaddr *)&_address, sizeof(_address)) < 0) { + cout << "Bind: " << strerror(errno) << "\n"; + return (EXIT_FAILURE); + } + cout << "Listener " << ip << " on port " << port << "\n"; + + if (listen(_fd, 3) < 0) { + cout << "Listen: " << strerror(errno) << "\n"; + return (EXIT_FAILURE); + } + cout << "Master: " << _fd << "\n"; + if (_fd < _min_fd) + _min_fd = _fd; + _amount++; + return (EXIT_SUCCESS); +} + +void Master::set_fds(void) { + FD_SET(_fd, &_readfds); + + int child_fd; + for (std::vector< Client * >::iterator it = _childs.begin(); + it < _childs.end(); it++) { + child_fd = (*it)->_fd; + FD_SET(child_fd, &_readfds); + if (child_fd > _max_fd) + _max_fd = child_fd; + } +} + +void Master::refresh(Env *env) { + int valread; + int addrlen = sizeof(_address); + char buffer[10000]; + if (FD_ISSET(_fd, &_readfds)) { + int new_socket = + accept(_fd, (struct sockaddr *)&_address, (socklen_t *)&addrlen); + if (new_socket < 0) { + cout << "Accept: " << strerror(errno) << "\n"; + exit(EXIT_FAILURE); + } +#ifdef __APPLE__ + // fcntl(new_socket, F_GETNOSIGPIPE); + fcntl(new_socket, F_SETFL, O_NONBLOCK); +#endif + cout << "New connection, socket fd is " << new_socket + << ", ip is : " << inet_ntoa(_address.sin_addr) + << ", port : " << ntohs(_address.sin_port) << "\n"; + _childs.push_back(new Client(new_socket, this)); + } + int child_fd; + for (std::vector< Client * >::iterator it = _childs.begin(); + it < _childs.end(); it++) { + child_fd = (*it)->_fd; + if (FD_ISSET(child_fd, &_readfds)) { + valread = read(child_fd, buffer, 10000); + if (valread == 0) { + getpeername(child_fd, (struct sockaddr *)&_address, + (socklen_t *)&addrlen); + cout << "Host disconnected, ip " << inet_ntoa(_address.sin_addr) + << ", port " << ntohs(_address.sin_port) << "\n"; + delete (*it); + _childs.erase(it); + } else { + buffer[valread] = '\0'; + if ((*it)->getRequest(buffer)) + (*it)->answer(env); + } + } + } +} + +Server *Master::choose_server(Env *env, string host) { + std::vector< Server * > exact; + std::vector< Server * > inrange; + std::vector< string > ip_listen; + std::vector< string > ip_required; + // string ip = inet_ntoa(sock->_address.sin_addr); + // int port = ntohs(sock->_address.sin_port); + + // cout << "Which server for " << ip << ":" << port << "?\n"; + cout << "Socket: " << _listen.ip << ":" << _listen.port << "\n"; + ip_required = split(_listen.ip, '.'); + for (std::vector< Server * >::iterator sit = env->_servers.begin(); + sit < env->_servers.end(); sit++) { + + std::vector< listen_t > serv_listens = (*sit)->_listens; + for (std::vector< listen_t >::iterator it = serv_listens.begin(); + it < serv_listens.end(); it++) { + + if (_listen.port != (*it).port) + continue; + if (_listen.ip == (*it).ip) { + exact.push_back(*sit); + continue; + } + bool is_inrange = true; + ip_listen = split((*it).ip, '.'); + std::vector< string >::iterator r = ip_required.begin(); + for (std::vector< string >::iterator l = ip_listen.begin(); + l < ip_listen.end(); l++) { + if (*l != *r && *l != "0") + is_inrange = false; + } + if (is_inrange == true) + inrange.push_back(*sit); + } + } + if (exact.size() == 0) { + for (std::vector< Server * >::iterator sit = inrange.begin(); + sit < inrange.end(); sit++) { + if (host == (*sit)->getName()) + return *sit; + } + return inrange.front(); + } else + return exact.front(); +} diff --git a/srcs/tools.cpp b/srcs/tools.cpp index f170fec..62983b3 100644 --- a/srcs/tools.cpp +++ b/srcs/tools.cpp @@ -221,15 +221,15 @@ string read_file(string path) { string str; string content; std::stringstream ret; - struct stat info; - if (stat(path.c_str(), &info) != 0 || S_ISDIR(info.st_mode)) { + // struct stat info; + /*if (stat(path.c_str(), &info) != 0 || S_ISDIR(info.st_mode)) { std::cerr << "stat() error on " << path << ": " << strerror(errno) << "\n"; return ""; - } + }*/ std::ifstream file(path.c_str()); - // if (!file.good()) - // return ""; + if (!file.good()) + return ""; while (file) { std::getline(file, str); content += str + "\n";