#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)); } // 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'; /* 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 New Rule for location paths, they never end in / Sooo If we get a url that ends in / ignore the last / */ std::string uri = path; if (uri[uri.size() - 1] == '/') uri.erase(uri.size() - 1); for (std::vector::const_iterator it = server.locations.begin(); it != server.locations.end(); it++) { std::cout << it->path << " -- "; if (it->path.size() > uri.size()) { std::cout << "skipping this one\n"; continue ; } if (uri.compare(0, it->path.size(), it->path) == 0) { if (it->path.size() == uri.size()) return (&(*it)); else if (uri[it->path.size()] == '/') return (&(*it)); // this works cuz only ever looking for a / burried in a longer path } } 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 /test/test_deeper/super_deep/ /test/aaaaaaaaaaa/super_deep/ */ } 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("")); }