From 4858184e19d4bafa6d4758211d2950dd51333f3d Mon Sep 17 00:00:00 2001 From: Shubham Kulkarni Date: Thu, 12 Mar 2020 19:12:27 +0530 Subject: [PATCH 1/3] esp_http_client: Add helper API to read larger data chunks from HTTP Stream --- components/esp_http_client/esp_http_client.c | 13 +++++++++++++ .../esp_http_client/include/esp_http_client.h | 14 ++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/components/esp_http_client/esp_http_client.c b/components/esp_http_client/esp_http_client.c index 69ced3406a..570496a4b5 100644 --- a/components/esp_http_client/esp_http_client.c +++ b/components/esp_http_client/esp_http_client.c @@ -1315,3 +1315,16 @@ void esp_http_client_add_auth(esp_http_client_handle_t client) ESP_LOGW(TAG, "This request requires authentication, but does not provide header information for that"); } } + +int esp_http_client_read_response(esp_http_client_handle_t client, char *buffer, int len) +{ + int read_len = 0; + while (read_len < len) { + int data_read = esp_http_client_read(client, buffer + read_len, len - read_len); + if (data_read <= 0) { + return read_len; + } + read_len += data_read; + } + return read_len; +} diff --git a/components/esp_http_client/include/esp_http_client.h b/components/esp_http_client/include/esp_http_client.h index 8d9243a0ba..b1c9aae32f 100644 --- a/components/esp_http_client/include/esp_http_client.h +++ b/components/esp_http_client/include/esp_http_client.h @@ -497,6 +497,20 @@ void esp_http_client_add_auth(esp_http_client_handle_t client); */ bool esp_http_client_is_complete_data_received(esp_http_client_handle_t client); +/** + * @brief Helper API to read larger data chunks + * This is a helper API which internally calls `esp_http_client_read` multiple times till the end of data is reached or till the buffer gets full. + * + * @param[in] client The esp_http_client handle + * @param buffer The buffer + * @param[in] len The buffer length + * + * @return + * - Length of data was read + */ + +int esp_http_client_read_response(esp_http_client_handle_t client, char *buffer, int len); + #ifdef __cplusplus } #endif From d0d99fe4c7b454983325355e6cb07726aec3eede Mon Sep 17 00:00:00 2001 From: Shubham Kulkarni Date: Tue, 17 Mar 2020 18:55:02 +0530 Subject: [PATCH 2/3] esp_http_client_example.c: Fix http_perform_as_stream_reader example Run Basic Auth examples only if ESP_HTTP_CLIENT_ENABLE_BASIC_AUTH is enabled Closes https://github.com/espressif/esp-idf/issues/4969 --- .../esp_http_client/main/esp_http_client_example.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/protocols/esp_http_client/main/esp_http_client_example.c b/examples/protocols/esp_http_client/main/esp_http_client_example.c index fc2485cce5..aa7110bf85 100644 --- a/examples/protocols/esp_http_client/main/esp_http_client_example.c +++ b/examples/protocols/esp_http_client/main/esp_http_client_example.c @@ -246,7 +246,7 @@ static void http_rest_with_hostname_path(void) esp_http_client_cleanup(client); } - +#if CONFIG_ESP_HTTP_CLIENT_ENABLE_BASIC_AUTH static void http_auth_basic(void) { esp_http_client_config_t config = { @@ -285,6 +285,7 @@ static void http_auth_basic_redirect(void) } esp_http_client_cleanup(client); } +#endif static void http_auth_digest(void) { @@ -433,7 +434,6 @@ static void http_perform_as_stream_reader(void) } esp_http_client_config_t config = { .url = "http://httpbin.org/get", - .event_handler = _http_event_handler, }; esp_http_client_handle_t client = esp_http_client_init(&config); esp_err_t err; @@ -516,8 +516,10 @@ static void http_test_task(void *pvParameters) { http_rest_with_url(); http_rest_with_hostname_path(); +#if CONFIG_ESP_HTTP_CLIENT_ENABLE_BASIC_AUTH http_auth_basic(); http_auth_basic_redirect(); +#endif http_auth_digest(); http_relative_redirect(); http_absolute_redirect(); From b9555db47f303bcbc218b8f16d00ff6fb75edb12 Mon Sep 17 00:00:00 2001 From: Shubham Kulkarni Date: Mon, 2 Mar 2020 15:49:50 +0530 Subject: [PATCH 3/3] examples: Add example to demonstrate use of low level APIs for GET and POST requests Use buffer to accumulate data of response from event handler and print the response on console Demonstrate use of user_data field in esp_http_client_config_t to get response body --- .../main/esp_http_client_example.c | 106 +++++++++++++++++- 1 file changed, 103 insertions(+), 3 deletions(-) diff --git a/examples/protocols/esp_http_client/main/esp_http_client_example.c b/examples/protocols/esp_http_client/main/esp_http_client_example.c index aa7110bf85..bc6a29e7f4 100644 --- a/examples/protocols/esp_http_client/main/esp_http_client_example.c +++ b/examples/protocols/esp_http_client/main/esp_http_client_example.c @@ -22,6 +22,7 @@ #include "esp_http_client.h" #define MAX_HTTP_RECV_BUFFER 512 +#define MAX_HTTP_OUTPUT_BUFFER 2048 static const char *TAG = "HTTP_CLIENT"; /* Root cert for howsmyssl.com, taken from howsmyssl_com_root_cert.pem @@ -39,6 +40,8 @@ extern const char howsmyssl_com_root_cert_pem_end[] asm("_binary_howsmyssl_com esp_err_t _http_event_handler(esp_http_client_event_t *evt) { + static char *output_buffer; // Buffer to store response of http request from event handler + static int output_len; // Stores number of bytes read switch(evt->event_id) { case HTTP_EVENT_ERROR: ESP_LOGD(TAG, "HTTP_EVENT_ERROR"); @@ -54,20 +57,49 @@ esp_err_t _http_event_handler(esp_http_client_event_t *evt) break; case HTTP_EVENT_ON_DATA: ESP_LOGD(TAG, "HTTP_EVENT_ON_DATA, len=%d", evt->data_len); + /* + * Check for chunked encoding is added as the URL for chunked encoding used in this example returns binary data. + * However, event handler can also be used in case chunked encoding is used. + */ if (!esp_http_client_is_chunked_response(evt->client)) { - // Write out data - // printf("%.*s", evt->data_len, (char*)evt->data); + // If user_data buffer is configured, copy the response into the buffer + if (evt->user_data) { + memcpy(evt->user_data + output_len, evt->data, evt->data_len); + } else { + if (output_buffer == NULL) { + output_buffer = (char *) malloc(esp_http_client_get_content_length(evt->client)); + output_len = 0; + if (output_buffer == NULL) { + ESP_LOGE(TAG, "Failed to allocate memory for output buffer"); + return ESP_FAIL; + } + } + memcpy(output_buffer + output_len, evt->data, evt->data_len); + } + output_len += evt->data_len; } break; case HTTP_EVENT_ON_FINISH: ESP_LOGD(TAG, "HTTP_EVENT_ON_FINISH"); + if (output_buffer != NULL) { + // Response is accumulated in output_buffer. Uncomment the below line to print the accumulated response + // ESP_LOG_BUFFER_HEX(TAG, output_buffer, output_len); + free(output_buffer); + output_buffer = NULL; + output_len = 0; + } break; case HTTP_EVENT_DISCONNECTED: ESP_LOGI(TAG, "HTTP_EVENT_DISCONNECTED"); int mbedtls_err = 0; esp_err_t err = esp_tls_get_and_clear_last_error(evt->data, &mbedtls_err, NULL); if (err != 0) { + if (output_buffer != NULL) { + free(output_buffer); + output_buffer = NULL; + output_len = 0; + } ESP_LOGI(TAG, "Last esp error code: 0x%x", err); ESP_LOGI(TAG, "Last mbedtls failure: 0x%x", mbedtls_err); } @@ -78,9 +110,11 @@ esp_err_t _http_event_handler(esp_http_client_event_t *evt) static void http_rest_with_url(void) { + char local_response_buffer[MAX_HTTP_OUTPUT_BUFFER] = {0}; esp_http_client_config_t config = { .url = "http://httpbin.org/get", .event_handler = _http_event_handler, + .user_data = local_response_buffer, // Pass address of local buffer to get response }; esp_http_client_handle_t client = esp_http_client_init(&config); @@ -93,11 +127,13 @@ static void http_rest_with_url(void) } else { ESP_LOGE(TAG, "HTTP GET request failed: %s", esp_err_to_name(err)); } + ESP_LOG_BUFFER_HEX(TAG, local_response_buffer, strlen(local_response_buffer)); // POST - const char *post_data = "field1=value1&field2=value2"; + const char *post_data = "{\"field1\":\"value1\"}"; esp_http_client_set_url(client, "http://httpbin.org/post"); esp_http_client_set_method(client, HTTP_METHOD_POST); + esp_http_client_set_header(client, "Content-Type", "application/json"); esp_http_client_set_post_field(client, post_data, strlen(post_data)); err = esp_http_client_perform(client); if (err == ESP_OK) { @@ -511,6 +547,69 @@ static void https_with_invalid_url(void) esp_http_client_cleanup(client); } +/* + * http_native_request() demonstrates use of low level APIs to connect to a server, + * make a http request and read response. Event handler is not used in this case. + * Note: This approach should only be used in case use of low level APIs is required. + * The easiest way is to use esp_http_perform() + */ +static void http_native_request(void) +{ + char output_buffer[MAX_HTTP_OUTPUT_BUFFER] = {0}; // Buffer to store response of http request + int content_length = 0; + esp_http_client_config_t config = { + .url = "http://httpbin.org/get", + }; + esp_http_client_handle_t client = esp_http_client_init(&config); + + // GET Request + esp_http_client_set_method(client, HTTP_METHOD_GET); + esp_err_t err = esp_http_client_open(client, 0); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to open HTTP connection: %s", esp_err_to_name(err)); + } else { + content_length = esp_http_client_fetch_headers(client); + if (content_length < 0) { + ESP_LOGE(TAG, "HTTP client fetch headers failed"); + } else { + int data_read = esp_http_client_read_response(client, output_buffer, MAX_HTTP_OUTPUT_BUFFER); + if (data_read >= 0) { + ESP_LOGI(TAG, "HTTP GET Status = %d, content_length = %d", + esp_http_client_get_status_code(client), + esp_http_client_get_content_length(client)); + ESP_LOG_BUFFER_HEX(TAG, output_buffer, strlen(output_buffer)); + } else { + ESP_LOGE(TAG, "Failed to read response"); + } + } + } + esp_http_client_close(client); + + // POST Request + const char *post_data = "{\"field1\":\"value1\"}"; + esp_http_client_set_url(client, "http://httpbin.org/post"); + esp_http_client_set_method(client, HTTP_METHOD_POST); + esp_http_client_set_header(client, "Content-Type", "application/json"); + err = esp_http_client_open(client, strlen(post_data)); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to open HTTP connection: %s", esp_err_to_name(err)); + } else { + int wlen = esp_http_client_write(client, post_data, strlen(post_data)); + if (wlen < 0) { + ESP_LOGE(TAG, "Write failed"); + } + int data_read = esp_http_client_read_response(client, output_buffer, MAX_HTTP_OUTPUT_BUFFER); + if (data_read >= 0) { + ESP_LOGI(TAG, "HTTP GET Status = %d, content_length = %d", + esp_http_client_get_status_code(client), + esp_http_client_get_content_length(client)); + ESP_LOG_BUFFER_HEX(TAG, output_buffer, strlen(output_buffer)); + } else { + ESP_LOGE(TAG, "Failed to read response"); + } + } + esp_http_client_cleanup(client); +} static void http_test_task(void *pvParameters) { @@ -530,6 +629,7 @@ static void http_test_task(void *pvParameters) http_perform_as_stream_reader(); https_async(); https_with_invalid_url(); + http_native_request(); ESP_LOGI(TAG, "Finish http example"); vTaskDelete(NULL);