Files
42_INT_12_webserv/srcs/webserv/response.cpp

216 lines
6.1 KiB
C++

#include "Webserv.hpp"
enum send_return
{
SEND_IN_PROGRESS,
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)
// || client->cgi_state == CGI_OUTPUT_COMPLETE) // DEBUG
_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";
if (client->response.empty())
{
_append_base_headers(client);
}
if (!client->status)
{
_construct_response(client);
if (client->cgi_state == CGI_WAIT_TO_EXEC
|| client->cgi_state == CGI_OUTPUT_READING)
return SEND_IN_PROGRESS;
}
_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";
// /* Debug */ 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;
}
// /* Debug */ std::cerr << "ret send() = " << ret << "\n"; // DEBUG
return SEND_COMPLETE;
}
void Webserv::_append_base_headers(Client *client)
{
std::cerr << "_append_base_headers()\n"; // debug
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);
}
void Webserv::_construct_response(Client *client)
{
std::string path;
std::string script_output;
path = _replace_url_root(client, client->get_rq_abs_path());
if (client->cgi_state == CGI_READY_TO_EXEC)
{
_write_body_to_cgi(client);
if (client->status)
return;
_exec_cgi(client);
}
else if (client->cgi_state == CGI_OUTPUT_COMPLETE)
{
// /*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(client->cgi_output); std::cout << B_PURPLE "-----------" RESET "\n\n";
_check_script_output(client, client->cgi_output); // FD_CGI : adjust for client->cgi_output;
if (client->status < 400 || client->status >= 600)
client->response += client->cgi_output;
// client->cgi_state = CGI_NO_CGI; // Not indispensable, reset when client.clear()
// /*DEBUG*/ std::cout << "\n" B_PURPLE "[response + output]:" RESET "\n"; ::print_special(client->response); std::cout << B_PURPLE "-----------" RESET "\n\n";
}
else if (_is_cgi(client, path))
_cgi_open_pipes(client);
else
_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
std::cerr << "Path again: " << path << '\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;
}
}
std::string Webserv::_replace_url_root(Client *client, std::string path)
{
// /* Debug */ std::cerr << "assigned_location->path = " << client->assigned_location->path << "\n"; // debug
// /* Debug */ std::cerr << "path before = " << path << "\n"; // DEBUG
if (client->assigned_location->path == "/")
path.insert(0, client->assigned_location->root);
else
path.replace(0, client->assigned_location->path.size(), client->assigned_location->root);
// /* Debug */ std::cerr << "path after = " << path << "\n"; // DEBUG
return path;
}
/*
https://www.rfc-editor.org/rfc/rfc9112.html#name-status-line
*/
void Webserv::_insert_status_line(Client *client)
{
std::string status_line;
std::string status = _http_status[client->status];
if (status.empty())
status = ::itos(client->status);
status_line.append("HTTP/1.1 ");
status_line.append(status);
status_line.append(CRLF);
client->response.insert(0, status_line);
}
void Webserv::_error_html_response(Client *client)
{
std::cout << "_error_html_response()\n";
if (client->assigned_server
&& !client->assigned_server->error_pages[client->status].empty()
&& ::eval_file_access(client->assigned_server->error_pages[client->status], R_OK) == 0 )
{
_get_file(client, client->assigned_server->error_pages[client->status]);
}
else
{
std::string status = _http_status[client->status];
if (status.empty())
status = "Error " + ::itos(client->status);
std::string html_page = HTML_ERROR;
::replace_all_substr(html_page, STATUS_PLACEHOLDER, status);
_append_body(client, html_page, "html");
}
}
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);
}