mirror of
				https://github.com/espressif/esp-idf.git
				synced 2025-11-03 22:08:28 +00:00 
			
		
		
		
	esp_http_client: Cache received data in http_on_body callback.
This change fixes issue with data loss when multiple body chunks are received after calling esp_http_client_fetch_headers.
This commit is contained in:
		@@ -40,6 +40,7 @@ typedef struct {
 | 
				
			|||||||
    char *data;         /*!< The HTTP data received from the server */
 | 
					    char *data;         /*!< The HTTP data received from the server */
 | 
				
			||||||
    int len;            /*!< The HTTP data len received from the server */
 | 
					    int len;            /*!< The HTTP data len received from the server */
 | 
				
			||||||
    char *raw_data;     /*!< The HTTP data after decoding */
 | 
					    char *raw_data;     /*!< The HTTP data after decoding */
 | 
				
			||||||
 | 
					    char *orig_raw_data;/*!< The Original pointer to HTTP data after decoding */
 | 
				
			||||||
    int raw_len;        /*!< The HTTP data len after decoding */
 | 
					    int raw_len;        /*!< The HTTP data len after decoding */
 | 
				
			||||||
    char *output_ptr;   /*!< The destination address of the data to be copied to after decoding */
 | 
					    char *output_ptr;   /*!< The destination address of the data to be copied to after decoding */
 | 
				
			||||||
} esp_http_buffer_t;
 | 
					} esp_http_buffer_t;
 | 
				
			||||||
@@ -82,6 +83,7 @@ typedef enum {
 | 
				
			|||||||
    HTTP_STATE_REQ_COMPLETE_HEADER,
 | 
					    HTTP_STATE_REQ_COMPLETE_HEADER,
 | 
				
			||||||
    HTTP_STATE_REQ_COMPLETE_DATA,
 | 
					    HTTP_STATE_REQ_COMPLETE_DATA,
 | 
				
			||||||
    HTTP_STATE_RES_COMPLETE_HEADER,
 | 
					    HTTP_STATE_RES_COMPLETE_HEADER,
 | 
				
			||||||
 | 
					    HTTP_STATE_RES_ON_DATA_START,
 | 
				
			||||||
    HTTP_STATE_RES_COMPLETE_DATA,
 | 
					    HTTP_STATE_RES_COMPLETE_DATA,
 | 
				
			||||||
    HTTP_STATE_CLOSE
 | 
					    HTTP_STATE_CLOSE
 | 
				
			||||||
} esp_http_state_t;
 | 
					} esp_http_state_t;
 | 
				
			||||||
@@ -122,6 +124,7 @@ struct esp_http_client {
 | 
				
			|||||||
    int                         header_index;
 | 
					    int                         header_index;
 | 
				
			||||||
    bool                        is_async;
 | 
					    bool                        is_async;
 | 
				
			||||||
    esp_transport_keep_alive_t  keep_alive_cfg;
 | 
					    esp_transport_keep_alive_t  keep_alive_cfg;
 | 
				
			||||||
 | 
					    unsigned                    cache_data_in_fetch_hdr: 1;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef struct esp_http_client esp_http_client_t;
 | 
					typedef struct esp_http_client esp_http_client_t;
 | 
				
			||||||
@@ -272,10 +275,24 @@ static int http_on_body(http_parser *parser, const char *at, size_t length)
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    esp_http_client_t *client = parser->data;
 | 
					    esp_http_client_t *client = parser->data;
 | 
				
			||||||
    ESP_LOGD(TAG, "http_on_body %d", length);
 | 
					    ESP_LOGD(TAG, "http_on_body %d", length);
 | 
				
			||||||
    client->response->buffer->raw_data = (char *)at;
 | 
					
 | 
				
			||||||
    if (client->response->buffer->output_ptr) {
 | 
					    if (client->response->buffer->output_ptr) {
 | 
				
			||||||
        memcpy(client->response->buffer->output_ptr, (char *)at, length);
 | 
					        memcpy(client->response->buffer->output_ptr, (char *)at, length);
 | 
				
			||||||
        client->response->buffer->output_ptr += length;
 | 
					        client->response->buffer->output_ptr += length;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        /* Do not cache body when http_on_body is called from esp_http_client_perform */
 | 
				
			||||||
 | 
					        if (client->state < HTTP_STATE_RES_ON_DATA_START && client->cache_data_in_fetch_hdr) {
 | 
				
			||||||
 | 
					            ESP_LOGI(TAG, "Body received in fetch header state, %p, %d", at, length);
 | 
				
			||||||
 | 
					            esp_http_buffer_t *res_buffer = client->response->buffer;
 | 
				
			||||||
 | 
					            assert(res_buffer->orig_raw_data == res_buffer->raw_data);
 | 
				
			||||||
 | 
					            res_buffer->orig_raw_data = (char *)realloc(res_buffer->orig_raw_data, res_buffer->raw_len + length);
 | 
				
			||||||
 | 
					            if (!res_buffer->orig_raw_data) {
 | 
				
			||||||
 | 
					                ESP_LOGE(TAG, "Failed to allocate memory for storing decoded data");
 | 
				
			||||||
 | 
					                return -1;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            memcpy(res_buffer->orig_raw_data + res_buffer->raw_len, at, length);
 | 
				
			||||||
 | 
					            res_buffer->raw_data = res_buffer->orig_raw_data;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    client->response->data_process += length;
 | 
					    client->response->data_process += length;
 | 
				
			||||||
@@ -695,6 +712,11 @@ esp_http_client_handle_t esp_http_client_init(const esp_http_client_config_t *co
 | 
				
			|||||||
        goto error;
 | 
					        goto error;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* As default behavior, cache data received in fetch header state. This will be
 | 
				
			||||||
 | 
					     * used in esp_http_client_read API only. For esp_http_perform we shall disable
 | 
				
			||||||
 | 
					     * this as data will be processed by event handler */
 | 
				
			||||||
 | 
					    client->cache_data_in_fetch_hdr = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    client->parser_settings->on_message_begin = http_on_message_begin;
 | 
					    client->parser_settings->on_message_begin = http_on_message_begin;
 | 
				
			||||||
    client->parser_settings->on_url = http_on_url;
 | 
					    client->parser_settings->on_url = http_on_url;
 | 
				
			||||||
    client->parser_settings->on_status = http_on_status;
 | 
					    client->parser_settings->on_status = http_on_status;
 | 
				
			||||||
@@ -734,6 +756,11 @@ esp_err_t esp_http_client_cleanup(esp_http_client_handle_t client)
 | 
				
			|||||||
        http_header_destroy(client->response->headers);
 | 
					        http_header_destroy(client->response->headers);
 | 
				
			||||||
        if (client->response->buffer) {
 | 
					        if (client->response->buffer) {
 | 
				
			||||||
            free(client->response->buffer->data);
 | 
					            free(client->response->buffer->data);
 | 
				
			||||||
 | 
					            if (client->response->buffer->orig_raw_data) {
 | 
				
			||||||
 | 
					                free(client->response->buffer->orig_raw_data);
 | 
				
			||||||
 | 
					                client->response->buffer->orig_raw_data = NULL;
 | 
				
			||||||
 | 
					                client->response->buffer->raw_data = NULL;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        free(client->response->buffer);
 | 
					        free(client->response->buffer);
 | 
				
			||||||
        free(client->response);
 | 
					        free(client->response);
 | 
				
			||||||
@@ -899,7 +926,7 @@ esp_err_t esp_http_client_set_method(esp_http_client_handle_t client, esp_http_c
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
static int esp_http_client_get_data(esp_http_client_handle_t client)
 | 
					static int esp_http_client_get_data(esp_http_client_handle_t client)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    if (client->state < HTTP_STATE_RES_COMPLETE_HEADER) {
 | 
					    if (client->state < HTTP_STATE_RES_ON_DATA_START) {
 | 
				
			||||||
        return ESP_FAIL;
 | 
					        return ESP_FAIL;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -948,6 +975,11 @@ int esp_http_client_read(esp_http_client_handle_t client, char *buffer, int len)
 | 
				
			|||||||
        res_buffer->raw_len -= remain_len;
 | 
					        res_buffer->raw_len -= remain_len;
 | 
				
			||||||
        res_buffer->raw_data += remain_len;
 | 
					        res_buffer->raw_data += remain_len;
 | 
				
			||||||
        ridx = remain_len;
 | 
					        ridx = remain_len;
 | 
				
			||||||
 | 
					        if (res_buffer->raw_len == 0) {
 | 
				
			||||||
 | 
					            free(res_buffer->orig_raw_data);
 | 
				
			||||||
 | 
					            res_buffer->orig_raw_data = NULL;
 | 
				
			||||||
 | 
					            res_buffer->raw_data = NULL;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    int need_read = len - ridx;
 | 
					    int need_read = len - ridx;
 | 
				
			||||||
    bool is_data_remain = true;
 | 
					    bool is_data_remain = true;
 | 
				
			||||||
@@ -1044,14 +1076,23 @@ esp_err_t esp_http_client_perform(esp_http_client_handle_t client)
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
                /* falls through */
 | 
					                /* falls through */
 | 
				
			||||||
            case HTTP_STATE_REQ_COMPLETE_DATA:
 | 
					            case HTTP_STATE_REQ_COMPLETE_DATA:
 | 
				
			||||||
 | 
					                /* Disable caching response body, as data should
 | 
				
			||||||
 | 
					                 * be handled by application event handler */
 | 
				
			||||||
 | 
					                client->cache_data_in_fetch_hdr = 0;
 | 
				
			||||||
                if (esp_http_client_fetch_headers(client) < 0) {
 | 
					                if (esp_http_client_fetch_headers(client) < 0) {
 | 
				
			||||||
                    if (client->is_async && errno == EAGAIN) {
 | 
					                    if (client->is_async && errno == EAGAIN) {
 | 
				
			||||||
                        return ESP_ERR_HTTP_EAGAIN;
 | 
					                        return ESP_ERR_HTTP_EAGAIN;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					                    /* Enable caching after error condition because next
 | 
				
			||||||
 | 
					                     * request could be performed using native APIs */
 | 
				
			||||||
 | 
					                    client->cache_data_in_fetch_hdr = 1;
 | 
				
			||||||
                    return ESP_ERR_HTTP_FETCH_HEADER;
 | 
					                    return ESP_ERR_HTTP_FETCH_HEADER;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                /* falls through */
 | 
					                /* falls through */
 | 
				
			||||||
            case HTTP_STATE_RES_COMPLETE_HEADER:
 | 
					            case HTTP_STATE_RES_ON_DATA_START:
 | 
				
			||||||
 | 
					                /* Enable caching after fetch headers state because next
 | 
				
			||||||
 | 
					                 * request could be performed using native APIs */
 | 
				
			||||||
 | 
					                client->cache_data_in_fetch_hdr = 1;
 | 
				
			||||||
                if ((err = esp_http_check_response(client)) != ESP_OK) {
 | 
					                if ((err = esp_http_check_response(client)) != ESP_OK) {
 | 
				
			||||||
                    ESP_LOGE(TAG, "Error response");
 | 
					                    ESP_LOGE(TAG, "Error response");
 | 
				
			||||||
                    return err;
 | 
					                    return err;
 | 
				
			||||||
@@ -1111,6 +1152,7 @@ int esp_http_client_fetch_headers(esp_http_client_handle_t client)
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
        http_parser_execute(client->parser, client->parser_settings, buffer->data, buffer->len);
 | 
					        http_parser_execute(client->parser, client->parser_settings, buffer->data, buffer->len);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    client->state = HTTP_STATE_RES_ON_DATA_START;
 | 
				
			||||||
    ESP_LOGD(TAG, "content_length = %d", client->response->content_length);
 | 
					    ESP_LOGD(TAG, "content_length = %d", client->response->content_length);
 | 
				
			||||||
    if (client->response->content_length <= 0) {
 | 
					    if (client->response->content_length <= 0) {
 | 
				
			||||||
        client->response->is_chunked = true;
 | 
					        client->response->is_chunked = true;
 | 
				
			||||||
@@ -1403,7 +1445,7 @@ void esp_http_client_add_auth(esp_http_client_handle_t client)
 | 
				
			|||||||
    if (client == NULL) {
 | 
					    if (client == NULL) {
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (client->state != HTTP_STATE_RES_COMPLETE_HEADER) {
 | 
					    if (client->state != HTTP_STATE_RES_ON_DATA_START) {
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (client->redirect_counter >= client->max_authorization_retries) {
 | 
					    if (client->redirect_counter >= client->max_authorization_retries) {
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user