Skip to content

Commit a8dbe3c

Browse files
committed
Fix (probably) GET && POST working, extend unit test3 with continued truncated GET request
1 parent dc1fac1 commit a8dbe3c

File tree

7 files changed

+155
-71
lines changed

7 files changed

+155
-71
lines changed

Debug.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ using namespace std;
1111

1212
#define DBG(level, debugmsg) \
1313
if (LOG_LEVEL > level) { \
14-
cout << __func__ << "(), " << __LINE__ << " - " << debugmsg << endl; \
14+
cout << dec << __func__ << "(), " << __LINE__ << " - " << debugmsg << endl; \
1515
}
1616

1717
#define ERR(errmsg) \
18-
cerr << __func__ << "(), " << __LINE__ << " - " << errmsg << endl; \
18+
cerr << dec << __func__ << "(), " << __LINE__ << " - " << errmsg << endl; \
1919

2020
#endif

Helper.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ class String {
9595
for (auto i = String.begin(); i != String.end(); ++i) {
9696
std::cout << "0x" << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(*i) << ' ';
9797
}
98+
std::cout << endl;
9899
}
99100
};
100101

ThreadHandler.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ typedef struct {
3939
} ThreadHandlerGlobals_t;
4040

4141

42-
class ClientThread: private HTTPParser, private SHMPythonAS
42+
class ClientThread: private HTTPParser
4343
{
4444

4545
public:

lib/http/httpparser.cpp

Lines changed: 141 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ HTTPParser::HTTPParser(ClientFD_t ClientFD) :
99
_RequestCountGet(0),
1010
_RequestCountPost(0),
1111
_RequestCountPostAS(0),
12-
_HTTPRequest("")
12+
_HTTPRequestBuffer("")
1313
{
1414
DBG(120, "Constructor");
1515
}
@@ -21,121 +21,193 @@ HTTPParser::~HTTPParser()
2121

2222
void HTTPParser::appendBuffer(const char* BufferRef, const uint16_t BufferSize)
2323
{
24-
_HTTPRequest = _HTTPRequest + string(&BufferRef[0], BufferSize);
25-
String::hexout(_HTTPRequest);
26-
//DBG(250, "HTTPRequest:'" << _HTTPRequest << "'");
27-
_splitRequests();
24+
_HTTPRequestBuffer = _HTTPRequestBuffer + string(&BufferRef[0], BufferSize);
25+
//String::hexout(_HTTPRequestBuffer);
26+
DBG(250, "_HTTPRequestBuffer:'" << _HTTPRequestBuffer << "'");
27+
28+
//-> only process on min 1 valid request
29+
const size_t EndMarkerFound = _HTTPRequestBuffer.find("\r\n\r\n");
30+
31+
if (EndMarkerFound != string::npos) {
32+
_splitRequests();
33+
}
2834
}
2935

3036
void HTTPParser::_splitRequests()
3137
{
32-
//DBG(180, "splitRequests Buffer:'" << _HTTPRequest << "'");
38+
//DBG(180, "splitRequests Buffer:'" << _HTTPRequestBuffer << "'");
3339

3440
//- reset request counters
3541
_RequestCountGet = 0;
3642
_RequestCountPost = 0;
3743
_RequestCountPostAS = 0;
3844

39-
//-> reset incomplete request string
40-
string incompleteRequest("");
41-
42-
//-> check for existing valid request end marker
43-
size_t LastDelimiterPos = _HTTPRequest.rfind("\r\n\r\n");
44-
DBG(120, "LastDelimiterPos:" << LastDelimiterPos);
45-
46-
//-> if min 1 full valid request && rest without end marker
47-
if (LastDelimiterPos != string::npos && LastDelimiterPos != _HTTPRequest.length()-4) {
48-
49-
//-> put incomplete last request into tmp string
50-
incompleteRequest = _HTTPRequest.substr(LastDelimiterPos+4);
51-
52-
//-> remove incomplete last request from _HTTPRequest
53-
_HTTPRequest.replace(_HTTPRequest.begin()+LastDelimiterPos+4, _HTTPRequest.end(), "");
54-
}
45+
//-> reset _SplittedRequests vector
46+
_SplittedRequests.clear();
5547

5648
//-> split requests into _SplittedRequests vector
57-
_SplittedRequests.clear();
58-
String::split(_HTTPRequest, "\r\n\r\n", _SplittedRequests);
49+
String::split(_HTTPRequestBuffer, "\r\n\r\n", _SplittedRequests);
5950
_RequestCount = _SplittedRequests.size();
6051
DBG(120, "splitRequests count after splitted into Vector:" << _RequestCount);
61-
62-
//-> "restore" incomplete last request buffer
63-
_HTTPRequest = incompleteRequest;
6452
}
6553

66-
uint HTTPParser::processRequests(SharedMemAddress_t SHMGetRequests, const ASRequestHandlerRef_t ASRequestHandlerRef)
54+
size_t HTTPParser::processRequests(SharedMemAddress_t SHMGetRequests, const ASRequestHandlerRef_t ASRequestHandlerRef)
6755
{
56+
//- set get requests SHM base
6857
setBaseAddress(SHMGetRequests);
6958

70-
for (auto &Request:_SplittedRequests) {
71-
_processRequestProperties(Request, ASRequestHandlerRef);
59+
//- iterate over splitted requests
60+
for(size_t i=0; i<_SplittedRequests.size(); ++i) {
61+
_processRequestProperties(i, ASRequestHandlerRef);
62+
}
63+
64+
//- restore truncated requests
65+
const auto LastElementIndex = _SplittedRequests.size();
66+
67+
if (LastElementIndex > 0) {
68+
const auto LastElementVal = _SplittedRequests.at(LastElementIndex-1);
69+
70+
if (!LastElementVal.empty()) {
71+
_HTTPRequestBuffer.append(LastElementVal);
72+
}
73+
}
74+
75+
if (LastElementIndex > 1) {
76+
const auto LastElementPrevVal = _SplittedRequests.at(LastElementIndex-2);
77+
78+
if (!LastElementPrevVal.empty()) {
79+
_HTTPRequestBuffer.append(LastElementPrevVal);
80+
}
7281
}
7382

7483
return _RequestCountGet;
7584
}
7685

77-
void HTTPParser::_processRequestProperties(string& Request, const ASRequestHandlerRef_t ASRequestHandlerRef)
86+
void HTTPParser::_processRequestProperties(const size_t Index, const ASRequestHandlerRef_t ASRequestHandlerRef)
7887
{
79-
//DBG(180, "HTTP Request:'" << Request << "'");
88+
//- get request ref at vector index
89+
auto Request = _SplittedRequests.at(Index);
90+
91+
//- on empty request return
92+
if (Request.empty()) { return; }
8093

8194
BasePropsResult_t BasePropsFound;
8295
this->_parseRequestProperties(Request, BasePropsFound);
8396

8497
DBG(140, "HTTP Version:" << BasePropsFound.at(0) << " File:" << BasePropsFound.at(1) << " Method:" << BasePropsFound.at(2));
85-
DBG(140, "HTTP Payload (c_str):" << Request.c_str());
98+
DBG(140, "Complete Request:" << Request.c_str());
8699

87-
//- check HTTP/1.2 (currently unimplemented)
88-
const size_t HTTPVersion1_2Found = BasePropsFound.at(0).find("HTTP/1.2");
100+
//- temp hardcode HTTPVersion
101+
uint16_t HTTPVersion = 1;
89102

90-
//- if not HTTP/1.1, do not process further
103+
//- check HTTP/1.2 or HTTP/1.2 (currently unimplemented)
91104
const size_t HTTPVersion1_1Found = BasePropsFound.at(0).find("HTTP/1.1");
105+
const size_t HTTPVersion1_2Found = BasePropsFound.at(2).find("HTTP/1.2");
92106

93-
const uint16_t HTTPMethod = (BasePropsFound.at(2).find("POST") != string::npos) ? 2 : 1;
94-
const uint16_t HTTPVersion = (BasePropsFound.at(0).find("HTTP/1.1") != string::npos) ? 1 : 2;
107+
//- if not HTTP/1.1 set request to "" in vector element, return
108+
if (HTTPVersion1_1Found == string::npos) {
109+
_SplittedRequests.at(Index) = "";
110+
return;
111+
}
95112

96-
//- check if POST request is an AS request
97-
const size_t PythonReqFound = BasePropsFound.at(1).find("/python/");
113+
//- check for method GET || POST
114+
const size_t HTTPMethodPOST = BasePropsFound.at(2).find("POST");
115+
const size_t HTTPMethodGET = BasePropsFound.at(2).find("GET");
98116

99-
//- get unique request nr
100-
uint16_t RequestNr = getNextReqNr();
117+
//- set numerical http method (GET: 1, POST: 2)
118+
uint16_t HTTPMethod = 0;
101119

102-
DBG(140, "HTTP RequestNr:" << RequestNr << " HTTPVersion:" << HTTPVersion << " HTTPMethod:" << HTTPMethod);
120+
if (HTTPMethodGET != string::npos) { HTTPMethod = 1; }
121+
if (HTTPMethodPOST != string::npos) { HTTPMethod = 2; }
122+
123+
DBG(140, "HTTPMethod:" << HTTPMethod);
124+
125+
//- if not GET || POST, set request to "" in vector element, return
126+
if (HTTPMethod == 0) {
127+
_SplittedRequests.at(Index) = "";
128+
return;
129+
}
103130

104-
if (HTTPMethod == 2) {
131+
//- check if POST request is a POSTAS request
132+
const size_t PythonReqFound = BasePropsFound.at(1).find("/python/");
133+
134+
//- if not POSTAS, set request to "" in vector element, return
135+
if (HTTPMethod == 2 && PythonReqFound == string::npos) {
105136
++this->_RequestCountPost;
137+
_SplittedRequests.at(Index) = "";
138+
return;
106139
}
107140

108-
if (HTTPMethod == 2 && PythonReqFound != BasePropsFound.at(1).npos) {
141+
//- get unique request nr
142+
const uint16_t RequestNr = getNextReqNr();
143+
144+
DBG(140, "HTTP RequestNr:" << RequestNr);
145+
146+
if (HTTPMethod == 2 && PythonReqFound != string::npos) {
109147

110-
DBG(140, "Python Request:" << Request);
148+
DBG(140, "Request Type PythonAS:" << Request);
111149

112-
++this->_RequestCountPostAS;
150+
//-> check first line end exists
151+
size_t FirstLineEndMarker = Request.find("\r\n");
152+
153+
//-> if not: truncated
154+
if (FirstLineEndMarker == string::npos) {
155+
DBG(200, "Truncated POST Request - no First Line end");
156+
return;
157+
}
113158

114159
//-> cut first properties line from request
115-
size_t FirstLineEndMarker = Request.find("\n\r");
116-
Request.replace(0, FirstLineEndMarker+2, "");
160+
if (FirstLineEndMarker != string::npos) {
161+
Request.replace(0, FirstLineEndMarker+2, "");
162+
}
117163

118164
RequestHeaderResult_t Headers;
119165
this->_parseRequestHeaders(Request, Headers);
120166

121-
auto ContentBytes = stoi(Headers.at("Content-Length"));
122-
string Payload = Request.substr(Request.length()-ContentBytes, ContentBytes);
167+
uint ContentBytes = 0;
123168

124-
DBG(140, "HTTP POST-AS payload:" << Payload);
169+
//- try get content length header
170+
try {
171+
ContentBytes = stoi(Headers.at("Content-Length"));
172+
}
173+
catch(const std::exception& e) {
174+
DBG(200, "Truncated POST Request - no Content-Length");
175+
return;
176+
}
125177

126-
//- add ASRequestHandler request
127-
ASRequestHandlerRef->addRequest({
128-
Headers.at("Host"),
129-
_ClientFD,
130-
HTTPMethod,
131-
HTTPVersion,
132-
RequestNr,
133-
Payload
134-
});
178+
//- try get next request +payload at vector index +1
179+
try {
180+
auto NextRequest = _SplittedRequests.at(Index+1);
181+
182+
if (NextRequest.length() >= ContentBytes) {
183+
string Payload = NextRequest.substr(0, ContentBytes);
184+
NextRequest.replace(0, ContentBytes, "");
185+
DBG(140, "HTTP POST-AS Payload:" << Payload);
186+
187+
//- increment request count
188+
++this->_RequestCountPostAS;
189+
190+
//- add ASRequestHandler request
191+
ASRequestHandlerRef->addRequest({
192+
Headers.at("Host"),
193+
_ClientFD,
194+
HTTPMethod,
195+
HTTPVersion,
196+
RequestNr,
197+
Payload
198+
});
199+
}
200+
}
201+
catch(const std::exception& e) {
202+
DBG(200, "Truncated POST Request - no Payload");
203+
return;
204+
}
135205
}
136206

137207
if (HTTPMethod == 1) {
138208

209+
DBG(140, "Request Type GET:" << Request);
210+
139211
++this->_RequestCountGet;
140212

141213
//- set values in get requests shared memory
@@ -168,7 +240,7 @@ void HTTPParser::_parseRequestProperties(string& Request, BasePropsResultRef_t R
168240
//- find first line endline
169241
size_t StartPos = Request.find("\r\n");
170242

171-
//-> if no headers (no \n), set start pos to end of string
243+
//-> if no headers (no \r\n), set start pos to end of string
172244
if (StartPos == string::npos) {
173245
StartPos = Request.length();
174246
}
@@ -187,13 +259,19 @@ void HTTPParser::_parseRequestHeaders(string& Request, RequestHeaderResultRef_t
187259
vector<string> Lines;
188260
String::split(Request, "\r\n", Lines);
189261

262+
//- add last line (unsplitted)
263+
const size_t LastDelimiter = Request.rfind("\r\n");
264+
if (LastDelimiter != string::npos) {
265+
Lines.push_back(Request.substr(LastDelimiter+2, Request.length()));
266+
}
267+
190268
//- loop over lines, split, put into result map
191269
for (auto &Line:Lines) {
192270

193271
DBG(120, "Line:'" << Line << "'");
194272

195273
vector<string> HeaderPair;
196-
if (Line.find(':') != string::npos) {
274+
if (Line.find(":") != string::npos) {
197275

198276
String::rsplit(Line, Line.length(), ": ", HeaderPair);
199277
string HeaderID = HeaderPair.at(1);

lib/http/httpparser.hpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,12 @@ class HTTPParser: private Client, private SHMStaticFS, private SHMPythonAS
4040
~HTTPParser();
4141

4242
void appendBuffer(const char*, const uint16_t);
43-
uint processRequests(SharedMemAddress_t, const ASRequestHandlerRef_t);
43+
size_t processRequests(SharedMemAddress_t, const ASRequestHandlerRef_t);
4444

4545
private:
4646

4747
void _splitRequests();
48-
void _processRequestProperties(string&, const ASRequestHandlerRef_t);
48+
void _processRequestProperties(const size_t, const ASRequestHandlerRef_t);
4949

5050
RequestHeader_t _RequestHeaders;
5151
vector<string> _SplittedRequests;
@@ -55,7 +55,7 @@ class HTTPParser: private Client, private SHMStaticFS, private SHMPythonAS
5555
size_t _RequestCountPost;
5656
size_t _RequestCountPostAS;
5757

58-
string _HTTPRequest;
58+
string _HTTPRequestBuffer;
5959

6060
protected:
6161

3.62 MB
Binary file not shown.

test/unit/http-parser/test-parser-requests.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,12 @@ BOOST_AUTO_TEST_CASE( test_multiple_get_request_truncated )
146146

147147
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");
148148
ClientObj->appendBuffer(Request.c_str(), Request.length());
149-
auto r = ClientObj->processRequests(SHMGetRequests, ASRequestHandlerRef);
149+
auto r1 = ClientObj->processRequests(SHMGetRequests, ASRequestHandlerRef);
150150

151-
BOOST_TEST(r == 2);
151+
std::string Request2("TP/1.1\r\nCustomHeader: c\r\n\r\n");
152+
ClientObj->appendBuffer(Request2.c_str(), Request2.length());
153+
auto r2 = ClientObj->processRequests(SHMGetRequests, ASRequestHandlerRef);
154+
155+
BOOST_TEST(r1 == 2);
156+
BOOST_TEST(r2 == 1);
152157
}

0 commit comments

Comments
 (0)