Compare commits
	
		
			No commits in common. "f930792a1166768926fd836e214dbc9be6deb03a" and "236f7399fe23b68d65287f3949a99da197f2dd16" have entirely different histories.
		
	
	
		
			f930792a11
			...
			236f7399fe
		
	
		
					 5 changed files with 52 additions and 142 deletions
				
			
		| 
						 | 
				
			
			@ -1,8 +1,6 @@
 | 
			
		|||
#include "request.hpp"
 | 
			
		||||
#include "../log/log.hpp"
 | 
			
		||||
#include "constants.hpp"
 | 
			
		||||
#include <map>
 | 
			
		||||
#include <cstring>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
 | 
			
		||||
namespace anthracite::http {
 | 
			
		||||
| 
						 | 
				
			
			@ -14,7 +12,7 @@ void request::parse_header(std::string& raw_line) {
 | 
			
		|||
    std::string header_name = raw_line.substr(0,delim_pos);
 | 
			
		||||
    std::string header_val = raw_line.substr(value_pos);
 | 
			
		||||
 | 
			
		||||
    _headers[header_name] = header_val;
 | 
			
		||||
    _headers[header_name] = header(header_name, header_val);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void request::parse_query_param(std::string& raw_param) {
 | 
			
		||||
| 
						 | 
				
			
			@ -24,32 +22,27 @@ void request::parse_query_param(std::string& raw_param) {
 | 
			
		|||
    std::string query_name = raw_param.substr(0,delim_pos);
 | 
			
		||||
    std::string query_val = raw_param.substr(value_pos);
 | 
			
		||||
 | 
			
		||||
    _query_params[query_name] = query_val;
 | 
			
		||||
    _query_params[query_name] = query_param(query_name, query_val);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void request::parse_path(char* raw_path) {
 | 
			
		||||
    char* saveptr = nullptr;
 | 
			
		||||
    char* tok = strtok_r(raw_path, "?", &saveptr);
 | 
			
		||||
void request::parse_path(std::string& raw_path) {
 | 
			
		||||
    std::stringstream ss(raw_path);
 | 
			
		||||
    std::string tok;
 | 
			
		||||
 | 
			
		||||
    if (tok){
 | 
			
		||||
    if (getline(ss, tok, '?')){
 | 
			
		||||
        _path = tok;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    tok = strtok_r(nullptr, "?", &saveptr);
 | 
			
		||||
    while(tok) {
 | 
			
		||||
        std::string rtok(tok);
 | 
			
		||||
        parse_query_param(rtok);
 | 
			
		||||
        tok = strtok_r(nullptr, "?", &saveptr);
 | 
			
		||||
    while(getline(ss, tok, '&')) {
 | 
			
		||||
        parse_query_param(tok);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void request::parse_request_line(char* raw_line) {
 | 
			
		||||
void request::parse_request_line(std::string& raw_line) {
 | 
			
		||||
        request_line_parser_state state = METHOD;
 | 
			
		||||
 | 
			
		||||
        char* saveptr = nullptr;
 | 
			
		||||
        char* tok = strtok_r(raw_line, " \r", &saveptr);
 | 
			
		||||
 | 
			
		||||
        while(tok){
 | 
			
		||||
        std::stringstream ss(raw_line);
 | 
			
		||||
        std::string tok;
 | 
			
		||||
        while(getline(ss, tok, ' ')){
 | 
			
		||||
            switch(state) {
 | 
			
		||||
                case METHOD: {
 | 
			
		||||
                    auto search = method_map.find(tok);
 | 
			
		||||
| 
						 | 
				
			
			@ -64,7 +57,6 @@ void request::parse_request_line(char* raw_line) {
 | 
			
		|||
                };
 | 
			
		||||
                
 | 
			
		||||
                case PATH: {
 | 
			
		||||
                    std::string str_tok(tok);
 | 
			
		||||
                    parse_path(tok);
 | 
			
		||||
                    state = VERSION;
 | 
			
		||||
                    break;
 | 
			
		||||
| 
						 | 
				
			
			@ -80,7 +72,6 @@ void request::parse_request_line(char* raw_line) {
 | 
			
		|||
                    return;
 | 
			
		||||
                };
 | 
			
		||||
            }
 | 
			
		||||
            tok = strtok_r(nullptr, " \r", &saveptr);
 | 
			
		||||
        }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -91,25 +82,22 @@ request::request(std::string& raw_data, const std::string& client_ip)
 | 
			
		|||
 | 
			
		||||
    parser_state state = REQUEST_LINE;
 | 
			
		||||
 | 
			
		||||
    char* saveptr = nullptr;
 | 
			
		||||
    char* tok = strtok_r(raw_data.data(), "\r\n", &saveptr);
 | 
			
		||||
    std::stringstream line_stream(raw_data);
 | 
			
		||||
    std::string line;
 | 
			
		||||
 | 
			
		||||
    while(tok && state != BODY_CONTENT){
 | 
			
		||||
    while(getline(line_stream, line, '\n') && state != BODY_CONTENT){
 | 
			
		||||
        line.pop_back(); // HTTP requests do newline as \r\n, this removes the \r 
 | 
			
		||||
        switch(state) {
 | 
			
		||||
            case REQUEST_LINE: {
 | 
			
		||||
                                   parse_request_line(tok); 
 | 
			
		||||
                                   parse_request_line(line); 
 | 
			
		||||
                                   state = HEADERS;
 | 
			
		||||
                                    tok = strtok_r(nullptr, "\n", &saveptr);
 | 
			
		||||
                                   break;
 | 
			
		||||
                               };
 | 
			
		||||
            case HEADERS: {
 | 
			
		||||
                if (tok[0] == '\r') {
 | 
			
		||||
                if (line.length() == 0) {
 | 
			
		||||
                    state = BODY_CONTENT;
 | 
			
		||||
                } else {
 | 
			
		||||
                    std::string rtok(tok);
 | 
			
		||||
                    rtok.pop_back();
 | 
			
		||||
                    parse_header(rtok); 
 | 
			
		||||
                tok = strtok_r(nullptr, "\n", &saveptr);
 | 
			
		||||
                    parse_header(line); 
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
            };
 | 
			
		||||
| 
						 | 
				
			
			@ -117,13 +105,9 @@ request::request(std::string& raw_data, const std::string& client_ip)
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    tok = strtok_r(nullptr, "", &saveptr);
 | 
			
		||||
    if (tok) {
 | 
			
		||||
        _body_content = std::string(tok);
 | 
			
		||||
    if (getline(line_stream, line, '\0')) {
 | 
			
		||||
        _body_content = line;
 | 
			
		||||
    }
 | 
			
		||||
    //if (getline(line_stream, line, '\0')) {
 | 
			
		||||
    //    _body_content = line;
 | 
			
		||||
    //}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string request::path() { return _path; }
 | 
			
		||||
| 
						 | 
				
			
			@ -147,7 +131,7 @@ bool request::close_connection()
 | 
			
		|||
    const auto& header = _headers.find("Connection");
 | 
			
		||||
    const bool found = header != _headers.end();
 | 
			
		||||
 | 
			
		||||
    if (found && header->second == "keep-alive") {
 | 
			
		||||
    if (found && header->second.value() == "keep-alive") {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -157,36 +141,19 @@ bool request::close_connection()
 | 
			
		|||
std::string request::to_string()
 | 
			
		||||
{
 | 
			
		||||
    std::string response = "";
 | 
			
		||||
    response += reverse_method_map.find(_method)->second + " " + _path;
 | 
			
		||||
    response += reverse_method_map.find(_method)->second + " " + _path + "?";
 | 
			
		||||
 | 
			
		||||
    if (_query_params.size() > 0) {
 | 
			
		||||
        response += "?";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    auto qp_map = std::map(_query_params.begin(), _query_params.end());
 | 
			
		||||
    auto qp = qp_map.begin();
 | 
			
		||||
    while (qp != qp_map.end()) {
 | 
			
		||||
        response += qp->first + "=" + qp->second;
 | 
			
		||||
        if (++qp != qp_map.end()) {
 | 
			
		||||
            response += "&";
 | 
			
		||||
        }
 | 
			
		||||
    for (auto qp : _query_params) {
 | 
			
		||||
        response += qp.second.to_string() + "&";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    response += " " + reverse_version_map.find(_http_version)->second + "\r\n";
 | 
			
		||||
 | 
			
		||||
    if (_headers.size() == 0) {
 | 
			
		||||
        response += "\r\n";
 | 
			
		||||
    for (auto header : _headers) {
 | 
			
		||||
        response += header.second.to_string();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    auto hd_map = std::map(_headers.begin(), _headers.end());
 | 
			
		||||
    auto hd = hd_map.begin();
 | 
			
		||||
    while (hd != hd_map.end()) {
 | 
			
		||||
        response += hd->first + ": " + hd->second + "\r\n";
 | 
			
		||||
        if (++hd == hd_map.end()) {
 | 
			
		||||
    response += "\r\n";
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    response += _body_content;
 | 
			
		||||
 | 
			
		||||
    return response;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,12 +24,12 @@ private:
 | 
			
		|||
    std::string _path;
 | 
			
		||||
    std::string _client_ipaddr;
 | 
			
		||||
    std::string _body_content;
 | 
			
		||||
    std::unordered_map<std::string, std::string> _headers; 
 | 
			
		||||
    std::unordered_map<std::string, std::string> _query_params;
 | 
			
		||||
 | 
			
		||||
    void parse_request_line(char* raw_line);
 | 
			
		||||
    std::unordered_map<std::string, header> _headers; // kinda goofy, whatever
 | 
			
		||||
    std::unordered_map<std::string, query_param> _query_params; // kinda goofy, whatever
 | 
			
		||||
                                                                //
 | 
			
		||||
    void parse_request_line(std::string& raw_line);
 | 
			
		||||
    void parse_header(std::string& raw_line);
 | 
			
		||||
    void parse_path(char* raw_path);
 | 
			
		||||
    void parse_path(std::string& raw_path);
 | 
			
		||||
    void parse_query_param(std::string& raw_param);
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,64 +3,26 @@
 | 
			
		|||
#include <chrono>
 | 
			
		||||
#include "../lib/http/request.hpp"
 | 
			
		||||
 | 
			
		||||
#ifdef SPEEDTEST_COMPARE_BOOST
 | 
			
		||||
#include <boost/beast.hpp>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
TEST(speed_tests, request_parse) {
 | 
			
		||||
    using std::chrono::high_resolution_clock;
 | 
			
		||||
    using std::chrono::duration_cast;
 | 
			
		||||
    using std::chrono::duration;
 | 
			
		||||
    using std::chrono::milliseconds;
 | 
			
		||||
 | 
			
		||||
constexpr uint32_t num_requests = 10000000;
 | 
			
		||||
 | 
			
		||||
TEST(speed_tests, request_parse) {
 | 
			
		||||
    std::ifstream t("./test_files/test_request.http");
 | 
			
		||||
    std::stringstream buffer;
 | 
			
		||||
    buffer << t.rdbuf();
 | 
			
		||||
    std::string raw_req = buffer.str();
 | 
			
		||||
 | 
			
		||||
    auto start = high_resolution_clock::now();
 | 
			
		||||
 | 
			
		||||
    for(int i = 0; i < num_requests; ++i) {
 | 
			
		||||
    auto t1 = high_resolution_clock::now();
 | 
			
		||||
    for(int i = 0; i < 1000000; ++i) {
 | 
			
		||||
        volatile anthracite::http::request req (raw_req, "0.0.0.0");
 | 
			
		||||
    }
 | 
			
		||||
    auto t2 = high_resolution_clock::now();
 | 
			
		||||
 | 
			
		||||
    auto end = high_resolution_clock::now();
 | 
			
		||||
    /* Getting number of milliseconds as an integer. */
 | 
			
		||||
    auto ms_int = duration_cast<milliseconds>(t2 - t1);
 | 
			
		||||
 | 
			
		||||
    auto ms_int = duration_cast<milliseconds>(end-start);
 | 
			
		||||
 | 
			
		||||
    double m_rps = ((1000.0 / ms_int.count()) * num_requests) / 1000000;
 | 
			
		||||
 | 
			
		||||
    std::cout << "Parsed " << (num_requests/1000000) << " Million requests in " << ms_int << " ms";
 | 
			
		||||
    std::cout << " at " << m_rps << " Million RPS " << std::endl;
 | 
			
		||||
 | 
			
		||||
    ASSERT_LT(ms_int.count(), 2000);
 | 
			
		||||
    std::cout << "Parsed 1 Million requests in " << ms_int << "ms" << std::endl;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef SPEEDTEST_COMPARE_BOOST
 | 
			
		||||
TEST(speed_tests, boost) {
 | 
			
		||||
    std::ifstream t("./test_files/test_request.http");
 | 
			
		||||
    std::stringstream buffer;
 | 
			
		||||
    buffer << t.rdbuf();
 | 
			
		||||
    std::string raw_req = buffer.str();
 | 
			
		||||
 | 
			
		||||
    auto start = high_resolution_clock::now();
 | 
			
		||||
 | 
			
		||||
    for(int i = 0; i < num_requests; ++i) {
 | 
			
		||||
        boost::system::error_code ec;
 | 
			
		||||
        boost::beast::http::request_parser<boost::beast::http::string_body> p;
 | 
			
		||||
        p.put(boost::asio::buffer(raw_req), ec);
 | 
			
		||||
        boost::beast::http::request<boost::beast::http::string_body> r = p.get();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    auto end = high_resolution_clock::now();
 | 
			
		||||
    auto ms_int = duration_cast<milliseconds>(end-start);
 | 
			
		||||
 | 
			
		||||
    double m_rps = ((1000.0 / ms_int.count()) * num_requests) / 1000000;
 | 
			
		||||
 | 
			
		||||
    std::cout << "Parsed " << (num_requests/1000000) << " Million requests in " << ms_int << " ms";
 | 
			
		||||
    std::cout << " at " << m_rps << " Million RPS " << std::endl;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,15 +1,13 @@
 | 
			
		|||
GET /foo/bar?test=a&test2=b HTTP/1.1
 | 
			
		||||
Host: example.org
 | 
			
		||||
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; fr; rv:1.9.2.8) Gecko/20100722 Firefox/3.6.8
 | 
			
		||||
Accept: */*
 | 
			
		||||
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
 | 
			
		||||
Accept-Encoding: gzip,deflate
 | 
			
		||||
Accept-Language: fr,fr-fr;q=0.8,en-us;q=0.5,en;q=0.3
 | 
			
		||||
Accept-Encoding: gzip,deflate
 | 
			
		||||
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
 | 
			
		||||
Keep-Alive: 115
 | 
			
		||||
Connection: keep-alive
 | 
			
		||||
Content-Type: application/x-www-form-urlencoded
 | 
			
		||||
Cookie: foo=bar; lorem=ipsum;
 | 
			
		||||
Host: example.org
 | 
			
		||||
Keep-Alive: 115
 | 
			
		||||
Referer: http://example.org/test
 | 
			
		||||
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; fr; rv:1.9.2.8) Gecko/20100722 Firefox/3.6.8
 | 
			
		||||
X-Requested-With: XMLHttpRequest
 | 
			
		||||
 | 
			
		||||
{ "test": "content" }
 | 
			
		||||
Referer: http://example.org/test
 | 
			
		||||
Cookie: foo=bar; lorem=ipsum;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,17 +0,0 @@
 | 
			
		|||
#include <gtest/gtest.h>
 | 
			
		||||
#include <fstream>
 | 
			
		||||
#include "../lib/http/request.hpp"
 | 
			
		||||
#include <boost/beast.hpp>
 | 
			
		||||
 | 
			
		||||
TEST(unit_tests, single_request_parse) {
 | 
			
		||||
    std::ifstream t("./test_files/test_request.http");
 | 
			
		||||
    std::stringstream buffer;
 | 
			
		||||
    buffer << t.rdbuf();
 | 
			
		||||
 | 
			
		||||
    std::string raw_req = buffer.str();
 | 
			
		||||
    std::string expected = buffer.str();
 | 
			
		||||
 | 
			
		||||
    anthracite::http::request req (raw_req, "0.0.0.0");
 | 
			
		||||
 | 
			
		||||
    ASSERT_EQ(expected, req.to_string());
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue