From 5309ca04d185497ed9f787286c48cdb25d745ce4 Mon Sep 17 00:00:00 2001 From: Jitin George Date: Tue, 7 Aug 2018 23:26:59 +0530 Subject: [PATCH] esp_http_client: Add support for non-blocking feature in esp_http_client_perform() API Four internal APIs `esp_http_client_connect()`, `http_client_prepare_first_line()`, `esp_http_client_request_send()` and `esp_http_client_send_post_data()` API have been added for perfoming connection establishment (sync as well as async), preparing first line, sending request and sending post data respectively. In `transport_ssl`, `ssl_connect_async()` API has been added for establishing asynchronous connection. --- components/esp32/esp_err_to_name.c | 6 + components/esp_http_client/esp_http_client.c | 292 +++++++++++++----- .../esp_http_client/include/esp_http_client.h | 8 +- components/tcp_transport/include/transport.h | 29 ++ components/tcp_transport/transport.c | 22 +- components/tcp_transport/transport_ssl.c | 50 ++- 6 files changed, 318 insertions(+), 89 deletions(-) diff --git a/components/esp32/esp_err_to_name.c b/components/esp32/esp_err_to_name.c index aee804b053..dab3a59f97 100644 --- a/components/esp32/esp_err_to_name.c +++ b/components/esp32/esp_err_to_name.c @@ -428,6 +428,12 @@ static const esp_err_msg_t esp_err_msg_table[] = { # ifdef ESP_ERR_HTTP_INVALID_TRANSPORT ERR_TBL_IT(ESP_ERR_HTTP_INVALID_TRANSPORT), /* 28677 0x7005 There are no transport support for the input scheme */ +# endif +# ifdef ESP_ERR_HTTP_CONNECTING + ERR_TBL_IT(ESP_ERR_HTTP_CONNECTING), /* 28678 0x7006 HTTP connection hasn't been established yet */ +# endif +# ifdef ESP_ERR_HTTP_EAGAIN + ERR_TBL_IT(ESP_ERR_HTTP_EAGAIN), /* 28679 0x7007 Mapping of errno EAGAIN to esp_err_t */ # endif // components/http_server/include/http_server.h # ifdef ESP_ERR_HTTPD_BASE diff --git a/components/esp_http_client/esp_http_client.c b/components/esp_http_client/esp_http_client.c index 6dc7e44993..ae174f137b 100644 --- a/components/esp_http_client/esp_http_client.c +++ b/components/esp_http_client/esp_http_client.c @@ -26,6 +26,7 @@ #include "sdkconfig.h" #include "transport.h" #include "esp_http_client.h" +#include "errno.h" #ifdef CONFIG_ESP_HTTP_CLIENT_ENABLE_HTTPS #include "transport_ssl.h" @@ -113,6 +114,11 @@ struct esp_http_client { int buffer_size; bool disable_auto_redirect; esp_http_client_event_t event; + int data_written_index; + int data_write_left; + bool first_line_prepared; + int header_index; + bool is_async; }; typedef struct esp_http_client esp_http_client_t; @@ -124,6 +130,10 @@ static esp_err_t _clear_connection_info(esp_http_client_handle_t client); #define DEFAULT_HTTP_PORT (80) #define DEFAULT_HTTPS_PORT (443) +#define ASYNC_TRANS_CONNECT_FAIL -1 +#define ASYNC_TRANS_CONNECTING 0 +#define ASYNC_TRANS_CONNECT_PASS 1 + static const char *DEFAULT_HTTP_USER_AGENT = "ESP32 HTTP Client/1.0"; static const char *DEFAULT_HTTP_PROTOCOL = "HTTP/1.1"; static const char *DEFAULT_HTTP_PATH = "/"; @@ -156,6 +166,11 @@ enum HttpStatus_Code HttpStatus_Unauthorized = 401 }; + +static esp_err_t esp_http_client_request_send(esp_http_client_handle_t client); +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); + static esp_err_t http_dispatch_event(esp_http_client_t *client, esp_http_client_event_id_t event_id, void *data, int len) { esp_http_client_event_t *event = &client->event; @@ -359,6 +374,9 @@ static esp_err_t _set_config(esp_http_client_handle_t client, const esp_http_cli if (client->timeout_ms == 0) { client->timeout_ms = DEFAULT_TIMEOUT_MS; } + if (config->is_async) { + client->is_async = true; + } return ESP_OK; } @@ -399,6 +417,7 @@ static esp_err_t esp_http_client_prepare(esp_http_client_handle_t client) { client->process_again = 0; client->response->data_process = 0; + client->first_line_prepared = false; http_parser_init(client->parser, HTTP_RESPONSE); if (client->connection_info.username) { char *auth_response = NULL; @@ -781,55 +800,89 @@ int esp_http_client_read(esp_http_client_handle_t client, char *buffer, int len) esp_err_t esp_http_client_perform(esp_http_client_handle_t client) { esp_err_t err; - if (client == NULL) { - return ESP_ERR_INVALID_ARG; - } do { - if ((err = esp_http_client_open(client, client->post_len)) != ESP_OK) { - return err; - } - if (client->post_data && client->post_len) { - if (esp_http_client_write(client, client->post_data, client->post_len) <= 0) { - ESP_LOGE(TAG, "Error upload data"); - return ESP_ERR_HTTP_WRITE_DATA; - } - } - if (esp_http_client_fetch_headers(client) < 0) { - return ESP_ERR_HTTP_FETCH_HEADER; + if (client->process_again) { + esp_http_client_prepare(client); } + switch (client->state) { + /* In case of blocking esp_http_client_perform(), the following states will fall through one after the after; + in case of non-blocking esp_http_client_perform(), if there is an error condition, like EINPROGRESS or EAGAIN, + then the esp_http_client_perform() API will return ESP_ERR_HTTP_EAGAIN error. The user may call + esp_http_client_perform API again, and for this reason, we maintain the states */ + case HTTP_STATE_INIT: + if ((err = esp_http_client_connect(client)) != ESP_OK) { + if (client->is_async && err == ESP_ERR_HTTP_CONNECTING) { + return ESP_ERR_HTTP_EAGAIN; + } + return err; + } + /* falls through */ + case HTTP_STATE_CONNECTED: + if ((err = esp_http_client_request_send(client)) != ESP_OK) { + if (client->is_async && errno == EAGAIN) { + return ESP_ERR_HTTP_EAGAIN; + } + return err; + } + /* falls through */ + case HTTP_STATE_REQ_COMPLETE_HEADER: + if ((err = esp_http_client_send_post_data(client)) != ESP_OK) { + if (client->is_async && errno == EAGAIN) { + return ESP_ERR_HTTP_EAGAIN; + } + return err; + } + /* falls through */ + case HTTP_STATE_REQ_COMPLETE_DATA: + if (esp_http_client_fetch_headers(client) < 0) { + if (client->is_async && errno == EAGAIN) { + return ESP_ERR_HTTP_EAGAIN; + } + return ESP_ERR_HTTP_FETCH_HEADER; + } + /* falls through */ + case HTTP_STATE_RES_COMPLETE_HEADER: + if ((err = esp_http_check_response(client)) != ESP_OK) { + ESP_LOGE(TAG, "Error response"); + return err; + } + while (client->response->is_chunked && !client->is_chunk_complete) { + if (esp_http_client_get_data(client) <= 0) { + if (client->is_async && errno == EAGAIN) { + return ESP_ERR_HTTP_EAGAIN; + } + ESP_LOGD(TAG, "Read finish or server requests close"); + break; + } + } + while (client->response->data_process < client->response->content_length) { + if (esp_http_client_get_data(client) <= 0) { + if (client->is_async && errno == EAGAIN) { + return ESP_ERR_HTTP_EAGAIN; + } + ESP_LOGD(TAG, "Read finish or server requests close"); + break; + } + } + http_dispatch_event(client, HTTP_EVENT_ON_FINISH, NULL, 0); - if ((err = esp_http_check_response(client)) != ESP_OK) { - ESP_LOGE(TAG, "Error response"); - return err; - } - while (client->response->is_chunked && !client->is_chunk_complete) { - if (esp_http_client_get_data(client) <= 0) { - ESP_LOGD(TAG, "Read finish or server requests close"); + if (!http_should_keep_alive(client->parser)) { + ESP_LOGD(TAG, "Close connection"); + esp_http_client_close(client); + } else { + if (client->state > HTTP_STATE_CONNECTED) { + client->state = HTTP_STATE_CONNECTED; + client->first_line_prepared = false; + } + } break; - } - } - while (client->response->data_process < client->response->content_length) { - if (esp_http_client_get_data(client) <= 0) { - ESP_LOGD(TAG, "Read finish or server requests close"); + default: break; - } - } - - http_dispatch_event(client, HTTP_EVENT_ON_FINISH, NULL, 0); - - if (!http_should_keep_alive(client->parser)) { - ESP_LOGD(TAG, "Close connection"); - esp_http_client_close(client); - } else { - if (client->state > HTTP_STATE_CONNECTED) { - client->state = HTTP_STATE_CONNECTED; - } } } while (client->process_again); return ESP_OK; } - int esp_http_client_fetch_headers(esp_http_client_handle_t client) { if (client->state < HTTP_STATE_REQ_COMPLETE_HEADER) { @@ -842,7 +895,7 @@ int esp_http_client_fetch_headers(esp_http_client_handle_t client) while (client->state < HTTP_STATE_RES_COMPLETE_HEADER) { buffer->len = transport_read(client->transport, buffer->data, client->buffer_size, client->timeout_ms); - if (buffer->len <= 0) { + if (buffer->len < 0) { return ESP_FAIL; } http_parser_execute(client->parser, client->parser_settings, buffer->data, buffer->len); @@ -855,9 +908,10 @@ int esp_http_client_fetch_headers(esp_http_client_handle_t client) return client->response->content_length; } -esp_err_t esp_http_client_open(esp_http_client_handle_t client, int write_len) +static esp_err_t esp_http_client_connect(esp_http_client_handle_t client) { esp_err_t err; + if (client->state == HTTP_STATE_UNINIT) { ESP_LOGE(TAG, "Client has not been initialized"); return ESP_ERR_INVALID_STATE; @@ -881,79 +935,165 @@ esp_err_t esp_http_client_open(esp_http_client_handle_t client, int write_len) #endif return ESP_ERR_HTTP_INVALID_TRANSPORT; } - if (transport_connect(client->transport, client->connection_info.host, client->connection_info.port, client->timeout_ms) < 0) { - ESP_LOGE(TAG, "Connection failed"); - return ESP_ERR_HTTP_CONNECT; + if (!client->is_async) { + if (transport_connect(client->transport, client->connection_info.host, client->connection_info.port, client->timeout_ms) < 0) { + ESP_LOGE(TAG, "Connection failed, sock < 0"); + return ESP_ERR_HTTP_CONNECT; + } + } else { + int ret = transport_connect_async(client->transport, client->connection_info.host, client->connection_info.port, client->timeout_ms); + if (ret == ASYNC_TRANS_CONNECT_FAIL) { + ESP_LOGE(TAG, "Connection failed"); + return ESP_ERR_HTTP_CONNECT; + } else if (ret == ASYNC_TRANS_CONNECTING) { + ESP_LOGD(TAG, "Connection not yet established"); + return ESP_ERR_HTTP_CONNECTING; + } } - http_dispatch_event(client, HTTP_EVENT_ON_CONNECTED, NULL, 0); client->state = HTTP_STATE_CONNECTED; + http_dispatch_event(client, HTTP_EVENT_ON_CONNECTED, NULL, 0); } + return ESP_OK; +} +static int http_client_prepare_first_line(esp_http_client_handle_t client, int write_len) +{ if (write_len >= 0) { http_header_set_format(client->request->headers, "Content-Length", "%d", write_len); - } else if (write_len < 0) { + } else { esp_http_client_set_header(client, "Transfer-Encoding", "chunked"); esp_http_client_set_method(client, HTTP_METHOD_POST); } - int header_index = 0; - int wlen = client->buffer_size; - const char *method = HTTP_METHOD_MAPPING[client->connection_info.method]; - int first_line = snprintf(client->request->buffer->data, - client->buffer_size, "%s %s", - method, - client->connection_info.path); - if (first_line > client->buffer_size) { + int first_line_len = snprintf(client->request->buffer->data, + client->buffer_size, "%s %s", + method, + client->connection_info.path); + if (first_line_len >= client->buffer_size) { ESP_LOGE(TAG, "Out of buffer"); - return ESP_ERR_HTTP_CONNECT; + return -1; } if (client->connection_info.query) { - first_line += snprintf(client->request->buffer->data + first_line, - client->buffer_size - first_line, "?%s", client->connection_info.query); - if (first_line > client->buffer_size) { + 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) { ESP_LOGE(TAG, "Out of buffer"); - return ESP_ERR_HTTP_CONNECT; + return -1; + } } - first_line += snprintf(client->request->buffer->data + first_line, - client->buffer_size - first_line, " %s\r\n", DEFAULT_HTTP_PROTOCOL); - if (first_line > client->buffer_size) { + 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) { ESP_LOGE(TAG, "Out of buffer"); - return ESP_ERR_HTTP_CONNECT; + return -1; } - wlen -= first_line; + return first_line_len; +} - while ((header_index = http_header_generate_string(client->request->headers, header_index, client->request->buffer->data + first_line, &wlen))) { +static esp_err_t esp_http_client_request_send(esp_http_client_handle_t client) +{ + int first_line_len = 0; + if (!client->first_line_prepared) { + if ((first_line_len = http_client_prepare_first_line(client, client->post_len)) < 0) { + return first_line_len; + } + client->first_line_prepared = true; + client->header_index = 0; + client->data_written_index = 0; + client->data_write_left = 0; + } + + if (client->data_write_left > 0) { + /* sending leftover data from previous call to esp_http_client_request_send() API */ + int wret = 0; + if (((wret = esp_http_client_write(client, client->request->buffer->data + client->data_written_index, client->data_write_left)) < 0)) { + ESP_LOGE(TAG, "Error write request"); + return ESP_ERR_HTTP_WRITE_DATA; + } + client->data_write_left -= wret; + client->data_written_index += wret; + if (client->is_async && client->data_write_left > 0) { + return ESP_ERR_HTTP_WRITE_DATA; /* In case of EAGAIN error, we return ESP_ERR_HTTP_WRITE_DATA, + and the handling of EAGAIN should be done in the higher level APIs. */ + } + } + + int wlen = client->buffer_size - 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; } - if (first_line) { - wlen += first_line; - first_line = 0; + if (first_line_len) { + wlen += first_line_len; + first_line_len = 0; } client->request->buffer->data[wlen] = 0; - ESP_LOGD(TAG, "Write header[%d]: %s", header_index, client->request->buffer->data); + ESP_LOGD(TAG, "Write header[%d]: %s", client->header_index, client->request->buffer->data); - int widx = 0, wret = 0; - while (wlen > 0) { - wret = transport_write(client->transport, client->request->buffer->data + widx, wlen, client->timeout_ms); + client->data_write_left = wlen; + client->data_written_index = 0; + while (client->data_write_left > 0) { + int wret = transport_write(client->transport, client->request->buffer->data + client->data_written_index, client->data_write_left, client->timeout_ms); if (wret <= 0) { ESP_LOGE(TAG, "Error write request"); esp_http_client_close(client); return ESP_ERR_HTTP_WRITE_DATA; } - widx += wret; - wlen -= wret; + client->data_write_left -= wret; + client->data_written_index += wret; } wlen = client->buffer_size; } + + client->data_written_index = 0; + client->data_write_left = client->post_len; client->state = HTTP_STATE_REQ_COMPLETE_HEADER; return ESP_OK; } +static esp_err_t esp_http_client_send_post_data(esp_http_client_handle_t client) +{ + if (client->state != HTTP_STATE_REQ_COMPLETE_HEADER) { + ESP_LOGE(TAG, "Invalid state"); + return ESP_ERR_INVALID_STATE; + } + if (!(client->post_data && client->post_len)) { + goto success; + } + + int wret = esp_http_client_write(client, client->post_data + client->data_written_index, client->data_write_left); + if (wret < 0) { + return wret; + } + client->data_write_left -= wret; + client->data_written_index += wret; + + if (client->data_write_left <= 0) { + goto success; + } else { + return ESP_ERR_HTTP_WRITE_DATA; + } + +success: + client->state = HTTP_STATE_REQ_COMPLETE_DATA; + return ESP_OK; +} + +esp_err_t esp_http_client_open(esp_http_client_handle_t client, int 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)) != ESP_OK) { + return err; + } + return ESP_OK; +} int esp_http_client_write(esp_http_client_handle_t client, const char *buffer, int len) { @@ -964,7 +1104,9 @@ int esp_http_client_write(esp_http_client_handle_t client, const char *buffer, i int wlen = 0, widx = 0; while (len > 0) { wlen = transport_write(client->transport, buffer + widx, len, client->timeout_ms); - if (wlen <= 0) { + /* client->async_block is initialised in case of non-blocking IO, and in this case we return how + much ever data was written by the transport_write() API. */ + if (client->is_async || wlen <= 0) { return wlen; } widx += wlen; diff --git a/components/esp_http_client/include/esp_http_client.h b/components/esp_http_client/include/esp_http_client.h index 9043a48fe7..b3b40ca0dc 100644 --- a/components/esp_http_client/include/esp_http_client.h +++ b/components/esp_http_client/include/esp_http_client.h @@ -114,6 +114,7 @@ typedef struct { 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) */ void *user_data; /*!< HTTP user_data context */ + bool is_async; /*!< Set asynchronous mode */ } esp_http_client_config_t; @@ -123,6 +124,8 @@ typedef struct { #define ESP_ERR_HTTP_WRITE_DATA (ESP_ERR_HTTP_BASE + 3) /*!< Error write HTTP data */ #define ESP_ERR_HTTP_FETCH_HEADER (ESP_ERR_HTTP_BASE + 4) /*!< Error read HTTP header from server */ #define ESP_ERR_HTTP_INVALID_TRANSPORT (ESP_ERR_HTTP_BASE + 5) /*!< There are no transport support for the input scheme */ +#define ESP_ERR_HTTP_CONNECTING (ESP_ERR_HTTP_BASE + 6) /*!< HTTP connection hasn't been established yet */ +#define ESP_ERR_HTTP_EAGAIN (ESP_ERR_HTTP_BASE + 7) /*!< Mapping of errno EAGAIN to esp_err_t */ /** * @brief Start a HTTP session @@ -141,7 +144,10 @@ esp_http_client_handle_t esp_http_client_init(const esp_http_client_config_t *co /** * @brief Invoke this function after `esp_http_client_init` and all the options calls are made, and will perform the * transfer as described in the options. It must be called with the same esp_http_client_handle_t as input as the esp_http_client_init call returned. - * esp_http_client_perform performs the entire request in a blocking manner and returns when done, or if it failed. + * esp_http_client_perform performs the entire request in either blocking or non-blocking manner. By default, the API performs request in a blocking manner and returns when done, + * or if it failed, and in non-blocking manner, it returns if EAGAIN/EWOULDBLOCK or EINPROGRESS is encountered, or if it failed. And in case of non-blocking request, + * the user may call this API multiple times unless request & response is complete or there is a failure. To enable non-blocking esp_http_client_perform(), `is_async` member of esp_http_client_config_t + * must be set while making a call to esp_http_client_init() API. * You can do any amount of calls to esp_http_client_perform while using the same esp_http_client_handle_t. The underlying connection may be kept open if the server allows it. * If you intend to transfer more than one file, you are even encouraged to do so. * esp_http_client will then attempt to re-use the same connection for the following transfers, thus making the operations faster, less CPU intense and using less network resources. diff --git a/components/tcp_transport/include/transport.h b/components/tcp_transport/include/transport.h index a54cb83c5a..f10a8f8d63 100644 --- a/components/tcp_transport/include/transport.h +++ b/components/tcp_transport/include/transport.h @@ -30,6 +30,7 @@ typedef int (*io_func)(transport_handle_t t, const char *buffer, int len, int ti typedef int (*io_read_func)(transport_handle_t t, char *buffer, int len, int timeout_ms); typedef int (*trans_func)(transport_handle_t t); typedef int (*poll_func)(transport_handle_t t, int timeout_ms); +typedef int (*connect_async_func)(transport_handle_t t, const char *host, int port, int timeout_ms); typedef transport_handle_t (*payload_transfer_func)(transport_handle_t); /** @@ -138,6 +139,20 @@ esp_err_t transport_set_default_port(transport_handle_t t, int port); */ int transport_connect(transport_handle_t t, const char *host, int port, int timeout_ms); +/** + * @brief Non-blocking transport connection function, to make a connection to server + * + * @param t The transport handle + * @param[in] host Hostname + * @param[in] port Port + * @param[in] timeout_ms The timeout milliseconds + * + * @return + * - socket for will use by this transport + * - (-1) if there are any errors, should check errno + */ +int transport_connect_async(transport_handle_t t, const char *host, int port, int timeout_ms); + /** * @brief Transport read function * @@ -259,6 +274,20 @@ esp_err_t transport_set_func(transport_handle_t t, poll_func _poll_write, trans_func _destroy, payload_transfer_func _parrent_transport); + + +/** + * @brief Set transport functions for the transport handle + * + * @param[in] t The transport handle + * @param[in] _connect_async_func The connect_async function pointer + * + * @return + * - ESP_OK + * - ESP_FAIL + */ +esp_err_t transport_set_async_connect_func(transport_handle_t t, connect_async_func _connect_async_func); + #ifdef __cplusplus } #endif diff --git a/components/tcp_transport/transport.c b/components/tcp_transport/transport.c index abff9670fd..c23ed8d342 100644 --- a/components/tcp_transport/transport.c +++ b/components/tcp_transport/transport.c @@ -40,6 +40,7 @@ struct transport_item_t { poll_func _poll_read; /*!< Poll and read */ poll_func _poll_write; /*!< Poll and write */ trans_func _destroy; /*!< Destroy and free transport */ + connect_async_func _connect_async; /*!< non-blocking connect function of this transport */ payload_transfer_func _parrent_transfer; /*!< Function returning underlying transport layer */ STAILQ_ENTRY(transport_item_t) next; @@ -145,6 +146,15 @@ int transport_connect(transport_handle_t t, const char *host, int port, int time return ret; } +int transport_connect_async(transport_handle_t t, const char *host, int port, int timeout_ms) +{ + int ret = -1; + if (t && t->_connect) { + return t->_connect_async(t, host, port, timeout_ms); + } + return ret; +} + int transport_read(transport_handle_t t, char *buffer, int len, int timeout_ms) { if (t && t->_read) { @@ -222,6 +232,7 @@ esp_err_t transport_set_func(transport_handle_t t, t->_poll_read = _poll_read; t->_poll_write = _poll_write; t->_destroy = _destroy; + t->_connect_async = NULL; t->_parrent_transfer = _parrent_transport; return ESP_OK; } @@ -246,4 +257,13 @@ esp_err_t transport_set_default_port(transport_handle_t t, int port) transport_handle_t transport_get_handle(transport_handle_t t) { return t; -} \ No newline at end of file +} + +esp_err_t transport_set_async_connect_func(transport_handle_t t, connect_async_func _connect_async_func) +{ + if (t == NULL) { + return ESP_FAIL; + } + t->_connect_async = _connect_async_func; + return ESP_OK; +} diff --git a/components/tcp_transport/transport_ssl.c b/components/tcp_transport/transport_ssl.c index 9ccaf4028e..8e995764e6 100644 --- a/components/tcp_transport/transport_ssl.c +++ b/components/tcp_transport/transport_ssl.c @@ -26,33 +26,58 @@ #include "transport_utils.h" static const char *TAG = "TRANS_SSL"; + +typedef enum { + TRANS_SSL_INIT = 0, + TRANS_SSL_CONNECTING, +} transport_ssl_conn_state_t; + /** * mbedtls specific transport data */ typedef struct { esp_tls_t *tls; - void *cert_pem_data; - int cert_pem_len; + esp_tls_cfg_t cfg; bool ssl_initialized; bool verify_server; + transport_ssl_conn_state_t conn_state; } transport_ssl_t; transport_handle_t transport_get_handle(transport_handle_t t); static int ssl_close(transport_handle_t t); +static int ssl_connect_async(transport_handle_t t, const char *host, int port, int timeout_ms) +{ + transport_ssl_t *ssl = transport_get_context_data(t); + if (ssl->conn_state == TRANS_SSL_INIT) { + if (ssl->cfg.cacert_pem_buf) { + ssl->verify_server = true; + } + ssl->cfg.timeout_ms = timeout_ms; + ssl->cfg.non_block = true; + ssl->ssl_initialized = true; + ssl->tls = calloc(1, sizeof(esp_tls_t)); + if (!ssl->tls) { + return -1; + } + ssl->conn_state = TRANS_SSL_CONNECTING; + } + if (ssl->conn_state == TRANS_SSL_CONNECTING) { + return esp_tls_conn_new_async(host, strlen(host), port, &ssl->cfg, ssl->tls); + } + return 0; +} + static int ssl_connect(transport_handle_t t, const char *host, int port, int timeout_ms) { transport_ssl_t *ssl = transport_get_context_data(t); - esp_tls_cfg_t cfg = { 0 }; - if (ssl->cert_pem_data) { + if (ssl->cfg.cacert_pem_buf) { ssl->verify_server = true; - cfg.cacert_pem_buf = ssl->cert_pem_data; - cfg.cacert_pem_bytes = ssl->cert_pem_len + 1; } - cfg.timeout_ms = timeout_ms; + ssl->cfg.timeout_ms = timeout_ms; ssl->ssl_initialized = true; - ssl->tls = esp_tls_conn_new(host, strlen(host), port, &cfg); + ssl->tls = esp_tls_conn_new(host, strlen(host), port, &ssl->cfg); if (!ssl->tls) { ESP_LOGE(TAG, "Failed to open a new connection"); return -1; @@ -94,7 +119,7 @@ static int ssl_write(transport_handle_t t, const char *buffer, int len, int time } ret = esp_tls_conn_write(ssl->tls, (const unsigned char *) buffer, len); if (ret <= 0) { - ESP_LOGE(TAG, "mbedtls_ssl_write error, errno=%s", strerror(errno)); + ESP_LOGE(TAG, "esp_tls_conn_write error, errno=%s", strerror(errno)); } return ret; } @@ -111,7 +136,7 @@ static int ssl_read(transport_handle_t t, char *buffer, int len, int timeout_ms) } ret = esp_tls_conn_read(ssl->tls, (unsigned char *)buffer, len); if (ret <= 0) { - ESP_LOGE(TAG, "mbedtls_ssl_read error, errno=%s", strerror(errno)); + ESP_LOGE(TAG, "esp_tls_conn_read error, errno=%s", strerror(errno)); } return ret; } @@ -140,8 +165,8 @@ void transport_ssl_set_cert_data(transport_handle_t t, const char *data, int len { transport_ssl_t *ssl = transport_get_context_data(t); if (t && ssl) { - ssl->cert_pem_data = (void *)data; - ssl->cert_pem_len = len; + ssl->cfg.cacert_pem_buf = (void *)data; + ssl->cfg.cacert_pem_bytes = len + 1; } } @@ -152,6 +177,7 @@ transport_handle_t transport_ssl_init() TRANSPORT_MEM_CHECK(TAG, ssl, return NULL); transport_set_context_data(t, ssl); transport_set_func(t, ssl_connect, ssl_read, ssl_write, ssl_close, ssl_poll_read, ssl_poll_write, ssl_destroy, transport_get_handle); + transport_set_async_connect_func(t, ssl_connect_async); return t; }