#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") _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); 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; } 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->response.append("Connection: close" CRLF); else client->response.append("Connection: keep-alive" CRLF); } void Webserv::_construct_response(Client *client) { // TODO : Move this in read(), stop read if content too large if (client->get_rq_body().size() > client->assigned_server->client_body_limit) { client->status = 413; return; } /* if (client->assigned_location->redirect_status) { // (for codes 301, 302, 303, 307, and 308) client->status = client->assigned_location->redirect_status; client->response.append("Location: "); client->response.append(client->assigned_location->redirect_uri); client->response.append(CRLF); } */ if (client->get_rq_abs_path().find("redirect_test") != std::string::npos) // Test block { // Weird behavior. The web browser seems to wait for a complete response until timeout. // (for codes 301, 302, 303, 307, and 308) client->status = 307; client->response.append("Location: "); client->response.append("https://www.rfc-editor.org/rfc/rfc3875#section-3.3"); client->response.append(CRLF); client->response.append(CRLF); return ; } _process_method(client); } void Webserv::_process_method(Client *client) { std::cerr << "assigned_location->path = " << client->assigned_location->path << "\n"; // debug std::cerr << "allow_methods = " << client->assigned_location->allow_methods << "\n"; // debug if (client->get_rq_method() == UNKNOWN) { client->status = 501; } else if (client->assigned_location->allow_methods & client->get_rq_method()) { switch (client->get_rq_method()) { case (GET): _get(client); break; case (POST): _post(client); break; case (DELETE): _delete(client); break; default: break; } } else { client->status = 405; client->response.append("Allow: "); client->response.append(::http_methods_to_str(client->assigned_location->allow_methods)); client->response.append(CRLF); } } 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/") != std::string::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); } ServerConfig *Webserv::_determine_process_server(Client *client) { /* http://nginx.org/en/docs/http/request_processing.html _determine_process_server() should be complete. TODO : test it */ std::string const &server_name = client->get_rq_headers("Host"); std::vector::iterator it = _servers.begin(); std::vector::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 LocationConfig *Webserv::_determine_location(const ServerConfig &server, const std::string &path) const { std::cout << "determin location path sent: " << path << '\n'; /// NO FUCKING IDEA WHY BUT... // basically if 2 strings are identical to a point, compare from // longer one or it'll freak out cuz of \0 or something idk //// Basically: str.compare() from the larger string... /* 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 */ std::vector::const_iterator best; std::cout << "\nMade it to weird location picker case.\n"; for (std::vector::const_iterator it = server.locations.begin(); it != server.locations.end(); it++) { std::cout << it->path << " -- "; // if (rit->path.size() > path.size()) /* if ((rit->path[rit->path.size() - 1] == '/' ? rit->path.size() : rit->path.size() - 1) > path.size()) { std::cout << "skipping this one\n"; continue ; } */ // OK I REALLY DON"T LOVE THIS PART, BUT IT DOES WORK FOR NOW... // if (it->path[it->path.size() - 1] == '/' // && it->path.compare(0, it->path.size(), path + "/") == 0) // HOLD ON THIS MIGHT BE GOOD, BUT I COULD USE SOME HELP... //test /test/ //test/redirect/index /test/redirect //test/redirec_something // thing is reverse sorted // if location path is longer than path sent, don't look at it // otherwise compare if (path.compare(0, it->path.size(), it->path) == 0) // do another size compare and look for / if location smaller than client otherwise /redirect_something /redirect // compare everything // if (it->path[it->path.size() - 1] == '/' && it->path.compare(0, it->path.size() - 1, path) == 0) { best = it; std::cout << "Picked a best! 1\n"; } int comp = path.compare(0, rit->path.size(), rit->path); //int comp = rit->path.compare(0, rit->path.size() - 1, path); std::cout << "rit path size: " << rit->path.size() << " comp: " << comp << '\n'; // if (rit->path.compare(0, rit->path.size(), path) == 0) if (comp == 0) if (path.compare(0, it->path.size(), it->path) == 0) { best = it; std::cout << "Picked a best! 2\n"; } } // //test.comare(0, 5, /test/something) // /test /test/something return (&(*best)); } std::string Webserv::_determine_file_extension(const std::string &path) const { size_t dot_pos = path.rfind("."); if (dot_pos != std::string::npos && dot_pos + 1 < path.size()) return ( path.substr(dot_pos + 1) ); return (std::string("")); }