Skip to content
Draft
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@ endif()
# set g++ compiler flags
if ( DEFINED DEBUG_BUILD )
add_compile_definitions(DEBUG_BUILD)
set(CMAKE_CXX_FLAGS "-g -fPIC -Wall -pthread -std=c++20 -Wno-unused-result -Wsign-compare -Wformat -Werror=format-security -fwrapv -rdynamic -fno-elide-constructors")
set(CMAKE_CXX_FLAGS "-g -fPIC -Wall -pthread -std=c++23 -Wno-unused-result -Wsign-compare -Wformat -Werror=format-security -fwrapv -rdynamic -fno-elide-constructors")
else()
set(CMAKE_CXX_FLAGS "-g -fPIC -Wall -pthread -O3 -fstack-protector-strong -std=c++20 -Wno-unused-result -Wsign-compare -Wformat -Werror=format-security -DNDEBUG -fwrapv -fno-elide-constructors")
set(CMAKE_CXX_FLAGS "-g -fPIC -Wall -pthread -O3 -fstack-protector-strong -std=c++23 -Wno-unused-result -Wsign-compare -Wformat -Werror=format-security -DNDEBUG -fwrapv -fno-elide-constructors")
endif()

# set boost specs
Expand Down
64 changes: 30 additions & 34 deletions lib/http/httpparser.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "httpparser.hpp"
#include "httpconstants.hpp"
#include <spanstream>

using namespace std;

Expand Down Expand Up @@ -31,7 +32,7 @@ void HTTPParser::appendBuffer(const char* BufferRef, const uint16_t RecvBytes)
//- on incomplete (single) POST request
if (_POSTWaitContentLength == true && _HTTPRequestBuffer.length() >= _POSTContentLength) {
_RequestProperties.Payload = _HTTPRequestBuffer.substr(0, _POSTContentLength);
_HTTPRequestBuffer.replace(0, _POSTContentLength, "");
_HTTPRequestBuffer.erase(0, _POSTContentLength);

_Requests.push_back(_RequestProperties);

Expand All @@ -50,7 +51,7 @@ void HTTPParser::appendBuffer(const char* BufferRef, const uint16_t RecvBytes)
}
}

RequestsMap_t HTTPParser::getRequests()
const RequestsMap_t& HTTPParser::getRequests() const
{
return _Requests;
}
Expand Down Expand Up @@ -135,7 +136,7 @@ inline bool HTTPParser::_processRequestProperties(const size_t Index)
//- content length bytes already in buffer
if (_HTTPRequestBuffer.length() >= _POSTContentLength) {
_RequestProperties.Payload = _HTTPRequestBuffer.substr(0, _POSTContentLength);
_HTTPRequestBuffer.replace(0, _POSTContentLength, "");
_HTTPRequestBuffer.erase(0, _POSTContentLength);

//- add request to requests map
_Requests.push_back(_RequestProperties);
Expand All @@ -151,7 +152,7 @@ inline bool HTTPParser::_processRequestProperties(const size_t Index)
auto &NextRequest = _SplittedRequests.at(Index+1);
if (NextRequest.length() >= _POSTContentLength) {
_RequestProperties.Payload = NextRequest.substr(0, _POSTContentLength);
NextRequest.replace(0, _POSTContentLength, "");
NextRequest.erase(0, _POSTContentLength);
//- add request to requests map
_Requests.push_back(_RequestProperties);
}
Expand All @@ -163,13 +164,13 @@ inline bool HTTPParser::_processRequestProperties(const size_t Index)
return true;
}

inline bool HTTPParser::_parseRequestProperties(string& Request, const RequestPropertiesRef_t ResultBaseProps)
inline bool HTTPParser::_parseRequestProperties(string_view Request, const RequestPropertiesRef_t ResultBaseProps)
{
//- find first line endline
size_t StartPos = Request.find("\r\n");

//-> if no headers (no \r\n), set start pos to end of string
if (StartPos == string::npos) {
if (StartPos == string_view::npos) {
StartPos = Request.length();
}

Expand All @@ -196,53 +197,48 @@ inline bool HTTPParser::_parseRequestProperties(string& Request, const RequestPr
return true;
}

inline void HTTPParser::_parseRequestHeaders(string& Request, RequestHeaderRef_t ResultRef)
inline void HTTPParser::_parseRequestHeaders(string_view Request, RequestHeaderRef_t ResultRef)
{
//- split / reverse split header lines
vector<string> Lines;
StringHelper::split(Request, "\r\n", Lines);

Lines.push_back(Request);

//- loop over lines, split, put into result map
for (auto &Line:Lines) {

vector<string> HeaderPair;
if (Line.find(":") != string::npos) {

StringHelper::rsplit(Line, Line.length(), ": ", HeaderPair);

if (HeaderPair.size() == 2 && !HeaderPair.at(1).empty()) {
ResultRef.emplace(
HeaderPair.at(1), HeaderPair.at(0).substr(0, HeaderPair.at(0).length())
);
//- wrap the raw buffer in a spanstream to parse lines without intermediate copies
ispanstream ss(span<const char>(Request.data(), Request.size()));
string line;
while (getline(ss, line, '\n')) {
if (!line.empty() && line.back() == '\r') line.pop_back();

//- require ": " (colon + space) to match original strict parsing behaviour
const size_t colonSpace = line.find(": ");
if (colonSpace != string::npos && colonSpace > 0) {
string_view lv(line);
string_view key = lv.substr(0, colonSpace);
string_view value = lv.substr(colonSpace + 2);
if (!value.empty()) {
ResultRef.emplace(string(key), string(value));
}
}
}
}

inline void HTTPParser::_parseGETParameter(const string& RequestURL, URLParamMapRef_t ResultRef)
inline void HTTPParser::_parseGETParameter(string_view RequestURL, URLParamMapRef_t ResultRef)
{
//- only process on init character "?" found
const size_t URLParamsStartPos = RequestURL.find("?");

if (URLParamsStartPos != string::npos && RequestURL.length() > URLParamsStartPos) {
if (URLParamsStartPos != string_view::npos && RequestURL.length() > URLParamsStartPos) {

string URLParamsPart = RequestURL.substr(URLParamsStartPos+1, RequestURL.length());
string_view URLParamsPart = RequestURL.substr(URLParamsStartPos + 1);

vector<string> ParamValuePairs;
vector<string_view> ParamValuePairs;
StringHelper::split(URLParamsPart, "&", ParamValuePairs);

ParamValuePairs.push_back(URLParamsPart);

//- loop over param-value pairs
for (auto &ParamValuePair:ParamValuePairs) {
for (const auto& ParamValuePair : ParamValuePairs) {

const size_t PVPDelimiterPos = ParamValuePair.find("=");

if (PVPDelimiterPos != string::npos && PVPDelimiterPos != 0 && ParamValuePair.length() > PVPDelimiterPos) {
if (PVPDelimiterPos != string_view::npos && PVPDelimiterPos != 0 && ParamValuePair.length() > PVPDelimiterPos) {
ResultRef.emplace(
ParamValuePair.substr(0, PVPDelimiterPos), ParamValuePair.substr(PVPDelimiterPos+1, ParamValuePair.length())
string(ParamValuePair.substr(0, PVPDelimiterPos)),
string(ParamValuePair.substr(PVPDelimiterPos + 1))
);
}
}
Expand Down
71 changes: 43 additions & 28 deletions lib/http/httpparser.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#pragma once

#include <string>
#include <string_view>
#include <span>
#include <vector>
#include <cstdint>
#include <utility>
Expand All @@ -9,11 +11,19 @@

using namespace std;

typedef unordered_map<string, string> RequestHeader_t;
typedef RequestHeader_t& RequestHeaderRef_t;
//- transparent hasher: enables heterogeneous lookup (string_view keys without string construction)
struct StringHash {
using is_transparent = void;
size_t operator()(string_view sv) const noexcept {
return hash<string_view>{}(sv);
}
};

typedef unordered_map<string, string> URLParamMap_t;
typedef URLParamMap_t& URLParamMapRef_t;
using RequestHeader_t = unordered_map<string, string, StringHash, equal_to<>>;
using RequestHeaderRef_t = RequestHeader_t&;

using URLParamMap_t = unordered_map<string, string, StringHash, equal_to<>>;
using URLParamMapRef_t = URLParamMap_t&;

struct RequestProperties_t
{
Expand All @@ -25,10 +35,10 @@ struct RequestProperties_t
URLParamMap_t URLParams;
};

typedef RequestProperties_t& RequestPropertiesRef_t;
using RequestPropertiesRef_t = RequestProperties_t&;

typedef vector<RequestProperties_t> RequestsMap_t;
typedef RequestsMap_t* RequestsMapPtr_t;
using RequestsMap_t = vector<RequestProperties_t>;
using RequestsMapPtr_t = RequestsMap_t*;


class HTTPParser
Expand All @@ -40,7 +50,7 @@ class HTTPParser
~HTTPParser();

void appendBuffer(const char*, const uint16_t);
RequestsMap_t getRequests();
const RequestsMap_t& getRequests() const;
RequestsMapPtr_t getRequestsPtr();

private:
Expand Down Expand Up @@ -68,61 +78,66 @@ class HTTPParser

protected:

bool _parseRequestProperties(string&, const RequestPropertiesRef_t);
void _parseRequestHeaders(string&, RequestHeaderRef_t);
void _parseGETParameter(const string&, URLParamMapRef_t);
bool _parseRequestProperties(string_view, const RequestPropertiesRef_t);
void _parseRequestHeaders(string_view, RequestHeaderRef_t);
void _parseGETParameter(string_view, URLParamMapRef_t);
};


class StringHelper {

public:

static void split(string& StringRef, const string Delimiter, vector<string>& ResultRef)
//- Non-destructive split: returns string_view slices into sv (zero heap allocation per token)
static void split(string_view sv, string_view delim, vector<string_view>& out)
{
string SplitElement;
auto pos = StringRef.find(Delimiter);
for (size_t pos; (pos = sv.find(delim)) != sv.npos; sv.remove_prefix(pos + delim.size()))
out.push_back(sv.substr(0, pos));
if (!sv.empty()) out.push_back(sv);
}

//- Destructive split: erases consumed tokens from StringRef in-place (used for buffer management)
static void split(string& StringRef, string_view Delimiter, vector<string>& ResultRef)
{
auto pos = StringRef.find(Delimiter);
while (pos != string::npos) {
SplitElement = StringRef.substr(0, pos);
ResultRef.push_back(SplitElement);
StringRef.erase(0, pos + Delimiter.length());
ResultRef.push_back(StringRef.substr(0, pos));
StringRef.erase(0, pos + Delimiter.size());
pos = StringRef.find(Delimiter);
}
}

static void rsplit(string& String, size_t StartPos, const string Delimiter, vector<string>& ResultRef)
static void rsplit(string_view String, size_t StartPos, string_view Delimiter, vector<string>& ResultRef)
{
size_t FindPos = 0;
size_t FindPosLast = 0;
string Token;

//- guard ensures StartPos >= Delimiter.length() before the subtraction below
if (StartPos < Delimiter.length()) {
ResultRef.push_back(String.substr(0, StartPos));
ResultRef.push_back(string(String.substr(0, StartPos)));
return;
}

StartPos -= Delimiter.length();
StartPos -= Delimiter.length(); //- safe: guarded by the check above

while ((FindPos = String.rfind(Delimiter, StartPos)) != String.npos) {
while ((FindPos = String.rfind(Delimiter, StartPos)) != string_view::npos) {

Token = String.substr(FindPos+Delimiter.length(), (StartPos-FindPos));
ResultRef.push_back(Token);
ResultRef.push_back(string(String.substr(FindPos + Delimiter.length(), StartPos - FindPos)));

//- guard ensures FindPos >= Delimiter.length() before the subtraction below
if (FindPos < Delimiter.length()) {
FindPosLast = FindPos;
break;
}

StartPos = FindPos - Delimiter.length();
StartPos = FindPos - Delimiter.length(); //- safe: guarded by the check above
FindPosLast = FindPos;
}

Token = String.substr(0, FindPosLast);
ResultRef.push_back(Token);
ResultRef.push_back(string(String.substr(0, FindPosLast)));
}

static bool is_digits(const string& checkdigits)
static bool is_digits(string_view checkdigits)
{
return all_of(checkdigits.begin(), checkdigits.end(), ::isdigit);
}
Expand Down
Loading