Skip to content

Commit 5aff8a9

Browse files
committed
Merge branch 'bugfix/ota_failing_for_aws_signed_url_with_partial_download_enabled' into 'master'
fix(esp_http_client): Fix OTA failure with partial download enabled Closes IDF-12590 See merge request espressif/esp-idf!37843
2 parents dc527fb + caf828d commit 5aff8a9

File tree

5 files changed

+83
-5
lines changed

5 files changed

+83
-5
lines changed

components/esp_http_client/esp_http_client.c

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ typedef struct {
6767
int64_t data_process; /*!< data processed */
6868
int method; /*!< http method */
6969
bool is_chunked;
70+
int64_t content_range; /*!< content range */
7071
} esp_http_data_t;
7172

7273
typedef struct {
@@ -269,7 +270,29 @@ static int http_on_header_value(http_parser *parser, const char *at, size_t leng
269270
if (client->current_header_key == NULL) {
270271
return 0;
271272
}
272-
if (strcasecmp(client->current_header_key, "Location") == 0) {
273+
if (strcasecmp(client->current_header_key, "Content-Range") == 0) {
274+
HTTP_RET_ON_FALSE_DBG(http_utils_append_string(&client->current_header_value, at, length), -1, TAG, "Failed to append string");
275+
276+
int64_t total_size = -1;
277+
client->response->content_range = -1;
278+
char *slash_pos = strchr(client->current_header_value, '/');
279+
280+
if (slash_pos) {
281+
if (slash_pos[1] == '*') {
282+
ESP_LOGE(TAG, "Content-Range header has unknown total size (bytes A-B/*)");
283+
} else {
284+
char *endptr;
285+
total_size = strtol(slash_pos + 1, &endptr, 10);
286+
if (total_size > 0 && *endptr == '\0') {
287+
client->response->content_range = total_size;
288+
} else {
289+
ESP_LOGE(TAG, "Failed to extract total size from Content-Range");
290+
}
291+
}
292+
} else {
293+
ESP_LOGE(TAG, "Invalid Content-Range format (missing '/')");
294+
}
295+
} else if (strcasecmp(client->current_header_key, "Location") == 0) {
273296
HTTP_RET_ON_FALSE_DBG(http_utils_append_string(&client->location, at, length), -1, TAG, "Failed to append string");
274297
} else if (strcasecmp(client->current_header_key, "Transfer-Encoding") == 0
275298
&& memcmp(at, "chunked", length) == 0) {
@@ -1817,6 +1840,11 @@ int64_t esp_http_client_get_content_length(esp_http_client_handle_t client)
18171840
return client->response->content_length;
18181841
}
18191842

1843+
int64_t esp_http_client_get_content_range(esp_http_client_handle_t client)
1844+
{
1845+
return client->response->content_range;
1846+
}
1847+
18201848
bool esp_http_client_is_chunked_response(esp_http_client_handle_t client)
18211849
{
18221850
return client->response->is_chunked;

components/esp_http_client/include/esp_http_client.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,7 @@ typedef struct {
232232
typedef enum {
233233
/* 2xx - Success */
234234
HttpStatus_Ok = 200,
235+
HttpStatus_PartialContent = 206,
235236

236237
/* 3xx - Redirection */
237238
HttpStatus_MultipleChoices = 300,
@@ -642,6 +643,19 @@ int esp_http_client_get_status_code(esp_http_client_handle_t client);
642643
*/
643644
int64_t esp_http_client_get_content_length(esp_http_client_handle_t client);
644645

646+
/**
647+
* @brief Get http response content range (from header Content-Range)
648+
* The returned value is valid only if this function is invoked after
649+
* a successful call to `esp_http_client_perform`.
650+
* Content-Range is set to -1 if parsing fails or if the Content-Range header is not present.
651+
*
652+
* @param[in] client The esp_http_client handle
653+
*
654+
* @return
655+
* - Content-Range value as bytes
656+
*/
657+
int64_t esp_http_client_get_content_range(esp_http_client_handle_t client);
658+
645659
/**
646660
* @brief Close http connection, still kept all http request resources
647661
*

components/esp_https_ota/src/esp_https_ota.c

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -365,20 +365,50 @@ esp_err_t esp_https_ota_begin(const esp_https_ota_config_t *ota_config, esp_http
365365

366366
if (https_ota_handle->partial_http_download) {
367367
esp_http_client_set_method(https_ota_handle->http_client, HTTP_METHOD_HEAD);
368+
368369
err = esp_http_client_perform(https_ota_handle->http_client);
369370
if (err == ESP_OK) {
370371
int status = esp_http_client_get_status_code(https_ota_handle->http_client);
371372
if (status != HttpStatus_Ok) {
372-
ESP_LOGE(TAG, "Received incorrect http status %d", status);
373-
err = ESP_FAIL;
374-
goto http_cleanup;
373+
// If server doesn't support HEAD request, we need to get image length from GET request
374+
// using Range header
375+
esp_http_client_set_header(https_ota_handle->http_client, "Range", "bytes=0-0");
376+
esp_http_client_set_method(https_ota_handle->http_client, HTTP_METHOD_GET);
377+
378+
err = esp_http_client_perform(https_ota_handle->http_client);
379+
if (err == ESP_OK) {
380+
status = esp_http_client_get_status_code(https_ota_handle->http_client);
381+
if (status != HttpStatus_Ok && status != HttpStatus_PartialContent) {
382+
ESP_LOGE(TAG, "Received incorrect http status %d", status);
383+
err = ESP_FAIL;
384+
goto http_cleanup;
385+
}
386+
} else {
387+
ESP_LOGE(TAG, "ESP HTTP client perform failed: %d", err);
388+
goto http_cleanup;
389+
}
390+
esp_http_client_set_header(https_ota_handle->http_client, "Range", NULL);
391+
392+
if (status == HttpStatus_Ok) {
393+
// If server responds with 200 OK, we can get image length from content-length header
394+
https_ota_handle->image_length = esp_http_client_get_content_length(https_ota_handle->http_client);
395+
} else {
396+
// If server responds with 206 Partial Content, we can get image length from content-range header
397+
https_ota_handle->image_length = esp_http_client_get_content_range(https_ota_handle->http_client);
398+
}
399+
} else {
400+
https_ota_handle->image_length = esp_http_client_get_content_length(https_ota_handle->http_client);
375401
}
376402
} else {
377403
ESP_LOGE(TAG, "ESP HTTP client perform failed: %d", err);
378404
goto http_cleanup;
379405
}
380406

381-
https_ota_handle->image_length = esp_http_client_get_content_length(https_ota_handle->http_client);
407+
if (https_ota_handle->image_length == -1) {
408+
ESP_LOGE(TAG, "Failed to get image length from http response");
409+
err = ESP_FAIL;
410+
goto http_cleanup;
411+
}
382412
#if CONFIG_ESP_HTTPS_OTA_DECRYPT_CB
383413
/* In case of pre ecnrypted OTA, actual image size of OTA binary includes header size
384414
* which stored in variable enc_img_header_size */

docs/en/api-reference/system/esp_https_ota.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ This option is useful while fetching image from a service like AWS S3, where mbe
4747

4848
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.
4949

50+
.. note::
51+
If the server uses chunked transfer encoding, partial downloads are not feasible because the total content length is not known in advance.
52+
5053
OTA Resumption
5154
--------------
5255

docs/zh_CN/api-reference/system/esp_https_ota.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ ESP HTTPS OTA 升级
4747

4848
mbedTLS Rx buffer 的默认大小为 16 KB,但如果将 ``partial_http_download`` 的 ``max_http_request_size`` 设置为 4 KB,便能将 mbedTLS Rx 的 buffer 减小到 4 KB。使用这一配置方式预计可以节省约 12 KB 内存。
4949

50+
.. note::
51+
如果服务器使用分块传输编码,则无法进行部分下载,因为无法预先获知总内容长度。
52+
5053
OTA 恢复
5154
--------
5255

0 commit comments

Comments
 (0)