Compare commits

...

4 Commits

Author SHA1 Message Date
a3e8421de8 update esp_modem to use esp_docs
migrated docs from github pages to docs.espressif.com
2023-03-07 15:16:10 +04:00
3bf0511938 Merge pull request #216 from gabsuren/feature/websocket-errorhandling
esp_websocket_client: Improve error handling (IDFGH-8444)
2023-02-24 07:35:41 +01:00
319fce018e Merge pull request #231 from tore-espressif/fix/esp_modem/on_data_cb
esp_modem: Return true from on_data callback in data mode
2023-02-24 07:34:20 +01:00
d047ff569c esp_websocket_client:
* Error handling improved to show status code from server
* Added new API `esp_websocket_client_set_headers`
* Dispatches 'WEBSOCKET_EVENT_BEFORE_CONNECT' event before tcp connection
2023-02-22 11:41:33 +04:00
5 changed files with 196 additions and 30 deletions

77
.github/workflows/publish-docs.yml vendored Normal file
View File

@ -0,0 +1,77 @@
name: Docs and Publish New
on:
push:
env:
DOCS_DEPLOY_URL_BASE: "${{ secrets.DOCS_PREVIEW_SERVER_URL }}"
#DOCS_DEPLOY_SERVER: "ci.espressif.cn:42348"
DOCS_DEPLOY_SERVER: "ci.espressif.cn:42348"
DOCS_DEPLOY_SERVER_USER: "${{ secrets.DOCS_SERVER_USER }}"
DOCS_DEPLOY_PRIVATEKEY : "${{ secrets.DOCS_DEPLOY_KEY }}"
DOCS_DEPLOY_KEY: "${{ secrets.DOCS_DEPLOY_KEY }}"
DOCS_DEPLOY_PATH : "${{ secrets.DOCS_PATH }}"
jobs:
docs_build:
name: Docs-Build-And-Upload
runs-on: ubuntu-latest
# Skip running on forks since it won't have access to secrets
#if: github.repository == 'espressif/esp-protocols'
steps:
- name: Checkout esp-protocols
uses: actions/checkout@master
with:
persist-credentials: false
fetch-depth: 0
submodules: recursive
- name: Generate and delploying docs
shell: bash
run: |
sudo apt-get update
sudo apt-get -y install doxygen clang python3-pip
python -m pip install breathe recommonmark esp-docs
cd $GITHUB_WORKSPACE/components/esp_websocket_client/docs
./generate_docs
source $GITHUB_WORKSPACE/docs/utils.sh
add_doc_server_ssh_keys $DOCS_DEPLOY_PRIVATEKEY $DOCS_DEPLOY_SERVER $DOCS_DEPLOY_SERVER_USER
export GIT_VER=$(git describe --always)
ls -l $GITHUB_WORKSPACE/components/esp_websocket_client/docs
export DOCS_BUILD_DIR=$GITHUB_WORKSPACE/components/esp_websocket_client/docs
deploy-docs
#build_docs:
# stage: build
# image: $ESP_DOCS_ENV_IMAGE
# tags:
# - build_docs
# artifacts:
# when: always
# paths:
# - docs/_build/*/*/*.txt
# - docs/_build/*/*/html/*
# expire_in: 4 days
# # No cleaning when the artifacts
# after_script: []
# script:
# - cd docs
# - pip install -r requirements.txt
# - build-docs -l en -t esp32
#
#.deploy_docs_template:
# stage: deploy
# image: $ESP_DOCS_ENV_IMAGE
# tags:
# - deploy_docs
# needs:
# - build_docs
# only:
# changes:
# - "docs/**/*"
# script:
# - source ${CI_PROJECT_DIR}/docs/utils.sh
# - add_doc_server_ssh_keys ${{ secrets.DOCS_DEPLOY_PRIVATEKEY }} ${{ secrets.DOCS_DEPLOY_SERVER }} ${{ secrets.DOCS_DEPLOY_SERVER_USER }}
# - export GIT_VER=$(git describe --always)
# - pip install -r ${CI_PROJECT_DIR}/docs/requirements.txt
# - deploy-docs
#

View File

@ -107,6 +107,7 @@ typedef enum {
struct esp_websocket_client {
esp_event_loop_handle_t event_handle;
TaskHandle_t task_handle;
esp_websocket_error_codes_t error_handle;
esp_transport_list_handle_t transport_list;
esp_transport_handle_t transport;
websocket_config_storage_t *config;
@ -196,6 +197,16 @@ static esp_err_t esp_websocket_client_dispatch_event(esp_websocket_client_handle
event_data.payload_len = client->payload_len;
event_data.payload_offset = client->payload_offset;
if (client->error_handle.error_type == WEBSOCKET_ERROR_TYPE_TCP_TRANSPORT) {
event_data.error_handle.esp_tls_last_esp_err = esp_tls_get_and_clear_last_error(esp_transport_get_error_handle(client->transport),
&client->error_handle.esp_tls_stack_err,
&client->error_handle.esp_tls_cert_verify_flags);
event_data.error_handle.esp_transport_sock_errno = esp_transport_get_errno(client->transport);
}
event_data.error_handle.error_type = client->error_handle.error_type;
event_data.error_handle.esp_ws_handshake_status_code = client->error_handle.esp_ws_handshake_status_code;
if ((err = esp_event_post_to(client->event_handle,
WEBSOCKET_EVENTS, event,
&event_data,
@ -206,7 +217,7 @@ static esp_err_t esp_websocket_client_dispatch_event(esp_websocket_client_handle
return esp_event_loop_run(client->event_handle, 0);
}
static esp_err_t esp_websocket_client_abort_connection(esp_websocket_client_handle_t client)
static esp_err_t esp_websocket_client_abort_connection(esp_websocket_client_handle_t client, esp_websocket_error_type_t error_type)
{
ESP_WS_CLIENT_STATE_CHECK(TAG, client, return ESP_FAIL);
esp_transport_close(client->transport);
@ -215,6 +226,8 @@ static esp_err_t esp_websocket_client_abort_connection(esp_websocket_client_hand
client->reconnect_tick_ms = _tick_get_ms();
ESP_LOGI(TAG, "Reconnect after %d ms", client->wait_timeout_ms);
}
client->error_handle.error_type = error_type;
client->state = WEBSOCKET_STATE_WAIT_TIMEOUT;
esp_websocket_client_dispatch_event(client, WEBSOCKET_EVENT_DISCONNECTED, NULL, 0);
return ESP_OK;
@ -725,6 +738,19 @@ esp_err_t esp_websocket_client_set_uri(esp_websocket_client_handle_t client, con
return ESP_OK;
}
esp_err_t esp_websocket_client_set_headers(esp_websocket_client_handle_t client, const char *headers)
{
if (client == NULL || client->state != WEBSOCKET_STATE_CONNECTED || headers == NULL) {
return ESP_ERR_INVALID_ARG;
}
xSemaphoreTakeRecursive(client->lock, portMAX_DELAY);
esp_err_t ret = esp_transport_ws_set_headers(client->transport, headers);
xSemaphoreGiveRecursive(client->lock);
return ret;
}
static esp_err_t esp_websocket_client_recv(esp_websocket_client_handle_t client)
{
int rlen;
@ -739,14 +765,10 @@ static esp_err_t esp_websocket_client_recv(esp_websocket_client_handle_t client)
esp_websocket_free_buf(client, false);
esp_tls_error_handle_t error_handle = esp_transport_get_error_handle(client->transport);
if (error_handle) {
ESP_LOGE(TAG, "esp_transport_read() failed with %d, last_error=%s, tls_error_code=%i, tls_flags=%i, errno=%d",
rlen, esp_err_to_name(error_handle->last_error), error_handle->esp_tls_error_code,
error_handle->esp_tls_flags, errno);
esp_websocket_client_error(client, "esp_transport_read() failed with %d, last_error=%s, tls_error_code=%i, tls_flags=%i, errno=%d",
esp_websocket_client_error(client, "esp_transport_read() failed with %d, transport_error=%s, tls_error_code=%i, tls_flags=%i, errno=%d",
rlen, esp_err_to_name(error_handle->last_error), error_handle->esp_tls_error_code,
error_handle->esp_tls_flags, errno);
} else {
ESP_LOGE(TAG, "esp_transport_read() failed with %d, errno=%d", rlen, errno);
esp_websocket_client_error(client, "esp_transport_read() failed with %d, errno=%d", rlen, errno);
}
return ESP_FAIL;
@ -817,32 +839,32 @@ static void esp_websocket_client_task(void *pv)
client->run = false;
break;
}
esp_websocket_client_dispatch_event(client, WEBSOCKET_EVENT_BEFORE_CONNECT, NULL, 0);
int result = esp_transport_connect(client->transport,
client->config->host,
client->config->port,
client->config->network_timeout_ms);
if (result < 0) {
esp_tls_error_handle_t error_handle = esp_transport_get_error_handle(client->transport);
client->error_handle.esp_ws_handshake_status_code = esp_transport_ws_get_upgrade_request_status(client->transport);
if (error_handle) {
ESP_LOGE(TAG, "esp_transport_connect() failed with %d, last_error=%s, tls_error_code=%i, tls_flags=%i, errno=%d",
result, esp_err_to_name(error_handle->last_error), error_handle->esp_tls_error_code,
error_handle->esp_tls_flags, errno);
esp_websocket_client_error(client, "esp_transport_connect() failed with %d, last_error=%s, tls_error_code=%i, tls_flags=%i, errno=%d",
esp_websocket_client_error(client, "esp_transport_connect() failed with %d,\
transport_error=%s, tls_error_code=%i, tls_flags=%i, esp_ws_handshake_status_code=%d, errno=%d",
result, esp_err_to_name(error_handle->last_error), error_handle->esp_tls_error_code,
error_handle->esp_tls_flags, errno);
error_handle->esp_tls_flags, client->error_handle.esp_ws_handshake_status_code, errno);
} else {
ESP_LOGE(TAG, "esp_transport_connect() failed with %d, errno=%d", result, errno);
esp_websocket_client_error(client, "esp_transport_connect() failed with %d, errno=%d", result, errno);
esp_websocket_client_error(client, "esp_transport_connect() failed with %d, esp_ws_handshake_status_code=%d, errno=%d",
result, client->error_handle.esp_ws_handshake_status_code, errno);
}
esp_websocket_client_abort_connection(client);
esp_websocket_client_abort_connection(client, WEBSOCKET_ERROR_TYPE_TCP_TRANSPORT);
break;
}
ESP_LOGD(TAG, "Transport connected to %s://%s:%d", client->config->scheme, client->config->host, client->config->port);
client->state = WEBSOCKET_STATE_CONNECTED;
client->wait_for_pong_resp = false;
client->error_handle.error_type = WEBSOCKET_ERROR_TYPE_NONE;
esp_websocket_client_dispatch_event(client, WEBSOCKET_EVENT_CONNECTED, NULL, 0);
break;
case WEBSOCKET_STATE_CONNECTED:
if ((CLOSE_FRAME_SENT_BIT & xEventGroupGetBits(client->status_bits)) == 0) { // only send and check for PING
@ -860,14 +882,14 @@ static void esp_websocket_client_task(void *pv)
if ( _tick_get_ms() - client->pingpong_tick_ms > client->config->pingpong_timeout_sec * 1000 ) {
if (client->wait_for_pong_resp) {
ESP_LOGE(TAG, "Error, no PONG received for more than %d seconds after PING", client->config->pingpong_timeout_sec);
esp_websocket_client_error(client, "Error, no PONG received for more than %d seconds after PING", client->config->pingpong_timeout_sec);
esp_websocket_client_abort_connection(client);
esp_websocket_client_abort_connection(client, WEBSOCKET_ERROR_TYPE_PONG_TIMEOUT);
break;
}
}
}
if (read_select == 0) {
ESP_LOGV(TAG, "Read poll timeout: skipping esp_transport_read()...");
break;
@ -876,7 +898,7 @@ static void esp_websocket_client_task(void *pv)
if (esp_websocket_client_recv(client) == ESP_FAIL) {
ESP_LOGE(TAG, "Error receive data");
esp_websocket_client_abort_connection(client);
esp_websocket_client_abort_connection(client, WEBSOCKET_ERROR_TYPE_TCP_TRANSPORT);
break;
}
break;
@ -910,17 +932,13 @@ static void esp_websocket_client_task(void *pv)
if (read_select < 0) {
esp_tls_error_handle_t error_handle = esp_transport_get_error_handle(client->transport);
if (error_handle) {
ESP_LOGE(TAG, "esp_transport_poll_read() returned %d, last_error=%s, tls_error_code=%i, tls_flags=%i, errno=%d",
read_select, esp_err_to_name(error_handle->last_error), error_handle->esp_tls_error_code,
error_handle->esp_tls_flags, errno);
esp_websocket_client_error(client, "esp_transport_poll_read() returned %d, last_error=%s, tls_error_code=%i, tls_flags=%i, errno=%d",
esp_websocket_client_error(client, "esp_transport_poll_read() returned %d, transport_error=%s, tls_error_code=%i, tls_flags=%i, errno=%d",
read_select, esp_err_to_name(error_handle->last_error), error_handle->esp_tls_error_code,
error_handle->esp_tls_flags, errno);
} else {
ESP_LOGE(TAG, "esp_transport_poll_read() returned %d, errno=%d", read_select, errno);
esp_websocket_client_error(client, "esp_transport_poll_read() returned %d, errno=%d", read_select, errno);
}
esp_websocket_client_abort_connection(client);
esp_websocket_client_abort_connection(client, WEBSOCKET_ERROR_TYPE_TCP_TRANSPORT);
}
} else if (WEBSOCKET_STATE_WAIT_TIMEOUT == client->state) {
// waiting for reconnecting...
@ -1120,17 +1138,13 @@ int esp_websocket_client_send_with_opcode(esp_websocket_client_handle_t client,
esp_websocket_free_buf(client, true);
esp_tls_error_handle_t error_handle = esp_transport_get_error_handle(client->transport);
if (error_handle) {
ESP_LOGE(TAG, "esp_transport_write() returned %d, last_error=%s, tls_error_code=%i, tls_flags=%i, errno=%d",
ret, esp_err_to_name(error_handle->last_error), error_handle->esp_tls_error_code,
error_handle->esp_tls_flags, errno);
esp_websocket_client_error(client, "esp_transport_write() returned %d, last_error=%s, tls_error_code=%i, tls_flags=%i, errno=%d",
esp_websocket_client_error(client, "esp_transport_write() returned %d, transport_error=%s, tls_error_code=%i, tls_flags=%i, errno=%d",
ret, esp_err_to_name(error_handle->last_error), error_handle->esp_tls_error_code,
error_handle->esp_tls_flags, errno);
} else {
ESP_LOGE(TAG, "esp_transport_write() returned %d, errno=%d", ret, errno);
esp_websocket_client_error(client, "esp_transport_write() returned %d, errno=%d", ret, errno);
}
esp_websocket_client_abort_connection(client);
esp_websocket_client_abort_connection(client, WEBSOCKET_ERROR_TYPE_TCP_TRANSPORT);
goto unlock_and_return;
}
current_opcode = 0;

View File

@ -37,6 +37,13 @@ static const char *TAG = "websocket";
static TimerHandle_t shutdown_signal_timer;
static SemaphoreHandle_t shutdown_sema;
static void log_error_if_nonzero(const char *message, int error_code)
{
if (error_code != 0) {
ESP_LOGE(TAG, "Last error %s: 0x%x", message, error_code);
}
}
static void shutdown_signaler(TimerHandle_t xTimer)
{
ESP_LOGI(TAG, "No data received for %d seconds, signaling shutdown", NO_DATA_TIMEOUT_SEC);
@ -71,6 +78,12 @@ static void websocket_event_handler(void *handler_args, esp_event_base_t base, i
break;
case WEBSOCKET_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "WEBSOCKET_EVENT_DISCONNECTED");
log_error_if_nonzero("HTTP status code", data->error_handle.esp_ws_handshake_status_code);
if (data->error_handle.error_type == WEBSOCKET_ERROR_TYPE_TCP_TRANSPORT) {
log_error_if_nonzero("reported from esp-tls", data->error_handle.esp_tls_last_esp_err);
log_error_if_nonzero("reported from tls stack", data->error_handle.esp_tls_stack_err);
log_error_if_nonzero("captured as transport's socket errno", data->error_handle.esp_transport_sock_errno);
}
break;
case WEBSOCKET_EVENT_DATA:
ESP_LOGI(TAG, "WEBSOCKET_EVENT_DATA");
@ -99,6 +112,12 @@ static void websocket_event_handler(void *handler_args, esp_event_base_t base, i
break;
case WEBSOCKET_EVENT_ERROR:
ESP_LOGI(TAG, "WEBSOCKET_EVENT_ERROR");
log_error_if_nonzero("HTTP status code", data->error_handle.esp_ws_handshake_status_code);
if (data->error_handle.error_type == WEBSOCKET_ERROR_TYPE_TCP_TRANSPORT) {
log_error_if_nonzero("reported from esp-tls", data->error_handle.esp_tls_last_esp_err);
log_error_if_nonzero("reported from tls stack", data->error_handle.esp_tls_stack_err);
log_error_if_nonzero("captured as transport's socket errno", data->error_handle.esp_transport_sock_errno);
}
break;
}
}

View File

@ -35,9 +35,35 @@ typedef enum {
WEBSOCKET_EVENT_DISCONNECTED, /*!< The connection has been disconnected */
WEBSOCKET_EVENT_DATA, /*!< When receiving data from the server, possibly multiple portions of the packet */
WEBSOCKET_EVENT_CLOSED, /*!< The connection has been closed cleanly */
WEBSOCKET_EVENT_BEFORE_CONNECT, /*!< The event occurs before connecting */
WEBSOCKET_EVENT_MAX
} esp_websocket_event_id_t;
/**
* @brief Websocket connection error codes propagated via ERROR event
*/
typedef enum {
WEBSOCKET_ERROR_TYPE_NONE = 0,
WEBSOCKET_ERROR_TYPE_TCP_TRANSPORT,
WEBSOCKET_ERROR_TYPE_PONG_TIMEOUT,
WEBSOCKET_ERROR_TYPE_HANDSHAKE
} esp_websocket_error_type_t;
/**
* @brief Websocket error code structure to be passed as a contextual information into ERROR event
*/
typedef struct {
/* compatible portion of the struct corresponding to struct esp_tls_last_error */
esp_err_t esp_tls_last_esp_err; /*!< last esp_err code reported from esp-tls component */
int esp_tls_stack_err; /*!< tls specific error code reported from underlying tls stack */
int esp_tls_cert_verify_flags; /*!< tls flags reported from underlying tls stack during certificate verification */
/* esp-websocket specific structure extension */
esp_websocket_error_type_t error_type;
int esp_ws_handshake_status_code; /*!< http status code of the websocket upgrade handshake */
/* tcp_transport extension */
int esp_transport_sock_errno; /*!< errno from the underlying socket */
} esp_websocket_error_codes_t;
/**
* @brief Websocket event data
*/
@ -50,6 +76,7 @@ typedef struct {
void *user_context; /*!< user_data context, from esp_websocket_client_config_t user_data */
int payload_len; /*!< Total payload length, payloads exceeding buffer will be posted through multiple events */
int payload_offset; /*!< Actual offset for the data associated with this event */
esp_websocket_error_codes_t error_handle; /*!< esp-websocket error handle including esp-tls errors as well as internal websocket errors */
} esp_websocket_event_data_t;
/**
@ -127,6 +154,17 @@ esp_websocket_client_handle_t esp_websocket_client_init(const esp_websocket_clie
*/
esp_err_t esp_websocket_client_set_uri(esp_websocket_client_handle_t client, const char *uri);
/**
* @brief Set additional websocket headers for the client, when performing this behavior, the headers will replace the old ones
* @pre Must stop the WebSocket client before set headers if the client has been connected
*
* @param[in] client The client
* @param headers additional header strings each terminated with \r\n
*
* @return esp_err_t
*/
esp_err_t esp_websocket_client_set_headers(esp_websocket_client_handle_t client, const char *headers);
/**
* @brief Open the WebSocket connection
*

18
docs/utils.sh Normal file
View File

@ -0,0 +1,18 @@
# Bash helper functions for adding SSH keys
function add_ssh_keys() {
local key_string="${1}"
mkdir -p ~/.ssh
chmod 700 ~/.ssh
echo -n "${key_string}" >~/.ssh/id_rsa_base64
base64 --decode --ignore-garbage ~/.ssh/id_rsa_base64 >~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
}
function add_doc_server_ssh_keys() {
local key_string="${1}"
local server_url="${2}"
local server_user="${3}"
add_ssh_keys "${key_string}"
echo -e "Host ${server_url}\n\tStrictHostKeyChecking no\n\tUser ${server_user}\n" >>~/.ssh/config
}