|
|
@ -2,6 +2,7 @@ |
|
|
|
|
|
|
|
Client::Client(int fd, ip_port_t ip_port, Master *parent) |
|
|
|
: _fd(fd), _ip_port(ip_port), _parent(parent) { |
|
|
|
clean(); |
|
|
|
cout << "New connection, socket fd is " << fd << ", ip is : " << _ip_port.ip |
|
|
|
<< ", port : " << _ip_port.port << "\n"; |
|
|
|
} |
|
|
@ -19,9 +20,11 @@ void Client::clean(void) { |
|
|
|
_method = ""; |
|
|
|
_uri = ""; |
|
|
|
_host = ""; |
|
|
|
_len = 0; |
|
|
|
_last_len = -1; |
|
|
|
|
|
|
|
_header = ""; |
|
|
|
_content = ""; |
|
|
|
_body = ""; |
|
|
|
_request.clear(); |
|
|
|
} |
|
|
|
|
|
|
@ -29,84 +32,71 @@ bool Client::getHeader(Env *env, string paquet) { |
|
|
|
if (paquet.length() < 1) |
|
|
|
send_error(403); |
|
|
|
if (header_pick("Method:", 0) != "") |
|
|
|
return true; |
|
|
|
return getBody(paquet); |
|
|
|
std::vector< string > lines = split(paquet, "\r\n"); |
|
|
|
for (std::vector< string >::iterator it = lines.begin(); it < lines.end(); |
|
|
|
it++) { |
|
|
|
if (*it == "") { |
|
|
|
size_t pos = paquet.find("\r\n"); |
|
|
|
if (pos != string::npos) |
|
|
|
paquet.erase(0, pos + 2); |
|
|
|
else |
|
|
|
paquet.clear(); |
|
|
|
_header += *it + (it + 1 != lines.end() ? "\r\n" : ""); |
|
|
|
if (_header.find("\r\n\r\n") != string::npos) { |
|
|
|
cout << "Header: \n-|" << _header << "|-\n"; |
|
|
|
if (!this->parseHeader(env)) |
|
|
|
return false; |
|
|
|
size_t pos = paquet.find("\r\n\r\n"); |
|
|
|
if (pos != string::npos) |
|
|
|
paquet.erase(pos + 4); |
|
|
|
return true; |
|
|
|
} else |
|
|
|
_header += *it + "\r\n"; |
|
|
|
if (header_pick("Method:", 0) == "GET" || |
|
|
|
header_pick("Method:", 0) == "HEAD") |
|
|
|
return true; |
|
|
|
else if (paquet.length() > 0) { |
|
|
|
// cout << "Remaining paquet: " << paquet.length() << "\n";
|
|
|
|
return getBody(paquet); |
|
|
|
} |
|
|
|
cout << "next: " << *it << "\n"; |
|
|
|
cout << "paquet length remain: " << paquet.length() << "\n"; |
|
|
|
} |
|
|
|
} |
|
|
|
_header.resize(_header.length() - 2); |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
bool Client::getBody(string paquet) { |
|
|
|
std::vector< string > lines = split(paquet, "\r\n"); |
|
|
|
|
|
|
|
long chunk_len = header_pick("Transfer-Encoding:", 0) == "chunked" |
|
|
|
? std::strtol(lines.at(0).c_str(), 0, 16) |
|
|
|
: -1; |
|
|
|
|
|
|
|
std::vector< string > lines = split(paquet, "\r\n"); |
|
|
|
std::vector< string >::iterator it; |
|
|
|
cout << paquet << "\n"; |
|
|
|
|
|
|
|
for (it = lines.begin(); it < lines.end(); it++) { |
|
|
|
if (chunk_len == -1 || it != lines.begin()) |
|
|
|
_content += *it + "\r\n"; |
|
|
|
cout << "line: " << *it << "\n"; |
|
|
|
if ((*it).length() && _len <= 0 && |
|
|
|
header_pick("Transfer-Encoding:", 0) == "chunked") { |
|
|
|
_len = std::strtol((*it).c_str(), 0, 16) + 2; |
|
|
|
_last_len = _len - 2; |
|
|
|
// +2 for the final \r\n closing chunk
|
|
|
|
} else if (_len > 0 || it != lines.begin()) { |
|
|
|
_body += *it + "\r\n"; |
|
|
|
_len -= ((*it).length() + 2); |
|
|
|
} |
|
|
|
cout << "Remaining chunk length: " << _len << "\n"; |
|
|
|
cout << "Previous chunk length: " << _last_len << "\n"; |
|
|
|
} |
|
|
|
_content.resize(_content.length() - 2); |
|
|
|
if ((header_pick("Method:", 0) == "GET" && *it == "") || |
|
|
|
(chunk_len == 0 || |
|
|
|
std::strtoul(header_pick("Content-Length:", 0).c_str(), 0, 10) <= |
|
|
|
_content.length())) { |
|
|
|
cout << "Request received\n"; |
|
|
|
if (_body.size()) |
|
|
|
_body.resize(_body.length() - 2); |
|
|
|
_len += 2; |
|
|
|
cout << "Remaining chunk characters: " << _len << "\n"; |
|
|
|
if (_last_len == 0 && _len == 0) { |
|
|
|
cout << "Content:\n-|" << _body << "|-\n"; |
|
|
|
return true; |
|
|
|
} |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
bool Client::getRequest(Env *env, string paquet) { |
|
|
|
cout << "|===|paquet|===>\n" << paquet << "|===||\n"; |
|
|
|
if (paquet.length() < 1) // HTTPS?
|
|
|
|
return false; |
|
|
|
std::vector< string > lines = split(paquet, "\n"); |
|
|
|
long chunk_len = (_content.length() > 0 && |
|
|
|
header_pick("Transfer-Encoding:", 0) == "chunked") |
|
|
|
? std::strtol(lines.at(0).c_str(), 0, 16) |
|
|
|
: -1; |
|
|
|
cout << "Chunk length: " << chunk_len << "\r\n"; |
|
|
|
for (std::vector< string >::iterator it = lines.begin(); it < lines.end(); |
|
|
|
it++) { |
|
|
|
if (*it == "\r" && header_pick("Method:", 0) == "") { |
|
|
|
if (!this->parseHeader(env)) |
|
|
|
return false; |
|
|
|
} else if (*it != "\r" && header_pick("Method:", 0) == "") |
|
|
|
_header += *it + "\n"; |
|
|
|
else if (*it != "\r" && (chunk_len == -1 || it != lines.begin())) |
|
|
|
_content += *it + "\n"; |
|
|
|
string content_length = header_pick("Content_Length:", 0); |
|
|
|
if (content_length != "" && |
|
|
|
std::strtoul(content_length.c_str(), 0, 10) <= _body.length()) { |
|
|
|
cout << "Content:\n-|" << _body << "|-\n"; |
|
|
|
return true; |
|
|
|
} |
|
|
|
cout << "Content: \n-|" << _content << "|-\n"; |
|
|
|
if (_content.length() > 0) { |
|
|
|
_content.resize(_content.length() - 1); |
|
|
|
if ((header_pick("Method:", 0) == "GET" && _content.find("\r\n\r\n")) || |
|
|
|
(chunk_len == 0 || |
|
|
|
std::strtoul(header_pick("Content-Length:", 0).c_str(), 0, 10) <= |
|
|
|
_content.length())) { |
|
|
|
cout << "Request received\n"; |
|
|
|
return true; |
|
|
|
} |
|
|
|
} else if (header_pick("Method:", 0) == "") |
|
|
|
_header.resize(_header.length() - 1); |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
bool Client::parseHeader(Env *env) { |
|
|
|
cout << "Header: \n-|" << _header << "|-\n"; |
|
|
|
std::vector< string > lines = split(_header, "\r\n"); |
|
|
|
std::vector< string > method = split(lines.at(0), " "); |
|
|
|
_request["Method:"] = method; |
|
|
@ -120,7 +110,8 @@ bool Client::parseHeader(Env *env) { |
|
|
|
std::vector< string >(line.begin() + 1, line.end()); |
|
|
|
} |
|
|
|
} |
|
|
|
if (method.at(0) == "POST" && header_pick("Content-Length:", 0) == "" && |
|
|
|
if ((method.at(0) == "POST" || method.at(0) == "PUT") && |
|
|
|
header_pick("Content-Length:", 0) == "" && |
|
|
|
header_pick("Transfer-Encoding:", 0) != "chunked") { |
|
|
|
send_error(400); |
|
|
|
return false; |
|
|
@ -131,22 +122,33 @@ bool Client::parseHeader(Env *env) { |
|
|
|
_host = header_pick("Host:", 0); |
|
|
|
_server = _parent->choose_server(env, _host); |
|
|
|
_route = _server->choose_route(_uri); |
|
|
|
_len = header_pick("Content-Length:", 0).c_str(); |
|
|
|
if (_len != "" && std::atoi(_len.c_str()) > _route->_client_max_body_size) { |
|
|
|
send_error(413); |
|
|
|
return false; |
|
|
|
string len = header_pick("Content-Length:", 0).c_str(); |
|
|
|
if (len != "") { |
|
|
|
_len = std::atoi(len.c_str()); |
|
|
|
_last_len = 0; |
|
|
|
if (_len > _route->_client_max_body_size) { |
|
|
|
send_error(413); |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
bool Client::check_method() { |
|
|
|
std::vector< string > allowed; |
|
|
|
if (_method != "GET" && _method != "POST" && _method != "DELETE") |
|
|
|
send_error(405); |
|
|
|
else if ((allowed = _route->_allowed_methods).size() > 0 && |
|
|
|
std::find(allowed.begin(), allowed.end(), _method) == |
|
|
|
allowed.end()) { |
|
|
|
if (_method != "GET" && _method != "POST" && _method != "DELETE" && |
|
|
|
_method != "PUT") |
|
|
|
send_error(405); |
|
|
|
else if ((allowed = _route->_allowed_methods).size() > 0) { |
|
|
|
if (std::find(allowed.begin(), allowed.end(), _method) == allowed.end()) |
|
|
|
send_error(405); |
|
|
|
else |
|
|
|
return true; |
|
|
|
} else if ((allowed = _server->_allowed_methods).size() > 0) { |
|
|
|
if (std::find(allowed.begin(), allowed.end(), _method) == allowed.end()) |
|
|
|
send_error(405); |
|
|
|
else |
|
|
|
return true; |
|
|
|
} else |
|
|
|
return (true); |
|
|
|
return (false); |
|
|
@ -165,18 +167,37 @@ void Client::answer() { |
|
|
|
cout << "URI: " << _uri << "\n"; |
|
|
|
cout << "Host: " << _host << "\n"; |
|
|
|
string ret; |
|
|
|
if (check_method()) { |
|
|
|
string path = _route->correctUri(_uri); |
|
|
|
string cgi = |
|
|
|
_route->_cgi.size() ? _route->_cgi[get_extension(path)] : ""; |
|
|
|
if (cgi == "") { |
|
|
|
if ((ret = _route->getIndex(_uri, path)) == "" && |
|
|
|
(ret = read_file(path)) == "") |
|
|
|
string path = _route->correctUri(_uri); |
|
|
|
string cgi = |
|
|
|
_route->_cgi.size() |
|
|
|
? _route->_cgi[get_extension(path)] |
|
|
|
: (_server->_cgi.size() ? _server->_cgi[get_extension(path)] : ""); |
|
|
|
cout << "Path: " << path << "\n"; |
|
|
|
if (_method == "PUT") |
|
|
|
create_file(path); |
|
|
|
if (cgi == "") { |
|
|
|
if (check_method()) { |
|
|
|
if ((ret = _route->getIndex(_uri, path)) == "") |
|
|
|
ret = read_file(path); |
|
|
|
if (ret == "404") |
|
|
|
send_error(404); |
|
|
|
else if (ret == "403") |
|
|
|
send_error(403); |
|
|
|
else |
|
|
|
send_answer("HTTP/1.1 200 OK\r\n" + ret); |
|
|
|
} else |
|
|
|
send_cgi(cgi, path); |
|
|
|
} |
|
|
|
} else |
|
|
|
send_cgi(cgi, path); |
|
|
|
} |
|
|
|
|
|
|
|
void Client::create_file(string path) { |
|
|
|
std::ofstream file(path.c_str()); |
|
|
|
if (!file.good()) |
|
|
|
send_error(403); |
|
|
|
else { |
|
|
|
file << _body; |
|
|
|
file.close(); |
|
|
|
send_answer("HTTP/1.1 201 Accepted\r\nContent-Length: 0\r\n\r\n"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@ -219,7 +240,8 @@ void Client::send_redir(int redir_code, string opt) { |
|
|
|
switch (redir_code) { |
|
|
|
case 301: |
|
|
|
return send_answer( |
|
|
|
"HTTTP/1.1 301 Moved Permanently\r\nLocation: " + opt + "\r\n\r\n"); |
|
|
|
"HTTTP/1.1 301 Moved Permanently\r\nLocation: " + opt + |
|
|
|
"\r\n\r\n"); |
|
|
|
} |
|
|
|
} |
|
|
|
*/ |
|
|
@ -228,6 +250,9 @@ void Client::send_error(int error_code) { |
|
|
|
case 400: |
|
|
|
return send_answer( |
|
|
|
"HTTP/1.1 400 Bad Request\r\nContent-Length: 0\r\n\r\n"); |
|
|
|
case 403: |
|
|
|
return send_answer( |
|
|
|
"HTTP/1.1 403 Forbidden\r\nContent-Length: 0\r\n\r\n"); |
|
|
|
case 404: |
|
|
|
return send_answer( |
|
|
|
"HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\n\r\n"); |
|
|
|