#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) _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_pipe_rfd) return SEND_IN_PROGRESS; } } else if (client->cgi_pipe_rfd) { // /*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, client->cgi_output); // FD_CGI : adjust for client->cgi_output; if (client->status < 400) client->response += client->cgi_output; // /*DEBUG*/ std::cout << "\n" B_PURPLE "[response + output]:" RESET "\n"; ::print_special(client->response); std::cout << B_PURPLE "-----------" RESET "\n\n"; } _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)) { _exec_cgi(client); 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; } } std::string Webserv::_replace_url_root(Client *client, std::string path) { std::cerr << "assigned_location->path = " << client->assigned_location->path << "\n"; // 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); 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); }