Merge branch 'feature/support_transport_keepalive' into 'master'

Transport: Add TCP keep-alive function

Closes IDFGH-4543

See merge request espressif/esp-idf!11909
This commit is contained in:
Mahavir Jain 2021-01-22 17:04:05 +08:00
commit ba1c8ceddb
15 changed files with 165 additions and 3 deletions

View File

@ -161,6 +161,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;
@ -199,6 +227,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);

View File

@ -126,6 +126,16 @@ typedef struct psk_key_hint {
const char* hint; /*!< hint in PSK authentication mode in string format */
} psk_hint_key_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
*
@ -213,6 +223,8 @@ typedef struct esp_tls_cfg {
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 */
const psk_hint_key_t* psk_hint_key; /*!< Pointer to PSK hint and key. if not NULL (and certificates are NULL)
then PSK authentication is enabled with configured setup.
Important note: the pointer must be valid for connection */

View File

@ -121,6 +121,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;
@ -142,6 +143,9 @@ static const char *DEFAULT_HTTP_PATH = "/";
static const int DEFAULT_MAX_REDIRECT = 10;
static const int DEFAULT_MAX_AUTH_RETRIES = 10;
static const 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",
@ -532,7 +536,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;
char *host_name;
bool _success;
@ -564,8 +568,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) &&
@ -594,6 +605,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) {

View File

@ -131,6 +131,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;
/**

View File

@ -40,6 +40,9 @@ static const char *TAG = "WEBSOCKET_CLIENT";
#define WEBSOCKET_PING_TIMEOUT_MS (10*1000)
#define WEBSOCKET_EVENT_QUEUE_SIZE (1)
#define WEBSOCKET_PINGPONG_TIMEOUT_SEC (120)
#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", __FUNCTION__, __LINE__, "Memory exhausted"); \
@ -108,6 +111,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)
@ -290,6 +294,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);
@ -303,6 +314,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
@ -346,6 +358,7 @@ esp_websocket_client_handle_t esp_websocket_client_init(const esp_websocket_clie
if (config->skip_cert_common_name_check) {
esp_transport_ssl_skip_common_name_check(ssl);
}
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);

View File

@ -95,6 +95,10 @@ typedef struct {
bool disable_pingpong_discon; /*!< Disable auto-disconnect due to no PONG received within pingpong_timeout_sec */
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_websocket_client_config_t;
/**

View File

@ -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;

View File

@ -165,6 +165,14 @@ void esp_transport_ssl_set_ds_data(esp_transport_handle_t t, void *ds_data);
*/
void esp_transport_ssl_set_psk_key_hint(esp_transport_handle_t t, const psk_hint_key_t* psk_hint_key);
/**
* @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

View File

@ -21,6 +21,15 @@
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
*

View File

@ -39,6 +39,7 @@ struct esp_transport_item_t {
get_socket_func _get_socket; /*!< Function returning the transport's socket */
struct esp_transport_error_s* error_handle; /*!< Error handle (based on esp-tls error handle)
* extended with transport's specific errors */
esp_transport_keep_alive_t *keep_alive_cfg; /*!< TCP keep-alive config */
STAILQ_ENTRY(esp_transport_item_t) next;
};

View File

@ -313,6 +313,14 @@ void esp_transport_ssl_set_ds_data(esp_transport_handle_t t, void *ds_data)
}
}
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(void)
{
esp_transport_handle_t t = esp_transport_init();

View File

@ -52,6 +52,34 @@ 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;
@ -81,7 +109,13 @@ 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 (t->keep_alive_cfg && t->keep_alive_cfg->keep_alive_enable) {
if (tcp_enable_keep_alive(tcp->sock, t->keep_alive_cfg) < 0) {
ESP_LOGE(TAG, "Error to set tcp [socket=%d] keep-alive", tcp->sock);
goto error;
}
}
// Set socket to non-blocking
int flags;
if ((flags = fcntl(tcp->sock, F_GETFL, NULL)) < 0) {
@ -251,6 +285,13 @@ static int tcp_get_socket(esp_transport_handle_t t)
return -1;
}
void esp_transport_tcp_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;
}
}
esp_transport_handle_t esp_transport_tcp_init(void)
{
esp_transport_handle_t t = esp_transport_init();

View File

@ -86,6 +86,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

View File

@ -97,6 +97,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

View File

@ -67,6 +67,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