frnetlib/src/HttpRequest.cpp
Unknown 5778310798 Fixed incompatabilities between GCC and MSCV
Build errors have been fixed, but there are build warnings remaining about unsafe conversions, because Visual C++'s network conversion functions have a differing return type to mine. To be fixed in the future.
2017-05-28 18:09:29 +01:00

173 lines
5.2 KiB
C++

//
// Created by fred on 10/12/16.
//
#include <algorithm>
#include "frnetlib/HttpRequest.h"
namespace fr
{
HttpRequest::HttpRequest()
: header_ended(false),
last_parsed_character(0),
content_length(0)
{
}
bool HttpRequest::parse(const std::string &request)
{
body += request;
//Ensure that the whole header has been parsed first
if(!header_ended)
{
//Check to see if this request data contains the end of the header
auto header_end = body.find("\r\n\r\n");
header_ended = header_end != std::string::npos;
//If the header end has not been found, return true, indicating that we need more data.
if(!header_ended)
{
return true;
}
else
{
parse_header(header_end);
body.clear();
}
content_length += 2; //The empty line between header and data
body += request.substr(header_end, request.size() - header_end);
}
//If we've got the whole request, parse the POST if it exists
if(body.size() >= content_length)
{
if(request_type == RequestType::Post)
parse_post_body();
return false;
}
return true;
}
void HttpRequest::parse_header(int32_t header_end_pos)
{
//Split the header into lines
size_t line = 0;
std::vector<std::string> header_lines = split_string(body.substr(0, header_end_pos));
if(header_lines.empty())
return;
//Parse request type & uri
parse_header_type(header_lines[line]);
parse_header_uri(header_lines[line]);
line++;
//Read in headers
for(; line < header_lines.size(); line++)
{
parse_header_line(header_lines[line]);
}
//Store content length value if it exists
auto length_header_iter = header_data.find("content-length");
if(length_header_iter != header_data.end())
content_length = (size_t)std::stoull(length_header_iter->second);
}
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\r\n";
//Add the headers to the request
for(const auto &header : header_data)
{
std::string data = header.first + ": " + header.second + "\r\n";
request += data;
}
//Generate post line
std::string post_string;
for(auto &post : post_data)
post_string += post.first + "=" + post.second + "&";
if(!post_string.empty())
{
post_string.erase(request.size() - 1, 1);
post_string += "\r\n";
}
//Add in required headers if they're missing
if(header_data.find("Connection") == header_data.end())
request += "Connection: keep-alive\n";
if(header_data.find("Host") == header_data.end())
request += "Host: " + host + "\r\n";
if(!body.empty())
request += "Content-Length: " + std::to_string(body.size() + post_string.size()) + "\r\n";
//Add in space
request += "\r\n";
//Add in post
request += post_string;
//Add in the body
request += body + "\r\n";
return request;
}
void HttpRequest::parse_post_body()
{
auto post_begin = body.find_first_not_of("\r\n");
if(post_begin != std::string::npos)
{
auto post = parse_argument_list(body.substr(post_begin, body.size() - post_begin));
for(auto &c : post)
post_data.emplace(std::move(c.first), std::move(c.second));
}
}
void HttpRequest::parse_header_type(const std::string &str)
{
//Find the request type
auto type_end = str.find(" ");
if(type_end != std::string::npos)
{
//Check what it is
if(str.compare(0, type_end, "GET") == 0)
request_type = fr::Http::Get;
else
request_type = fr::Http::Post;
return;
}
throw std::invalid_argument("No known request type found in: " + str);
}
void HttpRequest::parse_header_uri(const std::string &str)
{
auto uri_begin = str.find("/");
auto uri_end = str.find("HTTP") - 1;
if(uri_begin != std::string::npos)
{
//Extract URI
std::string uri = str.substr(uri_begin, uri_end - uri_begin);
//Parse GET variables
auto get_begin = str.find("?");
if(get_begin != std::string::npos)
{
auto get_vars = parse_argument_list(str.substr(get_begin, uri_end - get_begin));
for(auto &c : get_vars)
get_data.emplace(std::move(c.first), std::move(c.second));
uri.erase(get_begin, uri.size() - get_begin);
}
set_uri(uri);
return;
}
throw std::invalid_argument("No URI found in: " + str);
}
}