// // Created by fred on 10/12/16. // #include #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 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); } }