From ad9c69e3baa1773c2f3f4b5e36499927be3df1b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Tue, 14 Apr 2026 16:52:23 +0200 Subject: [PATCH 01/14] Add arduino port READMEs --- ports/README.md | 5 +++++ ports/arduino/README.md | 2 ++ ports/arduino/esp32/README.md | 3 +++ 3 files changed, 10 insertions(+) create mode 100644 ports/README.md create mode 100644 ports/arduino/README.md create mode 100644 ports/arduino/esp32/README.md diff --git a/ports/README.md b/ports/README.md new file mode 100644 index 0000000..8bcac27 --- /dev/null +++ b/ports/README.md @@ -0,0 +1,5 @@ +# Ported Systems +This directory contains ports to multiple systems. + +## Arduino +The ./arduino subdir contains ports to arduino based microcontrollers. diff --git a/ports/arduino/README.md b/ports/arduino/README.md new file mode 100644 index 0000000..07f6251 --- /dev/null +++ b/ports/arduino/README.md @@ -0,0 +1,2 @@ +# ESP32 +The ./esp32 subdir contains ports to ESP32 based boards, especially the ESP32-C3 single core SoC. diff --git a/ports/arduino/esp32/README.md b/ports/arduino/esp32/README.md new file mode 100644 index 0000000..c33de89 --- /dev/null +++ b/ports/arduino/esp32/README.md @@ -0,0 +1,3 @@ +# ESP32-C3 +As the first arduino port, the ESP32-C3 will be used for the prototyping. + From 87e6d3f6491c5093f22dc7a734fab3d5bd5c7e81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Sun, 19 Apr 2026 09:45:05 +0200 Subject: [PATCH 02/14] Remove boost:regex linking to libhttp, also remove outdated integration test --- lib/http/CMakeLists.txt | 2 +- lib/http/httpnet.hpp | 2 -- test/CMakeLists.txt | 4 ---- test/integration/boost-regex/CMakeLists.txt | 10 -------- .../boost-regex/test-boost-regex.cpp | 23 ------------------- 5 files changed, 1 insertion(+), 40 deletions(-) delete mode 100644 test/integration/boost-regex/CMakeLists.txt delete mode 100644 test/integration/boost-regex/test-boost-regex.cpp diff --git a/lib/http/CMakeLists.txt b/lib/http/CMakeLists.txt index e7708e8..fafb91d 100644 --- a/lib/http/CMakeLists.txt +++ b/lib/http/CMakeLists.txt @@ -1,5 +1,5 @@ # find boost::python -find_package(Boost 1.74 REQUIRED COMPONENTS regex) +find_package(Boost 1.74 REQUIRED) # add source dir aux_source_directory(. SRC_LIST_LIBHTTP) diff --git a/lib/http/httpnet.hpp b/lib/http/httpnet.hpp index 6a0b108..36c1b7a 100644 --- a/lib/http/httpnet.hpp +++ b/lib/http/httpnet.hpp @@ -1,8 +1,6 @@ #ifndef LibHTTP_net_hpp #define LibHTTP_net_hpp -#include - #include "../../src/Debug.cpp" #include "../../src/Helper.hpp" diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index df9d207..1011064 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,12 +1,8 @@ # find boost unittest package find_package(Boost COMPONENTS unit_test_framework REQUIRED) -# find qt6 package -#find_package(Qt6 REQUIRED COMPONENTS Core Test) - # add subdirs add_subdirectory(eval) add_subdirectory(integration) add_subdirectory(unit) add_subdirectory(performance) - diff --git a/test/integration/boost-regex/CMakeLists.txt b/test/integration/boost-regex/CMakeLists.txt deleted file mode 100644 index 229ef65..0000000 --- a/test/integration/boost-regex/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -# add source dir -aux_source_directory(. SRC_LIST_TEST_BOOST_REGEX) - -# find boost unittest package -find_package(Boost COMPONENTS regex REQUIRED) - -add_executable(test-boost-regex "test-boost-regex.cpp") - -target_link_libraries(test-boost-regex ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) -target_link_libraries(test-boost-regex ${Boost_LIBRARIES}) diff --git a/test/integration/boost-regex/test-boost-regex.cpp b/test/integration/boost-regex/test-boost-regex.cpp deleted file mode 100644 index dade1a9..0000000 --- a/test/integration/boost-regex/test-boost-regex.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#define BOOST_TEST_MAIN -#include - -#include - -using namespace std; -using namespace boost; - -BOOST_AUTO_TEST_CASE( test_regex_split ) -{ - - cout << "Check multiple Boost Regex variants." << endl; - - /* - //- does not work, c++20 does not implement list.push_back() - string Data("Test String \n\r Hello World\n\r Bla Bla"); - list ResultList; - list::iterator ListIter; - regex regexString{"\n\r"}; - regex_split(back_inserter(ListIter), Data, regexString); - */ - -} From 0f1a0c5acdb3c230927a8832a66b2049b101a251 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Sun, 19 Apr 2026 09:46:43 +0200 Subject: [PATCH 03/14] Also remove (forgotten) CMake boost:regex add_subdirectory command --- test/integration/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/test/integration/CMakeLists.txt b/test/integration/CMakeLists.txt index 0d0396e..6d6cc44 100644 --- a/test/integration/CMakeLists.txt +++ b/test/integration/CMakeLists.txt @@ -1,6 +1,5 @@ # add subdirs add_subdirectory(event) -add_subdirectory(boost-regex) add_subdirectory(string-functions) add_subdirectory(vector-multi-erase) add_subdirectory(signal-termination) From 73ae790eb66d5f57ca72dd829b0fb62d4b8fc21a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Sun, 19 Apr 2026 14:19:57 +0200 Subject: [PATCH 04/14] Rename http library --- CMakeLists.txt | 2 +- lib/http/CMakeLists.txt | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d6d059d..4966aea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -78,7 +78,7 @@ target_link_libraries( ${PYTHON_LIBRARIES} ${Boost_LIBRARIES} nlohmann_json::nlohmann_json - libhttp + httpparser ) # add custom targets diff --git a/lib/http/CMakeLists.txt b/lib/http/CMakeLists.txt index fafb91d..ef8979f 100644 --- a/lib/http/CMakeLists.txt +++ b/lib/http/CMakeLists.txt @@ -5,18 +5,18 @@ find_package(Boost 1.74 REQUIRED) aux_source_directory(. SRC_LIST_LIBHTTP) # add library -add_library(libhttp STATIC ${SRC_LIST_LIBHTTP}) +add_library(httpparser STATIC ${SRC_LIST_LIBHTTP}) # add lib parser and network add_custom_target( - libhttpheader + httpparserheader SOURCES ./httpparser.hpp ./httpnet.hpp ) # set target link libraries -target_link_libraries(libhttp ${Boost_LIBRARIES}) +target_link_libraries(httpparser ${Boost_LIBRARIES}) # install -install(TARGETS libhttp DESTINATION lib) +install(TARGETS httpparser DESTINATION lib) From 1f4d497e07b090620c73cc5041b72ac236e217ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Tue, 21 Apr 2026 15:36:58 +0200 Subject: [PATCH 05/14] Add cross-compilable static httpparser lib --- lib/http/httpnet.cpp | 15 ----- lib/http/httpnet.hpp | 24 -------- lib/http/httpparser.cpp | 67 +++++++--------------- lib/http/httpparser.hpp | 74 +++++++++++++++++-------- ports/arduino/esp32/CMakeLists.txt | 19 +++++++ ports/arduino/esp32/README.md | 17 +++++- ports/arduino/esp32/riscv32-cross.cmake | 17 ++++++ src/Client.cpp | 19 ++++--- src/Client.hpp | 26 ++------- src/ClientHandler.hpp | 2 - src/Helper.hpp | 36 ------------ 11 files changed, 139 insertions(+), 177 deletions(-) delete mode 100644 lib/http/httpnet.cpp delete mode 100644 lib/http/httpnet.hpp create mode 100644 ports/arduino/esp32/CMakeLists.txt create mode 100644 ports/arduino/esp32/riscv32-cross.cmake diff --git a/lib/http/httpnet.cpp b/lib/http/httpnet.cpp deleted file mode 100644 index 2d96bdb..0000000 --- a/lib/http/httpnet.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "httpnet.hpp" - -using namespace std; - - -HTTPNet::HTTPNet() -{ - DBG(120, "Constructor"); -} - -HTTPNet::~HTTPNet() -{ - DBG(120, "Destructor"); -} - diff --git a/lib/http/httpnet.hpp b/lib/http/httpnet.hpp deleted file mode 100644 index 36c1b7a..0000000 --- a/lib/http/httpnet.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef LibHTTP_net_hpp -#define LibHTTP_net_hpp - -#include "../../src/Debug.cpp" -#include "../../src/Helper.hpp" - -#include - -class HTTPNet -{ - -public: - - HTTPNet(); - ~HTTPNet(); - - -private: - -protected: - -}; - -#endif diff --git a/lib/http/httpparser.cpp b/lib/http/httpparser.cpp index a289122..2f4ebd3 100644 --- a/lib/http/httpparser.cpp +++ b/lib/http/httpparser.cpp @@ -3,9 +3,8 @@ using namespace std; -HTTPParser::HTTPParser(ClientFD_t ClientFD, NamespacesRef_t NamespacesRef) : - Client(ClientFD), - _Namespaces(NamespacesRef), +HTTPParser::HTTPParser() : + //Client(ClientFD), _RequestCount(0), _RequestCountGet(0), _RequestCountPost(0), @@ -21,20 +20,16 @@ HTTPParser::~HTTPParser() DBG(120, "Destructor"); } -void HTTPParser::appendBuffer(const char* BufferRef, const uint16_t BufferSize) +void HTTPParser::appendBuffer(const char* BufferRef, const uint16_t RecvBytes) { //-> reset _SplittedRequests vector _SplittedRequests.clear(); - //- workaround - if (BufferRef[0] == 0) { return; } - - DBG(250, "size:" << string(BufferRef).length() << "appendBuffer:'" << string(BufferRef) << "'"); - _HTTPRequestBuffer = _HTTPRequestBuffer + string(BufferRef); - //String::hexout(_HTTPRequestBuffer); + DBG(250, "Buffer received bytes:" << RecvBytes); + _HTTPRequestBuffer.append(BufferRef, RecvBytes); DBG(250, "_HTTPRequestBuffer:'" << _HTTPRequestBuffer << "'"); - //-> only process on min 1 valid request + //-> only process on minimum of 1 http request (end request marker found) const size_t EndMarkerFound = _HTTPRequestBuffer.find("\r\n\r\n"); if (EndMarkerFound != string::npos) { @@ -53,29 +48,26 @@ inline void HTTPParser::_splitRequests() _RequestNumber = 1; //-> split requests into _SplittedRequests vector - String::split(_HTTPRequestBuffer, "\r\n\r\n", _SplittedRequests); + StringHelper::split(_HTTPRequestBuffer, "\r\n\r\n", _SplittedRequests); _RequestCount = _SplittedRequests.size(); DBG(120, "splitRequests count after splitted into Vector:" << _RequestCount); DBG(120, "_HTTPRequestBuffer after split:'" << _HTTPRequestBuffer << "'"); } -size_t HTTPParser::processRequests(SharedMemAddress_t SHMGetRequests, const ASRequestHandlerRef_t& ASRequestHandlerRef) +size_t HTTPParser::processRequests() { DBG(250, "_HTTPRequestBuffer:'" << _HTTPRequestBuffer << "'"); - //- set get requests SHM base - setBaseAddress(SHMGetRequests); - //- iterate over splitted requests for(size_t i=0; i<_SplittedRequests.size(); ++i) { - _processRequestProperties(i, ASRequestHandlerRef); + _processRequestProperties(i); } return _RequestCountGet; } -void HTTPParser::_processRequestProperties(const size_t Index, const ASRequestHandlerRef_t& ASRequestHandlerRef) +void HTTPParser::_processRequestProperties(const size_t Index) { //- get request ref at vector index auto &Request = _SplittedRequests.at(Index); @@ -94,9 +86,8 @@ void HTTPParser::_processRequestProperties(const size_t Index, const ASRequestHa //- temp hardcode HTTPVersion uint16_t HTTPVersion = 1; - //- check HTTP/1.2 or HTTP/1.2 (currently unimplemented) + //- check for HTTP/1.1 version const size_t HTTPVersion1_1Found = BasePropsFound.at(0).find("HTTP/1.1"); - const size_t HTTPVersion1_2Found = BasePropsFound.at(2).find("HTTP/1.2"); //- if not HTTP/1.1 set request to "" in vector element, return if (HTTPVersion1_1Found == string::npos) { return; } @@ -139,6 +130,7 @@ void HTTPParser::_processRequestProperties(const size_t Index, const ASRequestHa //- parse request headers _parseRequestHeaders(Request, Headers); + /* const NamespaceProps_t NamespaceProps = _Namespaces.at(Headers.at("Host")); string JSONPayload("{ \"payload\": {"); @@ -165,6 +157,7 @@ void HTTPParser::_processRequestProperties(const size_t Index, const ASRequestHa return; } } + */ } //- AS POST request @@ -189,7 +182,7 @@ void HTTPParser::_processRequestProperties(const size_t Index, const ASRequestHa //- parse request headers _parseRequestHeaders(Request, Headers); - uint ContentBytes = 0; + uint16_t ContentBytes = 0; //- try get content length header try { @@ -224,11 +217,13 @@ void HTTPParser::_processRequestProperties(const size_t Index, const ASRequestHa DBG(140, "HTTP POST-AS Payload:" << Payload); + /* if (PayloadFound) { _processASPayload( ASRequestHandlerRef, Headers, HTTPMethod, HTTPVersion, RequestNr, Payload ); } + */ } //- Standard GET request @@ -238,6 +233,7 @@ void HTTPParser::_processRequestProperties(const size_t Index, const ASRequestHa ++_RequestCountGet; + /* //- set values in get requests shared memory const char* MsgCString = Request.c_str(); @@ -258,6 +254,7 @@ void HTTPParser::_processRequestProperties(const size_t Index, const ASRequestHa void* NextSegmentAddr = getNextAddress(MsgLength); DBG(120, "Set SharedMem ClientFD:" << ClientFDAddr << " PayloadLength:" << MsgLengthAddr << " Payload:" << MsgAddress << " NextSegment:" << NextSegmentAddr); + */ } } @@ -274,7 +271,7 @@ void HTTPParser::_parseRequestProperties(string& Request, BasePropsResultRef_t R } //- reverse split - String::rsplit(Request, StartPos, " ", ResultRef); + StringHelper::rsplit(Request, StartPos, " ", ResultRef); DBG(120, "HTTP Version:" << ResultRef.at(0) << " File:" << ResultRef.at(1) << " Method:" << ResultRef.at(2) << " Request:" << Request); } @@ -285,7 +282,7 @@ void HTTPParser::_parseRequestHeaders(string& Request, RequestHeaderResultRef_t //- reverse split header lines vector Lines; - String::split(Request, "\r\n", Lines); + StringHelper::split(Request, "\r\n", Lines); DBG(120, "Last Header Line:'" << Request << "'"); Lines.push_back(Request); @@ -298,7 +295,7 @@ void HTTPParser::_parseRequestHeaders(string& Request, RequestHeaderResultRef_t vector HeaderPair; if (Line.find(":") != string::npos) { - String::rsplit(Line, Line.length(), ": ", HeaderPair); + StringHelper::rsplit(Line, Line.length(), ": ", HeaderPair); string HeaderID = HeaderPair.at(1); string HeaderValue = HeaderPair.at(0).substr(0, HeaderPair.at(0).length()); @@ -359,25 +356,3 @@ inline string HTTPParser::_getASURLParamValue( } return "parse-error"; } - -inline void HTTPParser::_processASPayload( - const ASRequestHandlerRef_t& ASRequestHandlerRef, - const RequestHeaderResult_t& Headers, - const uint16_t HTTPMethod, - const uint16_t HTTPVersion, - const uint16_t RequestNr, - const string& Payload -){ - //- increment request count - ++_RequestCountPostAS; - - //- add ASRequestHandler request - ASRequestHandlerRef->addRequest({ - Headers.at("Host"), - _ClientFD, - HTTPMethod, - HTTPVersion, - RequestNr, - Payload - }); -} diff --git a/lib/http/httpparser.hpp b/lib/http/httpparser.hpp index c01dbbe..0eed07b 100644 --- a/lib/http/httpparser.hpp +++ b/lib/http/httpparser.hpp @@ -1,14 +1,11 @@ -#ifndef LibHTTP_parser_hpp -#define LibHTTP_parser_hpp +#pragma once + #include "../../src/Debug.cpp" -#include "../../src/Helper.hpp" -#include "../../src/IPCHandler.hpp" -#include "../../src/IPCHandlerAS.hpp" -#include "../../src/ASRequestHandler.hpp" #include "../../src/Client.hpp" #include - +#include +#include typedef pair HeaderPair_t; typedef unordered_map RequestHeader_t; @@ -20,7 +17,7 @@ typedef vector BasePropsResult_t; typedef BasePropsResult_t& BasePropsResultRef_t; -static const vector HeaderList +static const vector HTTPHeaderTypes { "Host", "Request-UUID", @@ -31,27 +28,25 @@ static const vector HeaderList }; -class HTTPParser: private Client, private SHMStaticFS, private SHMPythonAS +class HTTPParser { public: - HTTPParser(const ClientFD_t, const NamespacesRef_t); + HTTPParser(); ~HTTPParser(); void appendBuffer(const char*, const uint16_t); - size_t processRequests(SharedMemAddress_t, const ASRequestHandlerRef_t&); + size_t processRequests(); private: inline void _splitRequests(); - void _processRequestProperties(const size_t, const ASRequestHandlerRef_t&); + void _processRequestProperties(const size_t); RequestHeader_t _RequestHeaders; vector _SplittedRequests; - NamespacesRef_t _Namespaces; - size_t _RequestCount; size_t _RequestCountGet; size_t _RequestCountPost; @@ -71,15 +66,46 @@ class HTTPParser: private Client, private SHMStaticFS, private SHMPythonAS const uint16_t, string& ); - - inline void _processASPayload( - const ASRequestHandlerRef_t&, - const RequestHeaderResult_t&, - const uint16_t, - const uint16_t, - const uint16_t, - const string& - ); }; -#endif +class StringHelper { + +public: + + static void split(string& StringRef, const string Delimiter, vector& ResultRef) + { + string SplitElement; + auto pos = StringRef.find(Delimiter); + + while (pos != string::npos) { + SplitElement = StringRef.substr(0, pos); + DBG(220, "SplitElement:'" << SplitElement << "'"); + ResultRef.push_back(SplitElement); + StringRef.erase(0, pos + Delimiter.length()); + pos = StringRef.find(Delimiter); + } + + DBG(200, "String:'" << StringRef << "'"); + } + + //- TODO: ugly, refactor (lambda?) + static void rsplit(string& String, size_t StartPos, const string Delimiter, vector& ResultRef) + { + size_t FindPos = 0; + size_t FindPosLast = 0; + string Token; + StartPos -= Delimiter.length(); + while ((FindPos = String.rfind(Delimiter, StartPos)) != String.npos) { + DBG(200, "FindPos:" << FindPos << " StartPos:" << StartPos); + Token = String.substr(FindPos+Delimiter.length(), (StartPos-FindPos)); + DBG(200, "Token:" << Token); + ResultRef.push_back(Token); + StartPos = FindPos - Delimiter.length(); + FindPosLast = FindPos; + } + DBG(200, "End FindPos:" << FindPosLast); + Token = String.substr(0, FindPosLast); + ResultRef.push_back(Token); + } + +}; diff --git a/ports/arduino/esp32/CMakeLists.txt b/ports/arduino/esp32/CMakeLists.txt new file mode 100644 index 0000000..a8c0a40 --- /dev/null +++ b/ports/arduino/esp32/CMakeLists.txt @@ -0,0 +1,19 @@ +# project settings +cmake_minimum_required(VERSION 3.11) +project(falcon-as-arduino) + +set(CMAKE_CXX_FLAGS "-g -fPIC -Wall -O0 -std=c++11 -Wno-unused-result -Wsign-compare -Wformat -Werror=format-security -DNDEBUG -fwrapv") + +# add source dir +aux_source_directory(../../../lib/http/ SRC_LIST_LIBHTTP) + +# add library +add_library(httpparser STATIC ${SRC_LIST_LIBHTTP}) + +# add c++ header files +add_custom_target( + httpparserheader + SOURCES + ../../../lib/http/httpparser.hpp +) + diff --git a/ports/arduino/esp32/README.md b/ports/arduino/esp32/README.md index c33de89..6283c5a 100644 --- a/ports/arduino/esp32/README.md +++ b/ports/arduino/esp32/README.md @@ -1,3 +1,18 @@ # ESP32-C3 -As the first arduino port, the ESP32-C3 will be used for the prototyping. +As the first arduino port, the ESP32-C3 will be used for the prototyping. This CMake configuration will cross-compile the httpparser as static `httpparser.a` library which afterwards can be used in your ESP-IDF project / component. + +# Prerequisites + +Installed and **active** ESP-IDF build environment / cross-compiler installed in `$HOME/.espressif`. + +# Building + +```bash +./adjust-cross-build.sh +cmake -DCMAKE_TOOLCHAIN_FILE=riscv32-cross.cmake . +make +make install +``` + +The `make install` command will install the static library `/usr/local/lib/httpparser.a` and the c++ header file `/usr/local/include/httpparser.hpp`. diff --git a/ports/arduino/esp32/riscv32-cross.cmake b/ports/arduino/esp32/riscv32-cross.cmake new file mode 100644 index 0000000..7fedd83 --- /dev/null +++ b/ports/arduino/esp32/riscv32-cross.cmake @@ -0,0 +1,17 @@ +# the name of the target operating system +set(CMAKE_SYSTEM_NAME RISCV32) + +# which compilers to use for C and C++ +set(CMAKE_C_COMPILER /home/cpruefer/.espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/bin/riscv32-esp-elf-gcc) +set(CMAKE_CXX_COMPILER /home/cpruefer/.espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/bin/riscv32-esp-elf-g++) + +# where is the target environment located +set(CMAKE_FIND_ROOT_PATH ~/.espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf) + +# adjust the default behavior of the FIND_XXX() commands: +# search programs in the host environment +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) + +# search headers and libraries in the target environment +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) diff --git a/src/Client.cpp b/src/Client.cpp index 8894765..d2a1094 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -5,16 +5,13 @@ using namespace std; Client::Client(ClientFD_t ClientFD) : _ClientFD(ClientFD), _RequestNr(0), - _Error(false), - _ErrorID(0), - _RequestStartTime(0), - _RequestEndTime(0), - _TimeoutReached(false) + _SocketConnectTime(0), + _SocketDisconnectTime(0) { DBG(120, "Constructor"); - _RequestStartTime = time(nullptr); - _ResponseStartTime = time(nullptr); + _ClientConnectTime = time(nullptr); + _ClientDisconnectTime = time(nullptr); } Client::~Client() @@ -22,8 +19,12 @@ Client::~Client() DBG(120, "Destructor"); } -ClientRequestNr_t Client::getNextReqNr() +ClientRequestNr_t Client::getCurrentReqNr() { - _RequestNr += 1; return _RequestNr; } + +void Client::incrementReqNr() +{ + _RequestNr += 1; +} diff --git a/src/Client.hpp b/src/Client.hpp index 529437a..8d87ac5 100644 --- a/src/Client.hpp +++ b/src/Client.hpp @@ -1,5 +1,4 @@ -#ifndef Client_hpp -#define Client_hpp +#pragma once #include "Debug.cpp" @@ -7,20 +6,18 @@ #include #include - typedef uint16_t ClientFD_t; typedef uint16_t ClientRequestNr_t; - -class Client -{ +class Client { public: Client(ClientFD_t); ~Client(); - ClientRequestNr_t getNextReqNr(); + void incrementReqNr(); + ClientRequestNr_t getCurrentReqNr(); protected: @@ -29,17 +26,6 @@ class Client private: ClientRequestNr_t _RequestNr; - - bool _Error; - uint16_t _ErrorID; - - time_t _RequestStartTime; - time_t _RequestEndTime; - time_t _ResponseStartTime; - time_t _ResponseEndTime; - - bool _TimeoutReached; - + time_t _SocketConnectTime; + time_t _SocketDisconnectTime; }; - -#endif diff --git a/src/ClientHandler.hpp b/src/ClientHandler.hpp index 8c03e21..d66decd 100644 --- a/src/ClientHandler.hpp +++ b/src/ClientHandler.hpp @@ -17,8 +17,6 @@ #include "MemoryManager.hpp" #include "ASRequestHandler.hpp" -#include "../lib/http/httpparser.hpp" - typedef std::shared_ptr ClientRef_t; typedef pair ClientMapPair_t; diff --git a/src/Helper.hpp b/src/Helper.hpp index b5ab8d9..aa60c7c 100644 --- a/src/Helper.hpp +++ b/src/Helper.hpp @@ -57,42 +57,6 @@ class String { public: - static void split(string& StringRef, const string Delimiter, vector& ResultRef) - { - string SplitElement; - auto pos = StringRef.find(Delimiter); - - while (pos != string::npos) { - SplitElement = StringRef.substr(0, pos); - DBG(220, "SplitElement:'" << SplitElement << "'"); - ResultRef.push_back(SplitElement); - StringRef.erase(0, pos + Delimiter.length()); - pos = StringRef.find(Delimiter); - } - - DBG(200, "String:'" << StringRef << "'"); - } - - //- TODO: ugly, refactor (lambda?) - static void rsplit(string& String, size_t StartPos, const string Delimiter, vector& ResultRef) - { - size_t FindPos = 0; - size_t FindPosLast = 0; - string Token; - StartPos -= Delimiter.length(); - while ((FindPos = String.rfind(Delimiter, StartPos)) != String.npos) { - DBG(200, "FindPos:" << FindPos << " StartPos:" << StartPos); - Token = String.substr(FindPos+Delimiter.length(), (StartPos-FindPos)); - DBG(200, "Token:" << Token); - ResultRef.push_back(Token); - StartPos = FindPos - Delimiter.length(); - FindPosLast = FindPos; - } - DBG(200, "End FindPos:" << FindPosLast); - Token = String.substr(0, FindPosLast); - ResultRef.push_back(Token); - } - static void hexout(string& String) { for (auto i = String.begin(); i != String.end(); ++i) { From 4a3039b506a4dc07320566a5b3959fcfa4e5f19d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Thu, 23 Apr 2026 08:28:59 +0200 Subject: [PATCH 06/14] Update / add cmake install --- ports/arduino/{esp32 => esp32c3}/CMakeLists.txt | 6 ++++-- ports/arduino/{esp32 => esp32c3}/README.md | 0 ports/arduino/esp32c3/adust-cross-build.sh | 4 ++++ .../riscv32-cross.cmake => esp32c3/riscv32-cross.cmake.tpl} | 4 ++-- 4 files changed, 10 insertions(+), 4 deletions(-) rename ports/arduino/{esp32 => esp32c3}/CMakeLists.txt (71%) rename ports/arduino/{esp32 => esp32c3}/README.md (100%) create mode 100755 ports/arduino/esp32c3/adust-cross-build.sh rename ports/arduino/{esp32/riscv32-cross.cmake => esp32c3/riscv32-cross.cmake.tpl} (67%) diff --git a/ports/arduino/esp32/CMakeLists.txt b/ports/arduino/esp32c3/CMakeLists.txt similarity index 71% rename from ports/arduino/esp32/CMakeLists.txt rename to ports/arduino/esp32c3/CMakeLists.txt index a8c0a40..050b69c 100644 --- a/ports/arduino/esp32/CMakeLists.txt +++ b/ports/arduino/esp32c3/CMakeLists.txt @@ -1,8 +1,8 @@ # project settings cmake_minimum_required(VERSION 3.11) -project(falcon-as-arduino) +project(esp32c3-httplib) -set(CMAKE_CXX_FLAGS "-g -fPIC -Wall -O0 -std=c++11 -Wno-unused-result -Wsign-compare -Wformat -Werror=format-security -DNDEBUG -fwrapv") +set(CMAKE_CXX_FLAGS "-g -fPIC -Wall -Os -std=c++11 -Wno-unused-result -Wsign-compare -Wformat -Werror=format-security -DNDEBUG -fwrapv") # add source dir aux_source_directory(../../../lib/http/ SRC_LIST_LIBHTTP) @@ -17,3 +17,5 @@ add_custom_target( ../../../lib/http/httpparser.hpp ) +# install +install(TARGETS httpparser DESTINATION /usr/local/lib) diff --git a/ports/arduino/esp32/README.md b/ports/arduino/esp32c3/README.md similarity index 100% rename from ports/arduino/esp32/README.md rename to ports/arduino/esp32c3/README.md diff --git a/ports/arduino/esp32c3/adust-cross-build.sh b/ports/arduino/esp32c3/adust-cross-build.sh new file mode 100755 index 0000000..2cf3e19 --- /dev/null +++ b/ports/arduino/esp32c3/adust-cross-build.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +cp ./riscv32-cross.cmake.tpl ./riscv32-cross.cmake +sed -i "s/\[HOMEDIR\]/$HOME/g" ./riscv32-cross.cmake diff --git a/ports/arduino/esp32/riscv32-cross.cmake b/ports/arduino/esp32c3/riscv32-cross.cmake.tpl similarity index 67% rename from ports/arduino/esp32/riscv32-cross.cmake rename to ports/arduino/esp32c3/riscv32-cross.cmake.tpl index 7fedd83..62a3133 100644 --- a/ports/arduino/esp32/riscv32-cross.cmake +++ b/ports/arduino/esp32c3/riscv32-cross.cmake.tpl @@ -2,8 +2,8 @@ set(CMAKE_SYSTEM_NAME RISCV32) # which compilers to use for C and C++ -set(CMAKE_C_COMPILER /home/cpruefer/.espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/bin/riscv32-esp-elf-gcc) -set(CMAKE_CXX_COMPILER /home/cpruefer/.espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/bin/riscv32-esp-elf-g++) +set(CMAKE_C_COMPILER [HOMEDIR]/.espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/bin/riscv32-esp-elf-gcc) +set(CMAKE_CXX_COMPILER [HOMEDIR]/.espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/bin/riscv32-esp-elf-g++) # where is the target environment located set(CMAKE_FIND_ROOT_PATH ~/.espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf) From 06e4153e3c9a0a1d8a01e5d74ee1752da8ec6746 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Thu, 23 Apr 2026 08:39:33 +0200 Subject: [PATCH 07/14] Cleanup, refactor (remove non-lib-releated functionality) --- lib/http/CMakeLists.txt | 7 --- lib/http/httpconstants.hpp | 11 ++++ lib/http/httpparser.cpp | 125 +++++++++++-------------------------- lib/http/httpparser.hpp | 34 +++++----- 4 files changed, 68 insertions(+), 109 deletions(-) create mode 100644 lib/http/httpconstants.hpp diff --git a/lib/http/CMakeLists.txt b/lib/http/CMakeLists.txt index ef8979f..1f8e0f3 100644 --- a/lib/http/CMakeLists.txt +++ b/lib/http/CMakeLists.txt @@ -1,6 +1,3 @@ -# find boost::python -find_package(Boost 1.74 REQUIRED) - # add source dir aux_source_directory(. SRC_LIST_LIBHTTP) @@ -12,11 +9,7 @@ add_custom_target( httpparserheader SOURCES ./httpparser.hpp - ./httpnet.hpp ) -# set target link libraries -target_link_libraries(httpparser ${Boost_LIBRARIES}) - # install install(TARGETS httpparser DESTINATION lib) diff --git a/lib/http/httpconstants.hpp b/lib/http/httpconstants.hpp new file mode 100644 index 0000000..8261d4d --- /dev/null +++ b/lib/http/httpconstants.hpp @@ -0,0 +1,11 @@ +#pragma once + +constexpr uint16_t HTTP_VERSION_UNKNOWN = 0; +constexpr uint16_t HTTP_VERSION_1_1 = 1; + +constexpr uint16_t HTTP_METHOD_OTHER = 0; +constexpr uint16_t HTTP_METHOD_GET = 1; +constexpr uint16_t HTTP_METHOD_POST = 2; + +constexpr uint16_t URL_PARAM_NOT_FOUND = 10; +constexpr uint16_t URL_PARAM_PARSE_ERROR = 20; diff --git a/lib/http/httpparser.cpp b/lib/http/httpparser.cpp index 2f4ebd3..cc4d43d 100644 --- a/lib/http/httpparser.cpp +++ b/lib/http/httpparser.cpp @@ -4,20 +4,16 @@ using namespace std; HTTPParser::HTTPParser() : - //Client(ClientFD), _RequestCount(0), _RequestCountGet(0), _RequestCountPost(0), - _RequestCountPostAS(0), _HTTPRequestBuffer("") { - DBG(120, "Constructor"); _HTTPRequestBuffer.reserve(BUFFER_BYTES); } HTTPParser::~HTTPParser() { - DBG(120, "Destructor"); } void HTTPParser::appendBuffer(const char* BufferRef, const uint16_t RecvBytes) @@ -25,9 +21,7 @@ void HTTPParser::appendBuffer(const char* BufferRef, const uint16_t RecvBytes) //-> reset _SplittedRequests vector _SplittedRequests.clear(); - DBG(250, "Buffer received bytes:" << RecvBytes); _HTTPRequestBuffer.append(BufferRef, RecvBytes); - DBG(250, "_HTTPRequestBuffer:'" << _HTTPRequestBuffer << "'"); //-> only process on minimum of 1 http request (end request marker found) const size_t EndMarkerFound = _HTTPRequestBuffer.find("\r\n\r\n"); @@ -39,8 +33,6 @@ void HTTPParser::appendBuffer(const char* BufferRef, const uint16_t RecvBytes) inline void HTTPParser::_splitRequests() { - //DBG(180, "splitRequests Buffer:'" << _HTTPRequestBuffer << "'"); - //- reset request counters _RequestCountGet = 0; _RequestCountPost = 0; @@ -51,14 +43,10 @@ inline void HTTPParser::_splitRequests() StringHelper::split(_HTTPRequestBuffer, "\r\n\r\n", _SplittedRequests); _RequestCount = _SplittedRequests.size(); - DBG(120, "splitRequests count after splitted into Vector:" << _RequestCount); - DBG(120, "_HTTPRequestBuffer after split:'" << _HTTPRequestBuffer << "'"); } size_t HTTPParser::processRequests() { - DBG(250, "_HTTPRequestBuffer:'" << _HTTPRequestBuffer << "'"); - //- iterate over splitted requests for(size_t i=0; i<_SplittedRequests.size(); ++i) { _processRequestProperties(i); @@ -75,52 +63,23 @@ void HTTPParser::_processRequestProperties(const size_t Index) //- on empty request return if (Request.empty()) { return; } - DBG(140, "Processing Index:" << Index << " Request:'" << Request << "'"); - - BasePropsResult_t BasePropsFound; - _parseRequestProperties(Request, BasePropsFound); - - DBG(140, "HTTP Version:" << BasePropsFound.at(0) << " File:" << BasePropsFound.at(1) << " Method:" << BasePropsFound.at(2)); - DBG(140, "Complete Request:" << Request.c_str()); - - //- temp hardcode HTTPVersion - uint16_t HTTPVersion = 1; - - //- check for HTTP/1.1 version - const size_t HTTPVersion1_1Found = BasePropsFound.at(0).find("HTTP/1.1"); - - //- if not HTTP/1.1 set request to "" in vector element, return - if (HTTPVersion1_1Found == string::npos) { return; } + //- init unparsed base properties with default values + BaseProperties_t BaseProperties = { + .HTTPVersion = HTTP_VERSION_UNKNOWN, + .HTTPMethod = HTTP_METHOD_OTHER, + .URL = "/" + }; - //- check for method GET || POST - const size_t HTTPMethodPOST = BasePropsFound.at(2).find("POST"); - const size_t HTTPMethodGET = BasePropsFound.at(2).find("GET"); + //- parse base properties + _parseRequestProperties(Request, BaseProperties); - //- set numerical http method (GET: 1, POST: 2) - uint16_t HTTPMethod = 0; + //- only process HTTP/1.1 requests + if (BaseProperties.HTTPVersion != HTTP_VERSION_1_1) { return; } - if (HTTPMethodGET != string::npos) { HTTPMethod = 1; } - if (HTTPMethodPOST != string::npos) { HTTPMethod = 2; } + //- if not GET || POST method, return + if (BaseProperties.HTTPMethod == HTTP_METHOD_OTHER) { return; } - DBG(140, "HTTPMethod:" << HTTPMethod); - //- if not GET || POST, return - if (HTTPMethod == 0) { return; } - - //- check if POST request is a POSTAS request - const size_t AppServerReqFound = BasePropsFound.at(1).find("/backend/"); - - //- if not POSTAS, return - if (HTTPMethod == 2 && AppServerReqFound == string::npos) { - ++_RequestCountPost; - return; - } - - //- get unique request nr - const uint16_t RequestNr = _RequestNumber; - ++_RequestNumber; - - DBG(140, "HTTP RequestNr:" << RequestNr); RequestHeaderResult_t Headers; @@ -136,10 +95,10 @@ void HTTPParser::_processRequestProperties(const size_t Index) for (const auto& [Endpoint, EndpointProps]: NamespaceProps.JSONConfig["access"]["as-get"].items()) { DBG(200, "Endpoint:" << Endpoint); - const size_t EndpointFound = BasePropsFound.at(1).find("/backend" + Endpoint); + const size_t EndpointFound = BaseProperties.at(1).find("/backend" + Endpoint); if (EndpointFound != string::npos) { DBG(200, "Looping on params"); - string ProcessURL = BasePropsFound.at(1); + string ProcessURL = BaseProperties.at(1); for (size_t i=0; i= ContentBytes) { Payload = _HTTPRequestBuffer.substr(0, ContentBytes); @@ -215,8 +169,6 @@ void HTTPParser::_processRequestProperties(const size_t Index) } } - DBG(140, "HTTP POST-AS Payload:" << Payload); - /* if (PayloadFound) { _processASPayload( @@ -229,8 +181,6 @@ void HTTPParser::_processRequestProperties(const size_t Index) //- Standard GET request if (HTTPMethod == 1 && AppServerReqFound == string::npos) { - DBG(140, "Request Type GET:" << Request); - ++_RequestCountGet; /* @@ -258,10 +208,8 @@ void HTTPParser::_processRequestProperties(const size_t Index) } } -void HTTPParser::_parseRequestProperties(string& Request, BasePropsResultRef_t ResultRef) +void HTTPParser::_parseRequestProperties(string& Request, BasePropertiesRef_t ResultBaseProps) { - DBG(120, "HTTP Request:'" << Request << "'"); - //- find first line endline size_t StartPos = Request.find("\r\n"); @@ -270,28 +218,35 @@ void HTTPParser::_parseRequestProperties(string& Request, BasePropsResultRef_t R StartPos = Request.length(); } - //- reverse split - StringHelper::rsplit(Request, StartPos, " ", ResultRef); + //- get base request properties + vector SplitResult; + StringHelper::rsplit(Request, StartPos, " ", SplitResult); + + if (SplitResult.at(0).find("HTTP/1.1") != string::npos) { + ResultBaseProps.HTTPVersion = HTTP_VERSION_1_1; + } + + if (SplitResult.at(2).find("GET") != string::npos) { + ResultBaseProps.HTTPMethod = HTTP_METHOD_GET; + } + else if (SplitResult.at(2).find("POST") != string::npos) { + ResultBaseProps.HTTPMethod = HTTP_METHOD_POST; + } - DBG(120, "HTTP Version:" << ResultRef.at(0) << " File:" << ResultRef.at(1) << " Method:" << ResultRef.at(2) << " Request:" << Request); + ResultBaseProps.URL = SplitResult.at(1); } void HTTPParser::_parseRequestHeaders(string& Request, RequestHeaderResultRef_t ResultRef) { - DBG(120, "HTTP Request:'" << Request << "'"); - //- reverse split header lines vector Lines; StringHelper::split(Request, "\r\n", Lines); - DBG(120, "Last Header Line:'" << Request << "'"); Lines.push_back(Request); //- loop over lines, split, put into result map for (auto &Line:Lines) { - DBG(120, "Line:'" << Line << "'"); - vector HeaderPair; if (Line.find(":") != string::npos) { @@ -299,24 +254,19 @@ void HTTPParser::_parseRequestHeaders(string& Request, RequestHeaderResultRef_t string HeaderID = HeaderPair.at(1); string HeaderValue = HeaderPair.at(0).substr(0, HeaderPair.at(0).length()); - DBG(120, "HeaderID:'" << HeaderID << "'"); - DBG(120, "HeaderValue:'" << HeaderValue << "'"); - ResultRef.emplace( HeaderID, HeaderValue ); } } - DBG(120, "End parse headers."); } -inline string HTTPParser::_getASURLParamValue( +inline uint16_t HTTPParser::_getURLParamValue( const string& Param, const uint16_t Index, string& ReqURL ){ - DBG(200, "ReqURL:" << ReqURL); - //- process first ? parameter + //- process first "?" parameter if (Index == 0) { const size_t StartMarkerPos = ReqURL.find("?"); const size_t MidMarkerPos = ReqURL.find("="); @@ -327,15 +277,15 @@ inline string HTTPParser::_getASURLParamValue( if (CompletePos != string::npos && StartMarkerPos != string::npos && MidMarkerPos != string::npos && MidMarkerPos == CheckMidPos) { const size_t EndPos = (EndMarkerPos == string::npos) ? ReqURL.size() : EndMarkerPos; const string ReturnString = ReqURL.substr(MidMarkerPos+1, EndPos-(MidMarkerPos+1)); - DBG(200, "ReqURL StartMarkerPos:" << StartMarkerPos << " EndPos:" << EndPos); ReqURL.replace(StartMarkerPos, EndPos-StartMarkerPos, ""); return ReturnString; } else { - return "not-found"; + return URL_PARAM_NOT_FOUND; } } - //- process next & parameter(s) + + //- process next "&" parameter(s) if (Index > 0) { const size_t StartMarkerPos = ReqURL.find("&"); const size_t MidMarkerPos = ReqURL.find("="); @@ -346,13 +296,12 @@ inline string HTTPParser::_getASURLParamValue( const size_t NextMarkerPos = ReqURL.find("&", MidMarkerPos); const size_t EndPos = (NextMarkerPos == string::npos) ? ReqURL.size() : NextMarkerPos; const string ReturnString = ReqURL.substr(MidMarkerPos+1, EndPos-(MidMarkerPos+1)); - DBG(200, "ReqURL StartMarkerPos:" << StartMarkerPos << " EndPos:" << EndPos); ReqURL.replace(StartMarkerPos, EndPos-StartMarkerPos, ""); return ReturnString; } else { - return "not-found"; + return URL_PARAM_NOT_FOUND; } } - return "parse-error"; + return URL_PARAM_PARSE_ERROR ; } diff --git a/lib/http/httpparser.hpp b/lib/http/httpparser.hpp index 0eed07b..49244e3 100644 --- a/lib/http/httpparser.hpp +++ b/lib/http/httpparser.hpp @@ -1,26 +1,39 @@ #pragma once -#include "../../src/Debug.cpp" -#include "../../src/Client.hpp" - #include #include #include +#include "httpconstants.hpp" + typedef pair HeaderPair_t; typedef unordered_map RequestHeader_t; typedef RequestHeader_t RequestHeaderResult_t; typedef RequestHeader_t& RequestHeaderResultRef_t; -typedef vector BasePropsResult_t; -typedef BasePropsResult_t& BasePropsResultRef_t; +typedef unordered_map URLParam_t; + +struct BaseProperties_t +{ + uint16_t HTTPVersion; + uint16_t HTTPMethod; + string URL; +} +typedef BaseProperties_t& BasePropertiesRef_t; + +struct RequestStruct_t +{ + BaseProps_t BaseProperties; + RequestHeader_t RequestHeaders; + string Payload; + URLParam_t URLParams; +} static const vector HTTPHeaderTypes { "Host", - "Request-UUID", "Transfer-Encoding", "If-None-Match", "Content-Type", @@ -61,7 +74,7 @@ class HTTPParser void _parseRequestProperties(string&, BasePropsResultRef_t); void _parseRequestHeaders(string&, RequestHeaderResultRef_t); - inline string _getASURLParamValue( + inline uint16_t _getURLParamValue( const string&, const uint16_t, string& @@ -79,16 +92,12 @@ class StringHelper { while (pos != string::npos) { SplitElement = StringRef.substr(0, pos); - DBG(220, "SplitElement:'" << SplitElement << "'"); ResultRef.push_back(SplitElement); StringRef.erase(0, pos + Delimiter.length()); pos = StringRef.find(Delimiter); } - - DBG(200, "String:'" << StringRef << "'"); } - //- TODO: ugly, refactor (lambda?) static void rsplit(string& String, size_t StartPos, const string Delimiter, vector& ResultRef) { size_t FindPos = 0; @@ -96,14 +105,11 @@ class StringHelper { string Token; StartPos -= Delimiter.length(); while ((FindPos = String.rfind(Delimiter, StartPos)) != String.npos) { - DBG(200, "FindPos:" << FindPos << " StartPos:" << StartPos); Token = String.substr(FindPos+Delimiter.length(), (StartPos-FindPos)); - DBG(200, "Token:" << Token); ResultRef.push_back(Token); StartPos = FindPos - Delimiter.length(); FindPosLast = FindPos; } - DBG(200, "End FindPos:" << FindPosLast); Token = String.substr(0, FindPosLast); ResultRef.push_back(Token); } From 02f9c1bf92a43030098f70725860eb0a516f164f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Thu, 23 Apr 2026 10:57:32 +0200 Subject: [PATCH 08/14] Correct filename typo --- .../esp32c3/{adust-cross-build.sh => adjust-cross-build.sh} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename ports/arduino/esp32c3/{adust-cross-build.sh => adjust-cross-build.sh} (100%) diff --git a/ports/arduino/esp32c3/adust-cross-build.sh b/ports/arduino/esp32c3/adjust-cross-build.sh similarity index 100% rename from ports/arduino/esp32c3/adust-cross-build.sh rename to ports/arduino/esp32c3/adjust-cross-build.sh From 83c71431bc288dbdb57222c76984b2ab6b520b03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Fri, 24 Apr 2026 09:33:11 +0200 Subject: [PATCH 09/14] Update / compiling now, but not yet finished --- lib/http/httpconstants.hpp | 8 ++ lib/http/httpparser.cpp | 207 +++++++------------------------------ lib/http/httpparser.hpp | 85 +++++++++++---- 3 files changed, 110 insertions(+), 190 deletions(-) diff --git a/lib/http/httpconstants.hpp b/lib/http/httpconstants.hpp index 8261d4d..73fdf2a 100644 --- a/lib/http/httpconstants.hpp +++ b/lib/http/httpconstants.hpp @@ -1,5 +1,8 @@ #pragma once +#include +#include + constexpr uint16_t HTTP_VERSION_UNKNOWN = 0; constexpr uint16_t HTTP_VERSION_1_1 = 1; @@ -7,5 +10,10 @@ constexpr uint16_t HTTP_METHOD_OTHER = 0; constexpr uint16_t HTTP_METHOD_GET = 1; constexpr uint16_t HTTP_METHOD_POST = 2; +constexpr uint16_t HTTP_POST_MAX_CONTENT_LENGTH = 4096; + constexpr uint16_t URL_PARAM_NOT_FOUND = 10; constexpr uint16_t URL_PARAM_PARSE_ERROR = 20; + +static const std::string HTTP_1_1_END_MARKER = "\r\n\r\n"; +static const std::string HTTP_HEADER_CONTENT_LENGTH = "Content-Length"; diff --git a/lib/http/httpparser.cpp b/lib/http/httpparser.cpp index cc4d43d..1326497 100644 --- a/lib/http/httpparser.cpp +++ b/lib/http/httpparser.cpp @@ -3,13 +3,14 @@ using namespace std; -HTTPParser::HTTPParser() : +HTTPParser::HTTPParser(const uint16_t BufferSize) : _RequestCount(0), _RequestCountGet(0), _RequestCountPost(0), + _RequestParseError(0), _HTTPRequestBuffer("") { - _HTTPRequestBuffer.reserve(BUFFER_BYTES); + _HTTPRequestBuffer.reserve(BufferSize); } HTTPParser::~HTTPParser() @@ -24,7 +25,7 @@ void HTTPParser::appendBuffer(const char* BufferRef, const uint16_t RecvBytes) _HTTPRequestBuffer.append(BufferRef, RecvBytes); //-> only process on minimum of 1 http request (end request marker found) - const size_t EndMarkerFound = _HTTPRequestBuffer.find("\r\n\r\n"); + const size_t EndMarkerFound = _HTTPRequestBuffer.find(HTTP_1_1_END_MARKER); if (EndMarkerFound != string::npos) { _splitRequests(); @@ -33,35 +34,30 @@ void HTTPParser::appendBuffer(const char* BufferRef, const uint16_t RecvBytes) inline void HTTPParser::_splitRequests() { - //- reset request counters - _RequestCountGet = 0; - _RequestCountPost = 0; - _RequestCountPostAS = 0; - _RequestNumber = 1; - //-> split requests into _SplittedRequests vector - StringHelper::split(_HTTPRequestBuffer, "\r\n\r\n", _SplittedRequests); + StringHelper::split(_HTTPRequestBuffer, HTTP_1_1_END_MARKER, _SplittedRequests); - _RequestCount = _SplittedRequests.size(); + _RequestCount += _SplittedRequests.size(); } -size_t HTTPParser::processRequests() +inline void HTTPParser::processRequests() { //- iterate over splitted requests for(size_t i=0; i<_SplittedRequests.size(); ++i) { - _processRequestProperties(i); + if (_processRequestProperties(i) == false) { + _RequestParseError = 400; + break; + } } - - return _RequestCountGet; } -void HTTPParser::_processRequestProperties(const size_t Index) +inline bool HTTPParser::_processRequestProperties(const size_t Index) { //- get request ref at vector index auto &Request = _SplittedRequests.at(Index); //- on empty request return - if (Request.empty()) { return; } + if (Request.empty()) { return false; } //- init unparsed base properties with default values BaseProperties_t BaseProperties = { @@ -74,138 +70,58 @@ void HTTPParser::_processRequestProperties(const size_t Index) _parseRequestProperties(Request, BaseProperties); //- only process HTTP/1.1 requests - if (BaseProperties.HTTPVersion != HTTP_VERSION_1_1) { return; } + if (BaseProperties.HTTPVersion != HTTP_VERSION_1_1) { return false; } //- if not GET || POST method, return - if (BaseProperties.HTTPMethod == HTTP_METHOD_OTHER) { return; } - - - - RequestHeaderResult_t Headers; - - //- AS GET request - if (HTTPMethod == 1 && AppServerReqFound != string::npos) { - - //- parse request headers - _parseRequestHeaders(Request, Headers); - - /* - const NamespaceProps_t NamespaceProps = _Namespaces.at(Headers.at("Host")); - string JSONPayload("{ \"payload\": {"); - - for (const auto& [Endpoint, EndpointProps]: NamespaceProps.JSONConfig["access"]["as-get"].items()) { - DBG(200, "Endpoint:" << Endpoint); - const size_t EndpointFound = BaseProperties.at(1).find("/backend" + Endpoint); - if (EndpointFound != string::npos) { - DBG(200, "Looping on params"); - string ProcessURL = BaseProperties.at(1); - for (size_t i=0; i Index) { auto &NextRequest = _SplittedRequests.at(Index+1); if (NextRequest.length() >= ContentBytes) { Payload = NextRequest.substr(0, ContentBytes); NextRequest.replace(0, ContentBytes, ""); - PayloadFound = true; + } + else { + return false; } } - catch(const std::exception& e) { + else { //- try payload in _HTTPRequestBuffer if (_HTTPRequestBuffer.length() >= ContentBytes) { Payload = _HTTPRequestBuffer.substr(0, ContentBytes); _HTTPRequestBuffer.replace(0, ContentBytes, ""); - PayloadFound = true; } } - - /* - if (PayloadFound) { - _processASPayload( - ASRequestHandlerRef, Headers, HTTPMethod, HTTPVersion, RequestNr, Payload - ); - } - */ } - //- Standard GET request - if (HTTPMethod == 1 && AppServerReqFound == string::npos) { - - ++_RequestCountGet; - - /* - //- set values in get requests shared memory - const char* MsgCString = Request.c_str(); - - void* ClientFDAddr = getCurrentOffsetAddress(); - void* HTTPVersionAddr = getNextAddress(); - void* RequestNrAddr = getNextAddress(); - void* MsgLengthAddr = getNextAddress(); - - new(HTTPVersionAddr) uint16_t(HTTPVersion); - new(RequestNrAddr) uint16_t(RequestNr); - new(ClientFDAddr) ClientFD_t(_ClientFD); - - uint16_t MsgLength = Request.length(); - new(MsgLengthAddr) uint16_t(MsgLength); - - void* MsgAddress = getNextAddress(); - memcpy(MsgAddress, &MsgCString[0], MsgLength); - - void* NextSegmentAddr = getNextAddress(MsgLength); - DBG(120, "Set SharedMem ClientFD:" << ClientFDAddr << " PayloadLength:" << MsgLengthAddr << " Payload:" << MsgAddress << " NextSegment:" << NextSegmentAddr); - */ - } } void HTTPParser::_parseRequestProperties(string& Request, BasePropertiesRef_t ResultBaseProps) @@ -236,7 +152,7 @@ void HTTPParser::_parseRequestProperties(string& Request, BasePropertiesRef_t Re ResultBaseProps.URL = SplitResult.at(1); } -void HTTPParser::_parseRequestHeaders(string& Request, RequestHeaderResultRef_t ResultRef) +void HTTPParser::_parseRequestHeaders(string& Request, RequestHeaderRef_t ResultRef) { //- reverse split header lines vector Lines; @@ -260,48 +176,3 @@ void HTTPParser::_parseRequestHeaders(string& Request, RequestHeaderResultRef_t } } } - -inline uint16_t HTTPParser::_getURLParamValue( - const string& Param, - const uint16_t Index, - string& ReqURL -){ - //- process first "?" parameter - if (Index == 0) { - const size_t StartMarkerPos = ReqURL.find("?"); - const size_t MidMarkerPos = ReqURL.find("="); - const size_t EndMarkerPos = ReqURL.find("&"); - const size_t CompletePos = ReqURL.find("?" + Param + "="); - const size_t CheckMidPos = CompletePos+Param.size()+1; - - if (CompletePos != string::npos && StartMarkerPos != string::npos && MidMarkerPos != string::npos && MidMarkerPos == CheckMidPos) { - const size_t EndPos = (EndMarkerPos == string::npos) ? ReqURL.size() : EndMarkerPos; - const string ReturnString = ReqURL.substr(MidMarkerPos+1, EndPos-(MidMarkerPos+1)); - ReqURL.replace(StartMarkerPos, EndPos-StartMarkerPos, ""); - return ReturnString; - } - else { - return URL_PARAM_NOT_FOUND; - } - } - - //- process next "&" parameter(s) - if (Index > 0) { - const size_t StartMarkerPos = ReqURL.find("&"); - const size_t MidMarkerPos = ReqURL.find("="); - const size_t CompletePos = ReqURL.find("&" + Param + "="); - const size_t CheckMidPos = CompletePos+Param.size()+1; - - if (CompletePos != string::npos && StartMarkerPos != string::npos && MidMarkerPos != string::npos && MidMarkerPos == CheckMidPos) { - const size_t NextMarkerPos = ReqURL.find("&", MidMarkerPos); - const size_t EndPos = (NextMarkerPos == string::npos) ? ReqURL.size() : NextMarkerPos; - const string ReturnString = ReqURL.substr(MidMarkerPos+1, EndPos-(MidMarkerPos+1)); - ReqURL.replace(StartMarkerPos, EndPos-StartMarkerPos, ""); - return ReturnString; - } - else { - return URL_PARAM_NOT_FOUND; - } - } - return URL_PARAM_PARSE_ERROR ; -} diff --git a/lib/http/httpparser.hpp b/lib/http/httpparser.hpp index 49244e3..91aa769 100644 --- a/lib/http/httpparser.hpp +++ b/lib/http/httpparser.hpp @@ -1,16 +1,19 @@ #pragma once +#include "httpconstants.hpp" + #include #include +#include +#include #include -#include "httpconstants.hpp" +using namespace std; typedef pair HeaderPair_t; typedef unordered_map RequestHeader_t; -typedef RequestHeader_t RequestHeaderResult_t; -typedef RequestHeader_t& RequestHeaderResultRef_t; +typedef RequestHeader_t& RequestHeaderRef_t; typedef unordered_map URLParam_t; @@ -19,17 +22,17 @@ struct BaseProperties_t uint16_t HTTPVersion; uint16_t HTTPMethod; string URL; -} +}; typedef BaseProperties_t& BasePropertiesRef_t; -struct RequestStruct_t +struct RequestProperties_t { - BaseProps_t BaseProperties; + BaseProperties_t BaseProperties; RequestHeader_t RequestHeaders; string Payload; URLParam_t URLParams; -} +}; static const vector HTTPHeaderTypes { @@ -46,16 +49,16 @@ class HTTPParser public: - HTTPParser(); + HTTPParser(const uint16_t); ~HTTPParser(); void appendBuffer(const char*, const uint16_t); - size_t processRequests(); + inline void processRequests(); private: inline void _splitRequests(); - void _processRequestProperties(const size_t); + bool _processRequestProperties(const size_t); RequestHeader_t _RequestHeaders; vector _SplittedRequests; @@ -63,29 +66,23 @@ class HTTPParser size_t _RequestCount; size_t _RequestCountGet; size_t _RequestCountPost; - size_t _RequestCountPostAS; - uint16_t _RequestNumber; + uint16_t _RequestParseError; string _HTTPRequestBuffer; protected: - void _parseRequestProperties(string&, BasePropsResultRef_t); - void _parseRequestHeaders(string&, RequestHeaderResultRef_t); - - inline uint16_t _getURLParamValue( - const string&, - const uint16_t, - string& - ); + void _parseRequestProperties(string&, BasePropertiesRef_t); + void _parseRequestHeaders(string&, RequestHeaderRef_t); }; + class StringHelper { public: - static void split(string& StringRef, const string Delimiter, vector& ResultRef) + static void split(string& StringRef, const string Delimiter, vector& ResultRef, bool EraseFromSrc = true) { string SplitElement; auto pos = StringRef.find(Delimiter); @@ -93,7 +90,9 @@ class StringHelper { while (pos != string::npos) { SplitElement = StringRef.substr(0, pos); ResultRef.push_back(SplitElement); - StringRef.erase(0, pos + Delimiter.length()); + if (EraseFromSrc == true) { + StringRef.erase(0, pos + Delimiter.length()); + } pos = StringRef.find(Delimiter); } } @@ -114,4 +113,46 @@ class StringHelper { ResultRef.push_back(Token); } + static bool is_digits(const string& checkdigits) + { + return all_of(checkdigits.begin(), checkdigits.end(), ::isdigit); + } + +}; + +class JSON { + +public: + + static void convert_get_params() + { + /* + const NamespaceProps_t NamespaceProps = _Namespaces.at(Headers.at("Host")); + string JSONPayload("{ \"payload\": {"); + + for (const auto& [Endpoint, EndpointProps]: NamespaceProps.JSONConfig["access"]["as-get"].items()) { + DBG(200, "Endpoint:" << Endpoint); + const size_t EndpointFound = BaseProperties.at(1).find("/backend" + Endpoint); + if (EndpointFound != string::npos) { + DBG(200, "Looping on params"); + string ProcessURL = BaseProperties.at(1); + for (size_t i=0; i Date: Fri, 24 Apr 2026 14:06:21 +0200 Subject: [PATCH 10/14] Improve structure --- lib/http/httpparser.cpp | 59 ++++++++++++++++++++++------------------- lib/http/httpparser.hpp | 22 ++++++--------- 2 files changed, 40 insertions(+), 41 deletions(-) diff --git a/lib/http/httpparser.cpp b/lib/http/httpparser.cpp index 1326497..4f655dc 100644 --- a/lib/http/httpparser.cpp +++ b/lib/http/httpparser.cpp @@ -4,10 +4,10 @@ using namespace std; HTTPParser::HTTPParser(const uint16_t BufferSize) : - _RequestCount(0), _RequestCountGet(0), _RequestCountPost(0), _RequestParseError(0), + _POSTWaitContentLength(false), _HTTPRequestBuffer("") { _HTTPRequestBuffer.reserve(BufferSize); @@ -24,24 +24,19 @@ void HTTPParser::appendBuffer(const char* BufferRef, const uint16_t RecvBytes) _HTTPRequestBuffer.append(BufferRef, RecvBytes); - //-> only process on minimum of 1 http request (end request marker found) + //-> only process on minimum of 1 http request (end marker found) const size_t EndMarkerFound = _HTTPRequestBuffer.find(HTTP_1_1_END_MARKER); if (EndMarkerFound != string::npos) { - _splitRequests(); + _processRequests(); } } -inline void HTTPParser::_splitRequests() +inline void HTTPParser::_processRequests() { //-> split requests into _SplittedRequests vector StringHelper::split(_HTTPRequestBuffer, HTTP_1_1_END_MARKER, _SplittedRequests); - _RequestCount += _SplittedRequests.size(); -} - -inline void HTTPParser::processRequests() -{ //- iterate over splitted requests for(size_t i=0; i<_SplittedRequests.size(); ++i) { if (_processRequestProperties(i) == false) { @@ -60,20 +55,20 @@ inline bool HTTPParser::_processRequestProperties(const size_t Index) if (Request.empty()) { return false; } //- init unparsed base properties with default values - BaseProperties_t BaseProperties = { + RequestProperties_t RequestProperties = { .HTTPVersion = HTTP_VERSION_UNKNOWN, .HTTPMethod = HTTP_METHOD_OTHER, .URL = "/" }; //- parse base properties - _parseRequestProperties(Request, BaseProperties); + _parseRequestProperties(Request, RequestProperties); //- only process HTTP/1.1 requests - if (BaseProperties.HTTPVersion != HTTP_VERSION_1_1) { return false; } + if (RequestProperties.HTTPVersion != HTTP_VERSION_1_1) { return false; } //- if not GET || POST method, return - if (BaseProperties.HTTPMethod == HTTP_METHOD_OTHER) { return false; } + if (RequestProperties.HTTPMethod == HTTP_METHOD_OTHER) { return false; } RequestHeader_t Headers; @@ -81,14 +76,14 @@ inline bool HTTPParser::_processRequestProperties(const size_t Index) _parseRequestHeaders(Request, Headers); //- GET request - if (BaseProperties.HTTPMethod == HTTP_METHOD_GET) { + if (RequestProperties.HTTPMethod == HTTP_METHOD_GET) { //- parse GET parameters into vector } //- AS POST request - if (BaseProperties.HTTPMethod == HTTP_METHOD_POST) { + else if (RequestProperties.HTTPMethod == HTTP_METHOD_POST) { - //- if no content-length header + //- if request does not contain content-length header if (Headers.find(HTTP_HEADER_CONTENT_LENGTH) != Headers.end()) { return false; } @@ -100,10 +95,27 @@ inline bool HTTPParser::_processRequestProperties(const size_t Index) uint16_t ContentBytes = stoi(Headers.at(HTTP_HEADER_CONTENT_LENGTH)); + //- if content-length exeeds maximum + if (ContentBytes > HTTP_POST_MAX_CONTENT_LENGTH) { + return false; + } + string Payload = ""; - //- if next splitted message exists - if (_SplittedRequests.size() > Index) { + //- on single HTTP POST: payload after endmarker in _HTTPRequestBuffer + if (_SplittedRequests.size() == 1) { + //- if content length bytes already in buffer + if (_HTTPRequestBuffer.length() >= ContentBytes) { + Payload = _HTTPRequestBuffer.substr(0, ContentBytes); + _HTTPRequestBuffer.replace(0, ContentBytes, ""); + } + else { + //- set flag to wait until content-length reached + _POSTWaitContentLength = true; + } + } + //- otherwise payload in next splitted message + else if (_SplittedRequests.size() > Index) { auto &NextRequest = _SplittedRequests.at(Index+1); if (NextRequest.length() >= ContentBytes) { Payload = NextRequest.substr(0, ContentBytes); @@ -113,18 +125,11 @@ inline bool HTTPParser::_processRequestProperties(const size_t Index) return false; } } - else { - //- try payload in _HTTPRequestBuffer - if (_HTTPRequestBuffer.length() >= ContentBytes) { - Payload = _HTTPRequestBuffer.substr(0, ContentBytes); - _HTTPRequestBuffer.replace(0, ContentBytes, ""); - } - } } - + return true; } -void HTTPParser::_parseRequestProperties(string& Request, BasePropertiesRef_t ResultBaseProps) +void HTTPParser::_parseRequestProperties(string& Request, RequestPropertiesRef_t ResultBaseProps) { //- find first line endline size_t StartPos = Request.find("\r\n"); diff --git a/lib/http/httpparser.hpp b/lib/http/httpparser.hpp index 91aa769..2ed4078 100644 --- a/lib/http/httpparser.hpp +++ b/lib/http/httpparser.hpp @@ -17,23 +17,18 @@ typedef RequestHeader_t& RequestHeaderRef_t; typedef unordered_map URLParam_t; -struct BaseProperties_t +struct RequestProperties_t { uint16_t HTTPVersion; uint16_t HTTPMethod; - string URL; -}; - -typedef BaseProperties_t& BasePropertiesRef_t; - -struct RequestProperties_t -{ - BaseProperties_t BaseProperties; RequestHeader_t RequestHeaders; + string URL; string Payload; URLParam_t URLParams; }; +typedef RequestProperties_t& RequestPropertiesRef_t; + static const vector HTTPHeaderTypes { "Host", @@ -53,27 +48,26 @@ class HTTPParser ~HTTPParser(); void appendBuffer(const char*, const uint16_t); - inline void processRequests(); private: - inline void _splitRequests(); + void _processRequests(); bool _processRequestProperties(const size_t); RequestHeader_t _RequestHeaders; vector _SplittedRequests; - size_t _RequestCount; size_t _RequestCountGet; size_t _RequestCountPost; uint16_t _RequestParseError; + bool _POSTWaitContentLength; string _HTTPRequestBuffer; protected: - void _parseRequestProperties(string&, BasePropertiesRef_t); + void _parseRequestProperties(string&, RequestPropertiesRef_t); void _parseRequestHeaders(string&, RequestHeaderRef_t); }; @@ -124,7 +118,7 @@ class JSON { public: - static void convert_get_params() + static void get2JSON() { /* const NamespaceProps_t NamespaceProps = _Namespaces.at(Headers.at("Host")); From 3e668eb9f7f356ce928aa5452bb8d14fe3c28795 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Sun, 26 Apr 2026 09:23:11 +0200 Subject: [PATCH 11/14] Make tests compile --- CMakeLists.txt | 32 +-- lib/http/CMakeLists.txt | 14 +- lib/http/httpconstants.hpp | 20 +- lib/http/httpparser.cpp | 70 +++--- lib/http/httpparser.hpp | 19 +- ports/arduino/README.md | 4 +- scripts/install_startup_script.cmake | 14 +- src/Client.cpp | 5 +- src/Client.hpp | 4 +- src/ClientHandler.cpp | 15 +- src/ClientHandler.hpp | 16 +- src/ResultProcessor.hpp | 6 +- src/ThreadHandler.cpp | 2 +- .../test-string-functions.cpp | 11 +- test/unit/http-parser/CMakeLists.txt | 8 +- .../unit/http-parser/test-parser-requests.cpp | 188 +--------------- .../http-parser/test-parser-requests.cpp.old | 201 ++++++++++++++++++ 17 files changed, 343 insertions(+), 286 deletions(-) create mode 100644 test/unit/http-parser/test-parser-requests.cpp.old diff --git a/CMakeLists.txt b/CMakeLists.txt index 4966aea..183726f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -86,22 +86,22 @@ add_custom_target( ProjectHeaderFiles SOURCES ./src/Helper.hpp - ./src//Global.hpp - ./src//Server.hpp - ./src//Client.hpp - ./src//Filesystem.hpp - ./src//Configuration.hpp - ./src//MemoryManager.hpp - ./src//ThreadHandler.hpp - ./src//IPCHandler.hpp - ./src//IPCHandlerAS.hpp - ./src//ClientHandler.hpp - ./src//ASProcessHandler.hpp - ./src//ASRequestHandler.hpp - ./src//ResultProcessor.hpp - ./src//ResultOrder.hpp - ./src//Constant.hpp - ./src//Vector.hpp + ./src/Global.hpp + ./src/Server.hpp + ./src/Client.hpp + ./src/Filesystem.hpp + ./src/Configuration.hpp + ./src/MemoryManager.hpp + ./src/ThreadHandler.hpp + ./src/IPCHandler.hpp + ./src/IPCHandlerAS.hpp + ./src/ClientHandler.hpp + ./src/ASProcessHandler.hpp + ./src/ASRequestHandler.hpp + ./src/ResultProcessor.hpp + ./src/ResultOrder.hpp + ./src/Constant.hpp + ./src/Vector.hpp ) # add subdirs diff --git a/lib/http/CMakeLists.txt b/lib/http/CMakeLists.txt index 1f8e0f3..5bfdb94 100644 --- a/lib/http/CMakeLists.txt +++ b/lib/http/CMakeLists.txt @@ -11,5 +11,15 @@ add_custom_target( ./httpparser.hpp ) -# install -install(TARGETS httpparser DESTINATION lib) +# install static .a library +install( + TARGETS httpparser + LIBRARY DESTINATION /usr/local/lib +) + +# install header file +install( + FILES + ./httpparser.hpp + DESTINATION /usr/local/include +) diff --git a/lib/http/httpconstants.hpp b/lib/http/httpconstants.hpp index 73fdf2a..5e081b3 100644 --- a/lib/http/httpconstants.hpp +++ b/lib/http/httpconstants.hpp @@ -1,8 +1,10 @@ #pragma once #include +#include #include +//- constant expressions constexpr uint16_t HTTP_VERSION_UNKNOWN = 0; constexpr uint16_t HTTP_VERSION_1_1 = 1; @@ -12,8 +14,22 @@ constexpr uint16_t HTTP_METHOD_POST = 2; constexpr uint16_t HTTP_POST_MAX_CONTENT_LENGTH = 4096; -constexpr uint16_t URL_PARAM_NOT_FOUND = 10; -constexpr uint16_t URL_PARAM_PARSE_ERROR = 20; +constexpr uint16_t URL_PARAM_NOT_FOUND_ERROR = 10; +//- constant expressions (error) +constexpr uint16_t HTTP_ERROR_BAD_REQUEST = 400; + + +//- contant strings static const std::string HTTP_1_1_END_MARKER = "\r\n\r\n"; static const std::string HTTP_HEADER_CONTENT_LENGTH = "Content-Length"; + +//- contant multidimensional +static const std::vector HTTPHeaderTypes +{ + "Host", + "Transfer-Encoding", + "If-None-Match", + "Content-Type", + HTTP_HEADER_CONTENT_LENGTH +}; diff --git a/lib/http/httpparser.cpp b/lib/http/httpparser.cpp index 4f655dc..0c63bca 100644 --- a/lib/http/httpparser.cpp +++ b/lib/http/httpparser.cpp @@ -8,9 +8,11 @@ HTTPParser::HTTPParser(const uint16_t BufferSize) : _RequestCountPost(0), _RequestParseError(0), _POSTWaitContentLength(false), + _POSTContentLength(0), _HTTPRequestBuffer("") { _HTTPRequestBuffer.reserve(BufferSize); + _HTTPRequestBufferMax = BufferSize; } HTTPParser::~HTTPParser() @@ -19,28 +21,34 @@ HTTPParser::~HTTPParser() void HTTPParser::appendBuffer(const char* BufferRef, const uint16_t RecvBytes) { - //-> reset _SplittedRequests vector - _SplittedRequests.clear(); - _HTTPRequestBuffer.append(BufferRef, RecvBytes); - //-> only process on minimum of 1 http request (end marker found) - const size_t EndMarkerFound = _HTTPRequestBuffer.find(HTTP_1_1_END_MARKER); + //- on incomplete (single) POST request + if (_POSTWaitContentLength == true) { - if (EndMarkerFound != string::npos) { - _processRequests(); + } + else { + //- reset _SplittedRequests vector + _SplittedRequests.clear(); + + //- only process on minimum of 1 http request (end marker found) + const size_t EndMarkerFound = _HTTPRequestBuffer.find(HTTP_1_1_END_MARKER); + + if (EndMarkerFound != string::npos) { + _processRequests(); + } } } inline void HTTPParser::_processRequests() { - //-> split requests into _SplittedRequests vector + //- split requests into _SplittedRequests vector StringHelper::split(_HTTPRequestBuffer, HTTP_1_1_END_MARKER, _SplittedRequests); //- iterate over splitted requests for(size_t i=0; i<_SplittedRequests.size(); ++i) { if (_processRequestProperties(i) == false) { - _RequestParseError = 400; + _RequestParseError = HTTP_ERROR_BAD_REQUEST; break; } } @@ -55,59 +63,58 @@ inline bool HTTPParser::_processRequestProperties(const size_t Index) if (Request.empty()) { return false; } //- init unparsed base properties with default values - RequestProperties_t RequestProperties = { + _RequestProperties = { .HTTPVersion = HTTP_VERSION_UNKNOWN, .HTTPMethod = HTTP_METHOD_OTHER, .URL = "/" }; //- parse base properties - _parseRequestProperties(Request, RequestProperties); + _parseRequestProperties(Request, _RequestProperties); //- only process HTTP/1.1 requests - if (RequestProperties.HTTPVersion != HTTP_VERSION_1_1) { return false; } + if (_RequestProperties.HTTPVersion != HTTP_VERSION_1_1) { return false; } //- if not GET || POST method, return - if (RequestProperties.HTTPMethod == HTTP_METHOD_OTHER) { return false; } - - RequestHeader_t Headers; + if (_RequestProperties.HTTPMethod == HTTP_METHOD_OTHER) { return false; } //- parse request headers - _parseRequestHeaders(Request, Headers); + _parseRequestHeaders(Request, _RequestProperties.RequestHeaders); //- GET request - if (RequestProperties.HTTPMethod == HTTP_METHOD_GET) { - //- parse GET parameters into vector + if (_RequestProperties.HTTPMethod == HTTP_METHOD_GET) { + //- parse GET parameters + //- append (move) to requests vector + _Requests.push_back(move(_RequestProperties)); } //- AS POST request - else if (RequestProperties.HTTPMethod == HTTP_METHOD_POST) { + else if (_RequestProperties.HTTPMethod == HTTP_METHOD_POST) { //- if request does not contain content-length header - if (Headers.find(HTTP_HEADER_CONTENT_LENGTH) != Headers.end()) { + if (_RequestProperties.RequestHeaders.find(HTTP_HEADER_CONTENT_LENGTH) != _RequestProperties.RequestHeaders.end()) { return false; } //- if content-length header contains non-digits - if (StringHelper::is_digits(Headers.at(HTTP_HEADER_CONTENT_LENGTH)) == false) { + if (StringHelper::is_digits(_RequestProperties.RequestHeaders.at(HTTP_HEADER_CONTENT_LENGTH)) == false) { return false; } - uint16_t ContentBytes = stoi(Headers.at(HTTP_HEADER_CONTENT_LENGTH)); + _POSTContentLength = stoi(_RequestProperties.RequestHeaders.at(HTTP_HEADER_CONTENT_LENGTH)); //- if content-length exeeds maximum - if (ContentBytes > HTTP_POST_MAX_CONTENT_LENGTH) { + if (_POSTContentLength > HTTP_POST_MAX_CONTENT_LENGTH) { return false; } - string Payload = ""; - //- on single HTTP POST: payload after endmarker in _HTTPRequestBuffer if (_SplittedRequests.size() == 1) { //- if content length bytes already in buffer - if (_HTTPRequestBuffer.length() >= ContentBytes) { - Payload = _HTTPRequestBuffer.substr(0, ContentBytes); - _HTTPRequestBuffer.replace(0, ContentBytes, ""); + if (_HTTPRequestBuffer.length() >= _POSTContentLength) { + _RequestProperties.Payload = _HTTPRequestBuffer.substr(0, _POSTContentLength); + _HTTPRequestBuffer.replace(0, _POSTContentLength, ""); + _Requests.push_back(move(_RequestProperties)); } else { //- set flag to wait until content-length reached @@ -117,9 +124,10 @@ inline bool HTTPParser::_processRequestProperties(const size_t Index) //- otherwise payload in next splitted message else if (_SplittedRequests.size() > Index) { auto &NextRequest = _SplittedRequests.at(Index+1); - if (NextRequest.length() >= ContentBytes) { - Payload = NextRequest.substr(0, ContentBytes); - NextRequest.replace(0, ContentBytes, ""); + if (NextRequest.length() >= _POSTContentLength) { + _RequestProperties.Payload = NextRequest.substr(0, _POSTContentLength); + NextRequest.replace(0, _POSTContentLength, ""); + _Requests.push_back(move(_RequestProperties)); } else { return false; diff --git a/lib/http/httpparser.hpp b/lib/http/httpparser.hpp index 2ed4078..5e89fbb 100644 --- a/lib/http/httpparser.hpp +++ b/lib/http/httpparser.hpp @@ -10,9 +10,7 @@ using namespace std; -typedef pair HeaderPair_t; typedef unordered_map RequestHeader_t; - typedef RequestHeader_t& RequestHeaderRef_t; typedef unordered_map URLParam_t; @@ -29,14 +27,7 @@ struct RequestProperties_t typedef RequestProperties_t& RequestPropertiesRef_t; -static const vector HTTPHeaderTypes -{ - "Host", - "Transfer-Encoding", - "If-None-Match", - "Content-Type", - "Content-Length" -}; +typedef vector RequestsVector_t; class HTTPParser @@ -61,9 +52,15 @@ class HTTPParser size_t _RequestCountPost; uint16_t _RequestParseError; + bool _POSTWaitContentLength; + uint16_t _POSTContentLength; string _HTTPRequestBuffer; + uint16_t _HTTPRequestBufferMax; + + RequestProperties_t _RequestProperties; + RequestsVector_t _Requests; protected: @@ -118,7 +115,7 @@ class JSON { public: - static void get2JSON() + static void getRequest2JSON() { /* const NamespaceProps_t NamespaceProps = _Namespaces.at(Headers.at("Host")); diff --git a/ports/arduino/README.md b/ports/arduino/README.md index 07f6251..cd308e8 100644 --- a/ports/arduino/README.md +++ b/ports/arduino/README.md @@ -1,2 +1,2 @@ -# ESP32 -The ./esp32 subdir contains ports to ESP32 based boards, especially the ESP32-C3 single core SoC. +# ESP32-C3 +The ./esp32c3 subdir contains the first port to the ESP32-C3 based RISCV32 single core SoC board. diff --git a/scripts/install_startup_script.cmake b/scripts/install_startup_script.cmake index 5db5a55..4b2ac54 100644 --- a/scripts/install_startup_script.cmake +++ b/scripts/install_startup_script.cmake @@ -5,21 +5,21 @@ message(STATUS "========================================") message(STATUS "Installing startup script...") message(STATUS "========================================") -# Detect systemd +# detect systemd if(EXISTS "/run/systemd/system") set(INIT_SYSTEM "systemd") set(INIT_SCRIPT_SRC "${CMAKE_CURRENT_LIST_DIR}/startup/systemd/falcon-as.service") set(INIT_SCRIPT_DEST "/etc/systemd/system/falcon-as.service") message(STATUS "Detected init system: systemd") -# Detect OpenRC +# detect OpenRC elseif((EXISTS "/etc/init.d" AND EXISTS "/etc/runlevels") OR EXISTS "/sbin/openrc-run" OR EXISTS "/usr/sbin/openrc-run") set(INIT_SYSTEM "openrc") set(INIT_SCRIPT_SRC "${CMAKE_CURRENT_LIST_DIR}/startup/openrc/falcon-as") set(INIT_SCRIPT_DEST "/etc/init.d/falcon-as") message(STATUS "Detected init system: OpenRC") -# Fallback to SysVinit +# fallback to SysVinit elseif(EXISTS "/etc/init.d") set(INIT_SYSTEM "sysvinit") set(INIT_SCRIPT_SRC "${CMAKE_CURRENT_LIST_DIR}/startup/init.d/falcon-as") @@ -33,12 +33,12 @@ else() set(INIT_SCRIPT_DEST "/etc/init.d/falcon-as") endif() -# Check if source script exists +# check if source script exists if(NOT EXISTS "${INIT_SCRIPT_SRC}") message(FATAL_ERROR "Startup script not found: ${INIT_SCRIPT_SRC}") endif() -# Install the startup script +# install the startup script message(STATUS "Installing ${INIT_SYSTEM} startup script...") message(STATUS " Source: ${INIT_SCRIPT_SRC}") message(STATUS " Destination: ${INIT_SCRIPT_DEST}") @@ -52,7 +52,7 @@ execute_process( if(INSTALL_RESULT EQUAL 0) message(STATUS "✓ Startup script installed successfully") - # Make executable for init.d scripts + # make executable for init.d scripts if(INIT_SYSTEM STREQUAL "openrc" OR INIT_SYSTEM STREQUAL "sysvinit") execute_process( COMMAND chmod +x ${INIT_SCRIPT_DEST} @@ -63,7 +63,7 @@ if(INSTALL_RESULT EQUAL 0) endif() endif() - # Print instructions for the user + # print instructions for the user message(STATUS "") message(STATUS "========================================") if(INIT_SYSTEM STREQUAL "systemd") diff --git a/src/Client.cpp b/src/Client.cpp index d2a1094..3d3dc31 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -3,6 +3,7 @@ using namespace std; Client::Client(ClientFD_t ClientFD) : + HTTPParser(4096), _ClientFD(ClientFD), _RequestNr(0), _SocketConnectTime(0), @@ -10,8 +11,8 @@ Client::Client(ClientFD_t ClientFD) : { DBG(120, "Constructor"); - _ClientConnectTime = time(nullptr); - _ClientDisconnectTime = time(nullptr); + _SocketConnectTime = time(nullptr); + _SocketDisconnectTime = time(nullptr); } Client::~Client() diff --git a/src/Client.hpp b/src/Client.hpp index 8d87ac5..7fe2a88 100644 --- a/src/Client.hpp +++ b/src/Client.hpp @@ -1,6 +1,7 @@ #pragma once #include "Debug.cpp" +#include "../lib/http/httpparser.cpp" #include #include @@ -9,7 +10,8 @@ typedef uint16_t ClientFD_t; typedef uint16_t ClientRequestNr_t; -class Client { +class Client : public HTTPParser +{ public: diff --git a/src/ClientHandler.cpp b/src/ClientHandler.cpp index 53f24eb..b478e2e 100644 --- a/src/ClientHandler.cpp +++ b/src/ClientHandler.cpp @@ -48,11 +48,15 @@ void ClientHandler::addClient(const uint16_t ClientFD) //- set client connection non blocking Socket::makeNonblocking(ClientFD); - ClientRef_t ClientObj = std::make_shared(ClientFD, ConfigRef.Namespaces); + //ClientRef_t ClientObj = std::make_shared(ClientFD); + Client_t ClientObj(ClientFD); + /* Clients.emplace( - ClientFD, ClientObj + ClientFD, move(ClientObj) ); + */ + ClientsVector.push_back(move(ClientObj)); //- add fd to epoll EpollEvent.events = EPOLLIN | EPOLLET; @@ -121,12 +125,15 @@ void ClientHandler::readClientData(const uint16_t FDCount) else { //- if filedescriptor exists in map, append buffer data if (Clients.contains(ReadFD)) { - ClientRef_t ClientRef = Clients[ReadFD]; - ClientRef->appendBuffer(Buffer, RcvBytes); + //Clients[ReadFD].appendBuffer(Buffer, RcvBytes); + ClientsVector[0].appendBuffer(Buffer, RcvBytes); + + /* if (ClientRef->processRequests(SHMGetRequests, _ASRequestHandlerRef) > 0) { ++ProcessedClients; } + */ } } } diff --git a/src/ClientHandler.hpp b/src/ClientHandler.hpp index d66decd..17f1a17 100644 --- a/src/ClientHandler.hpp +++ b/src/ClientHandler.hpp @@ -1,5 +1,4 @@ -#ifndef ClientHandler_hpp -#define ClientHandler_hpp +#pragma once #include "Debug.cpp" @@ -17,10 +16,14 @@ #include "MemoryManager.hpp" #include "ASRequestHandler.hpp" +//typedef std::shared_ptr ClientRef_t; +//typedef pair ClientMapPair_t; -typedef std::shared_ptr ClientRef_t; -typedef pair ClientMapPair_t; -typedef unordered_map ClientMap_t; +typedef Client Client_t; +typedef Client& ClientRef_t; + +typedef unordered_map ClientMap_t; +typedef vector ClientVector_t; typedef struct { void* StaticFSPtr; @@ -52,6 +55,7 @@ class ClientHandler private: ClientMap_t Clients; + ClientVector_t ClientsVector; struct epoll_event EpollEvent, EpollEvents[EPOLL_FD_COUNT_MAX]; @@ -68,5 +72,3 @@ class ClientHandler ASRequestHandlerRef_t _ASRequestHandlerRef; }; - -#endif diff --git a/src/ResultProcessor.hpp b/src/ResultProcessor.hpp index ba93768..88c2f1f 100644 --- a/src/ResultProcessor.hpp +++ b/src/ResultProcessor.hpp @@ -1,5 +1,4 @@ -#ifndef ResultProcessor_hpp -#define ResultProcessor_hpp +#pragma once #include #include @@ -15,6 +14,7 @@ #include "IPCHandler.hpp" #include "Configuration.hpp" #include "ResultOrder.hpp" +#include "ASProcessHandler.hpp" typedef struct { void* StaticFSPtr; @@ -50,5 +50,3 @@ class ResultProcessor : private SHMStaticFS, public CPU, private ResultOrder, pr VHostOffsetsPrecalc_t _VHostOffsetsPrecalc; }; - -#endif diff --git a/src/ThreadHandler.cpp b/src/ThreadHandler.cpp index fd86efe..11b606f 100644 --- a/src/ThreadHandler.cpp +++ b/src/ThreadHandler.cpp @@ -97,7 +97,7 @@ ClientThread::ClientThread( Namespaces_t &Namespaces, ClientRequestDataVec_t Requests ) : - HTTPParser(ClientFD, Namespaces), + HTTPParser(4096), _ClientFD(ClientFD), _Namespaces(Namespaces), _ClientRequests(Requests) diff --git a/test/integration/string-functions/test-string-functions.cpp b/test/integration/string-functions/test-string-functions.cpp index d48b91c..1bf887a 100644 --- a/test/integration/string-functions/test-string-functions.cpp +++ b/test/integration/string-functions/test-string-functions.cpp @@ -2,6 +2,7 @@ #include #include "../../../src/Helper.hpp" +#include "../../../lib/http/httpparser.hpp" using namespace std; @@ -12,7 +13,7 @@ BOOST_AUTO_TEST_CASE( test_string_split_1 ) vector Result; string TestString1("TestValue1-TestValue2-TestValue3"); - String::split(TestString1, "-", Result); + StringHelper::split(TestString1, "-", Result); cout << "Res[0]:" << Result.at(0) << endl; cout << "Res[1]:" << Result.at(1) << endl; @@ -25,7 +26,7 @@ BOOST_AUTO_TEST_CASE( test_string_split_1 ) Result.clear(); string TestString2("TestHTTP1\n\rTestHTTP2\n\rTestHTTP3\n\n\rEnd"); - String::split(TestString2, "\n\r", Result); + StringHelper::split(TestString2, "\n\r", Result); cout << "Res[0]:" << Result.at(0) << endl; cout << "Res[1]:" << Result.at(1) << endl; @@ -44,7 +45,7 @@ BOOST_AUTO_TEST_CASE( test_string_split_remains ) vector Result; string TestString1("TestHTTP1\n\r"); - String::split(TestString1, "\n\r", Result); + StringHelper::split(TestString1, "\n\r", Result); cout << "Res[0]:" << Result.at(0) << endl; @@ -62,7 +63,7 @@ BOOST_AUTO_TEST_CASE( test_string_rsplit_1 ) string TestString1("GET /path/to/file.txt HTTP/1.1\n Continues \n Again... "); size_t StartPos = TestString1.find("\n"); - String::rsplit(TestString1, StartPos, " ", Result); + StringHelper::rsplit(TestString1, StartPos, " ", Result); cout << "Res[0]:" << Result.at(0) << endl; cout << "Res[1]:" << Result.at(1) << endl; @@ -76,7 +77,7 @@ BOOST_AUTO_TEST_CASE( test_string_rsplit_1 ) string TestString2("GET / HTTP/1.1\nHeader1: HeaderContent1\nHeader2: HeaderContent2\n\r"); size_t StartPos2 = TestString2.find("\n"); - String::rsplit(TestString2, StartPos2, " ", Result); + StringHelper::rsplit(TestString2, StartPos2, " ", Result); cout << "Res[0]:" << Result.at(0) << endl; cout << "Res[1]:" << Result.at(1) << endl; diff --git a/test/unit/http-parser/CMakeLists.txt b/test/unit/http-parser/CMakeLists.txt index 413eee5..c6fced0 100644 --- a/test/unit/http-parser/CMakeLists.txt +++ b/test/unit/http-parser/CMakeLists.txt @@ -4,10 +4,6 @@ aux_source_directory(. SRC_LIST_TEST_HTTPPARSER) add_executable( test-parser-requests "test-parser-requests.cpp" - ../../../src/Client.cpp - ../../../src/IPCHandler.cpp - ../../../src/IPCHandlerAS.cpp - ../../../src/ASRequestHandler.cpp ) # add custom targets @@ -19,6 +15,4 @@ add_custom_target( # link libraries target_link_libraries(test-parser-requests ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) -target_link_libraries(test-parser-requests ${Boost_LIBRARIES}) -target_link_libraries(test-parser-requests ${PYTHON_LIBRARIES}) -target_link_libraries(test-parser-requests libhttp) +target_link_libraries(test-parser-requests httpparser) diff --git a/test/unit/http-parser/test-parser-requests.cpp b/test/unit/http-parser/test-parser-requests.cpp index 3b550a2..1c54c6b 100644 --- a/test/unit/http-parser/test-parser-requests.cpp +++ b/test/unit/http-parser/test-parser-requests.cpp @@ -2,200 +2,20 @@ #include #include -#include "../../../src/Client.hpp" -#include "../../../src/ClientHandler.hpp" +#include "../../../lib/http/httpparser.hpp" using namespace std; -using json = nlohmann::json; BOOST_AUTO_TEST_CASE( test_single_get_request ) { cout << "Check single GET request." << endl; - void* SHMBase = mmap(NULL, SHMEM_STATICFS_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); - void* SHMGetRequests = static_cast(SHMBase) + sizeof(atomic_uint16_t) + sizeof(uint16_t); - - void* SHMASMeta = mmap(NULL, SHMEM_STATICFS_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); - void* SHMASRequests = mmap(NULL, SHMEM_STATICFS_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); - void* SHMASResults = mmap(NULL, SHMEM_STATICFS_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); - - const json JSONConfig = { { "interpreters", 2 }, { "path", "/test" } }; - - Namespaces_t Namespaces; - NamespaceProps_t NamespaceProps; - NamespaceProps.FilesystemRef = nullptr; - NamespaceProps.JSONConfig = JSONConfig; - - Namespaces.emplace( - "test1", NamespaceProps - ); - - ASRequestHandlerRef_t ASRequestHandlerRef = std::make_unique( - Namespaces, - BaseAdresses_t{ SHMASMeta, SHMASRequests, SHMASResults } - ); - - ClientFD_t ClientFD = 1; - ClientRef_t ClientObj(new HTTPParser(ClientFD, Namespaces)); + HTTPParser* parser = new HTTPParser(4096); std::string Request("GET /test/test.png HTTP/1.1\r\nCustomHeader: one\r\n\r\n"); - ClientObj->appendBuffer(Request.c_str(), Request.length()); - auto r = ClientObj->processRequests(SHMGetRequests, ASRequestHandlerRef); + parser->appendBuffer(Request.c_str(), Request.length()); + auto r=1; BOOST_TEST(r == 1); } - -BOOST_AUTO_TEST_CASE( test_multiple_get_request ) -{ - cout << "Check multiple (2) GET requests." << endl; - - void* SHMBase = mmap(NULL, SHMEM_STATICFS_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); - void* SHMGetRequests = static_cast(SHMBase) + sizeof(atomic_uint16_t) + sizeof(uint16_t); - - void* SHMASMeta = mmap(NULL, SHMEM_STATICFS_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); - void* SHMASRequests = mmap(NULL, SHMEM_STATICFS_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); - void* SHMASResults = mmap(NULL, SHMEM_STATICFS_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); - - const json JSONConfig = { { "interpreters", 2 }, { "path", "/test" } }; - - Namespaces_t Namespaces; - NamespaceProps_t NamespaceProps; - NamespaceProps.FilesystemRef = nullptr; - NamespaceProps.JSONConfig = JSONConfig; - - Namespaces.emplace( - "test1", NamespaceProps - ); - - ASRequestHandlerRef_t ASRequestHandlerRef = std::make_unique( - Namespaces, - BaseAdresses_t{ SHMASMeta, SHMASRequests, SHMASResults } - ); - - ClientFD_t ClientFD = 1; - ClientRef_t ClientObj(new HTTPParser(ClientFD, Namespaces)); - - std::string Request("GET /test/test1.png HTTP/1.1\r\nCustomHeader: one\r\n\r\nGET /test/test2.png HTTP/1.1\r\nCustomHeader: two\r\n\r\n"); - ClientObj->appendBuffer(Request.c_str(), Request.length()); - auto r = ClientObj->processRequests(SHMGetRequests, ASRequestHandlerRef); - - BOOST_TEST(r == 2); -} - -BOOST_AUTO_TEST_CASE( test_single_post_as_request ) -{ - cout << "Check single POST request." << endl; - - void* SHMBase = mmap(NULL, SHMEM_STATICFS_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); - void* SHMGetRequests = static_cast(SHMBase) + sizeof(atomic_uint16_t) + sizeof(uint16_t); - - void* SHMASMeta = mmap(NULL, SHMEM_STATICFS_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); - void* SHMASRequests = mmap(NULL, SHMEM_STATICFS_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); - void* SHMASResults = mmap(NULL, SHMEM_STATICFS_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); - - const json JSONConfig = { { "interpreters", 2 }, { "path", "/test" } }; - - Namespaces_t Namespaces; - NamespaceProps_t NamespaceProps; - NamespaceProps.FilesystemRef = nullptr; - NamespaceProps.JSONConfig = JSONConfig; - - Namespaces.emplace( - "test1", NamespaceProps - ); - - ASRequestHandlerRef_t ASRequestHandlerRef = std::make_unique( - Namespaces, - BaseAdresses_t{ SHMASMeta, SHMASRequests, SHMASResults } - ); - - ClientFD_t ClientFD = 1; - ClientRef_t ClientObj(new HTTPParser(ClientFD, Namespaces)); - - std::string Request("POST /backend/test1 HTTP/1.1\r\nHost: test.loalnet\r\nContent-Type: application/json\r\nContent-Length: 2\r\n\r\n{}"); - ClientObj->appendBuffer(Request.c_str(), Request.length()); - auto r = ClientObj->processRequests(SHMGetRequests, ASRequestHandlerRef); - - BOOST_TEST(r == 0); -} - -BOOST_AUTO_TEST_CASE( test_single_get_as_request ) -{ - cout << "Check single GET AS request." << endl; - - void* SHMBase = mmap(NULL, SHMEM_STATICFS_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); - void* SHMGetRequests = static_cast(SHMBase) + sizeof(atomic_uint16_t) + sizeof(uint16_t); - - void* SHMASMeta = mmap(NULL, SHMEM_STATICFS_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); - void* SHMASRequests = mmap(NULL, SHMEM_STATICFS_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); - void* SHMASResults = mmap(NULL, SHMEM_STATICFS_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); - - std::ifstream ConfigFile("./as-get.json"); - const json JSONConfig = json::parse(ConfigFile); - - Namespaces_t Namespaces; - NamespaceProps_t NamespaceProps; - NamespaceProps.FilesystemRef = nullptr; - NamespaceProps.JSONConfig = JSONConfig; - - Namespaces.emplace( - "test.local", NamespaceProps - ); - - ASRequestHandlerRef_t ASRequestHandlerRef = std::make_unique( - Namespaces, - BaseAdresses_t{ SHMASMeta, SHMASRequests, SHMASResults } - ); - - ClientFD_t ClientFD = 1; - ClientRef_t ClientObj(new HTTPParser(ClientFD, Namespaces)); - - std::string Request("GET /backend/test1?param1=test1¶m2=test2 HTTP/1.1\r\nHost: test.local\r\nCustomHeader: one\r\n\r\n"); - ClientObj->appendBuffer(Request.c_str(), Request.length()); - auto r = ClientObj->processRequests(SHMGetRequests, ASRequestHandlerRef); - - BOOST_TEST(r == 0); -} - -BOOST_AUTO_TEST_CASE( test_multiple_get_request_truncated ) -{ - cout << "Check multiple (3) GET requests, truncated over 2 requests." << endl; - - void* SHMBase = mmap(NULL, SHMEM_STATICFS_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); - void* SHMGetRequests = static_cast(SHMBase) + sizeof(atomic_uint16_t) + sizeof(uint16_t); - - void* SHMASMeta = mmap(NULL, SHMEM_STATICFS_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); - void* SHMASRequests = mmap(NULL, SHMEM_STATICFS_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); - void* SHMASResults = mmap(NULL, SHMEM_STATICFS_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); - - const json JSONConfig = { {"interpreters", 2}, {"path", "/test"} }; - - Namespaces_t Namespaces; - NamespaceProps_t NamespaceProps; - NamespaceProps.FilesystemRef = nullptr; - NamespaceProps.JSONConfig = JSONConfig; - - Namespaces.emplace( - "test1", NamespaceProps - ); - - ASRequestHandlerRef_t ASRequestHandlerRef = std::make_unique( - Namespaces, - BaseAdresses_t{ SHMASMeta, SHMASRequests, SHMASResults } - ); - - ClientFD_t ClientFD = 1; - ClientRef_t ClientObj(new HTTPParser(ClientFD, Namespaces)); - - std::string Request("GET /t/tA.png HTTP/1.1\r\nCustomHeader: a\r\n\r\nGET /t/tB.png HTTP/1.1\r\nCustomHeader: b\r\n\r\nGET /t/tC.png HT"); - ClientObj->appendBuffer(Request.c_str(), Request.length()); - auto r1 = ClientObj->processRequests(SHMGetRequests, ASRequestHandlerRef); - - std::string Request2("TP/1.1\r\nCustomHeader: c\r\n\r\n"); - ClientObj->appendBuffer(Request2.c_str(), Request2.length()); - auto r2 = ClientObj->processRequests(SHMGetRequests, ASRequestHandlerRef); - - BOOST_TEST(r1 == 2); - BOOST_TEST(r2 == 1); -} diff --git a/test/unit/http-parser/test-parser-requests.cpp.old b/test/unit/http-parser/test-parser-requests.cpp.old new file mode 100644 index 0000000..3b550a2 --- /dev/null +++ b/test/unit/http-parser/test-parser-requests.cpp.old @@ -0,0 +1,201 @@ +#define BOOST_TEST_MAIN +#include +#include + +#include "../../../src/Client.hpp" +#include "../../../src/ClientHandler.hpp" + +using namespace std; +using json = nlohmann::json; + + +BOOST_AUTO_TEST_CASE( test_single_get_request ) +{ + cout << "Check single GET request." << endl; + + void* SHMBase = mmap(NULL, SHMEM_STATICFS_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); + void* SHMGetRequests = static_cast(SHMBase) + sizeof(atomic_uint16_t) + sizeof(uint16_t); + + void* SHMASMeta = mmap(NULL, SHMEM_STATICFS_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); + void* SHMASRequests = mmap(NULL, SHMEM_STATICFS_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); + void* SHMASResults = mmap(NULL, SHMEM_STATICFS_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); + + const json JSONConfig = { { "interpreters", 2 }, { "path", "/test" } }; + + Namespaces_t Namespaces; + NamespaceProps_t NamespaceProps; + NamespaceProps.FilesystemRef = nullptr; + NamespaceProps.JSONConfig = JSONConfig; + + Namespaces.emplace( + "test1", NamespaceProps + ); + + ASRequestHandlerRef_t ASRequestHandlerRef = std::make_unique( + Namespaces, + BaseAdresses_t{ SHMASMeta, SHMASRequests, SHMASResults } + ); + + ClientFD_t ClientFD = 1; + ClientRef_t ClientObj(new HTTPParser(ClientFD, Namespaces)); + + std::string Request("GET /test/test.png HTTP/1.1\r\nCustomHeader: one\r\n\r\n"); + ClientObj->appendBuffer(Request.c_str(), Request.length()); + auto r = ClientObj->processRequests(SHMGetRequests, ASRequestHandlerRef); + + BOOST_TEST(r == 1); +} + +BOOST_AUTO_TEST_CASE( test_multiple_get_request ) +{ + cout << "Check multiple (2) GET requests." << endl; + + void* SHMBase = mmap(NULL, SHMEM_STATICFS_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); + void* SHMGetRequests = static_cast(SHMBase) + sizeof(atomic_uint16_t) + sizeof(uint16_t); + + void* SHMASMeta = mmap(NULL, SHMEM_STATICFS_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); + void* SHMASRequests = mmap(NULL, SHMEM_STATICFS_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); + void* SHMASResults = mmap(NULL, SHMEM_STATICFS_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); + + const json JSONConfig = { { "interpreters", 2 }, { "path", "/test" } }; + + Namespaces_t Namespaces; + NamespaceProps_t NamespaceProps; + NamespaceProps.FilesystemRef = nullptr; + NamespaceProps.JSONConfig = JSONConfig; + + Namespaces.emplace( + "test1", NamespaceProps + ); + + ASRequestHandlerRef_t ASRequestHandlerRef = std::make_unique( + Namespaces, + BaseAdresses_t{ SHMASMeta, SHMASRequests, SHMASResults } + ); + + ClientFD_t ClientFD = 1; + ClientRef_t ClientObj(new HTTPParser(ClientFD, Namespaces)); + + std::string Request("GET /test/test1.png HTTP/1.1\r\nCustomHeader: one\r\n\r\nGET /test/test2.png HTTP/1.1\r\nCustomHeader: two\r\n\r\n"); + ClientObj->appendBuffer(Request.c_str(), Request.length()); + auto r = ClientObj->processRequests(SHMGetRequests, ASRequestHandlerRef); + + BOOST_TEST(r == 2); +} + +BOOST_AUTO_TEST_CASE( test_single_post_as_request ) +{ + cout << "Check single POST request." << endl; + + void* SHMBase = mmap(NULL, SHMEM_STATICFS_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); + void* SHMGetRequests = static_cast(SHMBase) + sizeof(atomic_uint16_t) + sizeof(uint16_t); + + void* SHMASMeta = mmap(NULL, SHMEM_STATICFS_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); + void* SHMASRequests = mmap(NULL, SHMEM_STATICFS_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); + void* SHMASResults = mmap(NULL, SHMEM_STATICFS_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); + + const json JSONConfig = { { "interpreters", 2 }, { "path", "/test" } }; + + Namespaces_t Namespaces; + NamespaceProps_t NamespaceProps; + NamespaceProps.FilesystemRef = nullptr; + NamespaceProps.JSONConfig = JSONConfig; + + Namespaces.emplace( + "test1", NamespaceProps + ); + + ASRequestHandlerRef_t ASRequestHandlerRef = std::make_unique( + Namespaces, + BaseAdresses_t{ SHMASMeta, SHMASRequests, SHMASResults } + ); + + ClientFD_t ClientFD = 1; + ClientRef_t ClientObj(new HTTPParser(ClientFD, Namespaces)); + + std::string Request("POST /backend/test1 HTTP/1.1\r\nHost: test.loalnet\r\nContent-Type: application/json\r\nContent-Length: 2\r\n\r\n{}"); + ClientObj->appendBuffer(Request.c_str(), Request.length()); + auto r = ClientObj->processRequests(SHMGetRequests, ASRequestHandlerRef); + + BOOST_TEST(r == 0); +} + +BOOST_AUTO_TEST_CASE( test_single_get_as_request ) +{ + cout << "Check single GET AS request." << endl; + + void* SHMBase = mmap(NULL, SHMEM_STATICFS_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); + void* SHMGetRequests = static_cast(SHMBase) + sizeof(atomic_uint16_t) + sizeof(uint16_t); + + void* SHMASMeta = mmap(NULL, SHMEM_STATICFS_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); + void* SHMASRequests = mmap(NULL, SHMEM_STATICFS_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); + void* SHMASResults = mmap(NULL, SHMEM_STATICFS_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); + + std::ifstream ConfigFile("./as-get.json"); + const json JSONConfig = json::parse(ConfigFile); + + Namespaces_t Namespaces; + NamespaceProps_t NamespaceProps; + NamespaceProps.FilesystemRef = nullptr; + NamespaceProps.JSONConfig = JSONConfig; + + Namespaces.emplace( + "test.local", NamespaceProps + ); + + ASRequestHandlerRef_t ASRequestHandlerRef = std::make_unique( + Namespaces, + BaseAdresses_t{ SHMASMeta, SHMASRequests, SHMASResults } + ); + + ClientFD_t ClientFD = 1; + ClientRef_t ClientObj(new HTTPParser(ClientFD, Namespaces)); + + std::string Request("GET /backend/test1?param1=test1¶m2=test2 HTTP/1.1\r\nHost: test.local\r\nCustomHeader: one\r\n\r\n"); + ClientObj->appendBuffer(Request.c_str(), Request.length()); + auto r = ClientObj->processRequests(SHMGetRequests, ASRequestHandlerRef); + + BOOST_TEST(r == 0); +} + +BOOST_AUTO_TEST_CASE( test_multiple_get_request_truncated ) +{ + cout << "Check multiple (3) GET requests, truncated over 2 requests." << endl; + + void* SHMBase = mmap(NULL, SHMEM_STATICFS_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); + void* SHMGetRequests = static_cast(SHMBase) + sizeof(atomic_uint16_t) + sizeof(uint16_t); + + void* SHMASMeta = mmap(NULL, SHMEM_STATICFS_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); + void* SHMASRequests = mmap(NULL, SHMEM_STATICFS_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); + void* SHMASResults = mmap(NULL, SHMEM_STATICFS_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); + + const json JSONConfig = { {"interpreters", 2}, {"path", "/test"} }; + + Namespaces_t Namespaces; + NamespaceProps_t NamespaceProps; + NamespaceProps.FilesystemRef = nullptr; + NamespaceProps.JSONConfig = JSONConfig; + + Namespaces.emplace( + "test1", NamespaceProps + ); + + ASRequestHandlerRef_t ASRequestHandlerRef = std::make_unique( + Namespaces, + BaseAdresses_t{ SHMASMeta, SHMASRequests, SHMASResults } + ); + + ClientFD_t ClientFD = 1; + ClientRef_t ClientObj(new HTTPParser(ClientFD, Namespaces)); + + std::string Request("GET /t/tA.png HTTP/1.1\r\nCustomHeader: a\r\n\r\nGET /t/tB.png HTTP/1.1\r\nCustomHeader: b\r\n\r\nGET /t/tC.png HT"); + ClientObj->appendBuffer(Request.c_str(), Request.length()); + auto r1 = ClientObj->processRequests(SHMGetRequests, ASRequestHandlerRef); + + std::string Request2("TP/1.1\r\nCustomHeader: c\r\n\r\n"); + ClientObj->appendBuffer(Request2.c_str(), Request2.length()); + auto r2 = ClientObj->processRequests(SHMGetRequests, ASRequestHandlerRef); + + BOOST_TEST(r1 == 2); + BOOST_TEST(r2 == 1); +} From 114c5be24756c6918ee634088b75cd4473512bd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Mon, 27 Apr 2026 10:02:36 +0200 Subject: [PATCH 12/14] Fix, should handle all cases now --- lib/http/httpconstants.hpp | 6 +- lib/http/httpparser.cpp | 43 +++- lib/http/httpparser.hpp | 5 + .../unit/http-parser/test-parser-requests.cpp | 207 +++++++++++++++++- 4 files changed, 245 insertions(+), 16 deletions(-) diff --git a/lib/http/httpconstants.hpp b/lib/http/httpconstants.hpp index 5e081b3..b524666 100644 --- a/lib/http/httpconstants.hpp +++ b/lib/http/httpconstants.hpp @@ -17,12 +17,12 @@ constexpr uint16_t HTTP_POST_MAX_CONTENT_LENGTH = 4096; constexpr uint16_t URL_PARAM_NOT_FOUND_ERROR = 10; //- constant expressions (error) +constexpr uint16_t HTTP_ERROR_PARSE_BUFFER_EXCEEDED = 10; constexpr uint16_t HTTP_ERROR_BAD_REQUEST = 400; - //- contant strings -static const std::string HTTP_1_1_END_MARKER = "\r\n\r\n"; -static const std::string HTTP_HEADER_CONTENT_LENGTH = "Content-Length"; +static const std::string HTTP_1_1_END_MARKER("\r\n\r\n"); +static const std::string HTTP_HEADER_CONTENT_LENGTH("Content-Length"); //- contant multidimensional static const std::vector HTTPHeaderTypes diff --git a/lib/http/httpparser.cpp b/lib/http/httpparser.cpp index 0c63bca..8d53ffa 100644 --- a/lib/http/httpparser.cpp +++ b/lib/http/httpparser.cpp @@ -21,11 +21,18 @@ HTTPParser::~HTTPParser() void HTTPParser::appendBuffer(const char* BufferRef, const uint16_t RecvBytes) { + if (_HTTPRequestBuffer.length()+RecvBytes > _HTTPRequestBufferMax) { + return; + } + _HTTPRequestBuffer.append(BufferRef, RecvBytes); //- on incomplete (single) POST request - if (_POSTWaitContentLength == true) { - + if (_POSTWaitContentLength == true && _HTTPRequestBuffer.length() >= _POSTContentLength) { + _RequestProperties.Payload = _HTTPRequestBuffer.substr(0, _POSTContentLength); + _HTTPRequestBuffer.replace(0, _POSTContentLength, ""); + _Requests.push_back(move(_RequestProperties)); + _POSTWaitContentLength = false; } else { //- reset _SplittedRequests vector @@ -40,6 +47,19 @@ void HTTPParser::appendBuffer(const char* BufferRef, const uint16_t RecvBytes) } } +RequestsVector_t HTTPParser::getRequests() +{ + return _Requests; +} + +void HTTPParser::getNextRequest(RequestPropertiesRef_t Request) +{ + if (_Requests.size() > 0) { + Request = move(_Requests.front()); + _Requests.erase(_Requests.begin()); + } +} + inline void HTTPParser::_processRequests() { //- split requests into _SplittedRequests vector @@ -56,6 +76,8 @@ inline void HTTPParser::_processRequests() inline bool HTTPParser::_processRequestProperties(const size_t Index) { + cout << "Processing request properties!" << endl; + //- get request ref at vector index auto &Request = _SplittedRequests.at(Index); @@ -75,14 +97,19 @@ inline bool HTTPParser::_processRequestProperties(const size_t Index) //- only process HTTP/1.1 requests if (_RequestProperties.HTTPVersion != HTTP_VERSION_1_1) { return false; } + cout << "Check HTTP method" << endl; + //- if not GET || POST method, return if (_RequestProperties.HTTPMethod == HTTP_METHOD_OTHER) { return false; } + cout << "HTTP method is POST or GET" << endl; + //- parse request headers _parseRequestHeaders(Request, _RequestProperties.RequestHeaders); //- GET request if (_RequestProperties.HTTPMethod == HTTP_METHOD_GET) { + cout << "HTTP method is GET" << endl; //- parse GET parameters //- append (move) to requests vector _Requests.push_back(move(_RequestProperties)); @@ -90,11 +117,13 @@ inline bool HTTPParser::_processRequestProperties(const size_t Index) //- AS POST request else if (_RequestProperties.HTTPMethod == HTTP_METHOD_POST) { + cout << "HTTP method is POST" << endl; //- if request does not contain content-length header - if (_RequestProperties.RequestHeaders.find(HTTP_HEADER_CONTENT_LENGTH) != _RequestProperties.RequestHeaders.end()) { + if (_RequestProperties.RequestHeaders.find(HTTP_HEADER_CONTENT_LENGTH) == _RequestProperties.RequestHeaders.end()) { return false; } + cout << "Content-length header found" << endl; //- if content-length header contains non-digits if (StringHelper::is_digits(_RequestProperties.RequestHeaders.at(HTTP_HEADER_CONTENT_LENGTH)) == false) { @@ -103,6 +132,8 @@ inline bool HTTPParser::_processRequestProperties(const size_t Index) _POSTContentLength = stoi(_RequestProperties.RequestHeaders.at(HTTP_HEADER_CONTENT_LENGTH)); + cout << "POST Content-length: " << _POSTContentLength << endl; + //- if content-length exeeds maximum if (_POSTContentLength > HTTP_POST_MAX_CONTENT_LENGTH) { return false; @@ -110,7 +141,7 @@ inline bool HTTPParser::_processRequestProperties(const size_t Index) //- on single HTTP POST: payload after endmarker in _HTTPRequestBuffer if (_SplittedRequests.size() == 1) { - //- if content length bytes already in buffer + //- content length bytes already in buffer if (_HTTPRequestBuffer.length() >= _POSTContentLength) { _RequestProperties.Payload = _HTTPRequestBuffer.substr(0, _POSTContentLength); _HTTPRequestBuffer.replace(0, _POSTContentLength, ""); @@ -137,7 +168,7 @@ inline bool HTTPParser::_processRequestProperties(const size_t Index) return true; } -void HTTPParser::_parseRequestProperties(string& Request, RequestPropertiesRef_t ResultBaseProps) +inline void HTTPParser::_parseRequestProperties(string& Request, RequestPropertiesRef_t ResultBaseProps) { //- find first line endline size_t StartPos = Request.find("\r\n"); @@ -165,7 +196,7 @@ void HTTPParser::_parseRequestProperties(string& Request, RequestPropertiesRef_t ResultBaseProps.URL = SplitResult.at(1); } -void HTTPParser::_parseRequestHeaders(string& Request, RequestHeaderRef_t ResultRef) +inline void HTTPParser::_parseRequestHeaders(string& Request, RequestHeaderRef_t ResultRef) { //- reverse split header lines vector Lines; diff --git a/lib/http/httpparser.hpp b/lib/http/httpparser.hpp index 5e89fbb..1606356 100644 --- a/lib/http/httpparser.hpp +++ b/lib/http/httpparser.hpp @@ -8,6 +8,9 @@ #include #include +#include +#include + using namespace std; typedef unordered_map RequestHeader_t; @@ -39,6 +42,8 @@ class HTTPParser ~HTTPParser(); void appendBuffer(const char*, const uint16_t); + RequestsVector_t getRequests(); + void getNextRequest(RequestPropertiesRef_t); private: diff --git a/test/unit/http-parser/test-parser-requests.cpp b/test/unit/http-parser/test-parser-requests.cpp index 1c54c6b..7332ab5 100644 --- a/test/unit/http-parser/test-parser-requests.cpp +++ b/test/unit/http-parser/test-parser-requests.cpp @@ -6,16 +6,209 @@ using namespace std; +static const string HTTP_REQUEST_GET_SINGLE1("GET /test/test.png HTTP/1.1\r\nCustomHeader: one\r\n\r\n"); +static const string HTTP_REQUEST_GET_SINGLE2("GET /test/test2.png HTTP/1.1\r\nHost: test.dns\r\nAnotherHeader: two\r\n\r\n"); +static const string HTTP_REQUEST_GET_SINGLE3("GET /test/test3.png HTTP/1.1\r\nThirdHeader: three\r\n\r\n"); -BOOST_AUTO_TEST_CASE( test_single_get_request ) +static const string HTTP_REQUEST_POST_SINGLE("POST /backend/test1 HTTP/1.1\r\nHost: test.loalnet\r\nContent-Type: application/json\r\nContent-Length: 2\r\n\r\n{}"); + +static const string HTTP_REQUEST_POST_PARTIAL1("POST /other/path HTTP/1.1\r\nHost: test.loal"); +static const string HTTP_REQUEST_POST_PARTIAL2("net\r\nContent-Type: application/json\r\nContent-Length: 10\r"); +static const string HTTP_REQUEST_POST_PARTIAL3("\n\r\n{12345678}"); + + +BOOST_AUTO_TEST_CASE( test_single_valid_get_request_full_transmit ) +{ + cout << "Check single GET request (full buffer transmit at once)." << endl; + + RequestsVector_t Requests; + + unique_ptr parser = make_unique(4096); + + parser->appendBuffer(HTTP_REQUEST_GET_SINGLE1.c_str(), HTTP_REQUEST_GET_SINGLE1.length()); + Requests = parser->getRequests(); + + auto rs = Requests.size(); + auto rv = Requests.at(0).HTTPVersion; + auto rm = Requests.at(0).HTTPMethod; + auto ru = Requests.at(0).URL; + auto rh = Requests.at(0).RequestHeaders.at("CustomHeader"); + + cout << "Request size: " << rs << " version: " << rv << " method: " << rm << " URL: " << ru << " Header: " << rh << endl; + + BOOST_TEST(rs == 1); + BOOST_TEST(rv == 1); + BOOST_TEST(rm == 1); + BOOST_TEST(ru == "/test/test.png"); + BOOST_TEST(rh == "one"); + +} + +BOOST_AUTO_TEST_CASE( test_single_post_request_full_transmit ) +{ + cout << "Check single POST request (full buffer transmit at once)." << endl; + + RequestsVector_t Requests; + + unique_ptr parser = make_unique(4096); + + parser->appendBuffer(HTTP_REQUEST_POST_SINGLE.c_str(), HTTP_REQUEST_POST_SINGLE.length()); + Requests = parser->getRequests(); + + auto rs = Requests.size(); + auto rv = Requests.at(0).HTTPVersion; + auto rm = Requests.at(0).HTTPMethod; + auto ru = Requests.at(0).URL; + auto rh = Requests.at(0).RequestHeaders.at("Host"); + auto rp = Requests.at(0).Payload; + + cout << "Request size: " << rs << " version: " << rv << " method: " << rm << " URL: " << ru << " Header: " << rh << " Payload: " << rp << endl; + + BOOST_TEST(rs == 1); + BOOST_TEST(rv == 1); + BOOST_TEST(rm == 2); + BOOST_TEST(ru == "/backend/test1"); + BOOST_TEST(rh == "test.loalnet"); + BOOST_TEST(rp == "{}"); +} + +BOOST_AUTO_TEST_CASE( test_multiple_valid_get_request_full_transmit ) +{ + cout << "Check multiple GET requests (full buffer transmit at once)." << endl; + + RequestsVector_t Requests; + + unique_ptr parser = make_unique(4096); + + string full_request(HTTP_REQUEST_GET_SINGLE1); + full_request.append(HTTP_REQUEST_GET_SINGLE2); + full_request.append(HTTP_REQUEST_GET_SINGLE3); + + parser->appendBuffer(full_request.c_str(), full_request.length()); + Requests = parser->getRequests(); + + auto rs = Requests.size(); + auto rv1 = Requests.at(0).HTTPVersion; + auto rm1 = Requests.at(0).HTTPMethod; + auto ru1 = Requests.at(0).URL; + auto rh1 = Requests.at(0).RequestHeaders.at("CustomHeader"); + + auto rv2 = Requests.at(1).HTTPVersion; + auto rm2 = Requests.at(1).HTTPMethod; + auto ru2 = Requests.at(1).URL; + auto rh2 = Requests.at(1).RequestHeaders.at("AnotherHeader"); + + auto rv3 = Requests.at(2).HTTPVersion; + auto rm3 = Requests.at(2).HTTPMethod; + auto ru3 = Requests.at(2).URL; + auto rh3 = Requests.at(2).RequestHeaders.at("ThirdHeader"); + + cout << "Request size: " << rs << endl; + cout << "Request 1 version: " << rv1 << " method: " << rm1 << " URL: " << ru1 << " Header: " << rh1 << endl; + cout << "Request 2 version: " << rv2 << " method: " << rm2 << " URL: " << ru2 << " Header: " << rh2 << endl; + cout << "Request 3 version: " << rv3 << " method: " << rm3 << " URL: " << ru3 << " Header: " << rh3 << endl; + + BOOST_TEST(rs == 3); + + BOOST_TEST(rv1 == 1); + BOOST_TEST(rm1 == 1); + BOOST_TEST(ru1 == "/test/test.png"); + BOOST_TEST(rh1 == "one"); + + BOOST_TEST(rv2 == 1); + BOOST_TEST(rm2 == 1); + BOOST_TEST(ru2 == "/test/test2.png"); + BOOST_TEST(rh2 == "two"); + + BOOST_TEST(rv3 == 1); + BOOST_TEST(rm3 == 1); + BOOST_TEST(ru3 == "/test/test3.png"); + BOOST_TEST(rh3 == "three"); + +} + +BOOST_AUTO_TEST_CASE( test_multiple_valid_get_and_post_requests_full_transmit ) { - cout << "Check single GET request." << endl; + cout << "Check multiple GET and POST requests (full buffer transmit at once)." << endl; + + RequestsVector_t Requests; + + unique_ptr parser = make_unique(4096); + + string full_request(HTTP_REQUEST_GET_SINGLE1); + full_request.append(HTTP_REQUEST_POST_SINGLE); + full_request.append(HTTP_REQUEST_GET_SINGLE2); + full_request.append(HTTP_REQUEST_POST_SINGLE); + full_request.append(HTTP_REQUEST_GET_SINGLE3); + + parser->appendBuffer(full_request.c_str(), full_request.length()); + Requests = parser->getRequests(); + + auto rs = Requests.size(); + auto rv1 = Requests.at(0).HTTPVersion; + auto rm1 = Requests.at(0).HTTPMethod; + auto ru1 = Requests.at(0).URL; + auto rh1 = Requests.at(0).RequestHeaders.at("CustomHeader"); + + auto rv2 = Requests.at(1).HTTPVersion; + auto rm2 = Requests.at(1).HTTPMethod; + auto ru2 = Requests.at(1).URL; + auto rh2 = Requests.at(1).RequestHeaders.at("Content-Type"); + + auto rv3 = Requests.at(2).HTTPVersion; + auto rm3 = Requests.at(2).HTTPMethod; + auto ru3 = Requests.at(2).URL; + auto rh3 = Requests.at(2).RequestHeaders.at("Host"); + + cout << "Request size: " << rs << endl; + cout << "Request 1 version: " << rv1 << " method: " << rm1 << " URL: " << ru1 << " Header: " << rh1 << endl; + cout << "Request 2 version: " << rv2 << " method: " << rm2 << " URL: " << ru2 << " Header: " << rh2 << endl; + cout << "Request 3 version: " << rv3 << " method: " << rm3 << " URL: " << ru3 << " Header: " << rh3 << endl; + + BOOST_TEST(rs == 5); + + BOOST_TEST(rv1 == 1); + BOOST_TEST(rm1 == 1); + BOOST_TEST(ru1 == "/test/test.png"); + BOOST_TEST(rh1 == "one"); + + BOOST_TEST(rv2 == 1); + BOOST_TEST(rm2 == 2); + BOOST_TEST(ru2 == "/backend/test1"); + BOOST_TEST(rh2 == "application/json"); + + BOOST_TEST(rv3 == 1); + BOOST_TEST(rm3 == 1); + BOOST_TEST(ru3 == "/test/test2.png"); + BOOST_TEST(rh3 == "test.dns"); + +} + +BOOST_AUTO_TEST_CASE( test_single_valid_post_request_partial_transmit ) +{ + cout << "Check single POST request (partial transmit)." << endl; + + RequestsVector_t Requests; + + unique_ptr parser = make_unique(4096); + + parser->appendBuffer(HTTP_REQUEST_POST_PARTIAL1.c_str(), HTTP_REQUEST_POST_PARTIAL1.length()); + parser->appendBuffer(HTTP_REQUEST_POST_PARTIAL2.c_str(), HTTP_REQUEST_POST_PARTIAL2.length()); + parser->appendBuffer(HTTP_REQUEST_POST_PARTIAL3.c_str(), HTTP_REQUEST_POST_PARTIAL3.length()); + Requests = parser->getRequests(); + + auto rs = Requests.size(); + auto rv = Requests.at(0).HTTPVersion; + auto rm = Requests.at(0).HTTPMethod; + auto ru = Requests.at(0).URL; + auto rh = Requests.at(0).RequestHeaders.at("Content-Type"); - HTTPParser* parser = new HTTPParser(4096); + cout << "Request size: " << rs << " version: " << rv << " method: " << rm << " URL: " << ru << " Header: " << rh << endl; - std::string Request("GET /test/test.png HTTP/1.1\r\nCustomHeader: one\r\n\r\n"); - parser->appendBuffer(Request.c_str(), Request.length()); + BOOST_TEST(rs == 1); + BOOST_TEST(rv == 2); + BOOST_TEST(rm == 1); + BOOST_TEST(ru == "/other/path"); + BOOST_TEST(rh == "application/json"); - auto r=1; - BOOST_TEST(r == 1); } From e8c646ce0707d8ddce03bd634b40dbf532005f2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Mon, 27 Apr 2026 10:45:35 +0200 Subject: [PATCH 13/14] Remove debug (temporarily) --- lib/http/httpparser.cpp | 10 ---------- lib/http/httpparser.hpp | 3 --- 2 files changed, 13 deletions(-) diff --git a/lib/http/httpparser.cpp b/lib/http/httpparser.cpp index 8d53ffa..f137141 100644 --- a/lib/http/httpparser.cpp +++ b/lib/http/httpparser.cpp @@ -76,8 +76,6 @@ inline void HTTPParser::_processRequests() inline bool HTTPParser::_processRequestProperties(const size_t Index) { - cout << "Processing request properties!" << endl; - //- get request ref at vector index auto &Request = _SplittedRequests.at(Index); @@ -97,19 +95,15 @@ inline bool HTTPParser::_processRequestProperties(const size_t Index) //- only process HTTP/1.1 requests if (_RequestProperties.HTTPVersion != HTTP_VERSION_1_1) { return false; } - cout << "Check HTTP method" << endl; //- if not GET || POST method, return if (_RequestProperties.HTTPMethod == HTTP_METHOD_OTHER) { return false; } - cout << "HTTP method is POST or GET" << endl; - //- parse request headers _parseRequestHeaders(Request, _RequestProperties.RequestHeaders); //- GET request if (_RequestProperties.HTTPMethod == HTTP_METHOD_GET) { - cout << "HTTP method is GET" << endl; //- parse GET parameters //- append (move) to requests vector _Requests.push_back(move(_RequestProperties)); @@ -117,13 +111,11 @@ inline bool HTTPParser::_processRequestProperties(const size_t Index) //- AS POST request else if (_RequestProperties.HTTPMethod == HTTP_METHOD_POST) { - cout << "HTTP method is POST" << endl; //- if request does not contain content-length header if (_RequestProperties.RequestHeaders.find(HTTP_HEADER_CONTENT_LENGTH) == _RequestProperties.RequestHeaders.end()) { return false; } - cout << "Content-length header found" << endl; //- if content-length header contains non-digits if (StringHelper::is_digits(_RequestProperties.RequestHeaders.at(HTTP_HEADER_CONTENT_LENGTH)) == false) { @@ -132,8 +124,6 @@ inline bool HTTPParser::_processRequestProperties(const size_t Index) _POSTContentLength = stoi(_RequestProperties.RequestHeaders.at(HTTP_HEADER_CONTENT_LENGTH)); - cout << "POST Content-length: " << _POSTContentLength << endl; - //- if content-length exeeds maximum if (_POSTContentLength > HTTP_POST_MAX_CONTENT_LENGTH) { return false; diff --git a/lib/http/httpparser.hpp b/lib/http/httpparser.hpp index 1606356..6567ccc 100644 --- a/lib/http/httpparser.hpp +++ b/lib/http/httpparser.hpp @@ -8,9 +8,6 @@ #include #include -#include -#include - using namespace std; typedef unordered_map RequestHeader_t; From 77a1b87f8cd5beb126ea6dd025cafaddd9cfc88c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 27 Apr 2026 13:18:26 +0000 Subject: [PATCH 14/14] Add 1-byte segment tests for all existing parser tests Co-authored-by: clauspruefer <17313789+clauspruefer@users.noreply.github.com> --- .../unit/http-parser/test-parser-requests.cpp | 215 ++++++++++++++++++ 1 file changed, 215 insertions(+) diff --git a/test/unit/http-parser/test-parser-requests.cpp b/test/unit/http-parser/test-parser-requests.cpp index 7332ab5..5f33da2 100644 --- a/test/unit/http-parser/test-parser-requests.cpp +++ b/test/unit/http-parser/test-parser-requests.cpp @@ -44,6 +44,36 @@ BOOST_AUTO_TEST_CASE( test_single_valid_get_request_full_transmit ) } +BOOST_AUTO_TEST_CASE( test_single_valid_get_request_1byte_transmit ) +{ + cout << "Check single GET request (1-byte buffer transmit)." << endl; + + RequestsVector_t Requests; + + unique_ptr parser = make_unique(4096); + + // Send data byte by byte + for (size_t i = 0; i < HTTP_REQUEST_GET_SINGLE1.length(); i++) { + parser->appendBuffer(HTTP_REQUEST_GET_SINGLE1.c_str() + i, 1); + } + Requests = parser->getRequests(); + + auto rs = Requests.size(); + auto rv = Requests.at(0).HTTPVersion; + auto rm = Requests.at(0).HTTPMethod; + auto ru = Requests.at(0).URL; + auto rh = Requests.at(0).RequestHeaders.at("CustomHeader"); + + cout << "Request size: " << rs << " version: " << rv << " method: " << rm << " URL: " << ru << " Header: " << rh << endl; + + BOOST_TEST(rs == 1); + BOOST_TEST(rv == 1); + BOOST_TEST(rm == 1); + BOOST_TEST(ru == "/test/test.png"); + BOOST_TEST(rh == "one"); + +} + BOOST_AUTO_TEST_CASE( test_single_post_request_full_transmit ) { cout << "Check single POST request (full buffer transmit at once)." << endl; @@ -72,6 +102,37 @@ BOOST_AUTO_TEST_CASE( test_single_post_request_full_transmit ) BOOST_TEST(rp == "{}"); } +BOOST_AUTO_TEST_CASE( test_single_post_request_1byte_transmit ) +{ + cout << "Check single POST request (1-byte buffer transmit)." << endl; + + RequestsVector_t Requests; + + unique_ptr parser = make_unique(4096); + + // Send data byte by byte + for (size_t i = 0; i < HTTP_REQUEST_POST_SINGLE.length(); i++) { + parser->appendBuffer(HTTP_REQUEST_POST_SINGLE.c_str() + i, 1); + } + Requests = parser->getRequests(); + + auto rs = Requests.size(); + auto rv = Requests.at(0).HTTPVersion; + auto rm = Requests.at(0).HTTPMethod; + auto ru = Requests.at(0).URL; + auto rh = Requests.at(0).RequestHeaders.at("Host"); + auto rp = Requests.at(0).Payload; + + cout << "Request size: " << rs << " version: " << rv << " method: " << rm << " URL: " << ru << " Header: " << rh << " Payload: " << rp << endl; + + BOOST_TEST(rs == 1); + BOOST_TEST(rv == 1); + BOOST_TEST(rm == 2); + BOOST_TEST(ru == "/backend/test1"); + BOOST_TEST(rh == "test.loalnet"); + BOOST_TEST(rp == "{}"); +} + BOOST_AUTO_TEST_CASE( test_multiple_valid_get_request_full_transmit ) { cout << "Check multiple GET requests (full buffer transmit at once)." << endl; @@ -127,6 +188,64 @@ BOOST_AUTO_TEST_CASE( test_multiple_valid_get_request_full_transmit ) } +BOOST_AUTO_TEST_CASE( test_multiple_valid_get_request_1byte_transmit ) +{ + cout << "Check multiple GET requests (1-byte buffer transmit)." << endl; + + RequestsVector_t Requests; + + unique_ptr parser = make_unique(4096); + + string full_request(HTTP_REQUEST_GET_SINGLE1); + full_request.append(HTTP_REQUEST_GET_SINGLE2); + full_request.append(HTTP_REQUEST_GET_SINGLE3); + + // Send data byte by byte + for (size_t i = 0; i < full_request.length(); i++) { + parser->appendBuffer(full_request.c_str() + i, 1); + } + Requests = parser->getRequests(); + + auto rs = Requests.size(); + auto rv1 = Requests.at(0).HTTPVersion; + auto rm1 = Requests.at(0).HTTPMethod; + auto ru1 = Requests.at(0).URL; + auto rh1 = Requests.at(0).RequestHeaders.at("CustomHeader"); + + auto rv2 = Requests.at(1).HTTPVersion; + auto rm2 = Requests.at(1).HTTPMethod; + auto ru2 = Requests.at(1).URL; + auto rh2 = Requests.at(1).RequestHeaders.at("AnotherHeader"); + + auto rv3 = Requests.at(2).HTTPVersion; + auto rm3 = Requests.at(2).HTTPMethod; + auto ru3 = Requests.at(2).URL; + auto rh3 = Requests.at(2).RequestHeaders.at("ThirdHeader"); + + cout << "Request size: " << rs << endl; + cout << "Request 1 version: " << rv1 << " method: " << rm1 << " URL: " << ru1 << " Header: " << rh1 << endl; + cout << "Request 2 version: " << rv2 << " method: " << rm2 << " URL: " << ru2 << " Header: " << rh2 << endl; + cout << "Request 3 version: " << rv3 << " method: " << rm3 << " URL: " << ru3 << " Header: " << rh3 << endl; + + BOOST_TEST(rs == 3); + + BOOST_TEST(rv1 == 1); + BOOST_TEST(rm1 == 1); + BOOST_TEST(ru1 == "/test/test.png"); + BOOST_TEST(rh1 == "one"); + + BOOST_TEST(rv2 == 1); + BOOST_TEST(rm2 == 1); + BOOST_TEST(ru2 == "/test/test2.png"); + BOOST_TEST(rh2 == "two"); + + BOOST_TEST(rv3 == 1); + BOOST_TEST(rm3 == 1); + BOOST_TEST(ru3 == "/test/test3.png"); + BOOST_TEST(rh3 == "three"); + +} + BOOST_AUTO_TEST_CASE( test_multiple_valid_get_and_post_requests_full_transmit ) { cout << "Check multiple GET and POST requests (full buffer transmit at once)." << endl; @@ -184,6 +303,66 @@ BOOST_AUTO_TEST_CASE( test_multiple_valid_get_and_post_requests_full_transmit ) } +BOOST_AUTO_TEST_CASE( test_multiple_valid_get_and_post_requests_1byte_transmit ) +{ + cout << "Check multiple GET and POST requests (1-byte buffer transmit)." << endl; + + RequestsVector_t Requests; + + unique_ptr parser = make_unique(4096); + + string full_request(HTTP_REQUEST_GET_SINGLE1); + full_request.append(HTTP_REQUEST_POST_SINGLE); + full_request.append(HTTP_REQUEST_GET_SINGLE2); + full_request.append(HTTP_REQUEST_POST_SINGLE); + full_request.append(HTTP_REQUEST_GET_SINGLE3); + + // Send data byte by byte + for (size_t i = 0; i < full_request.length(); i++) { + parser->appendBuffer(full_request.c_str() + i, 1); + } + Requests = parser->getRequests(); + + auto rs = Requests.size(); + auto rv1 = Requests.at(0).HTTPVersion; + auto rm1 = Requests.at(0).HTTPMethod; + auto ru1 = Requests.at(0).URL; + auto rh1 = Requests.at(0).RequestHeaders.at("CustomHeader"); + + auto rv2 = Requests.at(1).HTTPVersion; + auto rm2 = Requests.at(1).HTTPMethod; + auto ru2 = Requests.at(1).URL; + auto rh2 = Requests.at(1).RequestHeaders.at("Content-Type"); + + auto rv3 = Requests.at(2).HTTPVersion; + auto rm3 = Requests.at(2).HTTPMethod; + auto ru3 = Requests.at(2).URL; + auto rh3 = Requests.at(2).RequestHeaders.at("Host"); + + cout << "Request size: " << rs << endl; + cout << "Request 1 version: " << rv1 << " method: " << rm1 << " URL: " << ru1 << " Header: " << rh1 << endl; + cout << "Request 2 version: " << rv2 << " method: " << rm2 << " URL: " << ru2 << " Header: " << rh2 << endl; + cout << "Request 3 version: " << rv3 << " method: " << rm3 << " URL: " << ru3 << " Header: " << rh3 << endl; + + BOOST_TEST(rs == 5); + + BOOST_TEST(rv1 == 1); + BOOST_TEST(rm1 == 1); + BOOST_TEST(ru1 == "/test/test.png"); + BOOST_TEST(rh1 == "one"); + + BOOST_TEST(rv2 == 1); + BOOST_TEST(rm2 == 2); + BOOST_TEST(ru2 == "/backend/test1"); + BOOST_TEST(rh2 == "application/json"); + + BOOST_TEST(rv3 == 1); + BOOST_TEST(rm3 == 1); + BOOST_TEST(ru3 == "/test/test2.png"); + BOOST_TEST(rh3 == "test.dns"); + +} + BOOST_AUTO_TEST_CASE( test_single_valid_post_request_partial_transmit ) { cout << "Check single POST request (partial transmit)." << endl; @@ -212,3 +391,39 @@ BOOST_AUTO_TEST_CASE( test_single_valid_post_request_partial_transmit ) BOOST_TEST(rh == "application/json"); } + +BOOST_AUTO_TEST_CASE( test_single_valid_post_request_partial_1byte_transmit ) +{ + cout << "Check single POST request (partial 1-byte buffer transmit)." << endl; + + RequestsVector_t Requests; + + unique_ptr parser = make_unique(4096); + + // Send each partial byte by byte + for (size_t i = 0; i < HTTP_REQUEST_POST_PARTIAL1.length(); i++) { + parser->appendBuffer(HTTP_REQUEST_POST_PARTIAL1.c_str() + i, 1); + } + for (size_t i = 0; i < HTTP_REQUEST_POST_PARTIAL2.length(); i++) { + parser->appendBuffer(HTTP_REQUEST_POST_PARTIAL2.c_str() + i, 1); + } + for (size_t i = 0; i < HTTP_REQUEST_POST_PARTIAL3.length(); i++) { + parser->appendBuffer(HTTP_REQUEST_POST_PARTIAL3.c_str() + i, 1); + } + Requests = parser->getRequests(); + + auto rs = Requests.size(); + auto rv = Requests.at(0).HTTPVersion; + auto rm = Requests.at(0).HTTPMethod; + auto ru = Requests.at(0).URL; + auto rh = Requests.at(0).RequestHeaders.at("Content-Type"); + + cout << "Request size: " << rs << " version: " << rv << " method: " << rm << " URL: " << ru << " Header: " << rh << endl; + + BOOST_TEST(rs == 1); + BOOST_TEST(rv == 2); + BOOST_TEST(rm == 1); + BOOST_TEST(ru == "/other/path"); + BOOST_TEST(rh == "application/json"); + +}