char *rx_buffer;
char *tx_buffer;
int buffer_size;
+ ws_transport_opcodes_t last_opcode;
};
static uint64_t _tick_get_ms(void)
esp_websocket_event_data_t event_data;
event_data.data_ptr = data;
event_data.data_len = data_len;
+ event_data.op_code = client->last_opcode;
if ((err = esp_event_post_to(client->event_handle,
WEBSOCKET_EVENTS, event,
if (_tick_get_ms() - client->ping_tick_ms > WEBSOCKET_PING_TIMEOUT_MS) {
client->ping_tick_ms = _tick_get_ms();
// 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) {
ESP_LOGD(TAG, "Timeout...");
esp_websocket_client_abort_connection(client);
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);
+ // 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;
case WEBSOCKET_STATE_WAIT_TIMEOUT:
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)
+{
+ 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 wlen = 0, widx = 0;
need_write = client->buffer_size;
}
memcpy(client->tx_buffer, data + widx, need_write);
- wlen = esp_transport_write(client->transport,
- (char *)client->tx_buffer,
- need_write,
- client->config->network_timeout_ms);
+ // send with ws specific way and specific opcode
+ wlen = esp_transport_ws_send_raw(client->transport, opcode, (char *)client->tx_buffer, need_write, timeout);
if (wlen <= 0) {
xSemaphoreGive(client->lock);
return wlen;
typedef struct {
const char *data_ptr; /*!< Data pointer */
int data_len; /*!< Data length */
+ uint8_t op_code; /*!< Received opcode */
} esp_websocket_event_data_t;
/**
} esp_websocket_event_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
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] data The data
*/
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
*
extern "C" {
#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
*/
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
}
#endif
char *path;
char *buffer;
char *sub_protocol;
+ uint8_t read_opcode;
esp_transport_handle_t parent;
} 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)
{
transport_ws_t *ws = esp_transport_get_context_data(t);
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)
{
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");
return _ws_write(t, WS_OPCODE_PING | WS_FIN, WS_MASK, NULL, 0, timeout_ms);
}
transport_ws_t *ws = esp_transport_get_context_data(t);
int payload_len;
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 poll_read;
if ((poll_read = esp_transport_poll_read(ws->parent, timeout_ms)) <= 0) {
ESP_LOGE(TAG, "Error read data");
return rlen;
}
- opcode = (*data_ptr & 0x0F);
+ ws->read_opcode = (*data_ptr & 0x0F);
data_ptr ++;
mask = ((*data_ptr >> 7) & 0x01);
payload_len = (*data_ptr & 0x7F);
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) {
// headerLen += 2;
if ((rlen = esp_transport_read(ws->parent, data_ptr, header, timeout_ms)) <= 0) {
}
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;
+}
case 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);
break;
case WEBSOCKET_EVENT_ERROR: