|
@ -1,200 +1,312 @@ |
|
|
/**
|
|
|
/**
|
|
|
* @file Client.cpp |
|
|
* @file Client.cpp |
|
|
* @brief The client sockets class which keep keep clients information and handle answer to them. |
|
|
* @brief The client sockets class which keep keep clients information and |
|
|
|
|
|
* handle answer to them. |
|
|
* @author Narnaud |
|
|
* @author Narnaud |
|
|
* @version 0.1 |
|
|
* @version 0.1 |
|
|
* @date 2023-01-12 |
|
|
* @date 2023-01-12 |
|
|
*/ |
|
|
*/ |
|
|
#include "webserv.hpp" |
|
|
#include "webserv.hpp" |
|
|
|
|
|
#include <iomanip> |
|
|
|
|
|
#include <iostream> |
|
|
|
|
|
|
|
|
inline string get_extension(string str) { |
|
|
inline string get_extension(string str) { |
|
|
size_t pos = str.rfind('.'); |
|
|
size_t pos = str.rfind('.'); |
|
|
if (pos != string::npos) return str.substr(pos); |
|
|
if (pos != string::npos) |
|
|
else return ""; |
|
|
return str.substr(pos); |
|
|
|
|
|
else |
|
|
|
|
|
return ""; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
Client::Client(int fd, ip_port_t ip_port, Master *parent) : _fd(fd), _ip_port(ip_port), _parent(parent) { |
|
|
Client::Client(int fd, ip_port_t ip_port, Master *parent) |
|
|
init(); |
|
|
: _fd(fd), _ip_port(ip_port), _parent(parent) { |
|
|
if (!SILENT) |
|
|
_requests_done = 0; |
|
|
cout << "New connection, socket fd is " << fd << ", ip is : " << _ip_port.ip << ", port : " << _ip_port.port |
|
|
_death_time = 0; |
|
|
<< "\n"; |
|
|
_finish = false; |
|
|
|
|
|
_route = NULL; |
|
|
|
|
|
_server = NULL; |
|
|
|
|
|
init(); |
|
|
|
|
|
if (!SILENT) |
|
|
|
|
|
cout << "New connection, socket fd is " << fd << ", ip is : " << _ip_port.ip |
|
|
|
|
|
<< ", port : " << _ip_port.port << "\n"; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
Client::~Client(void) { |
|
|
Client::~Client(void) { |
|
|
close(_fd); |
|
|
close(_fd); |
|
|
Master::_pollfds[_poll_id].fd = 0; |
|
|
Master::_pollfds[_poll_id].fd = 0; |
|
|
Master::_pollfds[_poll_id].events = 0; |
|
|
Master::_pollfds[_poll_id].events = 0; |
|
|
Master::_pollfds[_poll_id].revents = 0; |
|
|
Master::_pollfds[_poll_id].revents = 0; |
|
|
Master::_poll_id_amount--; |
|
|
Master::_poll_id_amount--; |
|
|
_headers.clear(); |
|
|
_headers.clear(); |
|
|
if (!SILENT) cout << "Host disconnected, ip " << _ip_port.ip << ", port " << _ip_port.port << "\n"; |
|
|
if (!SILENT) |
|
|
|
|
|
cout << "Host disconnected, ip " << _ip_port.ip << ", port " |
|
|
|
|
|
<< _ip_port.port << "\n"; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
void Client::init(void) { |
|
|
void Client::init(void) { |
|
|
_finish = false; |
|
|
_requests_done++; |
|
|
_server = NULL; |
|
|
if (_route && _route->_max_requests > 0) { |
|
|
_route = NULL; |
|
|
if (_requests_done > _route->_max_requests) |
|
|
_method = _uri = _host = _header = _body = ""; |
|
|
_finish = true; |
|
|
_len = 0; |
|
|
} else if (_server && _server->_max_requests > 0) { |
|
|
_last_chunk = false; |
|
|
if (_requests_done > _server->_max_requests) |
|
|
_headers.clear(); |
|
|
_finish = true; |
|
|
|
|
|
} |
|
|
|
|
|
_method = _uri = _host = _header = _body = ""; |
|
|
|
|
|
_len = 0; |
|
|
|
|
|
_last_chunk = false; |
|
|
|
|
|
_headers.clear(); |
|
|
|
|
|
} |
|
|
|
|
|
template <typename T> void tab(T t, const int &width) { |
|
|
|
|
|
std::cout << std::left << std::setw(width) << std::setfill(' ') << t; |
|
|
|
|
|
} |
|
|
|
|
|
void Client::debug(bool head) { |
|
|
|
|
|
|
|
|
|
|
|
if (head) { |
|
|
|
|
|
std::cout << "Client " << _poll_id |
|
|
|
|
|
<< " debug ===================================\n"; |
|
|
|
|
|
tab("Fd", 4); |
|
|
|
|
|
tab("Ip", 12); |
|
|
|
|
|
tab("Port", 6); |
|
|
|
|
|
tab("Servername", 12); |
|
|
|
|
|
tab("Route", 12); |
|
|
|
|
|
tab("Method", 6); |
|
|
|
|
|
tab("URI", 20); |
|
|
|
|
|
tab("Query", 12); |
|
|
|
|
|
tab("Host", 12); |
|
|
|
|
|
tab("Len", 6); |
|
|
|
|
|
tab("Keep", 4); |
|
|
|
|
|
tab("Death", 6); |
|
|
|
|
|
tab("Request", 6); |
|
|
|
|
|
tab("Finish", 6); |
|
|
|
|
|
std::cout << "\n"; |
|
|
|
|
|
} |
|
|
|
|
|
tab(_fd, 4); |
|
|
|
|
|
tab(_ip_port.ip, 12); |
|
|
|
|
|
tab(_ip_port.port, 6); |
|
|
|
|
|
tab(_server->getName(), 12); |
|
|
|
|
|
tab(_route->getLocation(), 12); |
|
|
|
|
|
tab(_method, 6); |
|
|
|
|
|
tab(_uri, 20); |
|
|
|
|
|
tab(_query, 12); |
|
|
|
|
|
tab(_host, 12); |
|
|
|
|
|
tab(_len, 6); |
|
|
|
|
|
tab(_keepalive, 4); |
|
|
|
|
|
tab(_death_time, 6); |
|
|
|
|
|
tab(_requests_done, 6); |
|
|
|
|
|
tab(_finish, 6); |
|
|
|
|
|
std::cout << "\n"; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
bool Client::getRequest(Env *env, string paquet) { |
|
|
bool Client::getRequest(Env *env, string paquet) { |
|
|
if (paquet.length() < 1) send_error(403); |
|
|
if (DEBUG) |
|
|
if (DEBUG) debug_block("Paquet: ", paquet); |
|
|
debug_block("Paquet: ", paquet); |
|
|
if (header_pick("Method:", 0) != "") return getBody(paquet); |
|
|
if (header_pick("Method:", 0) != "") |
|
|
vec_string lines = split(paquet, "\r\n"); |
|
|
return getBody(paquet); |
|
|
for (vec_string::iterator it = lines.begin(); it < lines.end(); it++) { |
|
|
vec_string lines = split(paquet, "\r\n"); |
|
|
size_t pos = paquet.find("\r\n"); |
|
|
for (vec_string::iterator it = lines.begin(); it < lines.end(); it++) { |
|
|
if (pos != string::npos) paquet.erase(0, pos + 2); |
|
|
size_t pos = paquet.find("\r\n"); |
|
|
else paquet.clear(); |
|
|
if (pos != string::npos) |
|
|
_header += *it + (it + 1 != lines.end() ? "\r\n" : ""); |
|
|
paquet.erase(0, pos + 2); |
|
|
if (_header.find("\r\n\r\n") != string::npos) |
|
|
else |
|
|
return !parseHeader(env) ? false : (_len != 0 ? getBody(paquet) : true); |
|
|
paquet.clear(); |
|
|
} |
|
|
_header += *it + (it + 1 != lines.end() ? "\r\n" : ""); |
|
|
return false; |
|
|
if (_header.find("\r\n\r\n") != string::npos) |
|
|
|
|
|
return !parseHeader(env) ? false : (_len != 0 ? getBody(paquet) : true); |
|
|
|
|
|
} |
|
|
|
|
|
return false; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
bool Client::getBody(string paquet) { |
|
|
bool Client::getBody(string paquet) { |
|
|
vec_string lines = split(paquet, "\r\n"); |
|
|
vec_string lines = split(paquet, "\r\n"); |
|
|
vec_string::iterator it; |
|
|
vec_string::iterator it; |
|
|
|
|
|
|
|
|
for (it = lines.begin(); it < lines.end(); it++) { |
|
|
for (it = lines.begin(); it < lines.end(); it++) { |
|
|
if (DEBUG) cout << "Remaining length: " << _len << "\n"; |
|
|
if (DEBUG) |
|
|
if ((*it).length() && _len <= 0 && header_pick("Transfer-Encoding:", 0) == "chunked") { |
|
|
cout << "Remaining length: " << _len << "\n"; |
|
|
_len = std::strtol((*it).c_str(), 0, 16) + 2; |
|
|
if ((*it).length() && _len <= 0 && |
|
|
_last_chunk = _len == 2 ? true : false; |
|
|
header_pick("Transfer-Encoding:", 0) == "chunked") { |
|
|
} else if (_len > 0 || it != lines.begin()) { |
|
|
_len = std::strtol((*it).c_str(), 0, 16) + 2; |
|
|
_body += *it + "\r\n"; |
|
|
_last_chunk = _len == 2 ? true : false; |
|
|
_len -= ((*it).length() + 2); |
|
|
} else if (_len > 0 || it != lines.begin()) { |
|
|
} |
|
|
_body += *it + "\r\n"; |
|
|
} |
|
|
_len -= ((*it).length() + 2); |
|
|
// if (_body.size())
|
|
|
} |
|
|
_body.resize(_body.length() - 2); |
|
|
} |
|
|
_len += 2; |
|
|
// if (_body.size())
|
|
|
return (_last_chunk && _len == 0) ? true : false; |
|
|
_body.resize(_body.length() - 2); |
|
|
|
|
|
_len += 2; |
|
|
|
|
|
return (_last_chunk && _len == 0) ? true : false; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
bool Client::parseHeader(Env *env) { |
|
|
bool Client::parseHeader(Env *env) { |
|
|
vec_string lines, method, line; |
|
|
vec_string lines, method, line; |
|
|
|
|
|
if (DEBUG) |
|
|
if (DEBUG) cout << "Parsing header...\n"; |
|
|
cout << "Parsing header...\n"; |
|
|
lines = split(_header, "\r\n"); |
|
|
lines = split(_header, "\r\n"); |
|
|
method = split(lines.at(0), " "); |
|
|
method = split(lines.at(0), " "); |
|
|
_headers["Method:"] = method; |
|
|
_headers["Method:"] = method; |
|
|
if (lines.size() > 0) { |
|
|
if (lines.size() > 0) { |
|
|
for (vec_string::iterator it = lines.begin() + 1; it < lines.end(); it++) { |
|
|
for (vec_string::iterator it = lines.begin() + 1; it < lines.end(); it++) { |
|
|
line = split(*it, " "); |
|
|
line = split(*it, " "); |
|
|
_headers[line.at(0)] = vec_string(line.begin() + 1, line.end()); |
|
|
_headers[line.at(0)] = vec_string(line.begin() + 1, line.end()); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
_method = header_pick("Method:", 0); |
|
|
_method = header_pick("Method:", 0); |
|
|
if ((_method == "POST" || _method == "PUT") && header_pick("Content-Length:", 0) == "" && |
|
|
if ((_method == "POST" || _method == "PUT") && |
|
|
header_pick("Transfer-Encoding:", 0) != "chunked") |
|
|
header_pick("Content-Length:", 0) == "" && |
|
|
return (send_error(400), false); |
|
|
header_pick("Transfer-Encoding:", 0) != "chunked") |
|
|
vec_string uri_split = split(header_pick("Method:", 1), "?"); |
|
|
return (send_error(400), false); |
|
|
_uri = uri_split.at(0); |
|
|
vec_string uri_split = split(header_pick("Method:", 1), "?"); |
|
|
if (uri_split.size() > 1) _query = uri_split.at(1); |
|
|
_uri = uri_split.at(0); |
|
|
_host = header_pick("Host:", 0); |
|
|
if (uri_split.size() > 1) |
|
|
_env = env; |
|
|
_query = uri_split.at(1); |
|
|
_server = _parent->choose_server(env, _host); |
|
|
_host = header_pick("Host:", 0); |
|
|
_route = _server->choose_route(_uri); |
|
|
_keepalive = header_pick("Connection:", 0) == "keep-alive"; |
|
|
if (DEBUG) debug_header(); |
|
|
_env = env; |
|
|
string len = header_pick("Content-Length:", 0).c_str(); |
|
|
struct timeval t; |
|
|
if (len != "") { |
|
|
gettimeofday(&t, NULL); |
|
|
_len = std::atoi(len.c_str()); |
|
|
_server = _parent->choose_server(env, _host); |
|
|
_last_chunk = true; |
|
|
if (_server->_timeout) |
|
|
int max_len = _route->_client_max_body_size > 0 ? _route->_client_max_body_size |
|
|
_death_time = _server->_timeout + t.tv_sec; |
|
|
: _server->_client_max_body_size > 0 ? _server->_client_max_body_size |
|
|
_route = _server->choose_route(_uri); |
|
|
: INT_MAX; |
|
|
if (_route->_timeout) |
|
|
if (_len > max_len) return (send_error(413), false); |
|
|
_death_time = _route->_timeout + t.tv_sec; |
|
|
} else _len = 0; |
|
|
if (DEBUG) |
|
|
return true; |
|
|
debug_header(); |
|
|
} |
|
|
string len = header_pick("Content-Length:", 0).c_str(); |
|
|
|
|
|
if (len != "") { |
|
|
string Client::header_pick(string key, size_t id) { return _headers[key].size() <= id ? "" : _headers[key].at(id); } |
|
|
_len = std::atoi(len.c_str()); |
|
|
|
|
|
_last_chunk = true; |
|
|
|
|
|
int max_len = |
|
|
|
|
|
_route->_client_max_body_size > 0 ? _route->_client_max_body_size |
|
|
|
|
|
: _server->_client_max_body_size > 0 ? _server->_client_max_body_size |
|
|
|
|
|
: INT_MAX; |
|
|
|
|
|
if (_len > max_len) |
|
|
|
|
|
return (send_error(413), false); |
|
|
|
|
|
} else |
|
|
|
|
|
_len = 0; |
|
|
|
|
|
return true; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
string Client::header_pick(string key, size_t id) { |
|
|
|
|
|
return _headers[key].size() <= id ? "" : _headers[key].at(id); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
bool Client::check_method(void) { |
|
|
bool Client::check_method(void) { |
|
|
vec_string allowed; |
|
|
vec_string allowed; |
|
|
if ((_route && (allowed = _route->_allowed_methods).size() > 0) || |
|
|
if ((_route && (allowed = _route->_allowed_methods).size() > 0) || |
|
|
(_server && (allowed = _server->_allowed_methods).size() > 0) || ((allowed = _env->_allowed_methods).size() > 0)) |
|
|
(_server && (allowed = _server->_allowed_methods).size() > 0) || |
|
|
return std::find(allowed.begin(), allowed.end(), _method) < allowed.end() ? true : false; |
|
|
((allowed = _env->_allowed_methods).size() > 0)) |
|
|
else if (_method == "GET" || _method == "POST" || _method == "DELETE" || _method == "PUT") return (true); |
|
|
return std::find(allowed.begin(), allowed.end(), _method) < allowed.end() |
|
|
return (false); |
|
|
? true |
|
|
|
|
|
: false; |
|
|
|
|
|
else if (_method == "GET" || _method == "POST" || _method == "DELETE" || |
|
|
|
|
|
_method == "PUT") |
|
|
|
|
|
return (true); |
|
|
|
|
|
return (false); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
void Client::handleRequest(void) { |
|
|
void Client::handleRequest(void) { |
|
|
if (DEBUG) { |
|
|
if (DEBUG) { |
|
|
debug_block("Header: ", _header); |
|
|
debug_block("Header: ", _header); |
|
|
debug_block("Body: ", _body); |
|
|
debug_block("Body: ", _body); |
|
|
} |
|
|
} |
|
|
string ret; |
|
|
if (_route->_ret_uri != "") |
|
|
string req_path = _route->getRoot() + _uri; |
|
|
send_error(_route->_ret_code, _route->_ret_uri); |
|
|
if (!SILENT) std::cout << "||-> Request for " << req_path << " received <-||\n"; |
|
|
else if (_server->_ret_uri != "") |
|
|
string cgi_path = _route->_cgi.size() ? _route->_cgi[get_extension(req_path)] |
|
|
send_error(_server->_ret_code, _server->_ret_uri); |
|
|
: _server->_cgi.size() ? _server->_cgi[get_extension(req_path)] |
|
|
else { |
|
|
: ""; |
|
|
string ret; |
|
|
if (DEBUG) cout << "Path: " << req_path << "\n"; |
|
|
struct timeval t; |
|
|
if (!check_method()) send_error(405); |
|
|
gettimeofday(&t, NULL); |
|
|
else { |
|
|
if (_death_time && t.tv_sec > _death_time) { |
|
|
if ((ret = _route->getIndex(_uri, req_path)) == "") ret = read_file(req_path); |
|
|
send_error(408); |
|
|
if (ret == "404") { |
|
|
_finish = true; |
|
|
if (_method == "POST" || _method == "PUT") create_file(req_path); |
|
|
return; |
|
|
else send_error(404); |
|
|
} |
|
|
} else if (ret == "403") send_error(403); |
|
|
string req_path = _route->getRoot() + _uri; |
|
|
else if (_method == "DELETE") std::remove(req_path.c_str()); |
|
|
if (!SILENT) |
|
|
else if (cgi_path != "") cgi(cgi_path, req_path); |
|
|
std::cout << "||-> Request for " << req_path << " received <-||\n"; |
|
|
else send_answer("HTTP/1.1 200 OK\r\n" + ret); |
|
|
string cgi_path = |
|
|
} |
|
|
_route->_cgi.size() ? _route->_cgi[get_extension(req_path)] |
|
|
|
|
|
: _server->_cgi.size() ? _server->_cgi[get_extension(req_path)] |
|
|
|
|
|
: ""; |
|
|
|
|
|
if (DEBUG) |
|
|
|
|
|
cout << "Path: " << req_path << "\n"; |
|
|
|
|
|
if (!check_method()) |
|
|
|
|
|
send_error(405); |
|
|
|
|
|
else { |
|
|
|
|
|
if ((ret = _route->getIndex(_uri, req_path)) == "") |
|
|
|
|
|
ret = file_answer(req_path); |
|
|
|
|
|
if (ret == "404") { |
|
|
|
|
|
if (_method == "POST" || _method == "PUT") |
|
|
|
|
|
create_file(req_path); |
|
|
|
|
|
else |
|
|
|
|
|
send_error(404); |
|
|
|
|
|
} else if (ret == "403") |
|
|
|
|
|
send_error(403); |
|
|
|
|
|
else if (_method == "DELETE") |
|
|
|
|
|
std::remove(req_path.c_str()); |
|
|
|
|
|
else if (cgi_path != "") |
|
|
|
|
|
cgi(cgi_path, req_path); |
|
|
|
|
|
else |
|
|
|
|
|
send_answer("HTTP/1.1 200 OK\r\n" + ret); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
void Client::create_file(string path) { |
|
|
void Client::create_file(string path) { |
|
|
std::ofstream file(path.c_str()); |
|
|
std::ofstream file(path.c_str()); |
|
|
if (!file.good()) send_error(403); |
|
|
if (!file.good()) |
|
|
else { |
|
|
send_error(403); |
|
|
file << _body; |
|
|
else { |
|
|
file.close(); |
|
|
file << _body; |
|
|
send_answer("HTTP/1.1 201 Accepted\r\n\r\n"); |
|
|
file.close(); |
|
|
} |
|
|
send_answer("HTTP/1.1 201 Accepted\r\n\r\n"); |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/**
|
|
|
/**
|
|
|
* @brief Launch cgi binary to parse the file requested by the client. |
|
|
* @brief Launch cgi binary to parse the file requested by the client. |
|
|
* |
|
|
* |
|
|
* @param cgi_path The cgi binary location specified in configuration file according to the file requested. |
|
|
* @param cgi_path The cgi binary location specified in configuration file |
|
|
|
|
|
* according to the file requested. |
|
|
* @param path The path to the file requested. |
|
|
* @param path The path to the file requested. |
|
|
*/ |
|
|
*/ |
|
|
void Client::cgi(string cgi_path, string path) { |
|
|
void Client::cgi(string cgi_path, string path) { |
|
|
int pipe_in[2]; |
|
|
int pipe_in[2]; |
|
|
|
|
|
|
|
|
send(_fd, "HTTP/1.1 200 OK\r\n", 17, MSG_NOSIGNAL); |
|
|
send(_fd, "HTTP/1.1 200 OK\r\n", 17, MSG_NOSIGNAL); |
|
|
if (!std::ifstream(cgi_path.c_str()).good()) return send_error(404); |
|
|
if (!std::ifstream(cgi_path.c_str()).good()) |
|
|
if (DEBUG) std::cout << "Send cgi\n"; |
|
|
return send_error(404); |
|
|
if (fork() == 0) { |
|
|
if (DEBUG) |
|
|
const char **args = new const char *[cgi_path.length() + 1]; |
|
|
std::cout << "Send cgi\n"; |
|
|
args[0] = cgi_path.c_str(); |
|
|
if (fork() == 0) { |
|
|
args[1] = NULL; |
|
|
const char **args = new const char *[cgi_path.length() + 1]; |
|
|
string path_info = "PATH_INFO=" + _route->getRoot(); |
|
|
args[0] = cgi_path.c_str(); |
|
|
string query = "QUERY_STRING=" + _query; |
|
|
args[1] = NULL; |
|
|
const char **env = new const char *[path_info.length() + query.length() + 2]; |
|
|
string path_info = "PATH_INFO=" + _route->getRoot(); |
|
|
env[0] = path_info.c_str(); |
|
|
string query = "QUERY_STRING=" + _query; |
|
|
env[1] = query.c_str(); |
|
|
const char **env = |
|
|
env[2] = NULL; |
|
|
new const char *[path_info.length() + query.length() + 2]; |
|
|
pipe(pipe_in); |
|
|
env[0] = path_info.c_str(); |
|
|
std::stringstream tmp; |
|
|
env[1] = query.c_str(); |
|
|
tmp << std::ifstream(path.c_str()).rdbuf(); |
|
|
env[2] = NULL; |
|
|
string file = tmp.str(); |
|
|
pipe(pipe_in); |
|
|
write(pipe_in[1], file.c_str(), file.size()); |
|
|
std::stringstream tmp; |
|
|
close(pipe_in[1]); |
|
|
tmp << std::ifstream(path.c_str()).rdbuf(); |
|
|
dup2(pipe_in[0], STDIN_FILENO); |
|
|
string file = tmp.str(); |
|
|
close(pipe_in[0]); |
|
|
write(pipe_in[1], file.c_str(), file.size()); |
|
|
dup2(_fd, STDOUT_FILENO); |
|
|
close(pipe_in[1]); |
|
|
close(_fd); |
|
|
dup2(pipe_in[0], STDIN_FILENO); |
|
|
execve(cgi_path.c_str(), (char **)args, (char **)env); |
|
|
close(pipe_in[0]); |
|
|
exit(1); |
|
|
dup2(_fd, STDOUT_FILENO); |
|
|
} |
|
|
close(_fd); |
|
|
_finish = true; |
|
|
execve(cgi_path.c_str(), (char **)args, (char **)env); |
|
|
|
|
|
exit(1); |
|
|
|
|
|
} |
|
|
|
|
|
_finish = true; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/**
|
|
|
/**
|
|
@ -203,22 +315,50 @@ void Client::cgi(string cgi_path, string path) { |
|
|
* @param error_code The HTTP response code to send. |
|
|
* @param error_code The HTTP response code to send. |
|
|
*/ |
|
|
*/ |
|
|
void Client::send_error(int error_code, string opt) { |
|
|
void Client::send_error(int error_code, string opt) { |
|
|
switch (error_code) { |
|
|
string error_path, body; |
|
|
case 301: |
|
|
error_path = _route->_err_page[error_code]; |
|
|
return send_answer("HTTP/1.1 301 Moved Permanently\r\nLocation: " + opt + "\r\n\r\n"); |
|
|
if (error_path == "") { |
|
|
case 400: |
|
|
error_path = _server->_err_page[error_code]; |
|
|
return send_answer("HTTP/1.1 400 Bad Request\r\n\r\n"); |
|
|
if (error_path != "") |
|
|
case 403: |
|
|
body = file_answer(_server->correctUri(error_path)); |
|
|
return send_answer("HTTP/1.1 403 Forbidden\r\n\r\n"); |
|
|
} else |
|
|
case 404: |
|
|
body = file_answer(_route->correctUri(error_path)); |
|
|
return send_answer("HTTP/1.1 404 Not Found\r\n\r\n"); |
|
|
|
|
|
case 405: |
|
|
switch (error_code) { |
|
|
return send_answer("HTTP/1.1 405 Method Not Allowed\r\nConnection: " |
|
|
case 301: |
|
|
"close\r\n\r\n"); |
|
|
return send_answer("HTTP/1.1 301 Moved Permanently\r\nLocation: " + opt + |
|
|
case 413: |
|
|
"\r\n" + body + "\r\n"); |
|
|
return send_answer("HTTP/1.1 413 Payload Too " |
|
|
case 302: |
|
|
"Large\r\nConnection: close\r\n\r\n"); |
|
|
return send_answer("HTTP/1.1 302 Found\r\nLocation: " + opt + "\r\n" + |
|
|
} |
|
|
body + "\r\n"); |
|
|
|
|
|
case 307: |
|
|
|
|
|
return send_answer("HTTP/1.1 307 Temporary Redirect\r\nLocation: " + opt + |
|
|
|
|
|
"\r\n" + body + "\r\n"); |
|
|
|
|
|
case 308: |
|
|
|
|
|
return send_answer("HTTP/1.1 308 Permanent Redirect\r\nLocation: " + opt + |
|
|
|
|
|
"\r\n" + body + "\r\n"); |
|
|
|
|
|
case 400: |
|
|
|
|
|
return send_answer("HTTP/1.1 400 Bad Request\r\n" + body + "\r\n"); |
|
|
|
|
|
case 403: |
|
|
|
|
|
return send_answer("HTTP/1.1 403 Forbidden\r\n" + body + "\r\n"); |
|
|
|
|
|
case 404: |
|
|
|
|
|
return send_answer("HTTP/1.1 404 Not Found\r\n" + body + "\r\n"); |
|
|
|
|
|
case 405: |
|
|
|
|
|
return send_answer("HTTP/1.1 405 Method Not Allowed\r\nConnection: " |
|
|
|
|
|
"close\r\n" + |
|
|
|
|
|
body + "\r\n"); |
|
|
|
|
|
case 408: |
|
|
|
|
|
return send_answer("HTTP/1.1 408 Request Timeout\r\nConnection: close\r\n" + |
|
|
|
|
|
body + "\r\n"); |
|
|
|
|
|
case 413: |
|
|
|
|
|
return send_answer( |
|
|
|
|
|
"HTTP/1.1 413 Payload Too Large\r\nConnection: close\r\n" + body + |
|
|
|
|
|
"\r\n"); |
|
|
|
|
|
case 429: |
|
|
|
|
|
return send_answer( |
|
|
|
|
|
"HTTP/1.1 429 Too Many Requests\r\nConnection: close\r\n" + body + |
|
|
|
|
|
"\r\n"); |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/**
|
|
|
/**
|
|
@ -227,12 +367,14 @@ void Client::send_error(int error_code, string opt) { |
|
|
* @param msg The HTTP message to send. |
|
|
* @param msg The HTTP message to send. |
|
|
*/ |
|
|
*/ |
|
|
void Client::send_answer(string msg) { |
|
|
void Client::send_answer(string msg) { |
|
|
if (DEBUG) debug_block("ANSWER: ", msg); |
|
|
if (DEBUG) |
|
|
|
|
|
debug_block("ANSWER: ", msg); |
|
|
#ifdef __linux__ |
|
|
#ifdef __linux__ |
|
|
send(_fd, msg.c_str(), msg.length(), MSG_NOSIGNAL); |
|
|
send(_fd, msg.c_str(), msg.length(), MSG_NOSIGNAL); |
|
|
#elif __APPLE__ |
|
|
#elif __APPLE__ |
|
|
write(_fd, msg.c_str(), msg.length()); |
|
|
write(_fd, msg.c_str(), msg.length()); |
|
|
#endif |
|
|
#endif |
|
|
init(); |
|
|
init(); |
|
|
_finish = true; |
|
|
if (!_keepalive) |
|
|
|
|
|
_finish = true; |
|
|
} |
|
|
} |
|
|