Skip to content

Commit

Permalink
feat(websocket): Added new API esp_websocket_client_append_header
Browse files Browse the repository at this point in the history
  • Loading branch information
suren-gabrielyan-espressif committed Oct 9, 2023
1 parent ea14e15 commit cf209a7
Show file tree
Hide file tree
Showing 3 changed files with 194 additions and 4 deletions.
45 changes: 45 additions & 0 deletions components/esp_websocket_client/esp_websocket_client.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
28 changes: 24 additions & 4 deletions components/esp_websocket_client/include/esp_websocket_client.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
*
Expand Down
125 changes: 125 additions & 0 deletions components/esp_websocket_client/linux/main/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#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;
}

0 comments on commit cf209a7

Please sign in to comment.