#include "Client.hpp" /********************************************* * COPLIENS *********************************************/ Client::Client() : status(0), header_complete(false), read_body_size(0), assigned_server(NULL), assigned_location(NULL), _fd(0), _port(""), _ip(""), _lsocket(NULL) { return; } Client::Client(int afd, listen_socket *lsocket, std::string aport, std::string aip) : status(0), header_complete(false), read_body_size(0), assigned_server(NULL), assigned_location(NULL), _fd(afd), _port(aport), _ip(aip), _lsocket(lsocket) { return; } Client::~Client() { return; } /* // WIP not sure fo what is more logic here Client::Client( Client const & src ) : status ( src.status ), header_complete ( src.header_complete ), read_body_size ( src.read_body_size ), assigned_server ( src.assigned_server ), assigned_location ( src.assigned_location ), _fd ( src._fd ), _port ( src._port ), _ip ( src._ip ), _lsocket ( src._lsocket ) { raw_request = src.raw_request; response = src.response; // buf = strdup(src.buf); // TODO: this doesn't work return; } */ /* // WIP placeholder because of const values Client & Client::operator=( Client const & rhs ) { if ( this != &rhs ) { // stuff } return *this; } */ /********************************************* * PUBLIC MEMBER FUNCTIONS *********************************************/ // http headers : // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers // https://www.ibm.com/docs/en/cics-ts/5.3?topic=protocol-http-requests // https://www.tutorialspoint.com/http/http_requests.htm void Client::parse_request_headers(std::vector &servers) { clear_request(); // not mandatory _parse_request_line(); // debug print_client("first line"); if (status) return; _parse_request_fields(); // debug print_client("headers"); if (status) return; assigned_server = ::_determine_process_server(this, servers); assigned_location = ::_determine_location(*assigned_server, _request.abs_path); _check_request_errors(); if (status) return; // use getter for headers because it works case insensitive _parse_port_hostname(this->get_rq_headers("Host")); // dont clear raw_request, we need it for future reparsing of body // see call of parse_request() in _read_request() // raw_request.clear(); } void Client::parse_request_body() { size_t pos; pos = raw_request.find(CRLF CRLF); pos += std::string(CRLF CRLF).size(); _request.body = raw_request.substr(pos); if (_request.body.size() > assigned_server->client_body_limit) status = 413; // HTTP Client Errors } bool Client::fill_script_path(std::string script) { size_t pos; int len = script.size(); std::string path = this->get_rq_abs_path(); std::string tmp; pos = path.find(script); if (pos == 0) { tmp = path.substr(0, pos + len); _request.script.path = "./srcs" + tmp; // TODO: root path ? _request.script.info = path.substr(pos + len); return true; } return false; } void Client::clear() { clear_request(); header_complete = false; read_body_size = 0; assigned_server = NULL; assigned_location = NULL; raw_request.clear(); response.clear(); status = 0; } void Client::clear_request() { clear_script(); _request.method = UNKNOWN; _request.uri.clear(); _request.version.clear(); _request.headers.clear(); _request.body.clear(); _request.abs_path.clear(); _request.query.clear(); _request.port.clear(); _request.hostname.clear(); } void Client::clear_script() { _request.script.path.clear(); _request.script.info.clear(); } // debug void Client::print_client(std::string message) { std::map::iterator it; std::cout << "\n=== DEBUG PRINT CLIENT ===\n"; std::cout << "\n" << message << ":----------\n\n" << "raw_request:\n__\n"; ::print_special(raw_request); std::cout << "\n__\n" << "get_cl_fd() : [" << get_cl_fd() << "]\n" << "get_cl_port() : [" << get_cl_port() << "]\n" << "get_cl_ip() : [" << get_cl_ip() << "]\n" << "get_rq_method_str() : [" << get_rq_method_str() << "]\n" << "get_rq_uri() : [" << get_rq_uri() << "]\n" << "get_rq_abs_path() : [" << get_rq_abs_path() << "]\n" << "get_rq_query() : [" << get_rq_query() << "]\n" << "get_rq_version() : [" << get_rq_version() << "]\n" << "get_rq_body() : [" << get_rq_body() << "]\n" << "get_rq_port() : [" << get_rq_port() << "]\n" << "get_rq_hostname() : [" << get_rq_hostname() << "]\n" << "get_rq_script_path() : [" << get_rq_script_path() << "]\n" << "get_rq_script_info() : [" << get_rq_script_info() << "]\n" << "headers :\n"; for (it = _request.headers.begin(); it != _request.headers.end(); it++) std::cout << " " << it->first << ": " << it->second << "\n"; std::cout << "\n=== END DEBUG ===\n\n"; } /********************************************* * GETTERS *********************************************/ // client side int Client::get_cl_fd() const { return _fd; } const std::string & Client::get_cl_ip() const { return _ip; } const std::string & Client::get_cl_port() const { return _port; } const listen_socket * Client::get_cl_lsocket() const { return _lsocket; } // requette http_method Client::get_rq_method() const { return _request.method; } std::string Client::get_rq_method_str() const { return ::http_methods_to_str(_request.method); } std::string Client::get_rq_uri() const { return _request.uri; } std::string Client::get_rq_abs_path() const { return _request.abs_path; } std::string Client::get_rq_query() const { return _request.query; } std::string Client::get_rq_version() const { return _request.version; } std::string Client::get_rq_body() const { return _request.body; } std::string Client::get_rq_port() const { return _request.port; } std::string Client::get_rq_hostname() const { return _request.hostname; } std::string Client::get_rq_script_path()const { return _request.script.path; } std::string Client::get_rq_script_info()const { return _request.script.info; } std::string Client::get_rq_headers(const std::string & key) const { std::map::const_iterator it; it = _request.headers.find(::str_tolower(key)); if (it == _request.headers.end()) return ""; return it->second; } /********************************************* * PRIVATE MEMBER FUNCTIONS *********************************************/ void Client::_parse_request_line() { std::vector line; std::string raw_line; raw_line = ::get_line(raw_request, 0, CRLF); line = ::split_trim(raw_line, " "); if (line.size() != 3) { std::cerr << "err _parse_first_line(): wrong number of elements (" << line.size() << " instead of 3)\n"; status = 400; // "bad request" } else { _request.method = str_to_http_method(line[0]); _request.uri = line[1]; _parse_request_uri(line[1]); _request.version = line[2]; } } void Client::_parse_request_uri( std::string uri ) { size_t pos; pos = uri.find("?"); if (pos != std::string::npos) _request.query = uri.substr(pos + 1); else _request.query = ""; _request.abs_path = uri.substr(0, pos); } void Client::_parse_request_fields() { std::string headers; size_t pos; int ret; headers = raw_request; // extract header part ::extract_line(headers, 0, CRLF); // delete first line pos = headers.find(CRLF CRLF); // ::extract_line(headers, pos); // delete from empty line to the end headers.erase(pos); //delete from empty line to the end // copy result of parser into headers ret = ::parse_http_headers(raw_request, _request.headers); if (ret > 0) { std::cerr << "err _parse_request_headers(): " << ret << " field are bad formated\n"; status = 400; // "bad request" } } void Client::_parse_port_hostname(std::string host) { size_t pos; if (host == "") std::cerr << "no host\n"; pos = host.find(':'); // port : if (pos == std::string::npos) _request.port = "4040"; // TODO: make equal to default port in config else _request.port = host.substr(pos); if (_request.port == ":") _request.port = ""; // hostname : _request.hostname = host.substr(0, pos); } void Client::_check_request_errors() { ////////////////////// // Request line checks if (_request.method == UNKNOWN) status = 501; // HTTP Client Errors else if (_request.version.compare(0, sizeof("HTTP/1") - 1, "HTTP/1") != 0) status = 505; // HTTP Client Errors else if (!(assigned_location->allow_methods & _request.method)) { status = 405; // HTTP Client Errors response.append("Allow: "); response.append(::http_methods_to_str(assigned_location->allow_methods)); response.append(CRLF); } else if (assigned_location->redirect_status) { // Weird behavior. Sometimes, the web browser seems to wait for a complete response until timeout. // (for codes 301, 302, 303, 307, and 308) status = assigned_location->redirect_status; response.append("Location: "); response.append(assigned_location->redirect_uri); response.append(CRLF CRLF); } if (status) return; ///////////////// // Headers checks if (!this->get_rq_headers("Content-Length").empty() && ::atoi(this->get_rq_headers("Content-Length").c_str()) > (int)assigned_server->client_body_limit) status = 413; // HTTP Client Errors return; } /********************************************* * OVERLOAD *********************************************/ bool operator==(const Client& lhs, const Client& rhs) { return lhs.get_cl_fd() == rhs.get_cl_fd(); } bool operator==(const Client& lhs, int fd) { return lhs.get_cl_fd() == fd; } bool operator==(int fd, const Client& rhs) { return fd == rhs.get_cl_fd(); }