#include "Webserv.hpp" enum send_return { SEND_IN_PROGRESS, SEND_COMPLETE, SEND_CLOSE, }; void Webserv::_response(Client *client) { int ret = _send_response(client); print_secure(client->get_cl_fd(), "....._response()\n"); 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 || 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; print_secure(client->get_cl_fd(), "....._send_response()\n"); if (client->response.empty()) { _append_base_headers(client); print_secure(client->get_cl_fd(), "....>_send_response()\n"); } if (!client->status) { _construct_response(client); if (client->cgi_state == CGI_WAIT_TO_EXEC || client->cgi_state == CGI_OUTPUT_READING) return SEND_IN_PROGRESS; print_secure(client->get_cl_fd(), "....>_send_response()\n"); } _insert_status_line(client); print_secure(client->get_cl_fd(), "....>_send_response()\n"); if (client->status >= 400) { _error_html_response(client); print_secure(client->get_cl_fd(), "....>_send_response()\n"); } print_secure(client->get_cl_fd(), "send()\n"); ret = ::send(client->get_cl_fd(), client->response.c_str(), client->response.size(), 0); if (ret == -1) { std::perror("err send()"); return SEND_CLOSE; } if (ret == 0) { std::cerr << "send() ret == 0 never happens ?" << "\n"; // Debug return SEND_CLOSE; } return SEND_COMPLETE; } void Webserv::_append_base_headers(Client *client) { client->response.append("Server: Webserv/0.1" CRLF); print_secure(client->get_cl_fd(), "....._append_base_headers()\n"); if (client->get_rq_headers("Connection") == "close" || client->status == 400 || 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; print_secure(client->get_cl_fd(), "....._construct_response()\n"); path = _replace_url_root(client, client->get_rq_abs_path()); print_secure(client->get_cl_fd(), "....>_construct_response()\n"); if (client->cgi_state == CGI_READY_TO_EXEC) { _write_body_to_cgi(client); print_secure(client->get_cl_fd(), "....>_construct_response()\n"); if (client->status) return; _exec_cgi(client); } else if (client->cgi_state == CGI_OUTPUT_COMPLETE) { _check_script_output(client, client->cgi_output); print_secure(client->get_cl_fd(), "....>_construct_response()\n"); if (client->status < 400 || client->status >= 600) client->response += client->cgi_output; } else if (_is_cgi(client, path)) _cgi_open_pipes(client); else _process_method(client, path); } void Webserv::_process_method(Client *client, std::string &path) { print_secure(client->get_cl_fd(), "....._process_method()\n"); 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) { print_secure(client->get_cl_fd(), "....._replace_url_root()\n"); 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); 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]; print_secure(client->get_cl_fd(), "....._insert_status_line()\n"); 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) { print_secure(client->get_cl_fd(), "....._error_html_response()\n"); std::cerr << "_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); print_secure(client->get_cl_fd(), "....>_error_html_response()\n"); _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]; print_secure(client->get_cl_fd(), "....._append_body()\n"); 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); }