document document document

This commit is contained in:
J. Nick Koston
2026-01-21 12:50:06 -10:00
parent 5efe5ff9fd
commit d56554100b
4 changed files with 51 additions and 16 deletions
@@ -185,6 +185,8 @@ bool Esp32HostedUpdate::fetch_manifest_() {
}
// Read manifest JSON into string (manifest is small, ~1KB max)
// NOTE: HttpContainer::read() has non-BSD socket semantics - see http_request.h
// Use http_read_loop_result() helper instead of checking return values directly
std::string json_str;
json_str.reserve(container->content_length);
uint8_t buf[256];
+38 -9
View File
@@ -79,7 +79,35 @@ inline bool is_redirect(int const status) {
*/
inline bool is_success(int const status) { return status >= HTTP_STATUS_OK && status < HTTP_STATUS_MULTIPLE_CHOICES; }
/*
* HTTP Container Read Semantics
* =============================
*
* IMPORTANT: These semantics differ from standard BSD sockets!
*
* BSD socket read() returns:
* > 0: bytes read
* == 0: connection closed (EOF)
* < 0: error (check errno)
*
* HttpContainer::read() returns:
* > 0: bytes read successfully
* == 0: no data available yet (non-blocking, caller should RETRY)
* < 0: error or connection closed (caller should EXIT)
* HTTP_ERROR_CONNECTION_CLOSED (-1) = connection closed prematurely
* other negative values = platform-specific errors
*
* This non-blocking design allows consistent behavior across:
* - ESP-IDF (async mode with EAGAIN handling)
* - Arduino (available() + connected() checks)
*
* Use the helper functions below instead of checking return values directly:
* - http_read_loop_result(): for manual loops with per-chunk processing
* - http_read_fully(): for simple "read N bytes into buffer" operations
*/
/// Error code returned by HttpContainer::read() when connection closed prematurely
/// NOTE: Unlike BSD sockets where 0 means EOF, here 0 means "no data yet, retry"
static constexpr int HTTP_ERROR_CONNECTION_CLOSED = -1;
/// Status of a read operation
@@ -135,25 +163,26 @@ class HttpContainer : public Parented<HttpRequestComponent> {
uint32_t duration_ms;
/**
* @brief Read data from the HTTP response body.
* @brief Read data from the HTTP response body (non-blocking).
*
* This is a non-blocking read operation. The semantics are consistent across
* all platforms (Arduino and ESP-IDF):
* WARNING: These semantics differ from BSD sockets!
* BSD sockets: 0 = EOF (connection closed)
* This method: 0 = no data yet (retry), negative = error/closed
*
* @param buf Buffer to read data into
* @param max_len Maximum number of bytes to read
* @return
* - > 0: Number of bytes read successfully
* - 0: No data available yet, caller should retry (data may still be arriving)
* - 0: No data available yet (NOT EOF!), caller should retry
* - HTTP_ERROR_CONNECTION_CLOSED (-1): Connection closed prematurely
* - < -1: Other error (platform-specific error code)
*
* The caller should use get_bytes_read() and content_length to track progress.
* When get_bytes_read() >= content_length, all expected data has been received.
* Use get_bytes_read() and content_length to track progress.
* When get_bytes_read() >= content_length, all data has been received.
*
* For non-blocking read loops, use http_read_loop_result() helper which handles
* timeout tracking and converts return values to HttpReadLoopResult enum.
* For simple buffer reads, use http_read_fully() helper.
* IMPORTANT: Do not use raw return values directly. Use these helpers:
* - http_read_loop_result(): for loops with per-chunk processing
* - http_read_fully(): for simple "read N bytes" operations
*/
virtual int read(uint8_t *buf, size_t max_len) = 0;
virtual void end() = 0;
@@ -141,6 +141,8 @@ std::shared_ptr<HttpContainer> HttpRequestArduino::perform(const std::string &ur
// Arduino HTTP read implementation
//
// WARNING: Return values differ from BSD sockets! See http_request.h for full documentation.
//
// Arduino's WiFiClient is inherently non-blocking - available() returns 0 when
// no data is ready. We use connected() to distinguish "no data yet" from
// "connection closed".
@@ -150,10 +152,10 @@ std::shared_ptr<HttpContainer> HttpRequestArduino::perform(const std::string &ur
// available() == 0 && connected(): no data yet, still connected
// available() == 0 && !connected(): connection closed
//
// We normalize these to the HttpContainer::read() contract:
// We normalize to HttpContainer::read() contract (NOT BSD socket semantics!):
// > 0: bytes read
// 0: no data yet, retry
// < 0: error (connection closed prematurely, or stream vanished)
// 0: no data yet, retry <-- NOTE: 0 means retry, NOT EOF!
// < 0: error/connection closed <-- connection closed returns -1, not 0
int HttpContainerArduino::read(uint8_t *buf, size_t max_len) {
const uint32_t start = millis();
watchdog::WatchdogManager wdm(this->parent_->get_watchdog_timeout());
@@ -212,17 +212,19 @@ std::shared_ptr<HttpContainer> HttpRequestIDF::perform(const std::string &url, c
// ESP-IDF HTTP read implementation
//
// WARNING: Return values differ from BSD sockets! See http_request.h for full documentation.
//
// Uses non-blocking mode (config.is_async = true) for consistent behavior with Arduino.
// esp_http_client_read() in async mode returns:
// > 0: bytes read
// 0: connection closed (end of stream)
// 0: connection closed (end of stream) <-- BSD socket EOF semantics
// -ESP_ERR_HTTP_EAGAIN (0x7007): no data available yet (would block)
// other negative: error
//
// We normalize these to the HttpContainer::read() contract:
// We normalize to HttpContainer::read() contract (NOT BSD socket semantics!):
// > 0: bytes read
// 0: no data yet, retry
// < 0: error (connection closed prematurely, or other error)
// 0: no data yet, retry <-- NOTE: 0 means retry, NOT EOF!
// < 0: error/connection closed <-- connection closed returns -1, not 0
int HttpContainerIDF::read(uint8_t *buf, size_t max_len) {
const uint32_t start = millis();
watchdog::WatchdogManager wdm(this->parent_->get_watchdog_timeout());