33int
HttpParser::parseStatusCode(const std::
string &statusLine)
35 std::istringstream stream(statusLine);
36 std::string httpVersion;
38 stream >> httpVersion >> code;
48 std::map<std::string, std::string> headers;
49 std::istringstream stream(rawHeaders);
52 while (std::getline(stream, line))
55 if (line ==
"\r" || line.empty())
60 auto colon = line.find(
':');
61 if (colon == std::string::npos || colon + 1 >= line.size())
66 std::string key = line.substr(0, colon);
67 std::string value = line.substr(colon + 1);
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());
76 std::transform(key.begin(), key.end(), key.begin(), ::tolower);
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)
83 value = value.substr(start, end - start + 1);
124 buffer.append(temp, n);
125 std::size_t headerEnd = buffer.find(
"\r\n\r\n");
127 if (headerEnd != std::string::npos)
129 std::string headerText = buffer.substr(0, headerEnd + 4);
130 std::string leftover = buffer.substr(headerEnd + 4);
131 return {headerText, leftover};
134 vTaskDelay(pdMS_TO_TICKS(1));
153 const std::map<std::string, std::string> &headers,
154 const std::string &leftoverBody,
155 std::string &outBody,
160 *wasTruncated =
false;
167 if (headers.count(
"content-length"))
177 auto it = headers.find(
"transfer-encoding");
178 return (it != headers.end() &&
toLower(it->second) ==
"chunked");
184 decoder.
feed(leftover, maxLength);
190 printf(
"Chunked: recv() failed or EOF");
193 decoder.
feed(std::string(temp, n), maxLength);
196 if (wasTruncated) *wasTruncated =
true;
207 int contentLength = std::stoi(headers.at(
"content-length"));
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;
218 while ((
int)outBody.size() < contentLength && attempts++ < 2000) {
220 int toRead = std::min<int>(
sizeof(buffer), contentLength - (
int)outBody.size());
225 vTaskDelay(pdMS_TO_TICKS(10));
226 if (idleCycles > 20)
return false;
231 if (outBody.size() + n > maxLength) {
232 outBody.append(buffer, maxLength - outBody.size());
233 if (wasTruncated) *wasTruncated =
true;
237 outBody.append(buffer, n);
240 return ((
int)outBody.size() == contentLength);
251 if (outBody.size() + n > maxLength) {
252 outBody.append(buffer, maxLength - outBody.size());
253 if (wasTruncated) *wasTruncated =
true;
257 outBody.append(buffer, n);
260 return !outBody.empty();
266 const std::string& leftover,
267 std::function<
bool(
const char*,
size_t)> writeFn,
272 decoder.
feedToFile(leftover, writeFn, maxLength);
278 printf(
"Chunked: recv() failed or EOF\n");
282 if (!decoder.
feedToFile(std::string(buffer, n), writeFn, maxLength)) {
283 printf(
"Chunked: writeFn failed\n");
288 if (wasTruncated) *wasTruncated =
true;
293 if (wasTruncated) *wasTruncated = decoder.
wasTruncated();
299 const std::map<std::string, std::string>& headers,
300 const std::string& leftover,
301 std::function<
bool(
const char*,
size_t)> writeFn,
305 if (wasTruncated) *wasTruncated =
false;
307 auto it = headers.find(
"content-length");
308 if (it == headers.end())
return false;
310 int contentLength = std::stoi(it->second);
311 size_t totalWritten = 0;
314 if (!leftover.empty()) {
315 size_t toWrite = std::min(leftover.size(), maxLength);
316 if (!writeFn(leftover.data(), toWrite))
return false;
317 totalWritten += toWrite;
319 if (leftover.size() > maxLength) {
320 if (wasTruncated) *wasTruncated =
true;
327 while (totalWritten < (
size_t)contentLength) {
328 size_t remaining = contentLength - totalWritten;
329 int toRead = std::min((
int)
sizeof(buffer), (
int)remaining);
333 vTaskDelay(pdMS_TO_TICKS(10));
334 if (++idleCycles > 20)
return false;
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;
345 if (toWrite < (
size_t)n) {
346 if (wasTruncated) *wasTruncated =
true;
Macro-based debug trace system with optional SD file logging.
#define TRACE_INIT(MODULE_NAME)
Declare trace usage in a source file for a given module.
void feed(const std::string &data, size_t maxLength=MAX_HTTP_BODY_LENGTH)
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.
static std::pair< std::string, std::string > receiveHeaderAndLeftover(Tcp &socket)
Receive raw headers from the socket until \r \r .
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 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 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)
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 std::map< std::string, std::string > parseHeaders(const std::string &rawHeaders)
Parses the HTTP headers from a raw header string.
static bool receiveChunkedBodyToFile(Tcp &socket, const std::string &leftover, std::function< bool(const char *, size_t)> writeFn, size_t maxLength, bool *wasTruncated)
General-purpose TCP socket wrapper with optional TLS support via mbedTLS (altcp).
int recv(char *buffer, size_t size, uint32_t timeout_ms)
Receive data from the connection.
Delegates to user or system configuration.
#define HTTP_RECEIVE_TIMEOUT
Timeout for receiving HTTP data in milliseconds.
std::string toLower(std::string str)
System utilities for diagnostics, memory, stack usage, and tracing.