diff --git a/include/frnetlib/Http.h b/include/frnetlib/Http.h index 2a94912..8275fef 100644 --- a/include/frnetlib/Http.h +++ b/include/frnetlib/Http.h @@ -155,6 +155,22 @@ namespace fr */ const std::string &get_body() const; + /*! + * URL Encodes a given string + * + * @param str The string to URL encode + * @return The URL encoded string + */ + static std::string url_encode(const std::string &str); + + /*! + * Decodes a URL encoded string. + * + * @param str The string to decode + * @return The decoded string + */ + static std::string url_decode(const std::string &str); + protected: /*! * Splits a string by new line. Ignores escaped \n's @@ -163,8 +179,26 @@ namespace fr */ std::vector split_string(const std::string &str); + /*! + * Converts a 'RequestType' enum value to + * a printable string. + * + * @param type The RequestType to convert + * @return The printable version of the enum value + */ std::string request_type_to_string(RequestType type) const; + /*! + * Converts hexadecimal to an integer. + * + * @param hex The hex value to convert + * @return The decimal equivilent of the hexadecimal value. + */ + static inline int dectohex(const std::string &hex) + { + return (int)strtol(&hex[0], 0, 16); + } + //Other request info std::unordered_map headers; std::unordered_map get_variables; diff --git a/src/Http.cpp b/src/Http.cpp index 675684d..5a7cc7a 100644 --- a/src/Http.cpp +++ b/src/Http.cpp @@ -3,6 +3,7 @@ // #include +#include #include "frnetlib/Http.h" namespace fr @@ -39,6 +40,7 @@ namespace fr } last_character = str[a]; } + result.emplace_back(str.substr(line_start, str.size() - line_start)); return result; } @@ -113,4 +115,42 @@ namespace fr { return body; } + + std::string Http::url_encode(const std::string &str) + { + std::stringstream encoded; + encoded << std::hex; + for(const auto &c : str) + { + if(isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') + encoded << c; + else if(c == ' ') + encoded << '+'; + else + encoded << "%" << std::uppercase << (int)c << std::nouppercase; + } + return encoded.str(); + } + + std::string Http::url_decode(const std::string &str) + { + std::string result; + for(size_t a = 0; a < str.size(); a++) + { + if(str[a] == '%' && a < str.size() - 1) + { + result += (char)dectohex(str.substr(a + 1, 2)); + a += 2; + } + else if(str[a] == '+') + { + result += " "; + } + else + { + result += str[a]; + } + } + return result; + } } \ No newline at end of file diff --git a/src/HttpRequest.cpp b/src/HttpRequest.cpp index 2eb2db8..6ac8f1b 100644 --- a/src/HttpRequest.cpp +++ b/src/HttpRequest.cpp @@ -42,11 +42,11 @@ namespace fr { if(uri_end == std::string::npos) //If no GET arguments { - uri = lines[0].substr(uri_start + 1, lines[0].size() - 1); + uri = url_decode(lines[0].substr(uri_start + 1, lines[0].size() - 1)); } else //There's get arguments { - uri = lines[0].substr(uri_start + 1, uri_end - uri_start - 1); + uri = url_decode(lines[0].substr(uri_start + 1, uri_end - uri_start - 1)); std::string get_lines = lines[0].substr(uri_end + 1, lines[0].size()); std::string name_buffer, value_buffer; @@ -55,7 +55,7 @@ namespace fr { if(get_lines[a] == '&') { - get_variables.emplace(name_buffer, value_buffer); + get_variables.emplace(name_buffer, url_decode(value_buffer)); name_buffer.clear(); value_buffer.clear(); state = false; @@ -74,7 +74,7 @@ namespace fr name_buffer += get_lines[a]; } } - get_variables.emplace(name_buffer, value_buffer); + get_variables.emplace(name_buffer, url_decode(value_buffer)); } } @@ -93,14 +93,29 @@ namespace fr //Store the header std::string header_name = lines[a].substr(0, colon_iter); - std::string header_content = lines[a].substr(colon_iter + 2, lines[a].size () - colon_iter - 3); + std::string header_content = url_decode(lines[a].substr(colon_iter + 2, lines[a].size () - colon_iter - 3)); headers.emplace(header_name, header_content); } - //Store request body - for(; a < lines.size(); a++) + //Extract POST data if it's a post request + if(request_type == Post) { - body += lines[a] + "\n"; + for(; a < lines.size(); a++) + { + size_t equals_pos = lines[a].find("="); + if(equals_pos != std::string::npos) + { + headers[lines[a].substr(0, equals_pos)] = url_decode(lines[a].substr(equals_pos + 1, (lines[a].size() - equals_pos) + 1)); + } + } + } + else + { + //Store request body + for(; a < lines.size(); a++) + { + body += lines[a] + "\n"; + } } return; } @@ -108,12 +123,12 @@ namespace fr std::string HttpRequest::construct(const std::string &host) const { //Add HTTP header - std::string request = request_type_to_string(request_type == Http::Unknown ? Http::Get : request_type) + " " + uri + " HTTP/1.1\n"; + std::string request = request_type_to_string(request_type == Http::Unknown ? Http::Get : request_type) + " " + uri + " HTTP/1.1\r\n"; //Add the headers to the request for(const auto &header : headers) { - std::string data = header.first + ": " + header.second + "\n"; + std::string data = header.first + ": " + header.second + "\r\n"; request += data; } @@ -121,13 +136,15 @@ namespace fr if(headers.find("Connection") == headers.end()) request += "Connection: keep-alive\n"; if(headers.find("Host") == headers.end()) - request += "Host: " + host + "\n"; + request += "Host: " + host + "\r\n"; + if(!body.empty()) + request += "Content-Length: " + std::to_string(body.size()) + "\r\n"; //Add in space - request += "\n"; + request += "\r\n"; //Add in the body - request += body + "\n"; + request += body + "\r\n"; return request; } diff --git a/src/HttpResponse.cpp b/src/HttpResponse.cpp index a5f1d71..67f0df6 100644 --- a/src/HttpResponse.cpp +++ b/src/HttpResponse.cpp @@ -44,7 +44,7 @@ namespace fr //Store the header std::string header_name = lines[a].substr(0, colon_iter); - std::string header_content = lines[a].substr(colon_iter + 2, lines[a].size () - colon_iter - 3); + std::string header_content = url_decode(lines[a].substr(colon_iter + 2, lines[a].size () - colon_iter - 3)); headers.emplace(header_name, header_content); } @@ -64,7 +64,7 @@ namespace fr //Add the headers to the response for(const auto &header : headers) { - std::string data = header.first + ": " + header.second + "\r\n"; + std::string data = header.first + ": " + url_encode(header.second) + "\r\n"; response += data; }