Files
42_INT_12_webserv/srcs/Client.cpp

363 lines
9.6 KiB
C++

#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(std::vector<ServerConfig> &servers)
{
clear_request(); // not mandatory
// debug
print_client("before");
_parse_request_line();
// debug
print_client("first line");
if (status)
return;
_parse_request_headers();
// 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<std::string, std::string>::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<std::string, std::string>::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<std::string> line;
std::string raw_line;
raw_line = ::get_line(raw_request, 0, CRLF);
// DEBUG
std::cout << "raw_line:[";
::print_special(raw_line);
std::cout << "]\n";
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_headers()
{
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(); }