frnetlib/src/HttpResponse.cpp
Fred Nicolson c8e03f2df8 Added more tests. Tweaks.
Receiving an http request/response will now return errors like HttpHeaderTooBig, instead of the type being set to that.

Added fr::Socket::status_to_string for converting status values into English strings.
2017-09-25 16:19:47 +01:00

123 lines
4.0 KiB
C++

//
// Created by fred on 10/12/16.
//
#include <iostream>
#include "frnetlib/HttpResponse.h"
namespace fr
{
fr::Socket::Status HttpResponse::parse(const char *response_data, size_t datasz)
{
body += std::string(response_data, datasz);
//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
uint16_t header_end_size = 4;
auto header_end = body.find("\r\n\r\n");
if(header_end == std::string::npos)
{
header_end = body.find("\n\n");
header_end_size = 2;
}
header_ended = header_end != std::string::npos;
//Ensure that the header doesn't exceed max length
if(!header_ended && body.size() > MAX_HTTP_HEADER_SIZE || header_ended && header_end > MAX_HTTP_HEADER_SIZE)
{
return fr::Socket::HttpHeaderTooBig;
}
//If the header end has not been found, ask for more data.
if(!header_ended)
return fr::Socket::NotEnoughData;
//Else parse it
if(!parse_header(header_end))
return fr::Socket::ParseError;
//Leave things after the header intact
body.erase(0, header_end + header_end_size);
}
//Ensure that body doesn't exceed maximum length
if(body.size() > MAX_HTTP_BODY_SIZE)
{
return fr::Socket::HttpBodyTooBig;
}
//Cut off any data if it exceeds content length
if(body.size() > content_length)
body.resize(content_length);
else if(body.size() < content_length)
return fr::Socket::NotEnoughData;
return fr::Socket::Success;
}
std::string HttpResponse::construct(const std::string &host) const
{
//Add HTTP header
std::string response = "HTTP/1.1 " + std::to_string(status) + " \r\n";
//Add the headers to the response
for(const auto &header : header_data)
{
std::string data = header.first + ": " + header.second + "\r\n";
response += data;
}
//Add in required headers if they're missing
if(header_data.find("connection") == header_data.end())
response += "connection: keep-alive\r\n";
if(header_data.find("content-type") == header_data.end())
response += "content-type: text/html\r\n";
if(header_data.find("content-length") == header_data.end())
response += "content-length: " + std::to_string(body.size()) + "\r\n";
//Add in space
response += "\r\n";
//Add in the body
response += body;
return response;
}
bool HttpResponse::parse_header(size_t header_end_pos)
{
try
{
//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 false;
//Get response code
auto status_begin = header_lines[0].find(' ');
if(status_begin == std::string::npos)
return false;
auto end_pos = header_lines[0].find(' ', status_begin + 1);
status = (RequestStatus)std::stoi(header_lines[0].substr(status_begin, end_pos - status_begin));
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 = std::stoull(length_header_iter->second);
}
catch(const std::exception &e)
{
return false;
}
return true;
}
}