Added URL encoding/decoding and proper POST parsing

Also fixed last body line being ignored in requests
This commit is contained in:
Fred Nicolson 2016-12-31 22:05:44 +00:00
parent e0e956cf78
commit 27b559b4ef
4 changed files with 106 additions and 15 deletions

View File

@ -155,6 +155,22 @@ namespace fr
*/ */
const std::string &get_body() const; 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: protected:
/*! /*!
* Splits a string by new line. Ignores escaped \n's * Splits a string by new line. Ignores escaped \n's
@ -163,8 +179,26 @@ namespace fr
*/ */
std::vector<std::string> split_string(const std::string &str); std::vector<std::string> 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; 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 //Other request info
std::unordered_map<std::string, std::string> headers; std::unordered_map<std::string, std::string> headers;
std::unordered_map<std::string, std::string> get_variables; std::unordered_map<std::string, std::string> get_variables;

View File

@ -3,6 +3,7 @@
// //
#include <iostream> #include <iostream>
#include <sstream>
#include "frnetlib/Http.h" #include "frnetlib/Http.h"
namespace fr namespace fr
@ -39,6 +40,7 @@ namespace fr
} }
last_character = str[a]; last_character = str[a];
} }
result.emplace_back(str.substr(line_start, str.size() - line_start));
return result; return result;
} }
@ -113,4 +115,42 @@ namespace fr
{ {
return body; 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;
}
} }

View File

@ -42,11 +42,11 @@ namespace fr
{ {
if(uri_end == std::string::npos) //If no GET arguments 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 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 get_lines = lines[0].substr(uri_end + 1, lines[0].size());
std::string name_buffer, value_buffer; std::string name_buffer, value_buffer;
@ -55,7 +55,7 @@ namespace fr
{ {
if(get_lines[a] == '&') if(get_lines[a] == '&')
{ {
get_variables.emplace(name_buffer, value_buffer); get_variables.emplace(name_buffer, url_decode(value_buffer));
name_buffer.clear(); name_buffer.clear();
value_buffer.clear(); value_buffer.clear();
state = false; state = false;
@ -74,7 +74,7 @@ namespace fr
name_buffer += get_lines[a]; 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 //Store the header
std::string header_name = lines[a].substr(0, colon_iter); 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); headers.emplace(header_name, header_content);
} }
//Store request body //Extract POST data if it's a post request
for(; a < lines.size(); a++) 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; return;
} }
@ -108,12 +123,12 @@ namespace fr
std::string HttpRequest::construct(const std::string &host) const std::string HttpRequest::construct(const std::string &host) const
{ {
//Add HTTP header //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 //Add the headers to the request
for(const auto &header : headers) for(const auto &header : headers)
{ {
std::string data = header.first + ": " + header.second + "\n"; std::string data = header.first + ": " + header.second + "\r\n";
request += data; request += data;
} }
@ -121,13 +136,15 @@ namespace fr
if(headers.find("Connection") == headers.end()) if(headers.find("Connection") == headers.end())
request += "Connection: keep-alive\n"; request += "Connection: keep-alive\n";
if(headers.find("Host") == headers.end()) 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 //Add in space
request += "\n"; request += "\r\n";
//Add in the body //Add in the body
request += body + "\n"; request += body + "\r\n";
return request; return request;
} }

View File

@ -44,7 +44,7 @@ namespace fr
//Store the header //Store the header
std::string header_name = lines[a].substr(0, colon_iter); 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); headers.emplace(header_name, header_content);
} }
@ -64,7 +64,7 @@ namespace fr
//Add the headers to the response //Add the headers to the response
for(const auto &header : headers) 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; response += data;
} }