mirror of
https://github.com/espressif/esp-idf.git
synced 2025-08-05 05:34:32 +02:00
fix(esp_http_client): Fix OTA failure with partial download enabled
This commit fixes an issue encountered during OTA when partial download is enabled using an AWS signed URL restricted to GET requests. It also adds an API to retrieve the OTA image size from the Content-Range header.
This commit is contained in:
@@ -67,6 +67,7 @@ typedef struct {
|
||||
int64_t data_process; /*!< data processed */
|
||||
int method; /*!< http method */
|
||||
bool is_chunked;
|
||||
int64_t content_range; /*!< content range */
|
||||
} esp_http_data_t;
|
||||
|
||||
typedef struct {
|
||||
@@ -269,7 +270,29 @@ static int http_on_header_value(http_parser *parser, const char *at, size_t leng
|
||||
if (client->current_header_key == NULL) {
|
||||
return 0;
|
||||
}
|
||||
if (strcasecmp(client->current_header_key, "Location") == 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");
|
||||
|
||||
int64_t total_size = -1;
|
||||
client->response->content_range = -1;
|
||||
char *slash_pos = strchr(client->current_header_value, '/');
|
||||
|
||||
if (slash_pos) {
|
||||
if (slash_pos[1] == '*') {
|
||||
ESP_LOGE(TAG, "Content-Range header has unknown total size (bytes A-B/*)");
|
||||
} else {
|
||||
char *endptr;
|
||||
total_size = strtol(slash_pos + 1, &endptr, 10);
|
||||
if (total_size > 0 && *endptr == '\0') {
|
||||
client->response->content_range = total_size;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Failed to extract total size from Content-Range");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Invalid Content-Range format (missing '/')");
|
||||
}
|
||||
} else 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");
|
||||
} else if (strcasecmp(client->current_header_key, "Transfer-Encoding") == 0
|
||||
&& memcmp(at, "chunked", length) == 0) {
|
||||
@@ -1817,6 +1840,11 @@ int64_t esp_http_client_get_content_length(esp_http_client_handle_t client)
|
||||
return client->response->content_length;
|
||||
}
|
||||
|
||||
int64_t esp_http_client_get_content_range(esp_http_client_handle_t client)
|
||||
{
|
||||
return client->response->content_range;
|
||||
}
|
||||
|
||||
bool esp_http_client_is_chunked_response(esp_http_client_handle_t client)
|
||||
{
|
||||
return client->response->is_chunked;
|
||||
|
@@ -232,6 +232,7 @@ typedef struct {
|
||||
typedef enum {
|
||||
/* 2xx - Success */
|
||||
HttpStatus_Ok = 200,
|
||||
HttpStatus_PartialContent = 206,
|
||||
|
||||
/* 3xx - Redirection */
|
||||
HttpStatus_MultipleChoices = 300,
|
||||
@@ -642,6 +643,19 @@ 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);
|
||||
|
||||
/**
|
||||
* @brief Get http response content range (from header Content-Range)
|
||||
* The returned value is valid only if this function is invoked after
|
||||
* a successful call to `esp_http_client_perform`.
|
||||
* Content-Range is set to -1 if parsing fails or if the Content-Range header is not present.
|
||||
*
|
||||
* @param[in] client The esp_http_client handle
|
||||
*
|
||||
* @return
|
||||
* - Content-Range value as bytes
|
||||
*/
|
||||
int64_t esp_http_client_get_content_range(esp_http_client_handle_t client);
|
||||
|
||||
/**
|
||||
* @brief Close http connection, still kept all http request resources
|
||||
*
|
||||
|
@@ -365,20 +365,45 @@ esp_err_t esp_https_ota_begin(const esp_https_ota_config_t *ota_config, esp_http
|
||||
|
||||
if (https_ota_handle->partial_http_download) {
|
||||
esp_http_client_set_method(https_ota_handle->http_client, HTTP_METHOD_HEAD);
|
||||
|
||||
err = esp_http_client_perform(https_ota_handle->http_client);
|
||||
if (err == ESP_OK) {
|
||||
int status = esp_http_client_get_status_code(https_ota_handle->http_client);
|
||||
if (status != HttpStatus_Ok) {
|
||||
ESP_LOGE(TAG, "Received incorrect http status %d", status);
|
||||
err = ESP_FAIL;
|
||||
goto http_cleanup;
|
||||
// If server doesn't support HEAD request, we need to get image length from GET request
|
||||
// using Range header
|
||||
esp_http_client_set_header(https_ota_handle->http_client, "Range", "bytes=0-0");
|
||||
esp_http_client_set_method(https_ota_handle->http_client, HTTP_METHOD_GET);
|
||||
|
||||
err = esp_http_client_perform(https_ota_handle->http_client);
|
||||
if (err == ESP_OK) {
|
||||
status = esp_http_client_get_status_code(https_ota_handle->http_client);
|
||||
if (status != HttpStatus_Ok && status != HttpStatus_PartialContent) {
|
||||
ESP_LOGE(TAG, "Received incorrect http status %d", status);
|
||||
err = ESP_FAIL;
|
||||
goto http_cleanup;
|
||||
}
|
||||
} else {
|
||||
ESP_LOGE(TAG, "ESP HTTP client perform failed: %d", err);
|
||||
goto http_cleanup;
|
||||
}
|
||||
esp_http_client_set_header(https_ota_handle->http_client, "Range", NULL);
|
||||
|
||||
if (status == HttpStatus_Ok) {
|
||||
// If server responds with 200 OK, we can get image length from content-length header
|
||||
https_ota_handle->image_length = esp_http_client_get_content_length(https_ota_handle->http_client);
|
||||
} else {
|
||||
// If server responds with 206 Partial Content, we can get image length from content-range header
|
||||
https_ota_handle->image_length = esp_http_client_get_content_range(https_ota_handle->http_client);
|
||||
}
|
||||
} else {
|
||||
https_ota_handle->image_length = esp_http_client_get_content_length(https_ota_handle->http_client);
|
||||
}
|
||||
} else {
|
||||
ESP_LOGE(TAG, "ESP HTTP client perform failed: %d", err);
|
||||
goto http_cleanup;
|
||||
}
|
||||
|
||||
https_ota_handle->image_length = esp_http_client_get_content_length(https_ota_handle->http_client);
|
||||
if (https_ota_handle->image_length == -1) {
|
||||
ESP_LOGE(TAG, "Failed to get image length from http response");
|
||||
err = ESP_FAIL;
|
||||
|
@@ -47,6 +47,9 @@ This option is useful while fetching image from a service like AWS S3, where mbe
|
||||
|
||||
Default value of mbedTLS Rx buffer size is set to 16 KB. By using ``partial_http_download`` with ``max_http_request_size`` of 4 KB, size of mbedTLS Rx buffer can be reduced to 4 KB. With this configuration, memory saving of around 12 KB is expected.
|
||||
|
||||
.. note::
|
||||
If the server uses chunked transfer encoding, partial downloads are not feasible because the total content length is not known in advance.
|
||||
|
||||
OTA Resumption
|
||||
--------------
|
||||
|
||||
|
@@ -47,6 +47,9 @@ ESP HTTPS OTA 升级
|
||||
|
||||
mbedTLS Rx buffer 的默认大小为 16 KB,但如果将 ``partial_http_download`` 的 ``max_http_request_size`` 设置为 4 KB,便能将 mbedTLS Rx 的 buffer 减小到 4 KB。使用这一配置方式预计可以节省约 12 KB 内存。
|
||||
|
||||
.. note::
|
||||
如果服务器使用分块传输编码,则无法进行部分下载,因为无法预先获知总内容长度。
|
||||
|
||||
OTA 恢复
|
||||
--------
|
||||
|
||||
|
Reference in New Issue
Block a user