Przeglądaj źródła

Merge branch 'feature/esp_http_client_add_example' into 'master'

Add example to demonstrate use of low level APIs in http client

Closes IDFGH-2773

See merge request espressif/esp-idf!7832
Mahavir Jain 5 lat temu
rodzic
commit
4e0e15631d

+ 13 - 0
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;
+}

+ 14 - 0
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

+ 107 - 5
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);