ws_client: fix for not sending ping responses, updated to pass events also for PING and PONG messages, added interfaces to send both binary and text data
closes https://github.com/espressif/esp-idf/issues/3982
This commit is contained in:
parent
4ed0d006b4
commit
44599d3a84
5 changed files with 128 additions and 11 deletions
|
@ -90,6 +90,7 @@ struct esp_websocket_client {
|
||||||
char *rx_buffer;
|
char *rx_buffer;
|
||||||
char *tx_buffer;
|
char *tx_buffer;
|
||||||
int buffer_size;
|
int buffer_size;
|
||||||
|
ws_transport_opcodes_t last_opcode;
|
||||||
};
|
};
|
||||||
|
|
||||||
static uint64_t _tick_get_ms()
|
static uint64_t _tick_get_ms()
|
||||||
|
@ -106,6 +107,7 @@ static esp_err_t esp_websocket_client_dispatch_event(esp_websocket_client_handle
|
||||||
esp_websocket_event_data_t event_data;
|
esp_websocket_event_data_t event_data;
|
||||||
event_data.data_ptr = data;
|
event_data.data_ptr = data;
|
||||||
event_data.data_len = data_len;
|
event_data.data_len = data_len;
|
||||||
|
event_data.op_code = client->last_opcode;
|
||||||
|
|
||||||
if ((err = esp_event_post_to(client->event_handle,
|
if ((err = esp_event_post_to(client->event_handle,
|
||||||
WEBSOCKET_EVENTS, event,
|
WEBSOCKET_EVENTS, event,
|
||||||
|
@ -457,7 +459,7 @@ static void esp_websocket_client_task(void *pv)
|
||||||
if (_tick_get_ms() - client->ping_tick_ms > WEBSOCKET_PING_TIMEOUT_MS) {
|
if (_tick_get_ms() - client->ping_tick_ms > WEBSOCKET_PING_TIMEOUT_MS) {
|
||||||
client->ping_tick_ms = _tick_get_ms();
|
client->ping_tick_ms = _tick_get_ms();
|
||||||
// Send PING
|
// Send PING
|
||||||
esp_transport_write(client->transport, NULL, 0, client->config->network_timeout_ms);
|
esp_transport_ws_send_raw(client->transport, WS_TRANSPORT_OPCODES_PING, NULL, 0, client->config->network_timeout_ms);
|
||||||
}
|
}
|
||||||
if (read_select == 0) {
|
if (read_select == 0) {
|
||||||
ESP_LOGD(TAG, "Timeout...");
|
ESP_LOGD(TAG, "Timeout...");
|
||||||
|
@ -471,8 +473,15 @@ static void esp_websocket_client_task(void *pv)
|
||||||
esp_websocket_client_abort_connection(client);
|
esp_websocket_client_abort_connection(client);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (rlen > 0) {
|
if (rlen >= 0) {
|
||||||
|
client->last_opcode = esp_transport_ws_get_read_opcode(client->transport);
|
||||||
esp_websocket_client_dispatch_event(client, WEBSOCKET_EVENT_DATA, client->rx_buffer, rlen);
|
esp_websocket_client_dispatch_event(client, WEBSOCKET_EVENT_DATA, client->rx_buffer, rlen);
|
||||||
|
// if a PING message received -> send out the PONG
|
||||||
|
if (client->last_opcode == WS_TRANSPORT_OPCODES_PING) {
|
||||||
|
const char *data = (rlen == 0) ? NULL : client->rx_buffer;
|
||||||
|
esp_transport_ws_send_raw(client->transport, WS_TRANSPORT_OPCODES_PONG, data, rlen,
|
||||||
|
client->config->network_timeout_ms);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case WEBSOCKET_STATE_WAIT_TIMEOUT:
|
case WEBSOCKET_STATE_WAIT_TIMEOUT:
|
||||||
|
@ -529,7 +538,24 @@ esp_err_t esp_websocket_client_stop(esp_websocket_client_handle_t client)
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int esp_websocket_client_send_with_opcode(esp_websocket_client_handle_t client, ws_transport_opcodes_t opcode, const char *data, int len, TickType_t timeout);
|
||||||
|
|
||||||
|
int esp_websocket_client_send_text(esp_websocket_client_handle_t client, const char *data, int len, TickType_t timeout)
|
||||||
|
{
|
||||||
|
return esp_websocket_client_send_with_opcode(client, WS_TRANSPORT_OPCODES_TEXT, data, len, timeout);
|
||||||
|
}
|
||||||
|
|
||||||
int esp_websocket_client_send(esp_websocket_client_handle_t client, const char *data, int len, TickType_t timeout)
|
int esp_websocket_client_send(esp_websocket_client_handle_t client, const char *data, int len, TickType_t timeout)
|
||||||
|
{
|
||||||
|
return esp_websocket_client_send_with_opcode(client, WS_TRANSPORT_OPCODES_BINARY, data, len, timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
int esp_websocket_client_send_bin(esp_websocket_client_handle_t client, const char *data, int len, TickType_t timeout)
|
||||||
|
{
|
||||||
|
return esp_websocket_client_send_with_opcode(client, WS_TRANSPORT_OPCODES_BINARY, data, len, timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int esp_websocket_client_send_with_opcode(esp_websocket_client_handle_t client, ws_transport_opcodes_t opcode, const char *data, int len, TickType_t timeout)
|
||||||
{
|
{
|
||||||
int need_write = len;
|
int need_write = len;
|
||||||
int wlen = 0, widx = 0;
|
int wlen = 0, widx = 0;
|
||||||
|
@ -558,10 +584,8 @@ int esp_websocket_client_send(esp_websocket_client_handle_t client, const char *
|
||||||
need_write = client->buffer_size;
|
need_write = client->buffer_size;
|
||||||
}
|
}
|
||||||
memcpy(client->tx_buffer, data + widx, need_write);
|
memcpy(client->tx_buffer, data + widx, need_write);
|
||||||
wlen = esp_transport_write(client->transport,
|
// send with ws specific way and specific opcode
|
||||||
(char *)client->tx_buffer,
|
wlen = esp_transport_ws_send_raw(client->transport, opcode, (char *)client->tx_buffer, need_write, timeout);
|
||||||
need_write,
|
|
||||||
client->config->network_timeout_ms);
|
|
||||||
if (wlen <= 0) {
|
if (wlen <= 0) {
|
||||||
xSemaphoreGive(client->lock);
|
xSemaphoreGive(client->lock);
|
||||||
return wlen;
|
return wlen;
|
||||||
|
|
|
@ -50,6 +50,7 @@ typedef enum {
|
||||||
typedef struct {
|
typedef struct {
|
||||||
const char *data_ptr; /*!< Data pointer */
|
const char *data_ptr; /*!< Data pointer */
|
||||||
int data_len; /*!< Data length */
|
int data_len; /*!< Data length */
|
||||||
|
uint8_t op_code; /*!< Received opcode */
|
||||||
} esp_websocket_event_data_t;
|
} esp_websocket_event_data_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -73,7 +74,6 @@ typedef struct {
|
||||||
} esp_websocket_event_t;
|
} esp_websocket_event_t;
|
||||||
|
|
||||||
typedef esp_websocket_event_t* esp_websocket_event_handle_t;
|
typedef esp_websocket_event_t* esp_websocket_event_handle_t;
|
||||||
typedef esp_err_t (* websocket_event_callback_t)(esp_websocket_event_handle_t event);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Websocket client setup configuration
|
* @brief Websocket client setup configuration
|
||||||
|
@ -150,7 +150,7 @@ esp_err_t esp_websocket_client_stop(esp_websocket_client_handle_t client);
|
||||||
esp_err_t esp_websocket_client_destroy(esp_websocket_client_handle_t client);
|
esp_err_t esp_websocket_client_destroy(esp_websocket_client_handle_t client);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Write data to the WebSocket connection
|
* @brief Generic write data to the WebSocket connection; defaults to binary send
|
||||||
*
|
*
|
||||||
* @param[in] client The client
|
* @param[in] client The client
|
||||||
* @param[in] data The data
|
* @param[in] data The data
|
||||||
|
@ -163,6 +163,34 @@ esp_err_t esp_websocket_client_destroy(esp_websocket_client_handle_t client);
|
||||||
*/
|
*/
|
||||||
int esp_websocket_client_send(esp_websocket_client_handle_t client, const char *data, int len, TickType_t timeout);
|
int esp_websocket_client_send(esp_websocket_client_handle_t client, const char *data, int len, TickType_t timeout);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Write binary data to the WebSocket connection (data send with WS OPCODE=02, i.e. binary)
|
||||||
|
*
|
||||||
|
* @param[in] client The client
|
||||||
|
* @param[in] data The data
|
||||||
|
* @param[in] len The length
|
||||||
|
* @param[in] timeout Write data timeout
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - Number of data was sent
|
||||||
|
* - (-1) if any errors
|
||||||
|
*/
|
||||||
|
int esp_websocket_client_send_bin(esp_websocket_client_handle_t client, const char *data, int len, TickType_t timeout);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Write textual data to the WebSocket connection (data send with WS OPCODE=01, i.e. text)
|
||||||
|
*
|
||||||
|
* @param[in] client The client
|
||||||
|
* @param[in] data The data
|
||||||
|
* @param[in] len The length
|
||||||
|
* @param[in] timeout Write data timeout
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - Number of data was sent
|
||||||
|
* - (-1) if any errors
|
||||||
|
*/
|
||||||
|
int esp_websocket_client_send_text(esp_websocket_client_handle_t client, const char *data, int len, TickType_t timeout);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Check the WebSocket connection status
|
* @brief Check the WebSocket connection status
|
||||||
*
|
*
|
||||||
|
|
|
@ -13,6 +13,13 @@
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
typedef enum ws_transport_opcodes {
|
||||||
|
WS_TRANSPORT_OPCODES_TEXT = 0x01,
|
||||||
|
WS_TRANSPORT_OPCODES_BINARY = 0x02,
|
||||||
|
WS_TRANSPORT_OPCODES_CLOSE = 0x08,
|
||||||
|
WS_TRANSPORT_OPCODES_PING = 0x09,
|
||||||
|
WS_TRANSPORT_OPCODES_PONG = 0x0a,
|
||||||
|
} ws_transport_opcodes_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Create web socket transport
|
* @brief Create web socket transport
|
||||||
|
@ -43,6 +50,37 @@ void esp_transport_ws_set_path(esp_transport_handle_t t, const char *path);
|
||||||
*/
|
*/
|
||||||
esp_err_t esp_transport_ws_set_subprotocol(esp_transport_handle_t t, const char *sub_protocol);
|
esp_err_t esp_transport_ws_set_subprotocol(esp_transport_handle_t t, const char *sub_protocol);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sends websocket raw message with custom opcode and payload
|
||||||
|
*
|
||||||
|
* Note that generic esp_transport_write for ws handle sends
|
||||||
|
* binary massages by default if size is > 0 and
|
||||||
|
* ping message if message size is set to 0.
|
||||||
|
* This API is provided to support explicit messages with arbitrary opcode,
|
||||||
|
* should it be PING, PONG or TEXT message with arbitrary data.
|
||||||
|
*
|
||||||
|
* @param[in] t Websocket transport handle
|
||||||
|
* @param[in] opcode ws operation code
|
||||||
|
* @param[in] buffer The buffer
|
||||||
|
* @param[in] len The length
|
||||||
|
* @param[in] timeout_ms The timeout milliseconds
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - Number of bytes was written
|
||||||
|
* - (-1) if there are any errors, should check errno
|
||||||
|
*/
|
||||||
|
int esp_transport_ws_send_raw(esp_transport_handle_t t, ws_transport_opcodes_t opcode, const char *b, int len, int timeout_ms);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns websocket op-code for last received data
|
||||||
|
*
|
||||||
|
* @param t websocket transport handle
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - Received op-code as enum
|
||||||
|
*/
|
||||||
|
ws_transport_opcodes_t esp_transport_ws_get_read_opcode(esp_transport_handle_t t);
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,9 +32,15 @@ typedef struct {
|
||||||
char *path;
|
char *path;
|
||||||
char *buffer;
|
char *buffer;
|
||||||
char *sub_protocol;
|
char *sub_protocol;
|
||||||
|
uint8_t read_opcode;
|
||||||
esp_transport_handle_t parent;
|
esp_transport_handle_t parent;
|
||||||
} transport_ws_t;
|
} transport_ws_t;
|
||||||
|
|
||||||
|
static inline uint8_t ws_get_bin_opcode(ws_transport_opcodes_t opcode)
|
||||||
|
{
|
||||||
|
return (uint8_t)opcode;
|
||||||
|
}
|
||||||
|
|
||||||
static esp_transport_handle_t ws_get_payload_transport_handle(esp_transport_handle_t t)
|
static esp_transport_handle_t ws_get_payload_transport_handle(esp_transport_handle_t t)
|
||||||
{
|
{
|
||||||
transport_ws_t *ws = esp_transport_get_context_data(t);
|
transport_ws_t *ws = esp_transport_get_context_data(t);
|
||||||
|
@ -221,9 +227,23 @@ static int _ws_write(esp_transport_handle_t t, int opcode, int mask_flag, const
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int esp_transport_ws_send_raw(esp_transport_handle_t t, ws_transport_opcodes_t opcode, const char *b, int len, int timeout_ms)
|
||||||
|
{
|
||||||
|
uint8_t op_code = ws_get_bin_opcode(opcode);
|
||||||
|
if (t == NULL) {
|
||||||
|
ESP_LOGE(TAG, "Transport must be a valid ws handle");
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
ESP_LOGD(TAG, "Sending raw ws message with opcode %d", op_code);
|
||||||
|
return _ws_write(t, op_code | WS_FIN, WS_MASK, b, len, timeout_ms);
|
||||||
|
}
|
||||||
|
|
||||||
static int ws_write(esp_transport_handle_t t, const char *b, int len, int timeout_ms)
|
static int ws_write(esp_transport_handle_t t, const char *b, int len, int timeout_ms)
|
||||||
{
|
{
|
||||||
if (len == 0) {
|
if (len == 0) {
|
||||||
|
// Default transport write of zero length in ws layer sends out a ping message.
|
||||||
|
// This behaviour could however be altered in IDF 5.0, since a separate API for sending
|
||||||
|
// messages with user defined opcodes has been introduced.
|
||||||
ESP_LOGD(TAG, "Write PING message");
|
ESP_LOGD(TAG, "Write PING message");
|
||||||
return _ws_write(t, WS_OPCODE_PING | WS_FIN, 0, NULL, 0, timeout_ms);
|
return _ws_write(t, WS_OPCODE_PING | WS_FIN, 0, NULL, 0, timeout_ms);
|
||||||
}
|
}
|
||||||
|
@ -235,7 +255,7 @@ static int ws_read(esp_transport_handle_t t, char *buffer, int len, int timeout_
|
||||||
transport_ws_t *ws = esp_transport_get_context_data(t);
|
transport_ws_t *ws = esp_transport_get_context_data(t);
|
||||||
int payload_len;
|
int payload_len;
|
||||||
char ws_header[MAX_WEBSOCKET_HEADER_SIZE];
|
char ws_header[MAX_WEBSOCKET_HEADER_SIZE];
|
||||||
char *data_ptr = ws_header, opcode, mask, *mask_key = NULL;
|
char *data_ptr = ws_header, mask, *mask_key = NULL;
|
||||||
int rlen;
|
int rlen;
|
||||||
int poll_read;
|
int poll_read;
|
||||||
if ((poll_read = esp_transport_poll_read(ws->parent, timeout_ms)) <= 0) {
|
if ((poll_read = esp_transport_poll_read(ws->parent, timeout_ms)) <= 0) {
|
||||||
|
@ -248,12 +268,12 @@ static int ws_read(esp_transport_handle_t t, char *buffer, int len, int timeout_
|
||||||
ESP_LOGE(TAG, "Error read data");
|
ESP_LOGE(TAG, "Error read data");
|
||||||
return rlen;
|
return rlen;
|
||||||
}
|
}
|
||||||
opcode = (*data_ptr & 0x0F);
|
ws->read_opcode = (*data_ptr & 0x0F);
|
||||||
data_ptr ++;
|
data_ptr ++;
|
||||||
mask = ((*data_ptr >> 7) & 0x01);
|
mask = ((*data_ptr >> 7) & 0x01);
|
||||||
payload_len = (*data_ptr & 0x7F);
|
payload_len = (*data_ptr & 0x7F);
|
||||||
data_ptr++;
|
data_ptr++;
|
||||||
ESP_LOGD(TAG, "Opcode: %d, mask: %d, len: %d\r\n", opcode, mask, payload_len);
|
ESP_LOGD(TAG, "Opcode: %d, mask: %d, len: %d\r\n", ws->read_opcode, mask, payload_len);
|
||||||
if (payload_len == 126) {
|
if (payload_len == 126) {
|
||||||
// headerLen += 2;
|
// headerLen += 2;
|
||||||
if ((rlen = esp_transport_read(ws->parent, data_ptr, header, timeout_ms)) <= 0) {
|
if ((rlen = esp_transport_read(ws->parent, data_ptr, header, timeout_ms)) <= 0) {
|
||||||
|
@ -378,3 +398,9 @@ esp_err_t esp_transport_ws_set_subprotocol(esp_transport_handle_t t, const char
|
||||||
}
|
}
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ws_transport_opcodes_t esp_transport_ws_get_read_opcode(esp_transport_handle_t t)
|
||||||
|
{
|
||||||
|
transport_ws_t *ws = esp_transport_get_context_data(t);
|
||||||
|
return ws->read_opcode;
|
||||||
|
}
|
||||||
|
|
|
@ -45,6 +45,7 @@ static void websocket_event_handler(void *handler_args, esp_event_base_t base, i
|
||||||
|
|
||||||
case WEBSOCKET_EVENT_DATA:
|
case WEBSOCKET_EVENT_DATA:
|
||||||
ESP_LOGI(TAG, "WEBSOCKET_EVENT_DATA");
|
ESP_LOGI(TAG, "WEBSOCKET_EVENT_DATA");
|
||||||
|
ESP_LOGI(TAG, "Received opcode=%d", data->op_code);
|
||||||
ESP_LOGW(TAG, "Received=%.*s\r\n", data->data_len, (char*)data->data_ptr);
|
ESP_LOGW(TAG, "Received=%.*s\r\n", data->data_len, (char*)data->data_ptr);
|
||||||
break;
|
break;
|
||||||
case WEBSOCKET_EVENT_ERROR:
|
case WEBSOCKET_EVENT_ERROR:
|
||||||
|
|
Loading…
Reference in a new issue