Merge branch 'feature/move_partial_download_code_under_config' into 'master'

feat(esp_http_client): move partial download related code under config

Closes IDF-13464

See merge request espressif/esp-idf!40270
This commit is contained in:
Aditya Patwardhan
2025-08-11 11:09:49 +05:30
11 changed files with 71 additions and 7 deletions

View File

@@ -29,6 +29,12 @@ menu "ESP HTTP client"
This option will enable injection of a custom tcp_transport handle, so the http operation This option will enable injection of a custom tcp_transport handle, so the http operation
will be performed on top of the user defined transport abstraction (if configured) will be performed on top of the user defined transport abstraction (if configured)
config ESP_HTTP_CLIENT_ENABLE_GET_CONTENT_RANGE
bool "Enable content range functionality"
default n
help
This enables use of content range header in esp_http_client component.
config ESP_HTTP_CLIENT_EVENT_POST_TIMEOUT config ESP_HTTP_CLIENT_EVENT_POST_TIMEOUT
int "Time in millisecond to wait for posting event" int "Time in millisecond to wait for posting event"
default 2000 default 2000

View File

@@ -67,7 +67,9 @@ typedef struct {
int64_t data_process; /*!< data processed */ int64_t data_process; /*!< data processed */
int method; /*!< http method */ int method; /*!< http method */
bool is_chunked; bool is_chunked;
#if CONFIG_ESP_HTTP_CLIENT_ENABLE_GET_CONTENT_RANGE
int64_t content_range; /*!< content range */ int64_t content_range; /*!< content range */
#endif
} esp_http_data_t; } esp_http_data_t;
typedef struct { typedef struct {
@@ -279,6 +281,7 @@ static int http_on_header_value(http_parser *parser, const char *at, size_t leng
if (client->current_header_key == NULL) { if (client->current_header_key == NULL) {
return 0; return 0;
} }
#if CONFIG_ESP_HTTP_CLIENT_ENABLE_GET_CONTENT_RANGE
if (strcasecmp(client->current_header_key, "Content-Range") == 0) { if (strcasecmp(client->current_header_key, "Content-Range") == 0) {
HTTP_RET_ON_FALSE_DBG(http_utils_append_string(&client->current_header_value, at, length), -1, TAG, "Failed to append string"); HTTP_RET_ON_FALSE_DBG(http_utils_append_string(&client->current_header_value, at, length), -1, TAG, "Failed to append string");
@@ -301,7 +304,9 @@ static int http_on_header_value(http_parser *parser, const char *at, size_t leng
} else { } else {
ESP_LOGE(TAG, "Invalid Content-Range format (missing '/')"); ESP_LOGE(TAG, "Invalid Content-Range format (missing '/')");
} }
} else if (strcasecmp(client->current_header_key, "Location") == 0) { } else
#endif
if (strcasecmp(client->current_header_key, "Location") == 0) {
HTTP_RET_ON_FALSE_DBG(http_utils_append_string(&client->location, at, length), -1, TAG, "Failed to append string"); HTTP_RET_ON_FALSE_DBG(http_utils_append_string(&client->location, at, length), -1, TAG, "Failed to append string");
} else if (strcasecmp(client->current_header_key, "Transfer-Encoding") == 0 } else if (strcasecmp(client->current_header_key, "Transfer-Encoding") == 0
&& memcmp(at, "chunked", length) == 0) { && memcmp(at, "chunked", length) == 0) {
@@ -1859,10 +1864,12 @@ int64_t esp_http_client_get_content_length(esp_http_client_handle_t client)
return client->response->content_length; return client->response->content_length;
} }
#if CONFIG_ESP_HTTP_CLIENT_ENABLE_GET_CONTENT_RANGE
int64_t esp_http_client_get_content_range(esp_http_client_handle_t client) int64_t esp_http_client_get_content_range(esp_http_client_handle_t client)
{ {
return client->response->content_range; return client->response->content_range;
} }
#endif
bool esp_http_client_is_chunked_response(esp_http_client_handle_t client) bool esp_http_client_is_chunked_response(esp_http_client_handle_t client)
{ {

View File

@@ -644,6 +644,7 @@ int esp_http_client_get_status_code(esp_http_client_handle_t client);
*/ */
int64_t esp_http_client_get_content_length(esp_http_client_handle_t client); int64_t esp_http_client_get_content_length(esp_http_client_handle_t client);
#if CONFIG_ESP_HTTP_CLIENT_ENABLE_GET_CONTENT_RANGE || __DOXYGEN__
/** /**
* @brief Get http response content range (from header Content-Range) * @brief Get http response content range (from header Content-Range)
* The returned value is valid only if this function is invoked after * The returned value is valid only if this function is invoked after
@@ -656,6 +657,7 @@ int64_t esp_http_client_get_content_length(esp_http_client_handle_t client);
* - Content-Range value as bytes * - Content-Range value as bytes
*/ */
int64_t esp_http_client_get_content_range(esp_http_client_handle_t client); int64_t esp_http_client_get_content_range(esp_http_client_handle_t client);
#endif
/** /**
* @brief Close http connection, still kept all http request resources * @brief Close http connection, still kept all http request resources

View File

@@ -25,4 +25,12 @@ menu "ESP HTTPS OTA"
This config option helps in setting the time in millisecond to wait for event to be posted to the This config option helps in setting the time in millisecond to wait for event to be posted to the
system default event loop. Set it to -1 if you need to set timeout to portMAX_DELAY. system default event loop. Set it to -1 if you need to set timeout to portMAX_DELAY.
config ESP_HTTPS_OTA_ENABLE_PARTIAL_DOWNLOAD
bool "Enable partial HTTP download for OTA"
default n
select ESP_HTTP_CLIENT_ENABLE_GET_CONTENT_RANGE
help
This enables use of range header in esp_https_ota component.
The firmware image will be downloaded over multiple HTTP requests.
endmenu endmenu

View File

@@ -62,8 +62,10 @@ typedef struct {
const esp_http_client_config_t *http_config; /*!< ESP HTTP client configuration */ const esp_http_client_config_t *http_config; /*!< ESP HTTP client configuration */
http_client_init_cb_t http_client_init_cb; /*!< Callback after ESP HTTP client is initialised */ http_client_init_cb_t http_client_init_cb; /*!< Callback after ESP HTTP client is initialised */
bool bulk_flash_erase; /*!< Erase entire flash partition during initialization. By default flash partition is erased during write operation and in chunk of 4K sector size */ bool bulk_flash_erase; /*!< Erase entire flash partition during initialization. By default flash partition is erased during write operation and in chunk of 4K sector size */
#if CONFIG_ESP_HTTPS_OTA_ENABLE_PARTIAL_DOWNLOAD || __DOXYGEN__
bool partial_http_download; /*!< Enable Firmware image to be downloaded over multiple HTTP requests */ bool partial_http_download; /*!< Enable Firmware image to be downloaded over multiple HTTP requests */
int max_http_request_size; /*!< Maximum request size for partial HTTP download */ int max_http_request_size; /*!< Maximum request size for partial HTTP download */
#endif
uint32_t buffer_caps; /*!< The memory capability to use when allocating the buffer for OTA update. Default capability is MALLOC_CAP_DEFAULT */ uint32_t buffer_caps; /*!< The memory capability to use when allocating the buffer for OTA update. Default capability is MALLOC_CAP_DEFAULT */
bool ota_resumption; /*!< Enable resumption in downloading of OTA image between reboots */ bool ota_resumption; /*!< Enable resumption in downloading of OTA image between reboots */
size_t ota_image_bytes_written; /*!< Number of OTA image bytes written to flash so far, updated by the application when OTA data is written successfully in the target OTA partition. */ size_t ota_image_bytes_written; /*!< Number of OTA image bytes written to flash so far, updated by the application when OTA data is written successfully in the target OTA partition. */

View File

@@ -52,10 +52,12 @@ struct esp_https_ota_handle {
size_t ota_upgrade_buf_size; size_t ota_upgrade_buf_size;
int binary_file_len; int binary_file_len;
int image_length; int image_length;
#if CONFIG_ESP_HTTPS_OTA_ENABLE_PARTIAL_DOWNLOAD
int max_http_request_size; int max_http_request_size;
bool partial_http_download;
#endif
esp_https_ota_state state; esp_https_ota_state state;
bool bulk_flash_erase; bool bulk_flash_erase;
bool partial_http_download;
int max_authorization_retries; int max_authorization_retries;
#if CONFIG_ESP_HTTPS_OTA_DECRYPT_CB #if CONFIG_ESP_HTTPS_OTA_DECRYPT_CB
decrypt_cb_t decrypt_cb; decrypt_cb_t decrypt_cb;
@@ -316,8 +318,10 @@ esp_err_t esp_https_ota_begin(const esp_https_ota_config_t *ota_config, esp_http
return ESP_ERR_NO_MEM; return ESP_ERR_NO_MEM;
} }
#if CONFIG_ESP_HTTPS_OTA_ENABLE_PARTIAL_DOWNLOAD
https_ota_handle->partial_http_download = ota_config->partial_http_download; https_ota_handle->partial_http_download = ota_config->partial_http_download;
https_ota_handle->max_http_request_size = (ota_config->max_http_request_size == 0) ? DEFAULT_REQUEST_SIZE : ota_config->max_http_request_size; https_ota_handle->max_http_request_size = (ota_config->max_http_request_size == 0) ? DEFAULT_REQUEST_SIZE : ota_config->max_http_request_size;
#endif
https_ota_handle->max_authorization_retries = ota_config->http_config->max_authorization_retries; https_ota_handle->max_authorization_retries = ota_config->http_config->max_authorization_retries;
if (https_ota_handle->max_authorization_retries == 0) { if (https_ota_handle->max_authorization_retries == 0) {
@@ -356,13 +360,18 @@ esp_err_t esp_https_ota_begin(const esp_https_ota_config_t *ota_config, esp_http
* Partial cases ('from-to') are handled separately below based on the remaining data to * Partial cases ('from-to') are handled separately below based on the remaining data to
* be downloaded and the max_http_request_size. * be downloaded and the max_http_request_size.
*/ */
if (https_ota_handle->binary_file_len > 0 && !https_ota_handle->partial_http_download) { if (https_ota_handle->binary_file_len > 0
#if CONFIG_ESP_HTTPS_OTA_ENABLE_PARTIAL_DOWNLOAD
&& !https_ota_handle->partial_http_download
#endif
) {
char *header_val = NULL; char *header_val = NULL;
asprintf(&header_val, "bytes=%d-", https_ota_handle->binary_file_len); asprintf(&header_val, "bytes=%d-", https_ota_handle->binary_file_len);
esp_http_client_set_header(https_ota_handle->http_client, "Range", header_val); esp_http_client_set_header(https_ota_handle->http_client, "Range", header_val);
free(header_val); free(header_val);
} }
#if CONFIG_ESP_HTTPS_OTA_ENABLE_PARTIAL_DOWNLOAD
if (https_ota_handle->partial_http_download) { if (https_ota_handle->partial_http_download) {
esp_http_client_set_method(https_ota_handle->http_client, HTTP_METHOD_HEAD); esp_http_client_set_method(https_ota_handle->http_client, HTTP_METHOD_HEAD);
@@ -435,6 +444,7 @@ esp_err_t esp_https_ota_begin(const esp_https_ota_config_t *ota_config, esp_http
} }
esp_http_client_set_method(https_ota_handle->http_client, HTTP_METHOD_GET); esp_http_client_set_method(https_ota_handle->http_client, HTTP_METHOD_GET);
} }
#endif
err = _http_connect(https_ota_handle); err = _http_connect(https_ota_handle);
if (err == ESP_ERR_HTTP_RANGE_NOT_SATISFIABLE && https_ota_handle->binary_file_len > 0) { if (err == ESP_ERR_HTTP_RANGE_NOT_SATISFIABLE && https_ota_handle->binary_file_len > 0) {
@@ -445,6 +455,7 @@ esp_err_t esp_https_ota_begin(const esp_https_ota_config_t *ota_config, esp_http
// If range in request header is not satisfiable, restart download from beginning // If range in request header is not satisfiable, restart download from beginning
esp_http_client_delete_header(https_ota_handle->http_client, "Range"); esp_http_client_delete_header(https_ota_handle->http_client, "Range");
#if CONFIG_ESP_HTTPS_OTA_ENABLE_PARTIAL_DOWNLOAD
if (https_ota_handle->partial_http_download && https_ota_handle->image_length > https_ota_handle->max_http_request_size) { if (https_ota_handle->partial_http_download && https_ota_handle->image_length > https_ota_handle->max_http_request_size) {
char *header_val = NULL; char *header_val = NULL;
asprintf(&header_val, "bytes=0-%d", https_ota_handle->max_http_request_size - 1); asprintf(&header_val, "bytes=0-%d", https_ota_handle->max_http_request_size - 1);
@@ -456,6 +467,7 @@ esp_err_t esp_https_ota_begin(const esp_https_ota_config_t *ota_config, esp_http
esp_http_client_set_header(https_ota_handle->http_client, "Range", header_val); esp_http_client_set_header(https_ota_handle->http_client, "Range", header_val);
free(header_val); free(header_val);
} }
#endif
err = _http_connect(https_ota_handle); err = _http_connect(https_ota_handle);
} }
@@ -468,7 +480,10 @@ esp_err_t esp_https_ota_begin(const esp_https_ota_config_t *ota_config, esp_http
esp_https_ota_dispatch_event(ESP_HTTPS_OTA_CONNECTED, NULL, 0); esp_https_ota_dispatch_event(ESP_HTTPS_OTA_CONNECTED, NULL, 0);
} }
if (!https_ota_handle->partial_http_download) { #if CONFIG_ESP_HTTPS_OTA_ENABLE_PARTIAL_DOWNLOAD
if (!https_ota_handle->partial_http_download)
#endif
{
https_ota_handle->image_length = esp_http_client_get_content_length(https_ota_handle->http_client); https_ota_handle->image_length = esp_http_client_get_content_length(https_ota_handle->http_client);
#if CONFIG_ESP_HTTPS_OTA_DECRYPT_CB #if CONFIG_ESP_HTTPS_OTA_DECRYPT_CB
/* In case of pre ecnrypted OTA, actual image size of OTA binary includes header size /* In case of pre ecnrypted OTA, actual image size of OTA binary includes header size
@@ -783,7 +798,10 @@ esp_err_t esp_https_ota_perform(esp_https_ota_handle_t https_ota_handle)
ESP_LOGE(TAG, "data read %d, errno %d", data_read, errno); ESP_LOGE(TAG, "data read %d, errno %d", data_read, errno);
return ESP_FAIL; return ESP_FAIL;
} }
if (!handle->partial_http_download || (handle->partial_http_download && handle->image_length == handle->binary_file_len)) { #if CONFIG_ESP_HTTPS_OTA_ENABLE_PARTIAL_DOWNLOAD
if (!handle->partial_http_download || (handle->partial_http_download && handle->image_length == handle->binary_file_len))
#endif
{
handle->state = ESP_HTTPS_OTA_SUCCESS; handle->state = ESP_HTTPS_OTA_SUCCESS;
} }
break; break;
@@ -792,6 +810,7 @@ esp_err_t esp_https_ota_perform(esp_https_ota_handle_t https_ota_handle)
return ESP_FAIL; return ESP_FAIL;
break; break;
} }
#if CONFIG_ESP_HTTPS_OTA_ENABLE_PARTIAL_DOWNLOAD
if (handle->partial_http_download) { if (handle->partial_http_download) {
if (handle->state == ESP_HTTPS_OTA_IN_PROGRESS && handle->image_length > handle->binary_file_len) { if (handle->state == ESP_HTTPS_OTA_IN_PROGRESS && handle->image_length > handle->binary_file_len) {
esp_http_client_close(handle->http_client); esp_http_client_close(handle->http_client);
@@ -820,6 +839,7 @@ esp_err_t esp_https_ota_perform(esp_https_ota_handle_t https_ota_handle)
return ESP_ERR_HTTPS_OTA_IN_PROGRESS; return ESP_ERR_HTTPS_OTA_IN_PROGRESS;
} }
} }
#endif
return ESP_OK; return ESP_OK;
} }
@@ -827,9 +847,12 @@ bool esp_https_ota_is_complete_data_received(esp_https_ota_handle_t https_ota_ha
{ {
bool ret = false; bool ret = false;
esp_https_ota_t *handle = (esp_https_ota_t *)https_ota_handle; esp_https_ota_t *handle = (esp_https_ota_t *)https_ota_handle;
#if CONFIG_ESP_HTTPS_OTA_ENABLE_PARTIAL_DOWNLOAD
if (handle->partial_http_download) { if (handle->partial_http_download) {
ret = (handle->image_length == handle->binary_file_len); ret = (handle->image_length == handle->binary_file_len);
} else { } else
#endif
{
ret = esp_http_client_is_complete_data_received(handle->http_client); ret = esp_http_client_is_complete_data_received(handle->http_client);
} }
return ret; return ret;

View File

@@ -41,7 +41,13 @@ Please refer to :ref:`ESP-TLS: TLS Server Verification <esp_tls_server_verificat
Partial Image Download over HTTPS Partial Image Download over HTTPS
--------------------------------- ---------------------------------
To use the partial image download feature, enable ``partial_http_download`` configuration in ``esp_https_ota_config_t``. When this configuration is enabled, firmware image will be downloaded in multiple HTTP requests of specified sizes. Maximum content length of each request can be specified by setting ``max_http_request_size`` to the required value. To use the partial image download feature, you need to:
* **Enable the component-level configuration**: enable :ref:`CONFIG_ESP_HTTPS_OTA_ENABLE_PARTIAL_DOWNLOAD` in menuconfig (``Component config````ESP HTTPS OTA````Enable partial HTTP download for OTA``)
* **Enable the feature in your application**: Set the ``partial_http_download`` field in :cpp:struct:`esp_https_ota_config_t` configuration structure
When this configuration is enabled, firmware image will be downloaded in multiple HTTP requests of specified sizes. Maximum content length of each request can be specified by setting ``max_http_request_size`` to the required value.
This option is useful while fetching image from a service like AWS S3, where mbedTLS Rx buffer size (:ref:`CONFIG_MBEDTLS_SSL_IN_CONTENT_LEN`) can be set to a lower value which is not possible without enabling this configuration. This option is useful while fetching image from a service like AWS S3, where mbedTLS Rx buffer size (:ref:`CONFIG_MBEDTLS_SSL_IN_CONTENT_LEN`) can be set to a lower value which is not possible without enabling this configuration.

View File

@@ -104,3 +104,10 @@ Binary data format has been dropped. `CONFIG_ESP_COREDUMP_DATA_FORMAT_BIN` is no
CRC data integrity check has been dropped. `ESP_COREDUMP_CHECKSUM_CRC32` is no longer supported. SHA256 is now the default checksum algorithm. CRC data integrity check has been dropped. `ESP_COREDUMP_CHECKSUM_CRC32` is no longer supported. SHA256 is now the default checksum algorithm.
The function :cpp:func:`esp_core_dump_partition_and_size_get()` now returns `ESP_ERR_NOT_FOUND` for blank (erased) partitions instead of `ESP_ERR_INVALID_SIZE`. The function :cpp:func:`esp_core_dump_partition_and_size_get()` now returns `ESP_ERR_NOT_FOUND` for blank (erased) partitions instead of `ESP_ERR_INVALID_SIZE`.
OTA Updates
-----------
The partial download functionality in ESP HTTPS OTA has been moved under a configuration option in order to reduce the memory footprint if partial download is not used.
To use partial download features in your OTA applications, you need to enable the component-level configuration :ref:`CONFIG_ESP_HTTPS_OTA_ENABLE_PARTIAL_DOWNLOAD` in menuconfig (``Component config````ESP HTTPS OTA````Enable partial HTTP download for OTA``).

View File

@@ -31,6 +31,7 @@ menu "Example Configuration"
config EXAMPLE_ENABLE_PARTIAL_HTTP_DOWNLOAD config EXAMPLE_ENABLE_PARTIAL_HTTP_DOWNLOAD
bool "Enable partial HTTP download" bool "Enable partial HTTP download"
default n default n
select ESP_HTTPS_OTA_ENABLE_PARTIAL_DOWNLOAD
select ESP_TLS_CLIENT_SESSION_TICKETS select ESP_TLS_CLIENT_SESSION_TICKETS
help help
This enables use of Range header in esp_https_ota component. This enables use of Range header in esp_https_ota component.

View File

@@ -2,6 +2,7 @@ CONFIG_EXAMPLE_FIRMWARE_UPGRADE_URL="FROM_STDIN"
CONFIG_EXAMPLE_SKIP_COMMON_NAME_CHECK=y CONFIG_EXAMPLE_SKIP_COMMON_NAME_CHECK=y
CONFIG_EXAMPLE_SKIP_VERSION_CHECK=y CONFIG_EXAMPLE_SKIP_VERSION_CHECK=y
CONFIG_EXAMPLE_OTA_RECV_TIMEOUT=3000 CONFIG_EXAMPLE_OTA_RECV_TIMEOUT=3000
CONFIG_ESP_HTTPS_OTA_ENABLE_PARTIAL_DOWNLOAD=y
CONFIG_EXAMPLE_ENABLE_PARTIAL_HTTP_DOWNLOAD=y CONFIG_EXAMPLE_ENABLE_PARTIAL_HTTP_DOWNLOAD=y
CONFIG_EXAMPLE_ENABLE_OTA_RESUMPTION=y CONFIG_EXAMPLE_ENABLE_OTA_RESUMPTION=y

View File

@@ -2,6 +2,7 @@ CONFIG_EXAMPLE_FIRMWARE_UPGRADE_URL="FROM_STDIN"
CONFIG_EXAMPLE_SKIP_COMMON_NAME_CHECK=y CONFIG_EXAMPLE_SKIP_COMMON_NAME_CHECK=y
CONFIG_EXAMPLE_SKIP_VERSION_CHECK=y CONFIG_EXAMPLE_SKIP_VERSION_CHECK=y
CONFIG_EXAMPLE_OTA_RECV_TIMEOUT=3000 CONFIG_EXAMPLE_OTA_RECV_TIMEOUT=3000
CONFIG_ESP_HTTPS_OTA_ENABLE_PARTIAL_DOWNLOAD=y
CONFIG_EXAMPLE_ENABLE_PARTIAL_HTTP_DOWNLOAD=y CONFIG_EXAMPLE_ENABLE_PARTIAL_HTTP_DOWNLOAD=y
CONFIG_LOG_DEFAULT_LEVEL_DEBUG=y CONFIG_LOG_DEFAULT_LEVEL_DEBUG=y