diff --git a/components/esp_https_server/CMakeLists.txt b/components/esp_https_server/CMakeLists.txt index 916597b723..1529a810c1 100644 --- a/components/esp_https_server/CMakeLists.txt +++ b/components/esp_https_server/CMakeLists.txt @@ -3,5 +3,5 @@ set(inc "include") idf_component_register(SRCS ${src} INCLUDE_DIRS ${inc} - REQUIRES esp_http_server esp-tls + REQUIRES esp_http_server esp-tls esp_event PRIV_REQUIRES lwip) diff --git a/components/esp_https_server/include/esp_https_server.h b/components/esp_https_server/include/esp_https_server.h index 1de884127e..786ab8e658 100644 --- a/components/esp_https_server/include/esp_https_server.h +++ b/components/esp_https_server/include/esp_https_server.h @@ -12,10 +12,24 @@ #include "esp_http_server.h" #include "esp_tls.h" +#include "esp_event.h" + #ifdef __cplusplus extern "C" { #endif +ESP_EVENT_DECLARE_BASE(ESP_HTTPS_SERVER_EVENT); + +typedef enum { + HTTPS_SERVER_EVENT_ERROR = 0, /*!< This event occurs when there are any errors during execution */ + HTTPS_SERVER_EVENT_START, /*!< This event occurs when HTTPS Server is started */ + HTTPS_SERVER_EVENT_ON_CONNECTED, /*!< Once the HTTPS Server has been connected to the client */ + HTTPS_SERVER_EVENT_ON_DATA, /*!< Occurs when receiving data from the client */ + HTTPS_SERVER_EVENT_SENT_DATA, /*!< Occurs when an ESP HTTPS server sends data to the client */ + HTTPS_SERVER_EVENT_DISCONNECTED, /*!< The connection has been disconnected */ + HTTPS_SERVER_EVENT_STOP, /*!< This event occurs when HTTPS Server is stopped */ +} esp_https_server_event_id_t; + typedef enum { HTTPD_SSL_TRANSPORT_SECURE, // SSL Enabled HTTPD_SSL_TRANSPORT_INSECURE // SSL disabled @@ -39,6 +53,8 @@ typedef struct esp_https_server_user_cb_arg { esp_tls_t *tls; /*!< ESP-TLS connection handle */ } esp_https_server_user_cb_arg_t; +typedef esp_tls_last_error_t esp_https_server_last_error_t; + /** * @brief Callback function prototype * Can be used to get connection or client information (SSL context) diff --git a/components/esp_https_server/src/https_server.c b/components/esp_https_server/src/https_server.c index c224b7c0e1..f68ebc4f8d 100644 --- a/components/esp_https_server/src/https_server.c +++ b/components/esp_https_server/src/https_server.c @@ -23,6 +23,16 @@ typedef struct httpd_ssl_transport_ctx { httpd_ssl_ctx_t *global_ctx; } httpd_ssl_transport_ctx_t; +ESP_EVENT_DEFINE_BASE(ESP_HTTPS_SERVER_EVENT); + +static void http_dispatch_event_to_event_loop(int32_t event_id, const void* event_data, size_t event_data_size) +{ + esp_err_t err = esp_event_post(ESP_HTTPS_SERVER_EVENT, event_id, event_data, event_data_size, portMAX_DELAY); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to post http_client event: %"PRId32", error: %s", event_id, esp_err_to_name(err)); + } +} + /** * SSL socket close handler * @@ -46,6 +56,7 @@ static void httpd_ssl_close(void *ctx) esp_tls_server_session_delete(tls); free(ctx); ESP_LOGD(TAG, "Secure socket closed"); + http_dispatch_event_to_event_loop(HTTPS_SERVER_EVENT_DISCONNECTED, NULL, 0); } /** @@ -61,7 +72,16 @@ static int httpd_ssl_pending(httpd_handle_t server, int sockfd) assert(transport_ctx != NULL); esp_tls_t *tls = transport_ctx->tls; assert(tls != NULL); - return esp_tls_get_bytes_avail(tls); + int ret = esp_tls_get_bytes_avail(tls); + if (ret < 0) { + esp_tls_error_handle_t error_handle; + if (esp_tls_get_error_handle(tls, &error_handle) == ESP_OK) { + esp_https_server_last_error_t last_error = {0}; + last_error.last_error = esp_tls_get_and_clear_last_error(error_handle, &last_error.esp_tls_error_code, &last_error.esp_tls_flags); + http_dispatch_event_to_event_loop(HTTPS_SERVER_EVENT_ERROR, &last_error, sizeof(last_error)); + } + } + return ret; } /** @@ -80,7 +100,18 @@ static int httpd_ssl_recv(httpd_handle_t server, int sockfd, char *buf, size_t b assert(transport_ctx != NULL); esp_tls_t *tls = transport_ctx->tls; assert(tls != NULL); - return esp_tls_conn_read(tls, buf, buf_len); + int ret = esp_tls_conn_read(tls, buf, buf_len); + if (ret < 0) { + esp_tls_error_handle_t error_handle; + if (esp_tls_get_error_handle(tls, &error_handle) == ESP_OK) { + esp_https_server_last_error_t last_error = {0}; + last_error.last_error = esp_tls_get_and_clear_last_error(error_handle, &last_error.esp_tls_error_code, &last_error.esp_tls_flags); + http_dispatch_event_to_event_loop(HTTPS_SERVER_EVENT_ERROR, &last_error, sizeof(last_error)); + } + } else { + http_dispatch_event_to_event_loop(HTTPS_SERVER_EVENT_ON_DATA, &ret, sizeof(int)); + } + return ret; } /** @@ -99,7 +130,18 @@ static int httpd_ssl_send(httpd_handle_t server, int sockfd, const char *buf, si assert(transport_ctx != NULL); esp_tls_t *tls = transport_ctx->tls; assert(tls != NULL); - return esp_tls_conn_write(tls, buf, buf_len); + int ret = esp_tls_conn_write(tls, buf, buf_len); + if (ret < 0) { + esp_tls_error_handle_t error_handle; + if (esp_tls_get_error_handle(tls, &error_handle) == ESP_OK) { + esp_https_server_last_error_t last_error = {0}; + last_error.last_error = esp_tls_get_and_clear_last_error(error_handle, &last_error.esp_tls_error_code, &last_error.esp_tls_flags); + http_dispatch_event_to_event_loop(HTTPS_SERVER_EVENT_ERROR, &last_error, sizeof(last_error)); + } + } else { + http_dispatch_event_to_event_loop(HTTPS_SERVER_EVENT_SENT_DATA, NULL, 0); + } + return ret; } /** @@ -120,12 +162,15 @@ static esp_err_t httpd_ssl_open(httpd_handle_t server, int sockfd) esp_tls_t *tls = esp_tls_init(); if (!tls) { + esp_https_server_last_error_t last_error = {0}; + last_error.last_error = ESP_ERR_NO_MEM; + http_dispatch_event_to_event_loop(HTTPS_SERVER_EVENT_ERROR, &last_error, sizeof(last_error)); return ESP_ERR_NO_MEM; } ESP_LOGI(TAG, "performing session handshake"); int ret = esp_tls_server_session_create(global_ctx->tls_cfg, sockfd, tls); if (ret != 0) { - ESP_LOGE(TAG, "esp_tls_create_server_session failed"); + ESP_LOGE(TAG, "esp_tls_create_server_session failed, 0x%04x", -ret); goto fail; } @@ -134,6 +179,9 @@ static esp_err_t httpd_ssl_open(httpd_handle_t server, int sockfd) // NOTE: allocated memory will be freed by httpd_ssl_close httpd_ssl_transport_ctx_t *transport_ctx = (httpd_ssl_transport_ctx_t *)calloc(1, sizeof(httpd_ssl_transport_ctx_t)); if (!transport_ctx) { + esp_https_server_last_error_t last_error = {0}; + last_error.last_error = ESP_ERR_NO_MEM; + http_dispatch_event_to_event_loop(HTTPS_SERVER_EVENT_ERROR, &last_error, sizeof(last_error)); return ESP_ERR_NO_MEM; } transport_ctx->tls = tls; @@ -160,10 +208,18 @@ static esp_err_t httpd_ssl_open(httpd_handle_t server, int sockfd) user_cb_data.tls = tls; (global_ctx->user_cb)((void *)&user_cb_data); } - + http_dispatch_event_to_event_loop(HTTPS_SERVER_EVENT_ON_CONNECTED, NULL, 0); return ESP_OK; fail: - esp_tls_server_session_delete(tls); + { + esp_tls_error_handle_t error_handle; + if (esp_tls_get_error_handle(tls, &error_handle) == ESP_OK) { + esp_https_server_last_error_t last_error = {0}; + last_error.last_error = esp_tls_get_and_clear_last_error(error_handle, &last_error.esp_tls_error_code, &last_error.esp_tls_flags); + http_dispatch_event_to_event_loop(HTTPS_SERVER_EVENT_ERROR, &last_error, sizeof(last_error)); + } + esp_tls_server_session_delete(tls); + } return ESP_FAIL; } @@ -192,35 +248,35 @@ static void free_secure_context(void *ctx) free(ssl_ctx); } -static httpd_ssl_ctx_t *create_secure_context(const struct httpd_ssl_config *config) +static esp_err_t create_secure_context(const struct httpd_ssl_config *config, httpd_ssl_ctx_t **ssl_ctx) { - httpd_ssl_ctx_t *ssl_ctx = calloc(1, sizeof(httpd_ssl_ctx_t)); - if (!ssl_ctx) { - return NULL; + if (!ssl_ctx || !*ssl_ctx) { + return ESP_ERR_INVALID_ARG; } + esp_err_t ret = ESP_OK; esp_tls_cfg_server_t *cfg = (esp_tls_cfg_server_t *)calloc(1, sizeof(esp_tls_cfg_server_t)); if (!cfg) { + ret = ESP_ERR_NO_MEM; goto exit; } if (config->session_tickets) { - if ( esp_tls_cfg_server_session_tickets_init(cfg) != ESP_OK ) { - ESP_LOGE(TAG, "Failed to init session ticket support"); + ret = esp_tls_cfg_server_session_tickets_init(cfg); + if ( ret != ESP_OK ) { + ESP_LOGE(TAG, "Failed to init session ticket support. error: %s", esp_err_to_name(ret)); goto exit; } } cfg->userdata = config->ssl_userdata; - - cfg->alpn_protos = config->alpn_protos; #if defined(CONFIG_ESP_TLS_SERVER_CERT_SELECT_HOOK) cfg->cert_select_cb = config->cert_select_cb; #endif - ssl_ctx->tls_cfg = cfg; - ssl_ctx->user_cb = config->user_cb; + (*ssl_ctx)->tls_cfg = cfg; + (*ssl_ctx)->user_cb = config->user_cb; /* cacert = CA which signs client cert, or client cert itself */ if (config->cacert_pem != NULL && config->cacert_len > 0) { @@ -231,6 +287,7 @@ static httpd_ssl_ctx_t *create_secure_context(const struct httpd_ssl_config *con cfg->cacert_bytes = config->cacert_len; } else { ESP_LOGE(TAG, "Could not allocate memory for client certificate authority"); + ret = ESP_ERR_NO_MEM; goto exit; } } @@ -244,6 +301,7 @@ static httpd_ssl_ctx_t *create_secure_context(const struct httpd_ssl_config *con cfg->servercert_bytes = config->servercert_len; } else { ESP_LOGE(TAG, "Could not allocate memory for server certificate"); + ret = ESP_ERR_NO_MEM; goto exit; } } else { @@ -251,6 +309,7 @@ static httpd_ssl_ctx_t *create_secure_context(const struct httpd_ssl_config *con if (config->cert_select_cb == NULL) { #endif ESP_LOGE(TAG, "No Server certificate supplied"); + ret = ESP_ERR_INVALID_ARG; goto exit; #if defined(CONFIG_ESP_TLS_SERVER_CERT_SELECT_HOOK) } else { @@ -264,8 +323,8 @@ static httpd_ssl_ctx_t *create_secure_context(const struct httpd_ssl_config *con if (!cfg->use_secure_element) { if (config->use_ecdsa_peripheral) { #ifdef CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN - ssl_ctx->tls_cfg->use_ecdsa_peripheral = config->use_ecdsa_peripheral; - ssl_ctx->tls_cfg->ecdsa_key_efuse_blk = config->ecdsa_key_efuse_blk; + (*ssl_ctx)->tls_cfg->use_ecdsa_peripheral = config->use_ecdsa_peripheral; + (*ssl_ctx)->tls_cfg->ecdsa_key_efuse_blk = config->ecdsa_key_efuse_blk; #else ESP_LOGE(TAG, "Please enable the support for signing using ECDSA peripheral in menuconfig."); goto exit; @@ -278,24 +337,27 @@ static httpd_ssl_ctx_t *create_secure_context(const struct httpd_ssl_config *con cfg->serverkey_bytes = config->prvtkey_len; } else { ESP_LOGE(TAG, "Could not allocate memory for server key"); + ret = ESP_ERR_NO_MEM; goto exit; } } else { #if defined(CONFIG_ESP_TLS_SERVER_CERT_SELECT_HOOK) if (config->cert_select_cb == NULL) { ESP_LOGE(TAG, "No Server key supplied and no certificate selection hook is present"); + ret = ESP_ERR_INVALID_ARG; goto exit; } else { ESP_LOGW(TAG, "Server key not supplied, make sure to supply it in the certificate selection hook"); } #else ESP_LOGE(TAG, "No Server key supplied"); + ret = ESP_ERR_INVALID_ARG; goto exit; #endif } } - return ssl_ctx; + return ret; exit: if (cfg) { @@ -303,8 +365,8 @@ exit: free((void *) cfg->cacert_buf); } free(cfg); - free(ssl_ctx); - return NULL; + free(*ssl_ctx); + return ret; } /** Start the server */ @@ -315,11 +377,16 @@ esp_err_t httpd_ssl_start(httpd_handle_t *pHandle, struct httpd_ssl_config *conf ESP_LOGI(TAG, "Starting server"); + esp_err_t ret = ESP_OK; if (HTTPD_SSL_TRANSPORT_SECURE == config->transport_mode) { - - httpd_ssl_ctx_t *ssl_ctx = create_secure_context(config); + httpd_ssl_ctx_t *ssl_ctx = calloc(1, sizeof(httpd_ssl_ctx_t)); if (!ssl_ctx) { - return -1; + return ESP_ERR_NO_MEM; + } + + ret = create_secure_context(config, &ssl_ctx); + if (ret != ESP_OK) { + return ret; } ESP_LOGD(TAG, "SSL context ready"); @@ -342,17 +409,22 @@ esp_err_t httpd_ssl_start(httpd_handle_t *pHandle, struct httpd_ssl_config *conf httpd_handle_t handle = NULL; - esp_err_t ret = httpd_start(&handle, &config->httpd); + ret = httpd_start(&handle, &config->httpd); if (ret != ESP_OK) return ret; *pHandle = handle; ESP_LOGI(TAG, "Server listening on port %d", config->httpd.server_port); + http_dispatch_event_to_event_loop(HTTPS_SERVER_EVENT_START, NULL, 0); return ESP_OK; } /** Stop the server */ esp_err_t httpd_ssl_stop(httpd_handle_t handle) { - return httpd_stop(handle); + esp_err_t ret = httpd_stop(handle); + if (ret == ESP_OK) { + http_dispatch_event_to_event_loop(HTTPS_SERVER_EVENT_STOP, NULL, 0); + } + return ret; } diff --git a/docs/en/api-reference/protocols/esp_https_server.rst b/docs/en/api-reference/protocols/esp_https_server.rst index 421de33698..6768a97ed5 100644 --- a/docs/en/api-reference/protocols/esp_https_server.rst +++ b/docs/en/api-reference/protocols/esp_https_server.rst @@ -46,6 +46,23 @@ Performance The initial session setup can take about two seconds, or more with slower clock speed or more verbose logging. Subsequent requests through the open secure socket are much faster (down to under 100 ms). +Event Handling +-------------- + +ESP HTTPS Server has various events for which a handler can be triggered by :doc:`the Event Loop library <../system/esp_event>` when the particular event occurs. The handler has to be registered using :cpp:func:`esp_event_handler_register`. This helps in event handling for ESP HTTPS Server. + +:cpp:enum:`esp_https_server_event_id_t` has all the events which can happen for ESP HTTPS server. + +Expected data type for different ESP HTTP server events in event loop: + + - HTTPS_SERVER_EVENT_ERROR : ``esp_https_server_last_error_t`` + - HTTPS_SERVER_EVENT_START : ``NULL`` + - HTTPS_SERVER_EVENT_ON_CONNECTED : ``NULL`` + - HTTPS_SERVER_EVENT_ON_DATA : ``int`` + - HTTPS_SERVER_EVENT_SENT_DATA : ``NULL`` + - HTTPS_SERVER_EVENT_DISCONNECTED : ``NULL`` + - HTTPS_SERVER_EVENT_STOP : ``NULL`` + API Reference -------------