Jelajahi Sumber

Merge branch 'feature/support_transport_keepalive_v4.0' into 'release/v4.0'

Feature/support transport keepalive v4.0 [backport v4.0]

See merge request espressif/esp-idf!12155
David Čermák 5 tahun lalu
induk
melakukan
a6bddd68d2

+ 34 - 0
components/esp-tls/esp_tls.c

@@ -94,6 +94,34 @@ static void ms_to_timeval(int timeout_ms, struct timeval *tv)
     tv->tv_usec = (timeout_ms % 1000) * 1000;
 }
 
+static int esp_tls_tcp_enable_keep_alive(int fd, tls_keep_alive_cfg_t *cfg)
+{
+    int keep_alive_enable = 1;
+    int keep_alive_idle = cfg->keep_alive_idle;
+    int keep_alive_interval = cfg->keep_alive_interval;
+    int keep_alive_count = cfg->keep_alive_count;
+
+    ESP_LOGD(TAG, "Enable TCP keep alive. idle: %d, interval: %d, count: %d", keep_alive_idle, keep_alive_interval, keep_alive_count);
+    if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &keep_alive_enable, sizeof(keep_alive_enable)) != 0) {
+        ESP_LOGE(TAG, "Fail to setsockopt SO_KEEPALIVE");
+        return -1;
+    }
+    if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &keep_alive_idle, sizeof(keep_alive_idle)) != 0) {
+        ESP_LOGE(TAG, "Fail to setsockopt TCP_KEEPIDLE");
+        return -1;
+    }
+    if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &keep_alive_interval, sizeof(keep_alive_interval)) != 0) {
+        ESP_LOGE(TAG, "Fail to setsockopt TCP_KEEPINTVL");
+        return -1;
+    }
+    if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &keep_alive_count, sizeof(keep_alive_count)) != 0) {
+        ESP_LOGE(TAG, "Fail to setsockopt TCP_KEEPCNT");
+        return -1;
+    }
+
+    return 0;
+}
+
 static esp_err_t esp_tcp_connect(const char *host, int hostlen, int port, int *sockfd, const esp_tls_t *tls, const esp_tls_cfg_t *cfg)
 {
     esp_err_t ret;
@@ -132,6 +160,12 @@ static esp_err_t esp_tcp_connect(const char *host, int hostlen, int port, int *s
             ms_to_timeval(cfg->timeout_ms, &tv);
             setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
             setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
+            if (cfg->keep_alive_cfg && cfg->keep_alive_cfg->keep_alive_enable) {
+                if (esp_tls_tcp_enable_keep_alive(fd, cfg->keep_alive_cfg) < 0) {
+                    ESP_LOGE(TAG, "Error setting keep-alive");
+                    goto err_freesocket;
+                }
+            }
         }
         if (cfg->non_block) {
             int flags = fcntl(fd, F_GETFL, 0);

+ 12 - 0
components/esp-tls/esp_tls.h

@@ -79,6 +79,16 @@ typedef enum esp_tls_role {
     ESP_TLS_SERVER,
 } esp_tls_role_t;
 
+/**
+ *  @brief Keep alive parameters structure
+ */
+typedef struct tls_keep_alive_cfg {
+    bool keep_alive_enable;               /*!< Enable keep-alive timeout */
+    int keep_alive_idle;                  /*!< Keep-alive idle time (second) */
+    int keep_alive_interval;              /*!< Keep-alive interval time (second) */
+    int keep_alive_count;                 /*!< Keep-alive packet retry send count */
+} tls_keep_alive_cfg_t;
+
 /**
  * @brief      ESP-TLS configuration parameters 
  */ 
@@ -128,6 +138,8 @@ typedef struct esp_tls_cfg {
                                                  If NULL, server certificate CN must match hostname. */
 
     bool skip_common_name;                  /*!< Skip any validation of server certificate CN field */
+
+    tls_keep_alive_cfg_t *keep_alive_cfg;   /*!< Enable TCP keep-alive timeout for SSL connection */
 } esp_tls_cfg_t;
 
 #ifdef CONFIG_ESP_TLS_SERVER

+ 17 - 2
components/esp_http_client/esp_http_client.c

@@ -119,6 +119,7 @@ struct esp_http_client {
     bool                        first_line_prepared;
     int                         header_index;
     bool                        is_async;
+    esp_transport_keep_alive_t  keep_alive_cfg;
 };
 
 typedef struct esp_http_client esp_http_client_t;
@@ -139,6 +140,9 @@ static const char *DEFAULT_HTTP_PROTOCOL = "HTTP/1.1";
 static const char *DEFAULT_HTTP_PATH = "/";
 static int DEFAULT_MAX_REDIRECT = 10;
 static int DEFAULT_TIMEOUT_MS = 5000;
+static const int DEFAULT_KEEP_ALIVE_IDLE = 5;
+static const int DEFAULT_KEEP_ALIVE_INTERVAL= 5;
+static const int DEFAULT_KEEP_ALIVE_COUNT= 3;
 
 static const char *HTTP_METHOD_MAPPING[] = {
     "GET",
@@ -486,7 +490,7 @@ esp_http_client_handle_t esp_http_client_init(const esp_http_client_config_t *co
 {
 
     esp_http_client_handle_t client;
-    esp_transport_handle_t tcp;
+    esp_transport_handle_t tcp = NULL;
     bool _success;
 
     _success = (
@@ -517,8 +521,15 @@ esp_http_client_handle_t esp_http_client_init(const esp_http_client_config_t *co
         ESP_LOGE(TAG, "Error initialize transport");
         goto error;
     }
+    if (config->keep_alive_enable == true) {
+        client->keep_alive_cfg.keep_alive_enable = true;
+        client->keep_alive_cfg.keep_alive_idle = (config->keep_alive_idle == 0) ? DEFAULT_KEEP_ALIVE_IDLE : config->keep_alive_idle;
+        client->keep_alive_cfg.keep_alive_interval = (config->keep_alive_interval == 0) ? DEFAULT_KEEP_ALIVE_INTERVAL : config->keep_alive_interval;
+        client->keep_alive_cfg.keep_alive_count =  (config->keep_alive_count == 0) ? DEFAULT_KEEP_ALIVE_COUNT : config->keep_alive_count;
+        esp_transport_tcp_set_keep_alive(tcp, &client->keep_alive_cfg);
+    }
 #ifdef CONFIG_ESP_HTTP_CLIENT_ENABLE_HTTPS
-    esp_transport_handle_t ssl;
+    esp_transport_handle_t ssl = NULL;
     _success = (
                    (ssl = esp_transport_ssl_init()) &&
                    (esp_transport_set_default_port(ssl, DEFAULT_HTTPS_PORT) == ESP_OK) &&
@@ -547,6 +558,10 @@ esp_http_client_handle_t esp_http_client_init(const esp_http_client_config_t *co
     if (config->skip_cert_common_name_check) {
         esp_transport_ssl_skip_common_name_check(ssl);
     }
+
+    if (config->keep_alive_enable == true) {
+        esp_transport_ssl_set_keep_alive(ssl, &client->keep_alive_cfg);
+    }
 #endif
 
     if (_set_config(client, config) != ESP_OK) {

+ 4 - 0
components/esp_http_client/include/esp_http_client.h

@@ -123,6 +123,10 @@ typedef struct {
     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 */
+    bool                        keep_alive_enable;   /*!< Enable keep-alive timeout */
+    int                         keep_alive_idle;     /*!< Keep-alive idle time. Default is 5 (second) */
+    int                         keep_alive_interval; /*!< Keep-alive interval time. Default is 5 (second) */
+    int                         keep_alive_count;    /*!< Keep-alive packet retry send count. Default is 3 counts */
 } esp_http_client_config_t;
 
 /**

+ 13 - 0
components/esp_websocket_client/esp_websocket_client.c

@@ -39,6 +39,9 @@ static const char *TAG = "WEBSOCKET_CLIENT";
 #define WEBSOCKET_NETWORK_TIMEOUT_MS    (10*1000)
 #define WEBSOCKET_PING_TIMEOUT_MS       (10*1000)
 #define WEBSOCKET_EVENT_QUEUE_SIZE      (1)
+#define WEBSOCKET_KEEP_ALIVE_IDLE       (5)
+#define WEBSOCKET_KEEP_ALIVE_INTERVAL   (5)
+#define WEBSOCKET_KEEP_ALIVE_COUNT      (3)
 
 #define ESP_WS_CLIENT_MEM_CHECK(TAG, a, action) if (!(a)) {                                         \
         ESP_LOGE(TAG,"%s:%d (%s): %s", __FILE__, __LINE__, __FUNCTION__, "Memory exhausted");       \
@@ -100,6 +103,7 @@ struct esp_websocket_client {
     ws_transport_opcodes_t      last_opcode;
     int                         payload_len;
     int                         payload_offset;
+    esp_transport_keep_alive_t  keep_alive_cfg;
 };
 
 static uint64_t _tick_get_ms(void)
@@ -258,6 +262,13 @@ esp_websocket_client_handle_t esp_websocket_client_init(const esp_websocket_clie
         return NULL;
     }
 
+    if (config->keep_alive_enable == true) {
+        client->keep_alive_cfg.keep_alive_enable = true;
+        client->keep_alive_cfg.keep_alive_idle = (config->keep_alive_idle == 0) ? WEBSOCKET_KEEP_ALIVE_IDLE : config->keep_alive_idle;
+        client->keep_alive_cfg.keep_alive_interval = (config->keep_alive_interval == 0) ? WEBSOCKET_KEEP_ALIVE_INTERVAL : config->keep_alive_interval;
+        client->keep_alive_cfg.keep_alive_count =  (config->keep_alive_count == 0) ? WEBSOCKET_KEEP_ALIVE_COUNT : config->keep_alive_count;
+    }
+
     client->lock = xSemaphoreCreateRecursiveMutex();
     ESP_WS_CLIENT_MEM_CHECK(TAG, client->lock, goto _websocket_init_fail);
 
@@ -271,6 +282,7 @@ esp_websocket_client_handle_t esp_websocket_client_init(const esp_websocket_clie
     ESP_WS_CLIENT_MEM_CHECK(TAG, tcp, goto _websocket_init_fail);
 
     esp_transport_set_default_port(tcp, WEBSOCKET_TCP_DEFAULT_PORT);
+    esp_transport_tcp_set_keep_alive(tcp, &client->keep_alive_cfg);
     esp_transport_list_add(client->transport_list, tcp, "_tcp"); // need to save to transport list, for cleanup
 
 
@@ -291,6 +303,7 @@ esp_websocket_client_handle_t esp_websocket_client_init(const esp_websocket_clie
     if (config->cert_pem) {
         esp_transport_ssl_set_cert_data(ssl, config->cert_pem, strlen(config->cert_pem));
     }
+    esp_transport_ssl_set_keep_alive(ssl, &client->keep_alive_cfg);
     esp_transport_list_add(client->transport_list, ssl, "_ssl"); // need to save to transport list, for cleanup
 
     esp_transport_handle_t wss = esp_transport_ws_init(ssl);

+ 4 - 0
components/esp_websocket_client/include/esp_websocket_client.h

@@ -84,6 +84,10 @@ typedef struct {
     const char                  *cert_pem;                  /*!< SSL Certification, PEM format as string, if the client requires to verify server */
     esp_websocket_transport_t   transport;                  /*!< Websocket transport type, see `esp_websocket_transport_t */
     char                        *subprotocol;               /*!< Websocket subprotocol */
+    bool                        keep_alive_enable;          /*!< Enable keep-alive timeout */
+    int                         keep_alive_idle;            /*!< Keep-alive idle time. Default is 5 (second) */
+    int                         keep_alive_interval;        /*!< Keep-alive interval time. Default is 5 (second) */
+    int                         keep_alive_count;           /*!< Keep-alive packet retry send count. Default is 3 counts */
 } esp_websocket_client_config_t;
 
 /**

+ 25 - 1
components/tcp_transport/include/esp_transport.h

@@ -16,11 +16,21 @@
 #define _ESP_TRANSPORT_H_
 
 #include <esp_err.h>
+#include <stdbool.h>
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
+/**
+*  @brief Keep alive parameters structure
+*/
+typedef struct esp_transport_keepalive {
+    bool            keep_alive_enable;      /*!< Enable keep-alive timeout */
+    int             keep_alive_idle;        /*!< Keep-alive idle time (second) */
+    int             keep_alive_interval;    /*!< Keep-alive interval time (second) */
+    int             keep_alive_count;       /*!< Keep-alive packet retry send count */
+} esp_transport_keep_alive_t;
 
 typedef struct esp_transport_internal* esp_transport_list_handle_t;
 typedef struct esp_transport_item_t* esp_transport_handle_t;
@@ -313,8 +323,22 @@ esp_err_t esp_transport_set_parent_transport_func(esp_transport_handle_t t, payl
   */
 esp_tls_error_handle_t esp_transport_get_error_handle(esp_transport_handle_t t);
 
+/**
+ * @brief      Set keep-alive configuration
+ *
+ * @param[in]  t               The transport handle
+ * @param[in]  keep_alive_cfg  The keep-alive config
+ */
+void esp_transport_set_keep_alive(esp_transport_handle_t t, esp_transport_keep_alive_t *keep_alive_cfg);
 
-
+/**
+ * @brief      Get keep-alive config of this transport
+ *
+ * @param[in]  t        The transport handle
+ *
+ * @return     The keep-alive configuration
+ */
+void *esp_transport_get_keep_alive(esp_transport_handle_t t);
 #ifdef __cplusplus
 }
 #endif

+ 8 - 0
components/tcp_transport/include/esp_transport_ssl.h

@@ -79,6 +79,14 @@ void esp_transport_ssl_set_client_key_data(esp_transport_handle_t t, const char
  */
 void esp_transport_ssl_skip_common_name_check(esp_transport_handle_t t);
 
+/**
+ * @brief      Set keep-alive status in current ssl context
+ *
+ * @param[in]  t               ssl transport
+ * @param[in]  keep_alive_cfg  The handle for keep-alive configuration
+ */
+void esp_transport_ssl_set_keep_alive(esp_transport_handle_t t, esp_transport_keep_alive_t *keep_alive_cfg);
+
 #ifdef __cplusplus
 }
 #endif

+ 8 - 0
components/tcp_transport/include/esp_transport_tcp.h

@@ -21,6 +21,14 @@
 extern "C" {
 #endif
 
+/**
+ * @brief      Set TCP keep-alive configuration
+ *
+ * @param[in]  t               The transport handle
+ * @param[in]  keep_alive_cfg  The keep-alive config
+ */
+void esp_transport_tcp_set_keep_alive(esp_transport_handle_t t, esp_transport_keep_alive_t *keep_alive_cfg);
+
 /**
  * @brief      Create TCP transport, the transport handle must be release esp_transport_destroy callback
  *

+ 17 - 2
components/tcp_transport/transport.c

@@ -44,7 +44,7 @@ struct esp_transport_item_t {
     connect_async_func _connect_async;      /*!< non-blocking connect function of this transport */
     payload_transfer_func  _parent_transfer;        /*!< Function returning underlying transport layer */
     esp_tls_error_handle_t     error_handle;            /*!< Pointer to esp-tls error handle */
-
+    esp_transport_keep_alive_t *keep_alive_cfg;    /*!< TCP keep-alive config */
     STAILQ_ENTRY(esp_transport_item_t) next;
 };
 
@@ -305,4 +305,19 @@ void esp_transport_set_errors(esp_transport_handle_t t, const esp_tls_error_hand
     if (t)  {
         memcpy(t->error_handle, error_handle, sizeof(esp_tls_last_error_t));
     }
-}
+}
+
+void esp_transport_set_keep_alive(esp_transport_handle_t t, esp_transport_keep_alive_t *keep_alive_cfg)
+{
+    if (t && keep_alive_cfg) {
+        t->keep_alive_cfg = keep_alive_cfg;
+    }
+}
+
+void *esp_transport_get_keep_alive(esp_transport_handle_t t)
+{
+    if (t) {
+        return t->keep_alive_cfg;
+    }
+    return NULL;
+}

+ 8 - 0
components/tcp_transport/transport_ssl.c

@@ -232,6 +232,14 @@ void esp_transport_ssl_skip_common_name_check(esp_transport_handle_t t)
     }
 }
 
+void esp_transport_ssl_set_keep_alive(esp_transport_handle_t t, esp_transport_keep_alive_t *keep_alive_cfg)
+{
+    transport_ssl_t *ssl = esp_transport_get_context_data(t);
+    if (t && ssl) {
+        ssl->cfg.keep_alive_cfg = (tls_keep_alive_cfg_t *)keep_alive_cfg;
+    }
+}
+
 esp_transport_handle_t esp_transport_ssl_init()
 {
     esp_transport_handle_t t = esp_transport_init();

+ 43 - 1
components/tcp_transport/transport_tcp.c

@@ -51,11 +51,40 @@ static int resolve_dns(const char *host, struct sockaddr_in *ip)
     return ESP_OK;
 }
 
+static int tcp_enable_keep_alive(int fd, esp_transport_keep_alive_t *keep_alive_cfg)
+{
+    int keep_alive_enable = 1;
+    int keep_alive_idle = keep_alive_cfg->keep_alive_idle;
+    int keep_alive_interval = keep_alive_cfg->keep_alive_interval;
+    int keep_alive_count = keep_alive_cfg->keep_alive_count;
+
+    ESP_LOGD(TAG, "Enable TCP keep alive. idle: %d, interval: %d, count: %d", keep_alive_idle, keep_alive_interval, keep_alive_count);
+    if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &keep_alive_enable, sizeof(keep_alive_enable)) != 0) {
+        ESP_LOGE(TAG, "Fail to setsockopt SO_KEEPALIVE");
+        return -1;
+    }
+    if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &keep_alive_idle, sizeof(keep_alive_idle)) != 0) {
+        ESP_LOGE(TAG, "Fail to setsockopt TCP_KEEPIDLE");
+        return -1;
+    }
+    if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &keep_alive_interval, sizeof(keep_alive_interval)) != 0) {
+        ESP_LOGE(TAG, "Fail to setsockopt TCP_KEEPINTVL");
+        return -1;
+    }
+    if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &keep_alive_count, sizeof(keep_alive_count)) != 0) {
+        ESP_LOGE(TAG, "Fail to setsockopt TCP_KEEPCNT");
+        return -1;
+    }
+
+    return 0;
+}
+
 static int tcp_connect(esp_transport_handle_t t, const char *host, int port, int timeout_ms)
 {
     struct sockaddr_in remote_ip;
     struct timeval tv;
     transport_tcp_t *tcp = esp_transport_get_context_data(t);
+    esp_transport_keep_alive_t *keep_alive_cfg = esp_transport_get_keep_alive(t);
 
     bzero(&remote_ip, sizeof(struct sockaddr_in));
 
@@ -80,7 +109,15 @@ static int tcp_connect(esp_transport_handle_t t, const char *host, int port, int
 
     setsockopt(tcp->sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
     setsockopt(tcp->sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
-
+    // Set socket keep-alive option
+    if (keep_alive_cfg && keep_alive_cfg->keep_alive_enable) {
+        if (tcp_enable_keep_alive(tcp->sock, keep_alive_cfg) < 0) {
+            ESP_LOGE(TAG, "Error to set tcp [socket=%d] keep-alive", tcp->sock);
+            close(tcp->sock);
+            tcp->sock = -1;
+            return -1;
+        }
+    }
     ESP_LOGD(TAG, "[sock=%d],connecting to server IP:%s,Port:%d...",
              tcp->sock, ipaddr_ntoa((const ip_addr_t*)&remote_ip.sin_addr.s_addr), port);
     if (connect(tcp->sock, (struct sockaddr *)(&remote_ip), sizeof(struct sockaddr)) != 0) {
@@ -180,6 +217,11 @@ static esp_err_t tcp_destroy(esp_transport_handle_t t)
     return 0;
 }
 
+void esp_transport_tcp_set_keep_alive(esp_transport_handle_t t, esp_transport_keep_alive_t *keep_alive_cfg)
+{
+    esp_transport_set_keep_alive(t, keep_alive_cfg);
+}
+
 esp_transport_handle_t esp_transport_tcp_init()
 {
     esp_transport_handle_t t = esp_transport_init();

+ 1 - 0
examples/system/ota/advanced_https_ota/main/advanced_https_ota_example.c

@@ -62,6 +62,7 @@ void advanced_ota_example_task(void *pvParameter)
         .url = CONFIG_EXAMPLE_FIRMWARE_UPGRADE_URL,
         .cert_pem = (char *)server_cert_pem_start,
         .timeout_ms = CONFIG_EXAMPLE_OTA_RECV_TIMEOUT,
+        .keep_alive_enable = true,
     };
 
 #ifdef CONFIG_EXAMPLE_FIRMWARE_UPGRADE_URL_FROM_STDIN

+ 1 - 0
examples/system/ota/native_ota_example/main/native_ota_example.c

@@ -98,6 +98,7 @@ static void ota_example_task(void *pvParameter)
         .url = CONFIG_EXAMPLE_FIRMWARE_UPG_URL,
         .cert_pem = (char *)server_cert_pem_start,
         .timeout_ms = CONFIG_EXAMPLE_OTA_RECV_TIMEOUT,
+        .keep_alive_enable = true,
     };
 
 #ifdef CONFIG_EXAMPLE_FIRMWARE_UPGRADE_URL_FROM_STDIN

+ 1 - 0
examples/system/ota/simple_ota_example/main/simple_ota_example.c

@@ -68,6 +68,7 @@ void simple_ota_example_task(void *pvParameter)
         .url = CONFIG_EXAMPLE_FIRMWARE_UPGRADE_URL,
         .cert_pem = (char *)server_cert_pem_start,
         .event_handler = _http_event_handler,
+        .keep_alive_enable = true,
     };
 
 #ifdef CONFIG_EXAMPLE_FIRMWARE_UPGRADE_URL_FROM_STDIN