Skip to content

Commit 4306f1f

Browse files
committed
Refactor the code handling WS data event to remove code duplication and fix the usages of pinfo->opcode in examples
1 parent e3fd7ad commit 4306f1f

4 files changed

Lines changed: 39 additions & 52 deletions

File tree

examples/WebSocket/WebSocket.ino

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,12 +104,13 @@ void setup() {
104104
} else if (type == WS_EVT_DATA) {
105105
AwsFrameInfo *info = (AwsFrameInfo *)arg;
106106
Serial.printf(
107-
"index: %" PRIu64 ", len: %" PRIu64 ", final: %" PRIu8 ", opcode: %" PRIu8 ", framelen: %d\n", info->index, info->len, info->final, info->opcode, len
107+
"index: %" PRIu64 ", len: %" PRIu64 ", final: %" PRIu8 ", opcode: %" PRIu8 ", framelen: %d\n", info->index, info->len, info->final,
108+
info->message_opcode, len
108109
);
109110

110111
// complete frame
111112
if (info->final && info->index == 0 && info->len == len) {
112-
if (info->opcode == WS_TEXT) {
113+
if (info->message_opcode == WS_TEXT) {
113114
Serial.printf("ws text: %s\n", (char *)data);
114115
client->ping();
115116
}

idf_component_examples/websocket/main/main.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ void setup() {
5454
AwsFrameInfo *info = (AwsFrameInfo *)arg;
5555
String msg = "";
5656
if (info->final && info->index == 0 && info->len == len) {
57-
if (info->opcode == WS_TEXT) {
57+
if (info->message_opcode == WS_TEXT) {
5858
Serial.printf("ws text: %s\n", (char *)data);
5959
}
6060
}

src/AsyncWebSocket.cpp

Lines changed: 32 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -656,31 +656,7 @@ void AsyncWebSocketClient::_onData(void *pbuf, size_t plen) {
656656
}
657657

658658
if (datalen > 0) {
659-
// ------------------------------------------------------------
660-
// Issue 384: https://github.com/ESP32Async/ESPAsyncWebServer/issues/384
661-
// Discussion: https://github.com/ESP32Async/ESPAsyncWebServer/pull/383#discussion_r2760425739
662-
// The initial design of the library was doing a backup of the byte following the data buffer because the client code
663-
// was allowed and documented to do something like data[len] = 0; to facilitate null-terminated string handling.
664-
// This was a bit hacky but it was working and it was documented, although completely incorrect because it was modifying a byte outside of the data buffer.
665-
// So to fix this behavior and to avoid breaking existing client code that may be relying on this behavior, we now have to copy the data to a temporary buffer that has an extra byte for the null terminator.
666-
// ------------------------------------------------------------
667-
uint8_t *copy = (uint8_t *)malloc(datalen + 1);
668-
669-
if (copy == NULL) {
670-
async_ws_log_e("Failed to allocate");
671-
_status = WS_DISCONNECTED;
672-
if (_client) {
673-
_client->abort();
674-
}
675-
return;
676-
}
677-
678-
memcpy(copy, data, datalen);
679-
copy[datalen] = 0;
680-
681-
_server->_handleEvent(this, WS_EVT_DATA, (void *)&_pinfo, copy, datalen);
682-
683-
free(copy);
659+
_handleDataEvent(data, datalen);
684660
}
685661

686662
// track index for next fragment
@@ -727,36 +703,13 @@ void AsyncWebSocketClient::_onData(void *pbuf, size_t plen) {
727703
} else if (_pinfo.opcode < WS_DISCONNECT) { // continuation or text/binary frame
728704
async_ws_log_v("WS[%" PRIu32 "]: processing data frame num=%" PRIu32 "", _clientId, _pinfo.num);
729705

730-
// ------------------------------------------------------------
731-
// Issue 384: https://github.com/ESP32Async/ESPAsyncWebServer/issues/384
732-
// Discussion: https://github.com/ESP32Async/ESPAsyncWebServer/pull/383#discussion_r2760425739
733-
// The initial design of the library was doing a backup of the byte following the data buffer because the client code
734-
// was allowed and documented to do something like data[len] = 0; to facilitate null-terminated string handling.
735-
// This was a bit hacky but it was working and it was documented, although completely incorrect because it was modifying a byte outside of the data buffer.
736-
// So to fix this behavior and to avoid breaking existing client code that may be relying on this behavior, we now have to copy the data to a temporary buffer that has an extra byte for the null terminator.
737-
// ------------------------------------------------------------
738-
uint8_t *copy = (uint8_t *)malloc(datalen + 1);
739-
740-
if (copy == NULL) {
741-
async_ws_log_e("Failed to allocate");
742-
_status = WS_DISCONNECTED;
743-
if (_client) {
744-
_client->abort();
745-
}
746-
return;
747-
}
748-
749-
memcpy(copy, data, datalen);
750-
copy[datalen] = 0;
706+
_handleDataEvent(data, datalen);
751707

752-
_server->_handleEvent(this, WS_EVT_DATA, (void *)&_pinfo, copy, datalen);
753708
if (_pinfo.final) {
754709
_pinfo.num = 0;
755710
} else {
756711
_pinfo.num += 1;
757712
}
758-
759-
free(copy);
760713
}
761714

762715
} else {
@@ -778,6 +731,36 @@ void AsyncWebSocketClient::_onData(void *pbuf, size_t plen) {
778731
}
779732
}
780733

734+
void AsyncWebSocketClient::_handleDataEvent(uint8_t *data, size_t len) {
735+
// ------------------------------------------------------------
736+
// Issue 384: https://github.com/ESP32Async/ESPAsyncWebServer/issues/384
737+
// Discussion: https://github.com/ESP32Async/ESPAsyncWebServer/pull/383#discussion_r2760425739
738+
// The initial design of the library was doing a backup of the byte following the data buffer because the client code
739+
// was allowed and documented to do something like data[len] = 0; to facilitate null-terminated string handling.
740+
// This was a bit hacky but it was working and it was documented, although completely incorrect because it was modifying a byte outside of the data buffer.
741+
// So to fix this behavior and to avoid breaking existing client code that may be relying on this behavior, we now have to copy the data to a temporary buffer that has an extra byte for the null terminator.
742+
// ------------------------------------------------------------
743+
// Optimization note:
744+
// - info->opcode stores the current WS frame type (binary, text, continuation)
745+
// - info->message_opcode stores the WS frame type of the first frame of the message, which is used for fragmented messages to know the message type when processing subsequent frame with opcode 0 (continuation)
746+
// So we can use info->message_opcode to avoid copying the data for non-text frames, and only copy the data for text frames when we need to add a null terminator for client code convenience.
747+
if (_pinfo.message_opcode == WS_TEXT) {
748+
std::unique_ptr<uint8_t[]> copy(new (std::nothrow) uint8_t[len + 1]());
749+
if (copy) {
750+
memcpy(copy.get(), data, len);
751+
copy[len] = 0;
752+
_server->_handleEvent(this, WS_EVT_DATA, (void *)&_pinfo, copy.get(), len);
753+
} else {
754+
async_ws_log_e("Failed to allocate");
755+
if (_client) {
756+
_client->abort();
757+
}
758+
}
759+
} else {
760+
_server->_handleEvent(this, WS_EVT_DATA, (void *)&_pinfo, data, len);
761+
}
762+
}
763+
781764
size_t AsyncWebSocketClient::printf(const char *format, ...) {
782765
va_list arg;
783766
va_start(arg, format);

src/AsyncWebSocket.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,9 @@ class AsyncWebSocketClient {
236236
void _runQueue();
237237
void _clearQueue();
238238

239+
// this function is called when a text message is received, in order to copy the buffer and place a null terminator at the end of the buffer for easier handling of text messages.
240+
void _handleDataEvent(uint8_t *data, size_t len);
241+
239242
public:
240243
void *_tempObject;
241244

0 commit comments

Comments
 (0)