diff --git a/components/esp_websocket_client/esp_websocket_client.c b/components/esp_websocket_client/esp_websocket_client.c index 27adc5ce26a..8c2b84eb904 100644 --- a/components/esp_websocket_client/esp_websocket_client.c +++ b/components/esp_websocket_client/esp_websocket_client.c @@ -792,6 +792,51 @@ esp_err_t esp_websocket_client_set_headers(esp_websocket_client_handle_t client, return ret; } +esp_err_t esp_websocket_client_append_header(esp_websocket_client_handle_t client, const char *key, const char *value) +{ + // Validate the input parameters + if (client == NULL || key == NULL || value == NULL) { + return ESP_ERR_INVALID_ARG; + } + + websocket_config_storage_t *cfg = client->config; + + // Calculate the length for "key: value\r\n" + size_t len = strlen(key) + strlen(value) + 5; // 5 accounts for ": \r\n" and null-terminator + + // If no previous headers exist + if (cfg->headers == NULL) { + cfg->headers = (char *)malloc(len); + if (cfg->headers == NULL) { + ESP_LOGE(TAG, "Failed to allocate..."); + return ESP_ERR_NO_MEM; + } + snprintf(cfg->headers, len, "%s: %s\r\n", key, value); + return ESP_OK; + } + + // Extend the current headers to accommodate the new key-value pair + size_t current_len = strlen(cfg->headers); + size_t new_len = current_len + len; + + // Allocate memory for new headers + char *new_headers = (char *)malloc(new_len); + if (new_headers == NULL) { + ESP_LOGE(TAG, "Failed to allocate..."); + return ESP_ERR_NO_MEM; + } + + // Copy old headers and append the new header + strcpy(new_headers, cfg->headers); + snprintf(new_headers + current_len, len, "%s: %s\r\n", key, value); + + // Free old headers and assign the new header pointer to cfg->headers + free(cfg->headers); + cfg->headers = new_headers; + + return ESP_OK; +} + static esp_err_t esp_websocket_client_recv(esp_websocket_client_handle_t client) { int rlen; diff --git a/components/esp_websocket_client/include/esp_websocket_client.h b/components/esp_websocket_client/include/esp_websocket_client.h index 0f7af07f44d..c47b0df0d85 100644 --- a/components/esp_websocket_client/include/esp_websocket_client.h +++ b/components/esp_websocket_client/include/esp_websocket_client.h @@ -155,16 +155,36 @@ 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 + * @brief Sets additional WebSocket headers for the client + * @pre The WebSocket client must be stopped before setting headers if it is already connected. * - * @param[in] client The client - * @param headers additional header strings each terminated with \r\n + * Notes: + * - When setting headers using this method, any previous headers will be replaced. + * To set multiple headers, you can concatenate them using the "\r\n" delimiter. + * Alternatively, use the `esp_websocket_client_append_header` function for individual header additions. + * Example: websocket_cfg.headers = "Sec-WebSocket-Key: my_key\r\nPassword: my_pass\r\n"; + * + * @param[in] client The WebSocket client handle. + * @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 Appends a new key-value pair to the headers of a WebSocket client. + * + * Notes: + * - Ensure that this function is called before starting the WebSocket client. + * + * @param[in] client The WebSocket client handle + * @param[in] key The header key to append + * @param[in] value The associated value for the given key + * + * @return esp_err_t + */ +esp_err_t esp_websocket_client_append_header(esp_websocket_client_handle_t client, const char *key, const char *value); + /** * @brief Open the WebSocket connection * diff --git a/components/esp_websocket_client/linux/main/main.c b/components/esp_websocket_client/linux/main/main.c new file mode 100644 index 00000000000..34fe2b71b49 --- /dev/null +++ b/components/esp_websocket_client/linux/main/main.c @@ -0,0 +1,125 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include "esp_log.h" +#include "nvs_flash.h" +#include "protocol_examples_common.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "freertos/event_groups.h" + +#include "esp_websocket_client.h" +#include "esp_system.h" +#include "esp_event.h" +#include "esp_log.h" +#include "esp_netif.h" + +static const char *TAG = "websocket"; + +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 websocket_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data) +{ + esp_websocket_event_data_t *data = (esp_websocket_event_data_t *)event_data; + switch (event_id) { + case WEBSOCKET_EVENT_CONNECTED: + ESP_LOGI(TAG, "WEBSOCKET_EVENT_CONNECTED"); + 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"); + ESP_LOGI(TAG, "Received opcode=%d", data->op_code); + if (data->op_code == 0x08 && data->data_len == 2) { + ESP_LOGW(TAG, "Received closed message with code=%d", 256 * data->data_ptr[0] + data->data_ptr[1]); + } else { + ESP_LOGW(TAG, "Received=%.*s", data->data_len, (char *)data->data_ptr); + } + + // If received data contains json structure it succeed to parse + ESP_LOGW(TAG, "Total payload length=%d, data_len=%d, current payload offset=%d\r\n", data->payload_len, data->data_len, data->payload_offset); + + 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; + } +} + + +static void websocket_app_start(void) +{ + esp_websocket_client_config_t websocket_cfg = {}; + + websocket_cfg.uri = CONFIG_WEBSOCKET_URI; + + ESP_LOGI(TAG, "Connecting to %s...", websocket_cfg.uri); + + esp_websocket_client_handle_t client = esp_websocket_client_init(&websocket_cfg); + // This call has no operational effect; it's used for testing and enhancing coverity analysis + esp_websocket_client_append_header(client, "HeaderNewKey", "value"); + + esp_websocket_register_events(client, WEBSOCKET_EVENT_ANY, websocket_event_handler, (void *)client); + + esp_websocket_client_start(client); + char data[32]; + int i = 0; + while (i < 1) { + if (esp_websocket_client_is_connected(client)) { + int len = sprintf(data, "hello %04d", i++); + ESP_LOGI(TAG, "Sending %s", data); + esp_websocket_client_send_text(client, data, len, portMAX_DELAY); + } + vTaskDelay(1000 / portTICK_PERIOD_MS); + } + + esp_websocket_client_destroy(client); +} + +int main(void) +{ + + ESP_LOGI(TAG, "[APP] Startup.."); + ESP_LOGI(TAG, "[APP] Free memory: %" PRIu32 " bytes", esp_get_free_heap_size()); + ESP_LOGI(TAG, "[APP] IDF version: %s", esp_get_idf_version()); + esp_log_level_set("*", ESP_LOG_INFO); + esp_log_level_set("websocket_client", ESP_LOG_DEBUG); + esp_log_level_set("transport_ws", ESP_LOG_DEBUG); + esp_log_level_set("trans_tcp", ESP_LOG_DEBUG); + + ESP_ERROR_CHECK(nvs_flash_init()); + ESP_ERROR_CHECK(esp_netif_init()); + ESP_ERROR_CHECK(esp_event_loop_create_default()); + + /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig. + * Read "Establishing Wi-Fi or Ethernet Connection" section in + * examples/protocols/README.md for more information about this function. + */ + ESP_ERROR_CHECK(example_connect()); + + websocket_app_start(); + return 0; +}