diff --git a/components/esp_http_client/esp_http_client.c b/components/esp_http_client/esp_http_client.c index a5e0c7f6d1..7e278b6f73 100644 --- a/components/esp_http_client/esp_http_client.c +++ b/components/esp_http_client/esp_http_client.c @@ -88,17 +88,7 @@ typedef struct { int max_store_header_size; } connection_info_t; -typedef enum { - HTTP_STATE_UNINIT = 0, - HTTP_STATE_INIT, - HTTP_STATE_CONNECTED, - HTTP_STATE_REQ_COMPLETE_HEADER, - HTTP_STATE_REQ_COMPLETE_DATA, - HTTP_STATE_RES_COMPLETE_HEADER, - HTTP_STATE_RES_ON_DATA_START, - HTTP_STATE_RES_COMPLETE_DATA, - HTTP_STATE_CLOSE -} esp_http_state_t; + typedef enum { SESSION_TICKET_UNUSED = 0, @@ -1429,6 +1419,8 @@ esp_err_t esp_http_client_perform(esp_http_client_handle_t client) then the esp_http_client_perform() API will return ESP_ERR_HTTP_EAGAIN error. The user may call esp_http_client_perform API again, and for this reason, we maintain the states */ case HTTP_STATE_INIT: + /* falls through */ + case HTTP_STATE_CONNECTING: if ((err = esp_http_client_connect(client)) != ESP_OK) { if (client->is_async && err == ESP_ERR_HTTP_CONNECTING) { return ESP_ERR_HTTP_EAGAIN; @@ -1576,6 +1568,7 @@ static esp_err_t esp_http_client_connect(esp_http_client_handle_t client) esp_http_client_close(client); return err; } + client->state = HTTP_STATE_CONNECTING; if (client->state < HTTP_STATE_CONNECTED) { #ifdef CONFIG_ESP_HTTP_CLIENT_ENABLE_CUSTOM_TRANSPORT @@ -1815,7 +1808,7 @@ int esp_http_client_write(esp_http_client_handle_t client, const char *buffer, i esp_err_t esp_http_client_close(esp_http_client_handle_t client) { - if (client->state >= HTTP_STATE_INIT) { + if (client->state > HTTP_STATE_INIT) { http_dispatch_event(client, HTTP_EVENT_DISCONNECTED, esp_transport_get_error_handle(client->transport), 0); http_dispatch_event_to_event_loop(HTTP_EVENT_DISCONNECTED, &client, sizeof(esp_http_client_handle_t)); client->state = HTTP_STATE_INIT; @@ -2025,3 +2018,11 @@ esp_err_t esp_http_client_get_chunk_length(esp_http_client_handle_t client, int } return ESP_OK; } + +esp_http_state_t esp_http_client_get_state(esp_http_client_handle_t client) +{ + if (client == NULL) { + return HTTP_STATE_UNINIT; + } + return client->state; +} diff --git a/components/esp_http_client/include/esp_http_client.h b/components/esp_http_client/include/esp_http_client.h index b707a22c03..a1b887b71c 100644 --- a/components/esp_http_client/include/esp_http_client.h +++ b/components/esp_http_client/include/esp_http_client.h @@ -145,6 +145,22 @@ typedef enum { HTTP_TLS_DYN_BUF_STRATEGY_MAX, /*!< to indicate max */ } esp_http_client_tls_dyn_buf_strategy_t; +/** + * @brief HTTP Client states + */ +typedef enum { + HTTP_STATE_UNINIT = 0, /*!< HTTP client uninitialized */ + HTTP_STATE_INIT, /*!< HTTP client initialized */ + HTTP_STATE_CONNECTING, /*!< HTTP client connecting to server */ + HTTP_STATE_CONNECTED, /*!< HTTP client connected to server */ + HTTP_STATE_REQ_COMPLETE_HEADER, /*!< HTTP request headers sent */ + HTTP_STATE_REQ_COMPLETE_DATA, /*!< HTTP request data sent */ + HTTP_STATE_RES_COMPLETE_HEADER, /*!< HTTP response headers received */ + HTTP_STATE_RES_ON_DATA_START, /*!< HTTP response data started */ + HTTP_STATE_RES_COMPLETE_DATA, /*!< HTTP response data completed */ + HTTP_STATE_CLOSE /*!< HTTP client connection closed */ +} esp_http_state_t; + /** * @brief HTTP configuration */ @@ -822,6 +838,15 @@ esp_err_t esp_http_client_get_url(esp_http_client_handle_t client, char *url, co */ esp_err_t esp_http_client_get_chunk_length(esp_http_client_handle_t client, int *len); +/** + * @brief Get the current state of the HTTP client + * + * @param[in] client The HTTP client handle + * + * @return Current state of the HTTP client + */ +esp_http_state_t esp_http_client_get_state(esp_http_client_handle_t client); + #ifdef __cplusplus } #endif diff --git a/components/esp_http_client/test_apps/main/test_http_client.c b/components/esp_http_client/test_apps/main/test_http_client.c index 309e948b7f..2c9d2128b0 100644 --- a/components/esp_http_client/test_apps/main/test_http_client.c +++ b/components/esp_http_client/test_apps/main/test_http_client.c @@ -186,6 +186,41 @@ TEST_CASE("esp_http_client_set_header() should not return error if header value esp_http_client_cleanup(client); } +static int disconnect_event_count = 0; + +static esp_err_t disconnect_event_handler(esp_http_client_event_t *evt) +{ + if (evt->event_id == HTTP_EVENT_DISCONNECTED) { + disconnect_event_count++; + } + return ESP_OK; +} + +TEST_CASE("esp_http_client_close() and cleanup() should not dispatch duplicate disconnect events", "[esp_http_client]") +{ + esp_http_client_config_t config = { + .url = "http://httpbin.org/get", + .event_handler = disconnect_event_handler, + }; + + // Reset event counter + disconnect_event_count = 0; + + esp_http_client_handle_t client = esp_http_client_init(&config); + TEST_ASSERT_NOT_NULL(client); + + // Close the client first + esp_err_t err = esp_http_client_close(client); + TEST_ASSERT_EQUAL(ESP_OK, err); + + // Then cleanup - this should not dispatch another disconnect event + err = esp_http_client_cleanup(client); + TEST_ASSERT_EQUAL(ESP_OK, err); + + // Verify that only one disconnect event was dispatched (or none if client was never connected) + TEST_ASSERT_LESS_OR_EQUAL(1, disconnect_event_count); +} + void app_main(void) { unity_run_menu();