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 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..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) { @@ -246,7 +282,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 +321,7 @@ static void http_auth_basic_redirect(void) } esp_http_client_cleanup(client); } +#endif static void http_auth_digest(void) { @@ -433,7 +470,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; @@ -511,13 +547,78 @@ 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) { 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(); @@ -528,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);