mirror of
https://github.com/esphome/esphome.git
synced 2026-09-22 04:28:43 +00:00
unify, make consistant
This commit is contained in:
@@ -79,6 +79,9 @@ inline bool is_redirect(int const status) {
|
||||
*/
|
||||
inline bool is_success(int const status) { return status >= HTTP_STATUS_OK && status < HTTP_STATUS_MULTIPLE_CHOICES; }
|
||||
|
||||
/// Error code returned by HttpContainer::read() when connection closed prematurely
|
||||
static constexpr int HTTP_ERROR_CONNECTION_CLOSED = -1;
|
||||
|
||||
/// Status of a read operation
|
||||
enum class HttpReadStatus : uint8_t {
|
||||
OK, ///< Read completed successfully
|
||||
@@ -131,6 +134,27 @@ class HttpContainer : public Parented<HttpRequestComponent> {
|
||||
int status_code;
|
||||
uint32_t duration_ms;
|
||||
|
||||
/**
|
||||
* @brief Read data from the HTTP response body.
|
||||
*
|
||||
* This is a non-blocking read operation. The semantics are consistent across
|
||||
* all platforms (Arduino and ESP-IDF):
|
||||
*
|
||||
* @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)
|
||||
* - 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
virtual int read(uint8_t *buf, size_t max_len) = 0;
|
||||
virtual void end() = 0;
|
||||
|
||||
|
||||
@@ -139,6 +139,21 @@ std::shared_ptr<HttpContainer> HttpRequestArduino::perform(const std::string &ur
|
||||
return container;
|
||||
}
|
||||
|
||||
// Arduino HTTP read implementation
|
||||
//
|
||||
// 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".
|
||||
//
|
||||
// WiFiClient behavior:
|
||||
// available() > 0: data ready to read
|
||||
// available() == 0 && connected(): no data yet, still connected
|
||||
// available() == 0 && !connected(): connection closed
|
||||
//
|
||||
// We normalize these to the HttpContainer::read() contract:
|
||||
// > 0: bytes read
|
||||
// 0: no data yet, retry
|
||||
// < 0: error (connection closed prematurely, or stream vanished)
|
||||
int HttpContainerArduino::read(uint8_t *buf, size_t max_len) {
|
||||
const uint32_t start = millis();
|
||||
watchdog::WatchdogManager wdm(this->parent_->get_watchdog_timeout());
|
||||
@@ -154,7 +169,15 @@ int HttpContainerArduino::read(uint8_t *buf, size_t max_len) {
|
||||
|
||||
if (bufsize == 0) {
|
||||
this->duration_ms += (millis() - start);
|
||||
return 0;
|
||||
// Check if we've read all expected content
|
||||
if (this->bytes_read_ >= this->content_length) {
|
||||
return 0; // All content read successfully
|
||||
}
|
||||
// No data available - check if connection is still open
|
||||
if (!stream_ptr->connected()) {
|
||||
return HTTP_ERROR_CONNECTION_CLOSED; // Connection closed prematurely
|
||||
}
|
||||
return 0; // No data yet, caller should retry
|
||||
}
|
||||
|
||||
App.feed_wdt();
|
||||
|
||||
@@ -210,6 +210,19 @@ std::shared_ptr<HttpContainer> HttpRequestIDF::perform(const std::string &url, c
|
||||
return container;
|
||||
}
|
||||
|
||||
// ESP-IDF HTTP read implementation
|
||||
//
|
||||
// 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)
|
||||
// -ESP_ERR_HTTP_EAGAIN (0x7007): no data available yet (would block)
|
||||
// other negative: error
|
||||
//
|
||||
// We normalize these to the HttpContainer::read() contract:
|
||||
// > 0: bytes read
|
||||
// 0: no data yet, retry
|
||||
// < 0: error (connection closed prematurely, or other error)
|
||||
int HttpContainerIDF::read(uint8_t *buf, size_t max_len) {
|
||||
const uint32_t start = millis();
|
||||
watchdog::WatchdogManager wdm(this->parent_->get_watchdog_timeout());
|
||||
@@ -217,7 +230,7 @@ int HttpContainerIDF::read(uint8_t *buf, size_t max_len) {
|
||||
// Check if we've already read all expected content
|
||||
if (this->bytes_read_ >= this->content_length) {
|
||||
this->duration_ms += (millis() - start);
|
||||
return 0; // All content read
|
||||
return 0; // All content read successfully
|
||||
}
|
||||
|
||||
this->feed_wdt();
|
||||
@@ -231,16 +244,17 @@ int HttpContainerIDF::read(uint8_t *buf, size_t max_len) {
|
||||
return read_len_or_error;
|
||||
}
|
||||
|
||||
// read_len_or_error < 0: check for EAGAIN (no data available in non-blocking mode)
|
||||
// ESP_ERR_HTTP_EAGAIN is returned as a negative error code
|
||||
// No data available yet in non-blocking mode
|
||||
// ESP_ERR_HTTP_EAGAIN (0x7007) is returned as negative
|
||||
if (read_len_or_error == -ESP_ERR_HTTP_EAGAIN) {
|
||||
return 0; // No data available yet, caller should retry
|
||||
return 0; // No data yet, caller should retry
|
||||
}
|
||||
|
||||
// Connection closed by server
|
||||
if (read_len_or_error == 0) {
|
||||
// Connection closed, but we haven't read all content yet (early check handles success case)
|
||||
// This is a premature close - return error
|
||||
return -1;
|
||||
// We haven't read all content yet (early check handles success case)
|
||||
// Return error so caller exits immediately instead of waiting for timeout
|
||||
return HTTP_ERROR_CONNECTION_CLOSED;
|
||||
}
|
||||
|
||||
// Other negative value - real error, return the actual error code for debugging
|
||||
|
||||
Reference in New Issue
Block a user