Logo Pico-Framework A web-first embedded framework for C++
Loading...
Searching...
No Matches
HttpParser Class Reference

#include <HttpParser.h>

+ Collaboration diagram for HttpParser:

Static Public Member Functions

static std::pair< std::string, std::string > receiveHeaderAndLeftover (Tcp &socket)
 Receive raw headers from the socket until \r
\r
.
 
static bool receiveBody (Tcp &socket, const std::map< std::string, std::string > &headers, const std::string &leftoverBody, std::string &outBody, size_t maxLength, bool *wasTruncated)
 Receive the HTTP body from socket, using Content-Length or connection close.
 
static bool receiveChunkedBodyToFile (Tcp &socket, const std::string &leftover, std::function< bool(const char *, size_t)> writeFn, size_t maxLength, bool *wasTruncated)
 
static bool receiveFixedLengthBodyToFile (Tcp &socket, const std::map< std::string, std::string > &headers, const std::string &leftover, std::function< bool(const char *, size_t)> writeFn, size_t maxLength, bool *wasTruncated)
 
static int parseStatusCode (const std::string &statusLine)
 Parses the HTTP status line to extract the status code.
 
static std::map< std::string, std::string > parseHeaders (const std::string &rawHeaders)
 Parses the HTTP headers from a raw header string.
 
static bool isChunkedEncoding (const std::map< std::string, std::string > &headers)
 
static bool receiveChunkedBodyToString (Tcp &socket, const std::string &leftover, std::string &out, size_t maxLength, bool *wasTruncated)
 
static bool receiveFixedLengthBodyToString (Tcp &socket, const std::map< std::string, std::string > &headers, const std::string &leftover, std::string &out, size_t maxLength, bool *wasTruncated)
 
static bool receiveUnknownLengthBodyToString (Tcp &socket, const std::string &leftover, std::string &out, size_t maxLength, bool *wasTruncated)
 

Detailed Description

Definition at line 23 of file HttpParser.h.

Member Function Documentation

◆ isChunkedEncoding()

bool HttpParser::isChunkedEncoding ( const std::map< std::string, std::string > &  headers)
static

Definition at line 175 of file HttpParser.cpp.

176{
177 auto it = headers.find("transfer-encoding");
178 return (it != headers.end() && toLower(it->second) == "chunked");
179}
std::string toLower(std::string str)
Definition utility.cpp:93

References toLower().

Referenced by receiveBody().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ parseHeaders()

std::map< std::string, std::string > HttpParser::parseHeaders ( const std::string &  rawHeaders)
static
Parameters
rawHeadersThe raw HTTP headers as a string.
Returns
A map of header names to their values.

Definition at line 46 of file HttpParser.cpp.

47{
48 std::map<std::string, std::string> headers;
49 std::istringstream stream(rawHeaders);
50 std::string line;
51
52 while (std::getline(stream, line))
53 {
54 // Stop on blank line (end of headers)
55 if (line == "\r" || line.empty())
56 {
57 break;
58 }
59
60 auto colon = line.find(':');
61 if (colon == std::string::npos || colon + 1 >= line.size())
62 {
63 continue;
64 }
65
66 std::string key = line.substr(0, colon);
67 std::string value = line.substr(colon + 1);
68
69 // Remove CR and quotes
70 key.erase(std::remove(key.begin(), key.end(), '\r'), key.end());
71 value.erase(std::remove(value.begin(), value.end(), '\r'), value.end());
72 key.erase(std::remove(key.begin(), key.end(), '"'), key.end());
73 value.erase(std::remove(value.begin(), value.end(), '"'), value.end());
74
75 // Lowercase key
76 std::transform(key.begin(), key.end(), key.begin(), ::tolower);
77
78 // Trim whitespace from value
79 size_t start = value.find_first_not_of(" \t");
80 size_t end = value.find_last_not_of(" \t");
81 if (start != std::string::npos && end != std::string::npos)
82 {
83 value = value.substr(start, end - start + 1);
84 }
85 else
86 {
87 value = "";
88 }
89
90 headers[key] = value;
91 }
92
93 return headers;
94}

Referenced by HttpRequest::parseHeaders().

+ Here is the caller graph for this function:

◆ parseStatusCode()

int HttpParser::parseStatusCode ( const std::string &  statusLine)
static
Parameters
statusLineThe HTTP status line (e.g., "HTTP/1.1 200 OK").
Returns
The HTTP status code as an integer (e.g., 200).

Definition at line 33 of file HttpParser.cpp.

34{
35 std::istringstream stream(statusLine);
36 std::string httpVersion;
37 int code = 0;
38 stream >> httpVersion >> code;
39 return code;
40}

◆ receiveBody()

bool HttpParser::receiveBody ( Tcp socket,
const std::map< std::string, std::string > &  headers,
const std::string &  leftoverBody,
std::string &  outBody,
size_t  maxLength,
bool *  wasTruncated 
)
static

Receives the HTTP body from a TCP socket based on headers.

Parameters
socketThe TCP socket to read from.
headersThe parsed HTTP headers.
leftoverBodyAny body data that was received after the headers.
outBodyThe output string to store the received body.
maxLengthThe maximum length of the body to receive.
wasTruncatedPointer to a boolean that will be set to true if the body was truncated.
Returns
True if the body was successfully received, false on error or EOF. This function handles different content transfer methods:
  • Chunked transfer encoding
  • Content-Length specified in headers
  • Unknown length (just read until EOF)

Definition at line 152 of file HttpParser.cpp.

158{
159 if (wasTruncated)
160 *wasTruncated = false;
161
162 if (isChunkedEncoding(headers))
163 {
164 return receiveChunkedBodyToString(socket, leftoverBody, outBody, maxLength, wasTruncated);
165 }
166
167 if (headers.count("content-length"))
168 {
169 return receiveFixedLengthBodyToString(socket, headers, leftoverBody, outBody, maxLength, wasTruncated);
170 }
171
172 return receiveUnknownLengthBodyToString(socket, leftoverBody, outBody, maxLength, wasTruncated);
173}
static bool receiveChunkedBodyToString(Tcp &socket, const std::string &leftover, std::string &out, size_t maxLength, bool *wasTruncated)
static bool receiveFixedLengthBodyToString(Tcp &socket, const std::map< std::string, std::string > &headers, const std::string &leftover, std::string &out, size_t maxLength, bool *wasTruncated)
static bool receiveUnknownLengthBodyToString(Tcp &socket, const std::string &leftover, std::string &out, size_t maxLength, bool *wasTruncated)
static bool isChunkedEncoding(const std::map< std::string, std::string > &headers)

References isChunkedEncoding(), receiveChunkedBodyToString(), receiveFixedLengthBodyToString(), and receiveUnknownLengthBodyToString().

+ Here is the call graph for this function:

◆ receiveChunkedBodyToFile()

bool HttpParser::receiveChunkedBodyToFile ( Tcp socket,
const std::string &  leftover,
std::function< bool(const char *, size_t)>  writeFn,
size_t  maxLength,
bool *  wasTruncated 
)
static

Definition at line 264 of file HttpParser.cpp.

270{
271 ChunkedDecoder decoder;
272 decoder.feedToFile(leftover, writeFn, maxLength);
273
274 char buffer[1460];
275 while (!decoder.isComplete()) {
276 int n = socket.recv(buffer, sizeof(buffer), HTTP_RECEIVE_TIMEOUT);
277 if (n <= 0) {
278 printf("Chunked: recv() failed or EOF\n");
279 return false;
280 }
281
282 if (!decoder.feedToFile(std::string(buffer, n), writeFn, maxLength)) {
283 printf("Chunked: writeFn failed\n");
284 return false;
285 }
286
287 if (decoder.wasTruncated()) {
288 if (wasTruncated) *wasTruncated = true;
289 return true;
290 }
291 }
292
293 if (wasTruncated) *wasTruncated = decoder.wasTruncated();
294 return true;
295}
bool isComplete() const
bool wasTruncated() const
Set if the content-length is longer than the maximum value allowed.
bool feedToFile(const std::string &data, std::function< bool(const char *data, size_t len)> writeFn, size_t maxLength=MAX_HTTP_BODY_LENGTH)
Feed data to the decoder, writing it to a file using the provided write function.
int recv(char *buffer, size_t size, uint32_t timeout_ms)
Receive data from the connection.
Definition Tcp.cpp:389
#define HTTP_RECEIVE_TIMEOUT
Timeout for receiving HTTP data in milliseconds.

References ChunkedDecoder::feedToFile(), HTTP_RECEIVE_TIMEOUT, ChunkedDecoder::isComplete(), Tcp::recv(), and ChunkedDecoder::wasTruncated().

+ Here is the call graph for this function:

◆ receiveChunkedBodyToString()

bool HttpParser::receiveChunkedBodyToString ( Tcp socket,
const std::string &  leftover,
std::string &  out,
size_t  maxLength,
bool *  wasTruncated 
)
static

Definition at line 181 of file HttpParser.cpp.

182{
183 ChunkedDecoder decoder;
184 decoder.feed(leftover, maxLength);
185
186 char temp[1460];
187 while (!decoder.isComplete()) {
188 int n = socket.recv(temp, sizeof(temp), HTTP_RECEIVE_TIMEOUT);
189 if (n <= 0) {
190 printf("Chunked: recv() failed or EOF");
191 return false;
192 }
193 decoder.feed(std::string(temp, n), maxLength);
194 if (decoder.wasTruncated()) {
195 outBody = decoder.getDecoded();
196 if (wasTruncated) *wasTruncated = true;
197 return true;
198 }
199 }
200
201 outBody = decoder.getDecoded();
202 return true;
203}
void feed(const std::string &data, size_t maxLength=MAX_HTTP_BODY_LENGTH)
std::string getDecoded()

References ChunkedDecoder::feed(), ChunkedDecoder::getDecoded(), HTTP_RECEIVE_TIMEOUT, ChunkedDecoder::isComplete(), Tcp::recv(), and ChunkedDecoder::wasTruncated().

Referenced by receiveBody().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ receiveFixedLengthBodyToFile()

bool HttpParser::receiveFixedLengthBodyToFile ( Tcp socket,
const std::map< std::string, std::string > &  headers,
const std::string &  leftover,
std::function< bool(const char *, size_t)>  writeFn,
size_t  maxLength,
bool *  wasTruncated 
)
static

Definition at line 297 of file HttpParser.cpp.

304{
305 if (wasTruncated) *wasTruncated = false;
306
307 auto it = headers.find("content-length");
308 if (it == headers.end()) return false;
309
310 int contentLength = std::stoi(it->second);
311 size_t totalWritten = 0;
312
313 // Write leftover first
314 if (!leftover.empty()) {
315 size_t toWrite = std::min(leftover.size(), maxLength);
316 if (!writeFn(leftover.data(), toWrite)) return false;
317 totalWritten += toWrite;
318
319 if (leftover.size() > maxLength) {
320 if (wasTruncated) *wasTruncated = true;
321 return true;
322 }
323 }
324
325 char buffer[1460];
326 int idleCycles = 0;
327 while (totalWritten < (size_t)contentLength) {
328 size_t remaining = contentLength - totalWritten;
329 int toRead = std::min((int)sizeof(buffer), (int)remaining);
330 int n = socket.recv(buffer, toRead, HTTP_RECEIVE_TIMEOUT);
331
332 if (n <= 0) {
333 vTaskDelay(pdMS_TO_TICKS(10));
334 if (++idleCycles > 20) return false;
335 continue;
336 }
337
338 idleCycles = 0;
339
340 size_t available = maxLength - totalWritten;
341 size_t toWrite = std::min((size_t)n, available);
342 if (!writeFn(buffer, toWrite)) return false;
343 totalWritten += toWrite;
344
345 if (toWrite < (size_t)n) {
346 if (wasTruncated) *wasTruncated = true;
347 return true;
348 }
349 }
350
351 return true;
352}

References HTTP_RECEIVE_TIMEOUT, and Tcp::recv().

+ Here is the call graph for this function:

◆ receiveFixedLengthBodyToString()

bool HttpParser::receiveFixedLengthBodyToString ( Tcp socket,
const std::map< std::string, std::string > &  headers,
const std::string &  leftover,
std::string &  out,
size_t  maxLength,
bool *  wasTruncated 
)
static

Definition at line 205 of file HttpParser.cpp.

206{
207 int contentLength = std::stoi(headers.at("content-length"));
208 outBody = leftover;
209
210 if ((int)outBody.size() >= contentLength) {
211 outBody = outBody.substr(0, std::min((size_t)contentLength, maxLength));
212 if ((size_t)contentLength > maxLength && wasTruncated) *wasTruncated = true;
213 return true;
214 }
215
216 int attempts = 0;
217 int idleCycles = 0;
218 while ((int)outBody.size() < contentLength && attempts++ < 2000) {
219 char buffer[1460];
220 int toRead = std::min<int>(sizeof(buffer), contentLength - (int)outBody.size());
221 int n = socket.recv(buffer, toRead, HTTP_RECEIVE_TIMEOUT);
222
223 if (n <= 0) {
224 idleCycles++;
225 vTaskDelay(pdMS_TO_TICKS(10));
226 if (idleCycles > 20) return false;
227 continue;
228 }
229
230 idleCycles = 0;
231 if (outBody.size() + n > maxLength) {
232 outBody.append(buffer, maxLength - outBody.size());
233 if (wasTruncated) *wasTruncated = true;
234 return true;
235 }
236
237 outBody.append(buffer, n);
238 }
239
240 return ((int)outBody.size() == contentLength);
241}

References HTTP_RECEIVE_TIMEOUT, and Tcp::recv().

Referenced by receiveBody().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ receiveHeaderAndLeftover()

std::pair< std::string, std::string > HttpParser::receiveHeaderAndLeftover ( Tcp socket)
static

Receives HTTP headers and any leftover body data from a TCP socket.

Parameters
socketThe socket to receive from
outHeadersThe received headers
leftoverBodyThe leftover body after headers
Returns
true on success, false on failure
Parameters
socketThe TCP socket to read from.
Returns
A pair containing the full header text and any leftover body data. If an error occurs, returns an empty header and leftover. This function reads data from the socket until it finds the end of the headers (indicated by a double CRLF "\r\n\r\n"). It accumulates data in a buffer and returns the complete header text along with any leftover body data that may have been received after the headers. It handles cases where the header may be split across multiple TCP packets. If the end of the headers is not found, it continues to read from the socket until it is found or an error occurs.
Note
This function is blocking and will wait until the header is fully received. It is designed for use in an embedded system with FreeRTOS and lwIP.

Definition at line 111 of file HttpParser.cpp.

112{
113 std::string buffer;
114 char temp[1460];
115
116 while (true)
117 {
118 int n = socket.recv(temp, sizeof(temp), HTTP_RECEIVE_TIMEOUT);
119 if (n <= 0)
120 {
121 return {"", ""}; // signal error (empty header)
122 }
123
124 buffer.append(temp, n);
125 std::size_t headerEnd = buffer.find("\r\n\r\n");
126
127 if (headerEnd != std::string::npos)
128 {
129 std::string headerText = buffer.substr(0, headerEnd + 4);
130 std::string leftover = buffer.substr(headerEnd + 4);
131 return {headerText, leftover};
132 }
133
134 vTaskDelay(pdMS_TO_TICKS(1));
135 }
136}

References HTTP_RECEIVE_TIMEOUT, and Tcp::recv().

+ Here is the call graph for this function:

◆ receiveUnknownLengthBodyToString()

bool HttpParser::receiveUnknownLengthBodyToString ( Tcp socket,
const std::string &  leftover,
std::string &  out,
size_t  maxLength,
bool *  wasTruncated 
)
static

Definition at line 243 of file HttpParser.cpp.

244{
245 outBody = leftover;
246 char buffer[1460];
247 while (true) {
248 int n = socket.recv(buffer, sizeof(buffer), HTTP_RECEIVE_TIMEOUT);
249 if (n <= 0) break;
250
251 if (outBody.size() + n > maxLength) {
252 outBody.append(buffer, maxLength - outBody.size());
253 if (wasTruncated) *wasTruncated = true;
254 return true;
255 }
256
257 outBody.append(buffer, n);
258 }
259
260 return !outBody.empty();
261}

References HTTP_RECEIVE_TIMEOUT, and Tcp::recv().

Referenced by receiveBody().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

The documentation for this class was generated from the following files: