]> granicus.if.org Git - esp-idf/commitdiff
ws_client: fix for not sending ping responses, updated to pass events also for PING...
authorDavid Cermak <cermak@espressif.com>
Mon, 16 Sep 2019 13:22:29 +0000 (15:22 +0200)
committerDavid Cermak <cermak@espressif.com>
Mon, 14 Oct 2019 09:05:27 +0000 (11:05 +0200)
closes https://github.com/espressif/esp-idf/issues/3982

components/esp_websocket_client/esp_websocket_client.c
components/esp_websocket_client/include/esp_websocket_client.h
components/tcp_transport/include/esp_transport_ws.h
components/tcp_transport/transport_ws.c
examples/protocols/websocket/main/websocket_example.c

index 413742d4ac92cef8bddde70ca87240eaafaaea2f..fcfc86cec57357a8038835a9c35217a4130321eb 100644 (file)
@@ -90,6 +90,7 @@ struct esp_websocket_client {
     char                        *rx_buffer;
     char                        *tx_buffer;
     int                         buffer_size;
+    ws_transport_opcodes_t      last_opcode;
 };
 
 static uint64_t _tick_get_ms(void)
@@ -106,6 +107,7 @@ static esp_err_t esp_websocket_client_dispatch_event(esp_websocket_client_handle
     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,
@@ -474,7 +476,7 @@ static void esp_websocket_client_task(void *pv)
                 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...");
@@ -488,8 +490,15 @@ static void esp_websocket_client_task(void *pv)
                     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:
@@ -546,7 +555,24 @@ esp_err_t esp_websocket_client_stop(esp_websocket_client_handle_t client)
     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;
@@ -575,10 +601,8 @@ int esp_websocket_client_send(esp_websocket_client_handle_t client, const char *
             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;
index 4f1e0a3fc0cdd18da4fe517ba32b0808c67d0a62..444343c82f6baf67cf33299b1a3743a92fb2e3ba 100644 (file)
@@ -49,6 +49,7 @@ typedef enum {
 typedef struct {
     const char *data_ptr;   /*!< Data pointer */
     int         data_len;   /*!< Data length */
+    uint8_t     op_code;    /*!< Received opcode */
 } esp_websocket_event_data_t;
 
 /**
@@ -72,7 +73,6 @@ typedef struct {
 } 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
@@ -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);
 
 /**
- * @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
@@ -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);
 
+/**
+ * @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
  *
index bb4f0bc013a9fa921abfee583094e264980dde5a..0876480a42077007646dd081147e326b24e03f8d 100644 (file)
 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
@@ -43,6 +50,38 @@ 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);
 
+/**
+ * @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
index c8f3da31c66b2241486ff5db82dace301f304aaa..e444c30d6a864b9cff09fdb393fef4110b3d5330 100644 (file)
@@ -31,9 +31,15 @@ typedef struct {
     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);
@@ -219,9 +225,23 @@ static int _ws_write(esp_transport_handle_t t, int opcode, int mask_flag, const
     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);
     }
@@ -233,7 +253,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);
     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) {
@@ -246,12 +266,12 @@ static int ws_read(esp_transport_handle_t t, char *buffer, int len, int timeout_
         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) {
@@ -376,3 +396,9 @@ esp_err_t esp_transport_ws_set_subprotocol(esp_transport_handle_t t, const char
     }
     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;
+}
index 901d2ad92c5c0bbfbc888d95da5b3fee0fa8214d..f82ac94947ec0e78e038bfc68c2e2959486d9756 100644 (file)
@@ -44,6 +44,7 @@ static void websocket_event_handler(void *handler_args, esp_event_base_t base, i
 
         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: