diff --git a/components/esp_http_client/CMakeLists.txt b/components/esp_http_client/CMakeLists.txt index 29861fdb..327e9077 100644 --- a/components/esp_http_client/CMakeLists.txt +++ b/components/esp_http_client/CMakeLists.txt @@ -1,11 +1,8 @@ -set(COMPONENT_SRCS "esp_http_client.c" - "lib/http_auth.c" - "lib/http_header.c" - "lib/http_utils.c") -set(COMPONENT_ADD_INCLUDEDIRS "include") -set(COMPONENT_PRIV_INCLUDEDIRS "lib/include") - -set(COMPONENT_REQUIRES "http_parser") -set(COMPONENT_PRIV_REQUIRES "mbedtls" "lwip" "esp-tls" "tcp_transport" "tcpip_adapter") - -register_component() +idf_component_register(SRCS "esp_http_client.c" + "lib/http_auth.c" + "lib/http_header.c" + "lib/http_utils.c" + INCLUDE_DIRS "include" + PRIV_INCLUDE_DIRS "lib/include" + REQUIRES nghttp + PRIV_REQUIRES mbedtls lwip esp-tls tcp_transport) diff --git a/components/esp_http_client/Kconfig b/components/esp_http_client/Kconfig index 755ec6bd..9cc8a680 100644 --- a/components/esp_http_client/Kconfig +++ b/components/esp_http_client/Kconfig @@ -1,18 +1,17 @@ menu "ESP HTTP client" -config ESP_HTTP_CLIENT_ENABLE_HTTPS - bool "Enable https" - default y - help - This option will enable https protocol by linking mbedtls library and initializing SSL transport - -config HTTP_BUF_SIZE - int "Default HTTP Buffer Size (both send and receive)" - default 512 - range 512 1460 - help - Set HTTP Buffer Size. The larger buffer size will make send and receive more size packet once. + config ESP_HTTP_CLIENT_ENABLE_HTTPS + bool "Enable https" + default y + help + This option will enable https protocol by linking mbedtls library and initializing SSL transport + config ESP_HTTP_CLIENT_ENABLE_BASIC_AUTH + bool "Enable HTTP Basic Authentication" + default n + help + This option will enable HTTP Basic Authentication. It is disabled by default as Basic + auth uses unencrypted encoding, so it introduces a vulnerability when not using TLS endmenu diff --git a/components/esp_http_client/component.mk b/components/esp_http_client/component.mk index 0bde2b05..96b5b6c4 100644 --- a/components/esp_http_client/component.mk +++ b/components/esp_http_client/component.mk @@ -2,6 +2,5 @@ # Component Makefile # -COMPONENT_SRCDIRS := . lib -COMPONENT_ADD_INCLUDEDIRS := include +COMPONENT_SRCDIRS := . lib COMPONENT_PRIV_INCLUDEDIRS := lib/include diff --git a/components/esp_http_client/esp_http_client.c b/components/esp_http_client/esp_http_client.c index 4a82a50e..b059a58d 100644 --- a/components/esp_http_client/esp_http_client.c +++ b/components/esp_http_client/esp_http_client.c @@ -110,7 +110,8 @@ struct esp_http_client { esp_http_state_t state; http_event_handle_cb event_handler; int timeout_ms; - int buffer_size; + int buffer_size_rx; + int buffer_size_tx; bool disable_auto_redirect; esp_http_client_event_t event; int data_written_index; @@ -152,20 +153,6 @@ static const char *HTTP_METHOD_MAPPING[] = { "OPTIONS" }; -/** - * Enum for the HTTP status codes. - */ -enum HttpStatus_Code -{ - /* 3xx - Redirection */ - HttpStatus_MovedPermanently = 301, - HttpStatus_Found = 302, - - /* 4xx - Client Error */ - HttpStatus_Unauthorized = 401 -}; - - static esp_err_t esp_http_client_request_send(esp_http_client_handle_t client, int write_len); static esp_err_t esp_http_client_connect(esp_http_client_handle_t client); static esp_err_t esp_http_client_send_post_data(esp_http_client_handle_t client); @@ -297,6 +284,63 @@ esp_err_t esp_http_client_delete_header(esp_http_client_handle_t client, const c return http_header_delete(client->request->headers, key); } +esp_err_t esp_http_client_get_username(esp_http_client_handle_t client, char **value) +{ + if (client == NULL || value == NULL) { + ESP_LOGE(TAG, "client or value must not be NULL"); + return ESP_ERR_INVALID_ARG; + } + *value = client->connection_info.username; + return ESP_OK; +} + +esp_err_t esp_http_client_set_username(esp_http_client_handle_t client, const char *username) +{ + if (client == NULL) { + ESP_LOGE(TAG, "client must not be NULL"); + return ESP_ERR_INVALID_ARG; + } + if (client->connection_info.username != NULL) { + free(client->connection_info.username); + } + client->connection_info.username = username ? strdup(username) : NULL; + return ESP_OK; +} + +esp_err_t esp_http_client_get_password(esp_http_client_handle_t client, char **value) +{ + if (client == NULL || value == NULL) { + ESP_LOGE(TAG, "client or value must not be NULL"); + return ESP_ERR_INVALID_ARG; + } + *value = client->connection_info.password; + return ESP_OK; +} + +esp_err_t esp_http_client_set_password(esp_http_client_handle_t client, char *password) +{ + if (client == NULL) { + ESP_LOGE(TAG, "client must not be NULL"); + return ESP_ERR_INVALID_ARG; + } + if (client->connection_info.password != NULL) { + memset(client->connection_info.password, 0, strlen(client->connection_info.password)); + free(client->connection_info.password); + } + client->connection_info.password = password ? strdup(password) : NULL; + return ESP_OK; +} + +esp_err_t esp_http_client_set_authtype(esp_http_client_handle_t client, esp_http_client_auth_type_t auth_type) +{ + if (client == NULL) { + ESP_LOGE(TAG, "client must not be NULL"); + return ESP_ERR_INVALID_ARG; + } + client->connection_info.auth_type = auth_type; + return ESP_OK; +} + static esp_err_t _set_config(esp_http_client_handle_t client, const esp_http_client_config_t *config) { client->connection_info.method = config->method; @@ -306,11 +350,16 @@ static esp_err_t _set_config(esp_http_client_handle_t client, const esp_http_cli client->timeout_ms = config->timeout_ms; client->max_redirection_count = config->max_redirection_count; client->user_data = config->user_data; - client->buffer_size = config->buffer_size; + client->buffer_size_rx = config->buffer_size; + client->buffer_size_tx = config->buffer_size_tx; client->disable_auto_redirect = config->disable_auto_redirect; if (config->buffer_size == 0) { - client->buffer_size = DEFAULT_HTTP_BUF_SIZE; + client->buffer_size_rx = DEFAULT_HTTP_BUF_SIZE; + } + + if (config->buffer_size_tx == 0) { + client->buffer_size_tx = DEFAULT_HTTP_BUF_SIZE; } if (client->max_redirection_count == 0) { @@ -461,8 +510,7 @@ esp_http_client_handle_t esp_http_client_init(const esp_http_client_config_t *co if (!_success) { ESP_LOGE(TAG, "Error allocate memory"); - esp_http_client_cleanup(client); - return NULL; + goto error; } _success = ( @@ -473,8 +521,7 @@ esp_http_client_handle_t esp_http_client_init(const esp_http_client_config_t *co ); if (!_success) { ESP_LOGE(TAG, "Error initialize transport"); - esp_http_client_cleanup(client); - return NULL; + goto error; } #ifdef CONFIG_ESP_HTTP_CLIENT_ENABLE_HTTPS esp_transport_handle_t ssl; @@ -486,41 +533,66 @@ esp_http_client_handle_t esp_http_client_init(const esp_http_client_config_t *co if (!_success) { ESP_LOGE(TAG, "Error initialize SSL Transport"); - esp_http_client_cleanup(client); - return NULL; + goto error; } - if (config->cert_pem) { + if (config->use_global_ca_store == true) { + esp_transport_ssl_enable_global_ca_store(ssl); + } else if (config->cert_pem) { esp_transport_ssl_set_cert_data(ssl, config->cert_pem, strlen(config->cert_pem)); } + + if (config->client_cert_pem) { + esp_transport_ssl_set_client_cert_data(ssl, config->client_cert_pem, strlen(config->client_cert_pem)); + } + + if (config->client_key_pem) { + esp_transport_ssl_set_client_key_data(ssl, config->client_key_pem, strlen(config->client_key_pem)); + } + + if (config->skip_cert_common_name_check) { + esp_transport_ssl_skip_common_name_check(ssl); + } #endif if (_set_config(client, config) != ESP_OK) { ESP_LOGE(TAG, "Error set configurations"); - esp_http_client_cleanup(client); - return NULL; + goto error; } _success = ( - (client->request->buffer->data = malloc(client->buffer_size)) && - (client->response->buffer->data = malloc(client->buffer_size)) + (client->request->buffer->data = malloc(client->buffer_size_tx)) && + (client->response->buffer->data = malloc(client->buffer_size_rx)) ); if (!_success) { ESP_LOGE(TAG, "Allocation failed"); - esp_http_client_cleanup(client); - return NULL; + goto error; } - _success = ( - (esp_http_client_set_url(client, config->url) == ESP_OK) && - (esp_http_client_set_header(client, "User-Agent", DEFAULT_HTTP_USER_AGENT) == ESP_OK) && - (esp_http_client_set_header(client, "Host", client->connection_info.host) == ESP_OK) - ); + if (config->host != NULL && config->path != NULL) { + _success = ( + (esp_http_client_set_header(client, "User-Agent", DEFAULT_HTTP_USER_AGENT) == ESP_OK) && + (esp_http_client_set_header(client, "Host", client->connection_info.host) == ESP_OK) + ); - if (!_success) { - ESP_LOGE(TAG, "Error set default configurations"); - esp_http_client_cleanup(client); - return NULL; + if (!_success) { + ESP_LOGE(TAG, "Error while setting default configurations"); + goto error; + } + } else if (config->url != NULL) { + _success = ( + (esp_http_client_set_url(client, config->url) == ESP_OK) && + (esp_http_client_set_header(client, "User-Agent", DEFAULT_HTTP_USER_AGENT) == ESP_OK) && + (esp_http_client_set_header(client, "Host", client->connection_info.host) == ESP_OK) + ); + + if (!_success) { + ESP_LOGE(TAG, "Error while setting default configurations"); + goto error; + } + } else { + ESP_LOGE(TAG, "config should have either URL or host & path"); + goto error; } client->parser_settings->on_message_begin = http_on_message_begin; @@ -537,6 +609,9 @@ esp_http_client_handle_t esp_http_client_init(const esp_http_client_config_t *co client->state = HTTP_STATE_INIT; return client; +error: + esp_http_client_cleanup(client); + return NULL; } esp_err_t esp_http_client_cleanup(esp_http_client_handle_t client) @@ -546,14 +621,22 @@ esp_err_t esp_http_client_cleanup(esp_http_client_handle_t client) } esp_http_client_close(client); esp_transport_list_destroy(client->transport_list); - http_header_destroy(client->request->headers); - free(client->request->buffer->data); - free(client->request->buffer); - free(client->request); - http_header_destroy(client->response->headers); - free(client->response->buffer->data); - free(client->response->buffer); - free(client->response); + if (client->request) { + http_header_destroy(client->request->headers); + if (client->request->buffer) { + free(client->request->buffer->data); + } + free(client->request->buffer); + free(client->request); + } + if (client->response) { + http_header_destroy(client->response->headers); + if (client->response->buffer) { + free(client->response->buffer->data); + } + free(client->response->buffer); + free(client->response); + } free(client->parser); free(client->parser_settings); @@ -567,10 +650,20 @@ esp_err_t esp_http_client_cleanup(esp_http_client_handle_t client) return ESP_OK; } +esp_err_t esp_http_client_set_redirection(esp_http_client_handle_t client) +{ + if (client == NULL) { + return ESP_ERR_INVALID_ARG; + } + if (client->location == NULL) { + return ESP_ERR_INVALID_ARG; + } + ESP_LOGD(TAG, "Redirect to %s", client->location); + return esp_http_client_set_url(client, client->location); +} + static esp_err_t esp_http_check_response(esp_http_client_handle_t client) { - char *auth_header = NULL; - if (client->redirect_counter >= client->max_redirection_count || client->disable_auto_redirect) { ESP_LOGE(TAG, "Error, reach max_redirection_count count=%d", client->redirect_counter); return ESP_ERR_HTTP_MAX_REDIRECT; @@ -578,44 +671,13 @@ static esp_err_t esp_http_check_response(esp_http_client_handle_t client) switch (client->response->status_code) { case HttpStatus_MovedPermanently: case HttpStatus_Found: - ESP_LOGI(TAG, "Redirect to %s", client->location); - esp_http_client_set_url(client, client->location); + case HttpStatus_TemporaryRedirect: + esp_http_client_set_redirection(client); client->redirect_counter ++; client->process_again = 1; break; case HttpStatus_Unauthorized: - auth_header = client->auth_header; - if (auth_header) { - http_utils_trim_whitespace(&auth_header); - ESP_LOGD(TAG, "UNAUTHORIZED: %s", auth_header); - client->redirect_counter ++; - if (http_utils_str_starts_with(auth_header, "Digest") == 0) { - ESP_LOGD(TAG, "type = Digest"); - client->connection_info.auth_type = HTTP_AUTH_TYPE_DIGEST; - } else if (http_utils_str_starts_with(auth_header, "Basic") == 0) { - ESP_LOGD(TAG, "type = Basic"); - client->connection_info.auth_type = HTTP_AUTH_TYPE_BASIC; - } else { - client->connection_info.auth_type = HTTP_AUTH_TYPE_NONE; - ESP_LOGE(TAG, "This authentication method is not supported: %s", auth_header); - break; - } - - _clear_auth_data(client); - - client->auth_data->method = strdup(HTTP_METHOD_MAPPING[client->connection_info.method]); - - client->auth_data->nc = 1; - client->auth_data->realm = http_utils_get_string_between(auth_header, "realm=\"", "\""); - client->auth_data->algorithm = http_utils_get_string_between(auth_header, "algorithm=", ","); - client->auth_data->qop = http_utils_get_string_between(auth_header, "qop=\"", "\""); - client->auth_data->nonce = http_utils_get_string_between(auth_header, "nonce=\"", "\""); - client->auth_data->opaque = http_utils_get_string_between(auth_header, "opaque=\"", "\""); - client->process_again = 1; - } else { - client->connection_info.auth_type = HTTP_AUTH_TYPE_NONE; - ESP_LOGW(TAG, "This request requires authentication, but does not provide header information for that"); - } + esp_http_client_add_auth(client); } return ESP_OK; } @@ -639,23 +701,34 @@ esp_err_t esp_http_client_set_url(esp_http_client_handle_t client, const char *u ESP_LOGE(TAG, "Error parse url %s", url); return ESP_ERR_INVALID_ARG; } - old_host = client->connection_info.host; + if (client->connection_info.host) { + old_host = strdup(client->connection_info.host); + } old_port = client->connection_info.port; if (purl.field_data[UF_HOST].len) { http_utils_assign_string(&client->connection_info.host, url + purl.field_data[UF_HOST].off, purl.field_data[UF_HOST].len); - HTTP_MEM_CHECK(TAG, client->connection_info.host, return ESP_ERR_NO_MEM); + HTTP_MEM_CHECK(TAG, client->connection_info.host, { + free(old_host); + return ESP_ERR_NO_MEM; + }); } // Close the connection if host was changed if (old_host && client->connection_info.host && strcasecmp(old_host, (const void *)client->connection_info.host) != 0) { ESP_LOGD(TAG, "New host assign = %s", client->connection_info.host); if (esp_http_client_set_header(client, "Host", client->connection_info.host) != ESP_OK) { + free(old_host); return ESP_ERR_NO_MEM; } esp_http_client_close(client); } + if (old_host) { + free(old_host); + old_host = NULL; + } + if (purl.field_data[UF_SCHEMA].len) { http_utils_assign_string(&client->connection_info.scheme, url + purl.field_data[UF_SCHEMA].off, purl.field_data[UF_SCHEMA].len); HTTP_MEM_CHECK(TAG, client->connection_info.scheme, return ESP_ERR_NO_MEM); @@ -693,13 +766,7 @@ esp_err_t esp_http_client_set_url(esp_http_client_handle_t client, const char *u } else { return ESP_ERR_NO_MEM; } - } else { - free(client->connection_info.username); - free(client->connection_info.password); - client->connection_info.username = NULL; - client->connection_info.password = NULL; - } - + } //Reset path and query if there are no information if (purl.field_data[UF_PATH].len) { @@ -740,13 +807,29 @@ static int esp_http_client_get_data(esp_http_client_handle_t client) ESP_LOGD(TAG, "data_process=%d, content_length=%d", client->response->data_process, client->response->content_length); - int rlen = esp_transport_read(client->transport, res_buffer->data, client->buffer_size, client->timeout_ms); + int rlen = esp_transport_read(client->transport, res_buffer->data, client->buffer_size_rx, client->timeout_ms); if (rlen >= 0) { http_parser_execute(client->parser, client->parser_settings, res_buffer->data, rlen); } return rlen; } +bool esp_http_client_is_complete_data_received(esp_http_client_handle_t client) +{ + if (client->response->is_chunked) { + if (!client->is_chunk_complete) { + ESP_LOGD(TAG, "Chunks were not completely read"); + return false; + } + } else { + if (client->response->data_process != client->response->content_length) { + ESP_LOGD(TAG, "Data processed %d != Data specified in content length %d", client->response->data_process, client->response->content_length); + return false; + } + } + return true; +} + int esp_http_client_read(esp_http_client_handle_t client, char *buffer, int len) { esp_http_buffer_t *res_buffer = client->response->buffer; @@ -770,19 +853,36 @@ int esp_http_client_read(esp_http_client_handle_t client, char *buffer, int len) } else { is_data_remain = client->response->data_process < client->response->content_length; } - ESP_LOGD(TAG, "is_data_remain=%d, is_chunked=%d", is_data_remain, client->response->is_chunked); + ESP_LOGD(TAG, "is_data_remain=%d, is_chunked=%d, content_length=%d", is_data_remain, client->response->is_chunked, client->response->content_length); if (!is_data_remain) { break; } int byte_to_read = need_read; - if (byte_to_read > client->buffer_size) { - byte_to_read = client->buffer_size; + if (byte_to_read > client->buffer_size_rx) { + byte_to_read = client->buffer_size_rx; } + errno = 0; rlen = esp_transport_read(client->transport, res_buffer->data, byte_to_read, client->timeout_ms); ESP_LOGD(TAG, "need_read=%d, byte_to_read=%d, rlen=%d, ridx=%d", need_read, byte_to_read, rlen, ridx); if (rlen <= 0) { - return ridx; + if (errno != 0) { + esp_log_level_t sev = ESP_LOG_WARN; + /* On connection close from server, recv should ideally return 0 but we have error conversion + * in `tcp_transport` SSL layer which translates it `-1` and hence below additional checks */ + if (rlen == -1 && errno == ENOTCONN && client->response->is_chunked) { + /* Explicit call to parser for invoking `message_complete` callback */ + http_parser_execute(client->parser, client->parser_settings, res_buffer->data, 0); + /* ...and lowering the message severity, as closed connection from server side is expected in chunked transport */ + sev = ESP_LOG_DEBUG; + } + ESP_LOG_LEVEL(sev, TAG, "esp_transport_read returned:%d and errno:%d ", rlen, errno); + } + if (rlen < 0 && ridx == 0) { + return ESP_FAIL; + } else { + return ridx; + } } res_buffer->output_ptr = buffer + ridx; http_parser_execute(client->parser, client->parser_settings, res_buffer->data, rlen); @@ -893,8 +993,8 @@ int esp_http_client_fetch_headers(esp_http_client_handle_t client) client->response->status_code = -1; while (client->state < HTTP_STATE_RES_COMPLETE_HEADER) { - buffer->len = esp_transport_read(client->transport, buffer->data, client->buffer_size, client->timeout_ms); - if (buffer->len < 0) { + buffer->len = esp_transport_read(client->transport, buffer->data, client->buffer_size_rx, client->timeout_ms); + if (buffer->len <= 0) { return ESP_FAIL; } http_parser_execute(client->parser, client->parser_settings, buffer->data, buffer->len); @@ -971,26 +1071,26 @@ static int http_client_prepare_first_line(esp_http_client_handle_t client, int w const char *method = HTTP_METHOD_MAPPING[client->connection_info.method]; int first_line_len = snprintf(client->request->buffer->data, - client->buffer_size, "%s %s", + client->buffer_size_tx, "%s %s", method, client->connection_info.path); - if (first_line_len >= client->buffer_size) { + if (first_line_len >= client->buffer_size_tx) { ESP_LOGE(TAG, "Out of buffer"); return -1; } if (client->connection_info.query) { first_line_len += snprintf(client->request->buffer->data + first_line_len, - client->buffer_size - first_line_len, "?%s", client->connection_info.query); - if (first_line_len >= client->buffer_size) { + client->buffer_size_tx - first_line_len, "?%s", client->connection_info.query); + if (first_line_len >= client->buffer_size_tx) { ESP_LOGE(TAG, "Out of buffer"); return -1; } } first_line_len += snprintf(client->request->buffer->data + first_line_len, - client->buffer_size - first_line_len, " %s\r\n", DEFAULT_HTTP_PROTOCOL); - if (first_line_len >= client->buffer_size) { + client->buffer_size_tx - first_line_len, " %s\r\n", DEFAULT_HTTP_PROTOCOL); + if (first_line_len >= client->buffer_size_tx) { ESP_LOGE(TAG, "Out of buffer"); return -1; } @@ -1025,7 +1125,7 @@ static esp_err_t esp_http_client_request_send(esp_http_client_handle_t client, i } } - int wlen = client->buffer_size - first_line_len; + int wlen = client->buffer_size_tx - first_line_len; while ((client->header_index = http_header_generate_string(client->request->headers, client->header_index, client->request->buffer->data + first_line_len, &wlen))) { if (wlen <= 0) { break; @@ -1049,11 +1149,12 @@ static esp_err_t esp_http_client_request_send(esp_http_client_handle_t client, i client->data_write_left -= wret; client->data_written_index += wret; } - wlen = client->buffer_size; + wlen = client->buffer_size_tx; } client->data_written_index = 0; client->data_write_left = client->post_len; + http_dispatch_event(client, HTTP_EVENT_HEADERS_SENT, NULL, 0); client->state = HTTP_STATE_REQ_COMPLETE_HEADER; return ESP_OK; } @@ -1088,12 +1189,13 @@ success: esp_err_t esp_http_client_open(esp_http_client_handle_t client, int write_len) { + client->post_len = write_len; esp_err_t err; if ((err = esp_http_client_connect(client)) != ESP_OK) { return err; } if ((err = esp_http_client_request_send(client, write_len)) != ESP_OK) { - return err; + return err; } return ESP_OK; } @@ -1121,7 +1223,7 @@ int esp_http_client_write(esp_http_client_handle_t client, const char *buffer, i esp_err_t esp_http_client_close(esp_http_client_handle_t client) { if (client->state >= HTTP_STATE_INIT) { - http_dispatch_event(client, HTTP_EVENT_DISCONNECTED, NULL, 0); + http_dispatch_event(client, HTTP_EVENT_DISCONNECTED, esp_transport_get_error_handle(client->transport), 0); client->state = HTTP_STATE_INIT; return esp_transport_close(client->transport); } @@ -1175,11 +1277,83 @@ bool esp_http_client_is_chunked_response(esp_http_client_handle_t client) esp_http_client_transport_t esp_http_client_get_transport_type(esp_http_client_handle_t client) { - if (!strcmp(client->connection_info.scheme, "https") ) { + if (!strcasecmp(client->connection_info.scheme, "https") ) { return HTTP_TRANSPORT_OVER_SSL; - } else if (!strcmp(client->connection_info.scheme, "http")) { + } else if (!strcasecmp(client->connection_info.scheme, "http")) { return HTTP_TRANSPORT_OVER_TCP; } else { return HTTP_TRANSPORT_UNKNOWN; } } + +void esp_http_client_add_auth(esp_http_client_handle_t client) +{ + if (client == NULL) { + return; + } + if (client->state != HTTP_STATE_RES_COMPLETE_HEADER) { + return; + } + + char *auth_header = client->auth_header; + if (auth_header) { + http_utils_trim_whitespace(&auth_header); + ESP_LOGD(TAG, "UNAUTHORIZED: %s", auth_header); + client->redirect_counter++; + if (http_utils_str_starts_with(auth_header, "Digest") == 0) { + ESP_LOGD(TAG, "type = Digest"); + client->connection_info.auth_type = HTTP_AUTH_TYPE_DIGEST; +#ifdef CONFIG_ESP_HTTP_CLIENT_ENABLE_BASIC_AUTH + } else if (http_utils_str_starts_with(auth_header, "Basic") == 0) { + ESP_LOGD(TAG, "type = Basic"); + client->connection_info.auth_type = HTTP_AUTH_TYPE_BASIC; +#endif + } else { + client->connection_info.auth_type = HTTP_AUTH_TYPE_NONE; + ESP_LOGE(TAG, "This authentication method is not supported: %s", auth_header); + return; + } + + _clear_auth_data(client); + + client->auth_data->method = strdup(HTTP_METHOD_MAPPING[client->connection_info.method]); + + client->auth_data->nc = 1; + client->auth_data->realm = http_utils_get_string_between(auth_header, "realm=\"", "\""); + client->auth_data->algorithm = http_utils_get_string_between(auth_header, "algorithm=", ","); + client->auth_data->qop = http_utils_get_string_between(auth_header, "qop=\"", "\""); + client->auth_data->nonce = http_utils_get_string_between(auth_header, "nonce=\"", "\""); + client->auth_data->opaque = http_utils_get_string_between(auth_header, "opaque=\"", "\""); + client->process_again = 1; + } else { + client->connection_info.auth_type = HTTP_AUTH_TYPE_NONE; + 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; +} + +esp_err_t esp_http_client_get_url(esp_http_client_handle_t client, char *url, const int len) +{ + if (client == NULL || url == NULL) { + return ESP_ERR_INVALID_ARG; + } + if (client->connection_info.host && client->connection_info.scheme && client->connection_info.path) { + snprintf(url, len, "%s://%s%s", client->connection_info.scheme, client->connection_info.host, client->connection_info.path); + return ESP_OK; + } else { + ESP_LOGE(TAG, "Failed to get URL"); + } + return ESP_FAIL; +} diff --git a/components/esp_http_client/include/esp_http_client.h b/components/esp_http_client/include/esp_http_client.h index a1bba70f..8c2c148a 100644 --- a/components/esp_http_client/include/esp_http_client.h +++ b/components/esp_http_client/include/esp_http_client.h @@ -24,7 +24,7 @@ extern "C" { #endif -#define DEFAULT_HTTP_BUF_SIZE CONFIG_HTTP_BUF_SIZE +#define DEFAULT_HTTP_BUF_SIZE (512) typedef struct esp_http_client *esp_http_client_handle_t; typedef struct esp_http_client_event *esp_http_client_event_handle_t; @@ -35,7 +35,9 @@ typedef struct esp_http_client_event *esp_http_client_event_handle_t; typedef enum { HTTP_EVENT_ERROR = 0, /*!< This event occurs when there are any errors during execution */ HTTP_EVENT_ON_CONNECTED, /*!< Once the HTTP has been connected to the server, no data exchange has been performed */ - HTTP_EVENT_HEADER_SENT, /*!< After sending all the headers to the server */ + HTTP_EVENT_HEADERS_SENT, /*!< After sending all the headers to the server */ + HTTP_EVENT_HEADER_SENT = HTTP_EVENT_HEADERS_SENT, /*!< This header has been kept for backward compatability + and will be deprecated in future versions esp-idf */ HTTP_EVENT_ON_HEADER, /*!< Occurs when receiving each header sent from the server */ HTTP_EVENT_ON_DATA, /*!< Occurs when receiving data from the server, possibly multiple portions of the packet */ HTTP_EVENT_ON_FINISH, /*!< Occurs when finish a HTTP session */ @@ -105,18 +107,35 @@ typedef struct { esp_http_client_auth_type_t auth_type; /*!< Http authentication type, see `esp_http_client_auth_type_t` */ const char *path; /*!< HTTP Path, if not set, default is `/` */ const char *query; /*!< HTTP query */ - const char *cert_pem; /*!< SSL Certification, PEM format as string, if the client requires to verify server */ + const char *cert_pem; /*!< SSL server certification, PEM format as string, if the client requires to verify server */ + const char *client_cert_pem; /*!< SSL client certification, PEM format as string, if the server requires to verify client */ + const char *client_key_pem; /*!< SSL client key, PEM format as string, if the server requires to verify client */ esp_http_client_method_t method; /*!< HTTP Method */ int timeout_ms; /*!< Network timeout in milliseconds */ bool disable_auto_redirect; /*!< Disable HTTP automatic redirects */ int max_redirection_count; /*!< Max redirection number, using default value if zero*/ http_event_handle_cb event_handler; /*!< HTTP Event Handle */ esp_http_client_transport_t transport_type; /*!< HTTP transport type, see `esp_http_client_transport_t` */ - int buffer_size; /*!< HTTP buffer size (both send and receive) */ + int buffer_size; /*!< HTTP receive buffer size */ + int buffer_size_tx; /*!< HTTP transmit buffer size */ void *user_data; /*!< HTTP user_data context */ bool is_async; /*!< Set asynchronous mode, only supported with HTTPS for now */ + bool use_global_ca_store; /*!< Use a global ca_store for all the connections in which this bool is set. */ + bool skip_cert_common_name_check; /*!< Skip any validation of server certificate CN field */ } esp_http_client_config_t; +/** + * Enum for the HTTP status codes. + */ +typedef enum { + /* 3xx - Redirection */ + HttpStatus_MovedPermanently = 301, + HttpStatus_Found = 302, + HttpStatus_TemporaryRedirect = 307, + + /* 4xx - Client Error */ + HttpStatus_Unauthorized = 401 +} HttpStatus_Code; #define ESP_ERR_HTTP_BASE (0x7000) /*!< Starting number of HTTP error codes */ #define ESP_ERR_HTTP_MAX_REDIRECT (ESP_ERR_HTTP_BASE + 1) /*!< The error exceeds the number of HTTP redirects */ @@ -232,13 +251,83 @@ esp_err_t esp_http_client_set_header(esp_http_client_handle_t client, const char */ esp_err_t esp_http_client_get_header(esp_http_client_handle_t client, const char *key, char **value); +/** + * @brief Get http request username. + * The address of username buffer will be assigned to value parameter. + * This function must be called after `esp_http_client_init`. + * + * @param[in] client The esp_http_client handle + * @param[out] value The username value + * + * @return + * - ESP_OK + * - ESP_ERR_INVALID_ARG + */ +esp_err_t esp_http_client_get_username(esp_http_client_handle_t client, char **value); + +/** + * @brief Set http request username. + * The value of username parameter will be assigned to username buffer. + * If the username parameter is NULL then username buffer will be freed. + * + * @param[in] client The esp_http_client handle + * @param[in] username The username value + * + * @return + * - ESP_OK + * - ESP_ERR_INVALID_ARG + */ +esp_err_t esp_http_client_set_username(esp_http_client_handle_t client, const char *username); + +/** + * @brief Get http request password. + * The address of password buffer will be assigned to value parameter. + * This function must be called after `esp_http_client_init`. + * + * @param[in] client The esp_http_client handle + * @param[out] value The password value + * + * @return + * - ESP_OK + * - ESP_ERR_INVALID_ARG + */ +esp_err_t esp_http_client_get_password(esp_http_client_handle_t client, char **value); + +/** + * @brief Set http request password. + * The value of password parameter will be assigned to password buffer. + * If the password parameter is NULL then password buffer will be freed. + * + * @param[in] client The esp_http_client handle + * @param[in] password The password value + * + * @return + * - ESP_OK + * - ESP_ERR_INVALID_ARG + */ +esp_err_t esp_http_client_set_password(esp_http_client_handle_t client, char *password); + +/** + * @brief Set http request auth_type. + * + * @param[in] client The esp_http_client handle + * @param[in] auth_type The esp_http_client auth type + * + * @return + * - ESP_OK + * - ESP_ERR_INVALID_ARG + */ +esp_err_t esp_http_client_set_authtype(esp_http_client_handle_t client, esp_http_client_auth_type_t auth_type); + /** * @brief Set http request method * * @param[in] client The esp_http_client handle * @param[in] method The method * - * @return ESP_OK + * @return + * - ESP_OK + * - ESP_ERR_INVALID_ARG */ esp_err_t esp_http_client_set_method(esp_http_client_handle_t client, esp_http_client_method_t method); @@ -373,6 +462,68 @@ esp_err_t esp_http_client_cleanup(esp_http_client_handle_t client); */ esp_http_client_transport_t esp_http_client_get_transport_type(esp_http_client_handle_t client); +/** + * @brief Set redirection URL. + * When received the 30x code from the server, the client stores the redirect URL provided by the server. + * This function will set the current URL to redirect to enable client to execute the redirection request. + * + * @param[in] client The esp_http_client handle + * + * @return + * - ESP_OK + * - ESP_FAIL + */ +esp_err_t esp_http_client_set_redirection(esp_http_client_handle_t client); + +/** + * @brief On receiving HTTP Status code 401, this API can be invoked to add authorization + * information. + * + * @note There is a possibility of receiving body message with redirection status codes, thus make sure + * to flush off body data after calling this API. + * + * @param[in] client The esp_http_client handle + */ +void esp_http_client_add_auth(esp_http_client_handle_t client); + +/** + * @brief Checks if entire data in the response has been read without any error. + * + * @param[in] client The esp_http_client handle + * + * @return + * - true + * - false + */ +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); + +/** + * @brief Get URL from client + * + * @param[in] client The esp_http_client handle + * @param[inout] url The buffer to store URL + * @param[in] len The buffer length + * + * @return + * - ESP_OK + * - ESP_FAIL + */ + +esp_err_t esp_http_client_get_url(esp_http_client_handle_t client, char *url, const int len); #ifdef __cplusplus } diff --git a/components/esp_http_client/lib/http_auth.c b/components/esp_http_client/lib/http_auth.c index c3975aba..1fcc2f11 100644 --- a/components/esp_http_client/lib/http_auth.c +++ b/components/esp_http_client/lib/http_auth.c @@ -17,11 +17,11 @@ #include #include -#include "tcpip_adapter.h" +#include "esp_netif.h" #include "lwip/sockets.h" -#include "rom/md5_hash.h" +#include "esp32/rom/md5_hash.h" +#include "mbedtls/base64.h" -#include "esp_base64.h" #include "esp_system.h" #include "esp_log.h" @@ -81,10 +81,6 @@ char *http_auth_digest(const char *username, const char *password, esp_http_auth return NULL; } - if (auth_data->algorithm == NULL) { - auth_data->algorithm = "md5"; - } - ha1 = calloc(1, MD5_MAX_LEN); HTTP_MEM_CHECK(TAG, ha1, goto _digest_exit); @@ -138,19 +134,17 @@ _digest_exit: char *http_auth_basic(const char *username, const char *password) { + int out; char *user_info = NULL; char *digest = NULL; - size_t n = 0, size = 0; + size_t n = 0; asprintf(&user_info, "%s:%s", username, password); HTTP_MEM_CHECK(TAG, user_info, return NULL); - size = strlen(user_info); - n = (size / 3) * 4 + 1; // String to Base64 length calculation - if (size % 3 != 0) - n += 4; - digest = calloc(1, 6 + n); + mbedtls_base64_encode(NULL, 0, &n, (const unsigned char *)user_info, strlen(user_info)); + digest = calloc(1, 6 + n + 1); HTTP_MEM_CHECK(TAG, digest, goto _basic_exit); strcpy(digest, "Basic "); - n = esp_base64_encode((const unsigned char *)user_info, strlen(user_info), (unsigned char *)digest + 6, n); + mbedtls_base64_encode((unsigned char *)digest + 6, n, (size_t *)&out, (const unsigned char *)user_info, strlen(user_info)); _basic_exit: free(user_info); return digest; diff --git a/components/esp_http_client/lib/http_header.c b/components/esp_http_client/lib/http_header.c index b771f6f6..b3dda8ee 100644 --- a/components/esp_http_client/lib/http_header.c +++ b/components/esp_http_client/lib/http_header.c @@ -37,7 +37,7 @@ typedef struct http_header_item { STAILQ_HEAD(http_header, http_header_item); -http_header_handle_t http_header_init() +http_header_handle_t http_header_init(void) { http_header_handle_t header = calloc(1, sizeof(struct http_header)); HTTP_MEM_CHECK(TAG, header, return NULL); @@ -178,6 +178,8 @@ int http_header_generate_string(http_header_handle_t header, int index, char *bu int idx = 0; int ret_idx = -1; bool is_end = false; + + // iterate over the header entries to calculate buffer size and determine last item STAILQ_FOREACH(item, header, next) { if (item->value && idx >= index) { siz += strlen(item->key); @@ -187,7 +189,10 @@ int http_header_generate_string(http_header_handle_t header, int index, char *bu idx ++; if (siz + 1 > *buffer_len - 2) { + // if this item would not fit to the buffer, return the index of the last fitting one ret_idx = idx - 1; + ESP_LOGE(TAG, "Buffer length is small to fit all the headers"); + break; } } @@ -195,10 +200,12 @@ int http_header_generate_string(http_header_handle_t header, int index, char *bu return 0; } if (ret_idx < 0) { + // all items would fit, mark this as the end of http header string ret_idx = idx; is_end = true; } + // iterate again over the header entries to write only the fitting indeces int str_len = 0; idx = 0; STAILQ_FOREACH(item, header, next) { @@ -208,6 +215,7 @@ int http_header_generate_string(http_header_handle_t header, int index, char *bu idx ++; } if (is_end) { + // write the http header terminator if all header entries have been written in this function call str_len += snprintf(buffer + str_len, *buffer_len - str_len, "\r\n"); } *buffer_len = str_len; diff --git a/components/esp_http_client/lib/include/http_header.h b/components/esp_http_client/lib/include/http_header.h index 7c680daf..9d8ea3ad 100644 --- a/components/esp_http_client/lib/include/http_header.h +++ b/components/esp_http_client/lib/include/http_header.h @@ -15,7 +15,7 @@ #ifndef _HTTP_HEADER_H_ #define _HTTP_HEADER_H_ -#include "rom/queue.h" +#include "sys/queue.h" #include "esp_err.h" #ifdef __cplusplus @@ -32,7 +32,7 @@ typedef struct http_header_item *http_header_item_handle_t; * - http_header_handle_t * - NULL if any errors */ -http_header_handle_t http_header_init(); +http_header_handle_t http_header_init(void); /** * @brief Cleanup and free all http header pairs diff --git a/components/esp_http_client/test/CMakeLists.txt b/components/esp_http_client/test/CMakeLists.txt new file mode 100644 index 00000000..202d2fed --- /dev/null +++ b/components/esp_http_client/test/CMakeLists.txt @@ -0,0 +1,3 @@ +idf_component_register(SRC_DIRS "." + PRIV_INCLUDE_DIRS "." + PRIV_REQUIRES unity test_utils esp_http_client) \ No newline at end of file diff --git a/components/esp_http_client/test/component.mk b/components/esp_http_client/test/component.mk new file mode 100644 index 00000000..ce464a21 --- /dev/null +++ b/components/esp_http_client/test/component.mk @@ -0,0 +1 @@ +COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive diff --git a/components/esp_http_client/test/test_http_client.c b/components/esp_http_client/test/test_http_client.c new file mode 100644 index 00000000..4a2f725d --- /dev/null +++ b/components/esp_http_client/test/test_http_client.c @@ -0,0 +1,139 @@ +// Copyright 2018 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include + +#include "unity.h" +#include "test_utils.h" + +#define HOST "httpbin.org" +#define USERNAME "user" +#define PASSWORD "challenge" + +TEST_CASE("Test in common case: Only URL and hostname are specified.", "[ESP HTTP CLIENT]") +{ + esp_http_client_config_t config_incorrect = {0}; + + test_case_uses_tcpip(); + + esp_http_client_handle_t client = esp_http_client_init(&config_incorrect); + TEST_ASSERT(client == NULL); + + esp_http_client_config_t config_with_url = { + .url = "http://httpbin.org/get", + }; + client = esp_http_client_init(&config_with_url); + TEST_ASSERT(client != NULL); + TEST_ASSERT(esp_http_client_cleanup(client) == ESP_OK); + + + esp_http_client_config_t config_with_hostname_path = { + .host = HOST, + .path = "/get", + }; + client = esp_http_client_init(&config_with_hostname_path); + TEST_ASSERT(client != NULL); + TEST_ASSERT(esp_http_client_cleanup(client) == ESP_OK); +} + +TEST_CASE("Get username and password after initialization.", "[ESP HTTP CLIENT]") +{ + esp_http_client_config_t config_with_auth = { + .host = HOST, + .path = "/", + .username = USERNAME, + .password = PASSWORD + }; + char *value = NULL; + esp_http_client_handle_t client = esp_http_client_init(&config_with_auth); + TEST_ASSERT_NOT_NULL(client); + // Test with username + esp_err_t r = esp_http_client_get_username(client, &value); + TEST_ASSERT_EQUAL(ESP_OK, r); + TEST_ASSERT_NOT_NULL(value); + TEST_ASSERT_EQUAL_STRING(USERNAME, value); + // Test with password + value = NULL; + r = esp_http_client_get_password(client, &value); + TEST_ASSERT_EQUAL(ESP_OK, r); + TEST_ASSERT_NOT_NULL(value); + TEST_ASSERT_EQUAL_STRING(PASSWORD, value); + esp_http_client_cleanup(client); +} + +/** + * Test case to test that, the esp_http_client_set_url won't drop username and password + * when pass a path "/abc" for url. + **/ +TEST_CASE("Username is unmodified when we change to new path", "[ESP HTTP CLIENT]") +{ + esp_http_client_config_t config_with_auth = { + .host = HOST, + .path = "/", + .username = USERNAME, + .password = PASSWORD + }; + char *value = NULL; + esp_http_client_handle_t client = esp_http_client_init(&config_with_auth); + TEST_ASSERT_NOT_NULL(client); + esp_err_t r = esp_http_client_get_username(client, &value); + TEST_ASSERT_EQUAL(ESP_OK, r); + TEST_ASSERT_NOT_NULL(value); + TEST_ASSERT_EQUAL_STRING(USERNAME, value); + esp_http_client_set_url(client, "/something-else/"); + r = esp_http_client_get_username(client, &value); + TEST_ASSERT_EQUAL(ESP_OK, r); + TEST_ASSERT_NOT_NULL(value); + TEST_ASSERT_EQUAL_STRING(USERNAME, value); + esp_http_client_cleanup(client); +} + +/** + * Test case to test that, the esp_http_client_set_url do not reset the auth credentials + * Explicit APIs esp_http_client_set_username and esp_http_client_set_password are used to change + * the auth credentials + **/ +TEST_CASE("Username and password will not reset if new absolute URL doesnot specify auth credentials.", "[ESP HTTP CLIENT]") +{ + esp_http_client_config_t config_with_auth = { + .host = HOST, + .path = "/", + .username = USERNAME, + .password = PASSWORD + }; + char *value = NULL; + esp_http_client_handle_t client = esp_http_client_init(&config_with_auth); + TEST_ASSERT_NOT_NULL(client); + esp_err_t r = esp_http_client_get_username(client, &value); + TEST_ASSERT_EQUAL(ESP_OK, r); + TEST_ASSERT_NOT_NULL(value); + TEST_ASSERT_EQUAL_STRING(USERNAME, value); + esp_http_client_set_url(client, "http://" HOST "/get"); + esp_http_client_set_username(client, value); + esp_http_client_set_password(client, value); + //checks if username is set or not + r = esp_http_client_get_username(client, &value); + TEST_ASSERT_EQUAL(ESP_OK, r); + //If username is set then value should not be NULL + TEST_ASSERT_NOT_NULL(value); + //checks if password is set or not + r = esp_http_client_get_password(client, &value); + TEST_ASSERT_EQUAL(ESP_OK, r); + //If password is set then value should not be NULL + TEST_ASSERT_NOT_NULL(value); + esp_http_client_cleanup(client); +} diff --git a/examples/protocols/esp_http_client/esp_http_client_test.py b/examples/protocols/esp_http_client/esp_http_client_test.py new file mode 100644 index 00000000..0332ecc6 --- /dev/null +++ b/examples/protocols/esp_http_client/esp_http_client_test.py @@ -0,0 +1,67 @@ +import re +import os + +import ttfw_idf + + +@ttfw_idf.idf_example_test(env_tag="Example_EthKitV1") +def test_examples_protocol_esp_http_client(env, extra_data): + """ + steps: | + 1. join AP + 2. Send HTTP request to httpbin.org + """ + dut1 = env.get_dut("esp_http_client", "examples/protocols/esp_http_client", dut_class=ttfw_idf.ESP32DUT) + # check and log bin size + binary_file = os.path.join(dut1.app.binary_path, "esp-http-client-example.bin") + bin_size = os.path.getsize(binary_file) + ttfw_idf.log_performance("esp_http_client_bin_size", "{}KB".format(bin_size // 1024)) + ttfw_idf.check_performance("esp_http_client_bin_size", bin_size // 1024, dut1.TARGET) + # start test + dut1.start_app() + dut1.expect("Connected to AP, begin http example", timeout=30) + dut1.expect(re.compile(r"HTTP GET Status = 200, content_length = (\d)")) + dut1.expect(re.compile(r"HTTP POST Status = 200, content_length = (\d)")) + dut1.expect(re.compile(r"HTTP PUT Status = 200, content_length = (\d)")) + dut1.expect(re.compile(r"HTTP PATCH Status = 200, content_length = (\d)")) + dut1.expect(re.compile(r"HTTP DELETE Status = 200, content_length = (\d)")) + dut1.expect(re.compile(r"HTTP HEAD Status = 200, content_length = (\d)")) + dut1.expect(re.compile(r"HTTP Basic Auth Status = 200, content_length = (\d)")) + dut1.expect(re.compile(r"HTTP Basic Auth redirect Status = 200, content_length = (\d)")) + dut1.expect(re.compile(r"HTTP Digest Auth Status = 200, content_length = (\d)")) + dut1.expect(re.compile(r"HTTPS Status = 200, content_length = (\d)")) + dut1.expect(re.compile(r"HTTP chunk encoding Status = 200, content_length = (-?\d)")) + # content-len for chunked encoding is typically -1, could be a positive length in some cases + dut1.expect(re.compile(r"HTTP Stream reader Status = 200, content_length = (\d)")) + dut1.expect(re.compile(r"Last esp error code: 0x8001")) + dut1.expect("Finish http example") + + # test mbedtls dynamic resource + dut1 = env.get_dut("esp_http_client", "examples/protocols/esp_http_client", dut_class=ttfw_idf.ESP32DUT, app_config_name='ssldyn') + # check and log bin size + binary_file = os.path.join(dut1.app.binary_path, "esp-http-client-example.bin") + bin_size = os.path.getsize(binary_file) + ttfw_idf.log_performance("esp_http_client_bin_size", "{}KB".format(bin_size // 1024)) + ttfw_idf.check_performance("esp_http_client_bin_size", bin_size // 1024, dut1.TARGET) + # start test + dut1.start_app() + dut1.expect("Connected to AP, begin http example", timeout=30) + dut1.expect(re.compile(r"HTTP GET Status = 200, content_length = (\d)")) + dut1.expect(re.compile(r"HTTP POST Status = 200, content_length = (\d)")) + dut1.expect(re.compile(r"HTTP PUT Status = 200, content_length = (\d)")) + dut1.expect(re.compile(r"HTTP PATCH Status = 200, content_length = (\d)")) + dut1.expect(re.compile(r"HTTP DELETE Status = 200, content_length = (\d)")) + dut1.expect(re.compile(r"HTTP HEAD Status = 200, content_length = (\d)")) + dut1.expect(re.compile(r"HTTP Basic Auth Status = 200, content_length = (\d)")) + dut1.expect(re.compile(r"HTTP Basic Auth redirect Status = 200, content_length = (\d)")) + dut1.expect(re.compile(r"HTTP Digest Auth Status = 200, content_length = (\d)")) + dut1.expect(re.compile(r"HTTPS Status = 200, content_length = (\d)")) + dut1.expect(re.compile(r"HTTP chunk encoding Status = 200, content_length = (-?\d)")) + # content-len for chunked encoding is typically -1, could be a positive length in some cases + dut1.expect(re.compile(r"HTTP Stream reader Status = 200, content_length = (\d)")) + dut1.expect(re.compile(r"Last esp error code: 0x8001")) + dut1.expect("Finish http example") + + +if __name__ == '__main__': + test_examples_protocol_esp_http_client() diff --git a/examples/protocols/esp_http_client/main/CMakeLists.txt b/examples/protocols/esp_http_client/main/CMakeLists.txt index 48f4e831..8cc92e71 100644 --- a/examples/protocols/esp_http_client/main/CMakeLists.txt +++ b/examples/protocols/esp_http_client/main/CMakeLists.txt @@ -1,8 +1,6 @@ -set(COMPONENT_SRCS "esp_http_client_example.c") - # Embed the server root certificate into the final binary # # (If this was a component, we would set COMPONENT_EMBED_TXTFILES here.) -set(COMPONENT_EMBED_TXTFILES howsmyssl_com_root_cert.pem) - -register_component() +idf_component_register(SRCS "esp_http_client_example.c" + INCLUDE_DIRS "." + EMBED_TXTFILES howsmyssl_com_root_cert.pem) \ No newline at end of file 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 4c5a751c..7cc3eb1d 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 @@ -9,20 +9,20 @@ #include #include - #include "freertos/FreeRTOS.h" #include "freertos/task.h" -#include "esp_system.h" #include "esp_log.h" -#include "esp_netif.h" -#include "esp_event.h" -#include "protocol_examples_common.h" -#include "nvs.h" +#include "esp_system.h" #include "nvs_flash.h" +#include "esp_event.h" +#include "esp_netif.h" +#include "protocol_examples_common.h" +#include "esp_tls.h" #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 @@ -40,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"); @@ -55,27 +57,73 @@ 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_LOGD(TAG, "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); + } break; } return ESP_OK; } -static void http_rest() +static void http_rest_with_url(void) { + char local_response_buffer[MAX_HTTP_OUTPUT_BUFFER] = {0}; + /** + * NOTE: All the configuration parameters for http_client must be spefied either in URL or as host and path parameters. + * If host and path parameters are not set, query parameter will be ignored. In such cases, + * query parameter should be specified in URL. + * + * If URL as well as host and path parameters are specified, values of host and path will be considered. + */ esp_http_client_config_t config = { - .url = "http://httpbin.org/get", + .host = "httpbin.org", + .path = "/get", + .query = "esp", .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); @@ -86,13 +134,15 @@ static void http_rest() esp_http_client_get_status_code(client), esp_http_client_get_content_length(client)); } else { - ESP_LOGE(TAG, "HTTP GET request failed: %d", err); + 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) { @@ -100,7 +150,7 @@ static void http_rest() esp_http_client_get_status_code(client), esp_http_client_get_content_length(client)); } else { - ESP_LOGE(TAG, "HTTP POST request failed: %d", err); + ESP_LOGE(TAG, "HTTP POST request failed: %s", esp_err_to_name(err)); } //PUT @@ -112,7 +162,7 @@ static void http_rest() esp_http_client_get_status_code(client), esp_http_client_get_content_length(client)); } else { - ESP_LOGE(TAG, "HTTP PUT request failed: %d", err); + ESP_LOGE(TAG, "HTTP PUT request failed: %s", esp_err_to_name(err)); } //PATCH @@ -125,7 +175,7 @@ static void http_rest() esp_http_client_get_status_code(client), esp_http_client_get_content_length(client)); } else { - ESP_LOGE(TAG, "HTTP PATCH request failed: %d", err); + ESP_LOGE(TAG, "HTTP PATCH request failed: %s", esp_err_to_name(err)); } //DELETE @@ -137,7 +187,7 @@ static void http_rest() esp_http_client_get_status_code(client), esp_http_client_get_content_length(client)); } else { - ESP_LOGE(TAG, "HTTP DELETE request failed: %d", err); + ESP_LOGE(TAG, "HTTP DELETE request failed: %s", esp_err_to_name(err)); } //HEAD @@ -149,13 +199,100 @@ static void http_rest() esp_http_client_get_status_code(client), esp_http_client_get_content_length(client)); } else { - ESP_LOGE(TAG, "HTTP HEAD request failed: %d", err); + ESP_LOGE(TAG, "HTTP HEAD request failed: %s", esp_err_to_name(err)); } esp_http_client_cleanup(client); } -static void http_auth_basic() +static void http_rest_with_hostname_path(void) +{ + esp_http_client_config_t config = { + .host = "httpbin.org", + .path = "/get", + .transport_type = HTTP_TRANSPORT_OVER_TCP, + .event_handler = _http_event_handler, + }; + esp_http_client_handle_t client = esp_http_client_init(&config); + + // GET + esp_err_t err = esp_http_client_perform(client); + if (err == ESP_OK) { + ESP_LOGI(TAG, "HTTP GET Status = %d, content_length = %d", + esp_http_client_get_status_code(client), + esp_http_client_get_content_length(client)); + } else { + ESP_LOGE(TAG, "HTTP GET request failed: %s", esp_err_to_name(err)); + } + + // POST + const char *post_data = "field1=value1&field2=value2"; + esp_http_client_set_url(client, "/post"); + esp_http_client_set_method(client, HTTP_METHOD_POST); + esp_http_client_set_post_field(client, post_data, strlen(post_data)); + err = esp_http_client_perform(client); + if (err == ESP_OK) { + ESP_LOGI(TAG, "HTTP POST Status = %d, content_length = %d", + esp_http_client_get_status_code(client), + esp_http_client_get_content_length(client)); + } else { + ESP_LOGE(TAG, "HTTP POST request failed: %s", esp_err_to_name(err)); + } + + //PUT + esp_http_client_set_url(client, "/put"); + esp_http_client_set_method(client, HTTP_METHOD_PUT); + err = esp_http_client_perform(client); + if (err == ESP_OK) { + ESP_LOGI(TAG, "HTTP PUT Status = %d, content_length = %d", + esp_http_client_get_status_code(client), + esp_http_client_get_content_length(client)); + } else { + ESP_LOGE(TAG, "HTTP PUT request failed: %s", esp_err_to_name(err)); + } + + //PATCH + esp_http_client_set_url(client, "/patch"); + esp_http_client_set_method(client, HTTP_METHOD_PATCH); + esp_http_client_set_post_field(client, NULL, 0); + err = esp_http_client_perform(client); + if (err == ESP_OK) { + ESP_LOGI(TAG, "HTTP PATCH Status = %d, content_length = %d", + esp_http_client_get_status_code(client), + esp_http_client_get_content_length(client)); + } else { + ESP_LOGE(TAG, "HTTP PATCH request failed: %s", esp_err_to_name(err)); + } + + //DELETE + esp_http_client_set_url(client, "/delete"); + esp_http_client_set_method(client, HTTP_METHOD_DELETE); + err = esp_http_client_perform(client); + if (err == ESP_OK) { + ESP_LOGI(TAG, "HTTP DELETE Status = %d, content_length = %d", + esp_http_client_get_status_code(client), + esp_http_client_get_content_length(client)); + } else { + ESP_LOGE(TAG, "HTTP DELETE request failed: %s", esp_err_to_name(err)); + } + + //HEAD + esp_http_client_set_url(client, "/get"); + esp_http_client_set_method(client, HTTP_METHOD_HEAD); + err = esp_http_client_perform(client); + if (err == ESP_OK) { + ESP_LOGI(TAG, "HTTP HEAD Status = %d, content_length = %d", + esp_http_client_get_status_code(client), + esp_http_client_get_content_length(client)); + } else { + ESP_LOGE(TAG, "HTTP HEAD request failed: %s", esp_err_to_name(err)); + } + + 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 = { .url = "http://user:passwd@httpbin.org/basic-auth/user/passwd", @@ -170,12 +307,12 @@ static void http_auth_basic() esp_http_client_get_status_code(client), esp_http_client_get_content_length(client)); } else { - ESP_LOGE(TAG, "Error perform http request %d", err); + ESP_LOGE(TAG, "Error perform http request %s", esp_err_to_name(err)); } esp_http_client_cleanup(client); } -static void http_auth_basic_redirect() +static void http_auth_basic_redirect(void) { esp_http_client_config_t config = { .url = "http://user:passwd@httpbin.org/basic-auth/user/passwd", @@ -183,18 +320,19 @@ static void http_auth_basic_redirect() }; esp_http_client_handle_t client = esp_http_client_init(&config); esp_err_t err = esp_http_client_perform(client); + if (err == ESP_OK) { ESP_LOGI(TAG, "HTTP Basic Auth redirect Status = %d, content_length = %d", esp_http_client_get_status_code(client), esp_http_client_get_content_length(client)); } else { - ESP_LOGE(TAG, "Error perform http request %d", err); + ESP_LOGE(TAG, "Error perform http request %s", esp_err_to_name(err)); } - esp_http_client_cleanup(client); } +#endif -static void http_auth_digest() +static void http_auth_digest(void) { esp_http_client_config_t config = { .url = "http://user:passwd@httpbin.org/digest-auth/auth/user/passwd/MD5/never", @@ -202,17 +340,18 @@ static void http_auth_digest() }; esp_http_client_handle_t client = esp_http_client_init(&config); esp_err_t err = esp_http_client_perform(client); + if (err == ESP_OK) { ESP_LOGI(TAG, "HTTP Digest Auth Status = %d, content_length = %d", esp_http_client_get_status_code(client), esp_http_client_get_content_length(client)); } else { - ESP_LOGE(TAG, "Error perform http request %d", err); + ESP_LOGE(TAG, "Error perform http request %s", esp_err_to_name(err)); } esp_http_client_cleanup(client); } -static void https() +static void https_with_url(void) { esp_http_client_config_t config = { .url = "https://www.howsmyssl.com", @@ -227,12 +366,34 @@ static void https() esp_http_client_get_status_code(client), esp_http_client_get_content_length(client)); } else { - ESP_LOGE(TAG, "Error perform http request %d", err); + ESP_LOGE(TAG, "Error perform http request %s", esp_err_to_name(err)); } esp_http_client_cleanup(client); } -static void http_relative_redirect() +static void https_with_hostname_path(void) +{ + esp_http_client_config_t config = { + .host = "www.howsmyssl.com", + .path = "/", + .transport_type = HTTP_TRANSPORT_OVER_SSL, + .event_handler = _http_event_handler, + .cert_pem = howsmyssl_com_root_cert_pem_start, + }; + esp_http_client_handle_t client = esp_http_client_init(&config); + esp_err_t err = esp_http_client_perform(client); + + if (err == ESP_OK) { + ESP_LOGI(TAG, "HTTPS Status = %d, content_length = %d", + esp_http_client_get_status_code(client), + esp_http_client_get_content_length(client)); + } else { + ESP_LOGE(TAG, "Error perform http request %s", esp_err_to_name(err)); + } + esp_http_client_cleanup(client); +} + +static void http_relative_redirect(void) { esp_http_client_config_t config = { .url = "http://httpbin.org/relative-redirect/3", @@ -246,12 +407,12 @@ static void http_relative_redirect() esp_http_client_get_status_code(client), esp_http_client_get_content_length(client)); } else { - ESP_LOGE(TAG, "Error perform http request %d", err); + ESP_LOGE(TAG, "Error perform http request %s", esp_err_to_name(err)); } esp_http_client_cleanup(client); } -static void http_absolute_redirect() +static void http_absolute_redirect(void) { esp_http_client_config_t config = { .url = "http://httpbin.org/absolute-redirect/3", @@ -265,12 +426,12 @@ static void http_absolute_redirect() esp_http_client_get_status_code(client), esp_http_client_get_content_length(client)); } else { - ESP_LOGE(TAG, "Error perform http request %d", err); + ESP_LOGE(TAG, "Error perform http request %s", esp_err_to_name(err)); } esp_http_client_cleanup(client); } -static void http_redirect_to_https() +static void http_redirect_to_https(void) { esp_http_client_config_t config = { .url = "http://httpbin.org/redirect-to?url=https%3A%2F%2Fwww.howsmyssl.com", @@ -284,13 +445,13 @@ static void http_redirect_to_https() esp_http_client_get_status_code(client), esp_http_client_get_content_length(client)); } else { - ESP_LOGE(TAG, "Error perform http request %d", err); + ESP_LOGE(TAG, "Error perform http request %s", esp_err_to_name(err)); } esp_http_client_cleanup(client); } -static void http_download_chunk() +static void http_download_chunk(void) { esp_http_client_config_t config = { .url = "http://httpbin.org/stream-bytes/8912", @@ -304,12 +465,12 @@ static void http_download_chunk() esp_http_client_get_status_code(client), esp_http_client_get_content_length(client)); } else { - ESP_LOGE(TAG, "Error perform http request %d", err); + ESP_LOGE(TAG, "Error perform http request %s", esp_err_to_name(err)); } esp_http_client_cleanup(client); } -static void http_perform_as_stream_reader() +static void http_perform_as_stream_reader(void) { char *buffer = malloc(MAX_HTTP_RECV_BUFFER + 1); if (buffer == NULL) { @@ -318,12 +479,11 @@ static void http_perform_as_stream_reader() } 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; if ((err = esp_http_client_open(client, 0)) != ESP_OK) { - ESP_LOGE(TAG, "Failed to open HTTP connection: %d", err); + ESP_LOGE(TAG, "Failed to open HTTP connection: %s", esp_err_to_name(err)); free(buffer); return; } @@ -345,7 +505,7 @@ static void http_perform_as_stream_reader() free(buffer); } -static void https_async() +static void https_async(void) { esp_http_client_config_t config = { .url = "https://postman-echo.com/post", @@ -372,37 +532,135 @@ static void https_async() esp_http_client_get_status_code(client), esp_http_client_get_content_length(client)); } else { - ESP_LOGE(TAG, "Error perform http request %d", err); + ESP_LOGE(TAG, "Error perform http request %s", esp_err_to_name(err)); + } + esp_http_client_cleanup(client); +} + +static void https_with_invalid_url(void) +{ + esp_http_client_config_t config = { + .url = "https://not.existent.url", + .event_handler = _http_event_handler, + }; + esp_http_client_handle_t client = esp_http_client_init(&config); + esp_err_t err = esp_http_client_perform(client); + + if (err == ESP_OK) { + ESP_LOGI(TAG, "HTTPS Status = %d, content_length = %d", + esp_http_client_get_status_code(client), + esp_http_client_get_content_length(client)); + } else { + ESP_LOGE(TAG, "Error perform http request %s", esp_err_to_name(err)); + } + 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) { - ESP_LOGI(TAG, "Connected to AP, begin http example"); - http_rest(); + 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(); - https(); + https_with_url(); + https_with_hostname_path(); http_redirect_to_https(); http_download_chunk(); http_perform_as_stream_reader(); https_async(); + https_with_invalid_url(); + http_native_request(); + ESP_LOGI(TAG, "Finish http example"); vTaskDelete(NULL); } -void app_main() +void app_main(void) { - ESP_ERROR_CHECK(nvs_flash_init()); + esp_err_t ret = nvs_flash_init(); + if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { + ESP_ERROR_CHECK(nvs_flash_erase()); + ret = nvs_flash_init(); + } + ESP_ERROR_CHECK(ret); ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); - + /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig. + * Read "Establishing Wi-Fi or Ethernet Connection" section in + * examples/protocols/README.md for more information about this function. + */ ESP_ERROR_CHECK(example_connect()); + ESP_LOGI(TAG, "Connected to AP, begin http example"); xTaskCreate(&http_test_task, "http_test_task", 8192, NULL, 5, NULL); } diff --git a/examples/protocols/esp_http_client/sdkconfig.ci b/examples/protocols/esp_http_client/sdkconfig.ci new file mode 100644 index 00000000..6132bbfb --- /dev/null +++ b/examples/protocols/esp_http_client/sdkconfig.ci @@ -0,0 +1,10 @@ +CONFIG_EXAMPLE_CONNECT_ETHERNET=y +CONFIG_EXAMPLE_CONNECT_WIFI=n +CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET=y +CONFIG_EXAMPLE_ETH_PHY_IP101=y +CONFIG_EXAMPLE_ETH_MDC_GPIO=23 +CONFIG_EXAMPLE_ETH_MDIO_GPIO=18 +CONFIG_EXAMPLE_ETH_PHY_RST_GPIO=5 +CONFIG_EXAMPLE_ETH_PHY_ADDR=1 +CONFIG_EXAMPLE_CONNECT_IPV6=y +CONFIG_ESP_HTTP_CLIENT_ENABLE_BASIC_AUTH=y diff --git a/examples/protocols/esp_http_client/sdkconfig.ci.ssldyn b/examples/protocols/esp_http_client/sdkconfig.ci.ssldyn new file mode 100644 index 00000000..d25890eb --- /dev/null +++ b/examples/protocols/esp_http_client/sdkconfig.ci.ssldyn @@ -0,0 +1,13 @@ +CONFIG_EXAMPLE_CONNECT_ETHERNET=y +CONFIG_EXAMPLE_CONNECT_WIFI=n +CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET=y +CONFIG_EXAMPLE_ETH_PHY_IP101=y +CONFIG_EXAMPLE_ETH_MDC_GPIO=23 +CONFIG_EXAMPLE_ETH_MDIO_GPIO=18 +CONFIG_EXAMPLE_ETH_PHY_RST_GPIO=5 +CONFIG_EXAMPLE_ETH_PHY_ADDR=1 +CONFIG_EXAMPLE_CONNECT_IPV6=y +CONFIG_ESP_HTTP_CLIENT_ENABLE_BASIC_AUTH=y +CONFIG_MBEDTLS_DYNAMIC_BUFFER=y +CONFIG_MBEDTLS_DYNAMIC_FREE_PEER_CERT=y +CONFIG_MBEDTLS_DYNAMIC_FREE_CONFIG_DATA=y diff --git a/examples/protocols/esp_http_client/sdkconfig.defaults b/examples/protocols/esp_http_client/sdkconfig.defaults deleted file mode 100644 index 2b9ef82e..00000000 --- a/examples/protocols/esp_http_client/sdkconfig.defaults +++ /dev/null @@ -1,3 +0,0 @@ -CONFIG_NEWLIB_LIBRARY_LEVEL_NORMAL=y -CONFIG_MBEDTLS_SSL_OUT_CONTENT_LEN=1024 -CONFIG_MBEDTLS_SSL_IN_CONTENT_LEN=16384