#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) { /* Switch between normal behavior or CGI here ? maybe better than in _get(), _post(), ...*/ _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 switch (client->get_rq_method()) { case (GET): _get(client); break; case (POST): _post(client); break; case (DELETE): _delete(client); 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/") != 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); } // Temporary Global Scope. Probably move to Client in the future. ServerConfig *_determine_process_server(Client *client, std::vector &servers) { /* 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 *_determine_location_COOP_FIX(const ServerConfig &server, const std::string &path) { /* Pseudo-code : - comparer les size(), si location.path > client.path, stop comparaison. - client.path.compare(0, location.path.size(), location.path) if ( == 0) { if (location.path.size() == client.path.size()) { FOUND; } else if (client.path[location.path.size()] == '/') { FOUND; } } else { NOT FOUND; ++next; } */ std::vector::const_iterator it = server.locations.begin(); while (it != server.locations.end()) { if (it->path.size() > path.size()) { // prendre en compte l'éventuel "/" final si location est un dossier if (it->path.size()-1 > path.size() || it->path[it->path.size()-1] != '/') { ++it; continue; } } if (path.compare(0, it->path.size(), it->path) == 0) { if (it->path.size() == path.size()) break; else if (path[it->path.size()-1] == '/') break; } ++it; } if (it != server.locations.end()) return (&(*it)); else return (&(server.locations.back())); } // Temporary Global Scope. Probably move to Client in the future. const LocationConfig *_determine_location(const ServerConfig &server, const std::string &path) { 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 */ // IS THERE A WAY TO SIMPLIFY THIS LOGIC ??? // what if i tack on a / at the end of path if there isn't one // and then compare it to things that are their normal length? std::string tmp = path; if (tmp[tmp.size() - 1] != '/') tmp.push_back('/'); /test/index.html/ /test/index.html for (std::vector::const_iterator it = server.locations.begin(); it != server.locations.end(); it++) { std::cout << it->path << " -- "; if (it->path.size() > tmp.size()) continue ; if (tmp.compare(0, it->path.size(), it->path) == 0) { std::cout << "checking with last /\n"; if (it->path.size() == tmp.size()) { std::cout << "path sizes are equal \n"; return (&(*it)); } else if (tmp[it->path.size() - 1] == '/') { std::cout << "ends in /\n"; return (&(*it)); } } /* // std::cout << it->path[it->path.size() - 1] << " "; // it->path.size() -1 only when path ends in / because // if path doesn't end in / then we are looking for a file // meaning all it->paths that end in / are wrong if they >= // if (it->path[it->path.size() - 1] == '/' ? it->path.size() - 1 > path.size() : it->path.size() > path.size()) if (path[path.size() - 1] == '/' ? it->path.size() > path.size() : it->path.size() - 1 > path.size()) { std::cout << "skipping this one\n"; continue ; } // if (it->path[it->path.size() - 1] == '/') if (path[path.size() - 1] == '/') { if (path.compare(0, it->path.size(), it->path) == 0) { std::cout << "checking with last /\n"; if (it->path.size() == path.size()) { std::cout << "path sizes are equal \n"; return (&(*it)); } else if (path[it->path.size() - 1] == '/') { std::cout << "ends in /\n"; return (&(*it)); } } } else { if (path.size() <= it->path.size()) { std::cout << "path is missing a /\n"; if (it->path.compare(0, path.size(), path) == 0) return (&(*it)); // means we are looking for /test/test_deeper/ // with /test/test_deeper } else { // if (it->path.compare(0, it->path.size() - 1, path) == 0) if (path.compare(0, it->path.size(), it->path) == 0) { std::cout << "checking without last /\n"; if (it->path.size() - 1 == path.size()) return (&(*it)); else if (path[it->path.size() - 1] == '/') return (&(*it)); } } } */ } // if (it != server.locations.end()) // return (&(*it)); // else 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 */ } 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("")); }