Files
42_INT_12_webserv/srcs/webserv/response.cpp
2022-08-15 21:18:06 +02:00

275 lines
7.1 KiB
C++

#include "Webserv.hpp"
enum send_return
{
SEND_IN_PROGRESS, // unused
SEND_COMPLETE,
SEND_CLOSE,
};
void Webserv::_response(Client *client)
{
int ret = _send_response(client);
if (g_last_signal)
_handle_last_signal();
if (ret == SEND_CLOSE)
{
_close_client(client->get_cl_fd());
}
else if (ret == SEND_COMPLETE)
{
if (client->get_rq_headers("Connection") == "close"
|| client->status == 400 // TODO: Refactoring
|| client->status == 408
|| client->status == 413)
_close_client(client->get_cl_fd());
else
{
_epoll_update(client->get_cl_fd(), EPOLLIN, EPOLL_CTL_MOD);
client->clear();
}
}
}
int Webserv::_send_response(Client *client)
{
ssize_t ret;
std::cerr << "send()\n";
_append_base_headers(client);
if (!client->status)
_construct_response(client);
_insert_status_line(client);
if (client->status >= 400)
_error_html_response(client);
/*DEBUG*/ std::cout << "\n" B_PURPLE "[response + output + headers]:" RESET "\n"; ::print_special(client->response); std::cout << "\n" B_PURPLE "-----------" RESET "\n\n";
std::cerr << "client->response.size() = " << client->response.size() << "\n"; // DEBUG
ret = ::send(client->get_cl_fd(), client->response.c_str(), client->response.size(), 0);
if (ret == -1)
{
std::perror("err send()");
std::cerr << "client.fd =" << client->get_cl_fd() << "\n"; // DEBUG
return SEND_CLOSE;
}
if (ret == 0) // actually never happen ?
{
std::cerr << "SEND RET 0 for client.fd =" << client->get_cl_fd() << "\n"; // DEBUG
return SEND_CLOSE;
}
std::cerr << "ret send() = " << ret << "\n"; // DEBUG
return SEND_COMPLETE;
}
void Webserv::_append_base_headers(Client *client)
{
client->response.append("Server: Webserv/0.1" CRLF);
if (client->get_rq_headers("Connection") == "close"
|| client->status == 400 // TODO: Refactoring
|| client->status == 408
|| client->status == 413)
client->response.append("Connection: close" CRLF);
else
client->response.append("Connection: keep-alive" CRLF);
}
// TODO HUGO : wip
void Webserv::_construct_response(Client *client)
{
std::string path;
std::string script_output;
path = _replace_url_root(client, client->get_rq_abs_path());
if (_is_cgi(client, path))
{
script_output = _exec_cgi(client);
/*DEBUG*/ std::cout << "\n" B_PURPLE "[response]:" RESET "\n"; ::print_special(client->response); std::cout << B_PURPLE "-----------" RESET "\n\n";
/*DEBUG*/ std::cout << "\n" B_PURPLE "[script output]:" RESET "\n"; ::print_special(script_output); std::cout << B_PURPLE "-----------" RESET "\n\n";
_check_script_output(client, script_output);
client->response += script_output;
///*DEBUG*/ std::cout << B_YELLOW "inside cgi" RESET "\n";
/*DEBUG*/ std::cout << "\n" B_PURPLE "[response + output]:" RESET "\n"; ::print_special(client->response); std::cout << B_PURPLE "-----------" RESET "\n\n";
return;
}
_process_method(client, path);
}
void Webserv::_process_method(Client *client, std::string &path)
{
std::cerr << "allow_methods = " << http_methods_to_str(client->assigned_location->allow_methods) << "\n"; // debug
switch (client->get_rq_method())
{
case (GET):
_get(client, path); break;
case (POST):
_post(client, path); break;
case (DELETE):
_delete(client, path); break;
default:
break;
}
}
void Webserv::_insert_status_line(Client *client)
{
std::string status_line;
status_line.append("HTTP/1.1 ");
status_line.append(_http_status[client->status]);
status_line.append(CRLF);
client->response.insert(0, status_line);
}
void Webserv::_error_html_response(Client *client)
{
if (!client->assigned_server || client->assigned_server->error_pages[client->status].empty())
{
std::string html_page = HTML_ERROR;
::replace_all_substr(html_page, STATUS_PLACEHOLDER, _http_status[client->status]);
_append_body(client, html_page, "html");
}
else
_get_file(client, client->assigned_server->error_pages[client->status]);
}
void Webserv::_append_body(Client *client, const std::string &body, const std::string &file_extension)
{
const std::string &mime_type = _mime_types[file_extension];
client->response.append("Content-Type: ");
if (mime_type.empty())
client->response.append(MIME_TYPE_DEFAULT);
else
{
client->response.append(mime_type);
if (mime_type.find("text/") != NPOS)
client->response.append("; charset=UTF-8");
}
client->response.append(CRLF);
client->response.append("Content-Length: ");
std::string tmp = ::itos(body.size());
client->response.append(tmp);
client->response.append(CRLF);
client->response.append(CRLF);
client->response.append(body);
}
// Temporary Global Scope. Probably move to Client in the future.
ServerConfig *_determine_process_server(Client *client, std::vector<ServerConfig> &servers)
{
/*
Behavior like this :
http://nginx.org/en/docs/http/request_processing.html
*/
std::string server_name = client->get_rq_headers("Host");
std::cerr << "server_name = " << server_name << "\n";
size_t pos = server_name.rfind(':');
if (pos != NPOS)
server_name.erase(pos);
std::cerr << "server_name = " << server_name << "\n";
std::vector<ServerConfig>::iterator it = servers.begin();
std::vector<ServerConfig>::iterator default_server = servers.end();
while (it != servers.end())
{
if (it->host == client->get_cl_lsocket()->host
&& it->port == client->get_cl_lsocket()->port)
{
if ( std::find(it->server_name.begin(), it->server_name.end(), server_name) != it->server_name.end() )
break;
else if (default_server == servers.end())
default_server = it;
}
++it;
}
if (it != servers.end())
return (&(*it));
else
return (&(*default_server));
}
// const?
// Temporary Global Scope. Probably move to Client in the future.
// is it still TMP Global Scope?
const LocationConfig *_determine_location(const ServerConfig &server, const std::string &path)
{
/* RULES ***
If a path coresponds exactly to a location, use that one
if no path coresponds then use the most correct one
most correct means the most precise branch that is still above
the point we are aiming for
New Rule for location paths, they never end in /
Sooo
If we get a url that ends in / ignore the last /
*/
std::string uri = path;
if (uri[uri.size() - 1] == '/' && uri.size() != 1)
uri.erase(uri.size() - 1);
for (std::vector<LocationConfig>::const_iterator it = server.locations.begin(); it != server.locations.end(); it++)
{
// std::cout << it->path << " -- ";
if (it->path.size() > uri.size())
continue ;
if (uri.compare(0, it->path.size(), it->path) == 0)
{
if (it->path.size() == uri.size())
return (&(*it));
else if (uri[it->path.size()] == '/')
return (&(*it));
// this works cuz only ever looking for a / burried in a longer path
}
}
return (&(server.locations.back()));
// /test/mdr
// /test/mdr/
// /test/mdrBST
/* More stuff to check this still works with ***
/test/test_
/test/test_/
/test/test_deeper
/test/test_deeper/
/test/test_deepei
/test/test_deepei/
/test/test_deeperi
/test/test_deeper/super_deep/
/test/aaaaaaaaaaa/super_deep/
*/
}
std::string Webserv::_determine_file_extension(const std::string &path) const
{
size_t dot_pos = path.rfind(".");
if (dot_pos != NPOS && dot_pos + 1 < path.size())
return ( path.substr(dot_pos + 1) );
return (std::string(""));
}