Skip to content

Commit ea14e15

Browse files
authored
Merge pull request #368 from gabsuren/feature/websocket_new_api
Added new API s to allow fragmented text to be send (IDFGH-10198)
2 parents af1f39e + fae80e2 commit ea14e15

File tree

4 files changed

+144
-38
lines changed

4 files changed

+144
-38
lines changed

components/esp_websocket_client/esp_websocket_client.c

Lines changed: 61 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
2+
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*/
@@ -545,6 +545,42 @@ static esp_err_t esp_websocket_client_create_transport(esp_websocket_client_hand
545545
return ESP_OK;
546546
}
547547

548+
static bool esp_websocket_client_send_with_exact_opcode(esp_websocket_client_handle_t client, ws_transport_opcodes_t opcode, const uint8_t *data, int len, TickType_t timeout)
549+
{
550+
int ret = -1;
551+
int need_write = len;
552+
int wlen = 0, widx = 0;
553+
554+
while (widx < len || opcode) { // allow for sending "current_opcode" only message with len==0
555+
if (need_write > client->buffer_size) {
556+
need_write = client->buffer_size;
557+
}
558+
memcpy(client->tx_buffer, data + widx, need_write);
559+
// send with ws specific way and specific opcode
560+
wlen = esp_transport_ws_send_raw(client->transport, opcode, (char *)client->tx_buffer, need_write,
561+
(timeout == portMAX_DELAY) ? -1 : timeout * portTICK_PERIOD_MS);
562+
if (wlen < 0 || (wlen == 0 && need_write != 0)) {
563+
ret = wlen;
564+
esp_websocket_free_buf(client, true);
565+
esp_tls_error_handle_t error_handle = esp_transport_get_error_handle(client->transport);
566+
if (error_handle) {
567+
esp_websocket_client_error(client, "esp_transport_write() returned %d, transport_error=%s, tls_error_code=%i, tls_flags=%i, errno=%d",
568+
ret, esp_err_to_name(error_handle->last_error), error_handle->esp_tls_error_code,
569+
error_handle->esp_tls_flags, errno);
570+
} else {
571+
esp_websocket_client_error(client, "esp_transport_write() returned %d, errno=%d", ret, errno);
572+
}
573+
esp_websocket_client_abort_connection(client, WEBSOCKET_ERROR_TYPE_TCP_TRANSPORT);
574+
return false;
575+
}
576+
opcode = 0;
577+
widx += wlen;
578+
need_write = len - widx;
579+
}
580+
esp_websocket_free_buf(client, true);
581+
return true;
582+
}
583+
548584
esp_websocket_client_handle_t esp_websocket_client_init(const esp_websocket_client_config_t *config)
549585
{
550586
esp_websocket_client_handle_t client = calloc(1, sizeof(struct esp_websocket_client));
@@ -1092,17 +1128,33 @@ int esp_websocket_client_send_text(esp_websocket_client_handle_t client, const c
10921128
return esp_websocket_client_send_with_opcode(client, WS_TRANSPORT_OPCODES_TEXT, (const uint8_t *)data, len, timeout);
10931129
}
10941130

1131+
int esp_websocket_client_send_text_partial(esp_websocket_client_handle_t client, const char *data, int len, TickType_t timeout)
1132+
{
1133+
return esp_websocket_client_send_with_exact_opcode(client, WS_TRANSPORT_OPCODES_TEXT, (const uint8_t *)data, len, timeout);
1134+
}
1135+
1136+
int esp_websocket_client_send_cont_msg(esp_websocket_client_handle_t client, const char *data, int len, TickType_t timeout)
1137+
{
1138+
return esp_websocket_client_send_with_exact_opcode(client, WS_TRANSPORT_OPCODES_CONT, (const uint8_t *)data, len, timeout);
1139+
}
1140+
10951141
int esp_websocket_client_send_bin(esp_websocket_client_handle_t client, const char *data, int len, TickType_t timeout)
10961142
{
10971143
return esp_websocket_client_send_with_opcode(client, WS_TRANSPORT_OPCODES_BINARY, (const uint8_t *)data, len, timeout);
10981144
}
10991145

1100-
int esp_websocket_client_send_with_opcode(esp_websocket_client_handle_t client, ws_transport_opcodes_t opcode, const uint8_t *data, int len, TickType_t timeout)
1146+
int esp_websocket_client_send_bin_partial(esp_websocket_client_handle_t client, const char *data, int len, TickType_t timeout)
11011147
{
1102-
int need_write = len;
1103-
int wlen = 0, widx = 0;
1104-
int ret = ESP_FAIL;
1148+
return esp_websocket_client_send_with_exact_opcode(client, WS_TRANSPORT_OPCODES_BINARY, (const uint8_t *)data, len, timeout);
1149+
}
11051150

1151+
int esp_websocket_client_send_fin(esp_websocket_client_handle_t client, TickType_t timeout)
1152+
{
1153+
return esp_websocket_client_send_with_exact_opcode(client, WS_TRANSPORT_OPCODES_FIN, NULL, 0, timeout);
1154+
}
1155+
1156+
int esp_websocket_client_send_with_opcode(esp_websocket_client_handle_t client, ws_transport_opcodes_t opcode, const uint8_t *data, int len, TickType_t timeout)
1157+
{
11061158
if (client == NULL || len < 0 || (data == NULL && len > 0)) {
11071159
ESP_LOGE(TAG, "Invalid arguments");
11081160
return ESP_FAIL;
@@ -1126,41 +1178,13 @@ int esp_websocket_client_send_with_opcode(esp_websocket_client_handle_t client,
11261178
ESP_LOGE(TAG, "Failed to setup tx buffer");
11271179
goto unlock_and_return;
11281180
}
1129-
uint32_t current_opcode = opcode;
1130-
while (widx < len || current_opcode) { // allow for sending "current_opcode" only message with len==0
1131-
if (need_write > client->buffer_size) {
1132-
need_write = client->buffer_size;
1133-
} else {
1134-
current_opcode |= WS_TRANSPORT_OPCODES_FIN;
1135-
}
1136-
memcpy(client->tx_buffer, data + widx, need_write);
1137-
// send with ws specific way and specific opcode
1138-
wlen = esp_transport_ws_send_raw(client->transport, current_opcode, (char *)client->tx_buffer, need_write,
1139-
(timeout == portMAX_DELAY) ? -1 : timeout * portTICK_PERIOD_MS);
1140-
if (wlen < 0 || (wlen == 0 && need_write != 0)) {
1141-
ret = wlen;
1142-
esp_websocket_free_buf(client, true);
1143-
esp_tls_error_handle_t error_handle = esp_transport_get_error_handle(client->transport);
1144-
if (error_handle) {
1145-
esp_websocket_client_error(client, "esp_transport_write() returned %d, transport_error=%s, tls_error_code=%i, tls_flags=%i, errno=%d",
1146-
ret, esp_err_to_name(error_handle->last_error), error_handle->esp_tls_error_code,
1147-
error_handle->esp_tls_flags, errno);
1148-
} else {
1149-
esp_websocket_client_error(client, "esp_transport_write() returned %d, errno=%d", ret, errno);
1150-
}
1151-
esp_websocket_client_abort_connection(client, WEBSOCKET_ERROR_TYPE_TCP_TRANSPORT);
1152-
goto unlock_and_return;
1153-
}
1154-
current_opcode = 0;
1155-
widx += wlen;
1156-
need_write = len - widx;
1157-
1181+
if (esp_websocket_client_send_with_exact_opcode(client, opcode | WS_TRANSPORT_OPCODES_FIN, data, len, timeout) != true) {
1182+
ESP_LOGE(TAG, "Failed to send the buffer");
1183+
goto unlock_and_return;
11581184
}
1159-
ret = widx;
1160-
esp_websocket_free_buf(client, true);
11611185
unlock_and_return:
11621186
xSemaphoreGiveRecursive(client->lock);
1163-
return ret;
1187+
return ESP_FAIL;
11641188
}
11651189

11661190
bool esp_websocket_client_is_connected(esp_websocket_client_handle_t client)

components/esp_websocket_client/examples/target/main/websocket_example.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,14 @@ static void websocket_app_start(void)
161161
vTaskDelay(1000 / portTICK_PERIOD_MS);
162162
}
163163

164+
ESP_LOGI(TAG, "Sending fragmented message");
165+
vTaskDelay(1000 / portTICK_PERIOD_MS);
166+
memset(data, 'a', sizeof(data));
167+
esp_websocket_client_send_text_partial(client, data, sizeof(data), portMAX_DELAY);
168+
memset(data, 'b', sizeof(data));
169+
esp_websocket_client_send_cont_msg(client, data, sizeof(data), portMAX_DELAY);
170+
esp_websocket_client_send_fin(client, portMAX_DELAY);
171+
164172
xSemaphoreTake(shutdown_sema, portMAX_DELAY);
165173
esp_websocket_client_close(client, portMAX_DELAY);
166174
ESP_LOGI(TAG, "Websocket Stopped");

components/esp_websocket_client/examples/target/pytest_websocket.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,10 @@ def test_recv_long_msg(dut, websocket, msg_len, repeats):
133133
\nreceived: {}\nwith length {}'.format(
134134
send_msg, len(send_msg), recv_msg, len(recv_msg)))
135135

136+
def test_fragmented_msg(dut):
137+
dut.expect('Received=' + 32 * 'a' + 32 * 'b')
138+
print('Fragmented data received')
139+
136140
# Starting of the test
137141
try:
138142
if dut.app.sdkconfig.get('WEBSOCKET_URI_FROM_STDIN') is True:
@@ -156,6 +160,7 @@ def test_recv_long_msg(dut, websocket, msg_len, repeats):
156160
# Message length should exceed DUT's buffer size to test fragmentation, default is 1024 byte
157161
test_recv_long_msg(dut, ws, 2000, 3)
158162
test_json(dut, ws)
163+
test_fragmented_msg(dut)
159164
test_close(dut)
160165
else:
161166
print('DUT connecting to {}'.format(uri))

components/esp_websocket_client/include/esp_websocket_client.h

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
2+
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*/
@@ -231,6 +231,24 @@ esp_err_t esp_websocket_client_destroy_on_exit(esp_websocket_client_handle_t cli
231231
*/
232232
int esp_websocket_client_send_bin(esp_websocket_client_handle_t client, const char *data, int len, TickType_t timeout);
233233

234+
/**
235+
* @brief Write binary data to the WebSocket connection and sends it without setting the FIN flag(data send with WS OPCODE=02, i.e. binary)
236+
*
237+
* Notes:
238+
* - To send continuation frame, you should use 'esp_websocket_client_send_cont_msg(...)' API.
239+
* - To mark the end of fragmented data, you should use the 'esp_websocket_client_send_fin(...)' API. This sends a FIN frame.
240+
*
241+
* @param[in] client The client
242+
* @param[in] data The data
243+
* @param[in] len The length
244+
* @param[in] timeout Write data timeout in RTOS ticks
245+
*
246+
* @return
247+
* - Number of data was sent
248+
* - (-1) if any errors
249+
*/
250+
int esp_websocket_client_send_bin_partial(esp_websocket_client_handle_t client, const char *data, int len, TickType_t timeout);
251+
234252
/**
235253
* @brief Write textual data to the WebSocket connection (data send with WS OPCODE=01, i.e. text)
236254
*
@@ -245,6 +263,55 @@ int esp_websocket_client_send_bin(esp_websocket_client_handle_t client, const ch
245263
*/
246264
int esp_websocket_client_send_text(esp_websocket_client_handle_t client, const char *data, int len, TickType_t timeout);
247265

266+
/**
267+
* @brief Write textual data to the WebSocket connection and sends it without setting the FIN flag(data send with WS OPCODE=01, i.e. text)
268+
*
269+
* Notes:
270+
* - To send continuation frame, you should use 'esp_websocket_client_send_cont_mgs(...)' API.
271+
* - To mark the end of fragmented data, you should use the 'esp_websocket_client_send_fin(...)' API. This sends a FIN frame.
272+
*
273+
* @param[in] client The client
274+
* @param[in] data The data
275+
* @param[in] len The length
276+
* @param[in] timeout Write data timeout in RTOS ticks
277+
*
278+
* @return
279+
* - Number of data was sent
280+
* - (-1) if any errors
281+
*/
282+
int esp_websocket_client_send_text_partial(esp_websocket_client_handle_t client, const char *data, int len, TickType_t timeout);
283+
284+
/**
285+
* @brief Write textual data to the WebSocket connection and sends it as continuation frame (OPCODE=0x0)
286+
*
287+
* Notes:
288+
* - Continuation frames have an opcode of 0x0 and do not explicitly signify whether they are continuing a text or a binary message.
289+
* - You determine the type of message (text or binary) being continued by looking at the opcode of the initial frame in the sequence of fragmented frames.
290+
* - To mark the end of fragmented data, you should use the 'esp_websocket_client_send_fin(...)' API. This sends a FIN frame.
291+
*
292+
* @param[in] client The client
293+
* @param[in] data The data
294+
* @param[in] len The length
295+
* @param[in] timeout Write data timeout in RTOS ticks
296+
*
297+
* @return
298+
* - Number of data was sent
299+
* - (-1) if any errors
300+
*/
301+
int esp_websocket_client_send_cont_msg(esp_websocket_client_handle_t client, const char *data, int len, TickType_t timeout);
302+
303+
/**
304+
* @brief Sends FIN frame
305+
*
306+
* @param[in] client The client
307+
* @param[in] timeout Write data timeout in RTOS ticks
308+
*
309+
* @return
310+
* - Number of data was sent
311+
* - (-1) if any errors
312+
*/
313+
int esp_websocket_client_send_fin(esp_websocket_client_handle_t client, TickType_t timeout);
314+
248315
/**
249316
* @brief Write opcode data to the WebSocket connection
250317
*
@@ -256,6 +323,8 @@ int esp_websocket_client_send_text(esp_websocket_client_handle_t client, const c
256323
*
257324
* Notes:
258325
* - In order to send a zero payload, data and len should be set to NULL/0
326+
* - This API sets the FIN bit on the last fragment of message
327+
*
259328
*
260329
* @return
261330
* - Number of data was sent

0 commit comments

Comments
 (0)