From 40db85d2bb93a2578a217cb520d92afab4502bef Mon Sep 17 00:00:00 2001 From: Mathieu Carbou Date: Thu, 25 Jan 2024 23:42:12 +0100 Subject: [PATCH] API Compatibility with `AsyncWebSocketMessageBuffer` and `makeBuffer()` --- README.md | 27 +++++++++++--- src/AsyncWebSocket.cpp | 81 ++++++++++++++++++++++++++++++++++++++---- src/AsyncWebSocket.h | 26 ++++++++++++++ 3 files changed, 122 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 9cf1e0e03..8b213e81d 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ This fork is based on https://github.com/yubox-node-org/ESPAsyncWebServer and in - Deployed in PlatformIO registry and Arduino IDE library manager - CI - Only supports ESP32 +- Resurrected `AsyncWebSocketMessageBuffer` and `makeBuffer()` in order to make the fork API-compatible with the original library from me-no-dev regarding WebSocket. ## Documentation @@ -23,13 +24,29 @@ Please look at the original libraries for more examples and documentation. [https://github.com/yubox-node-org/ESPAsyncWebServer](https://github.com/yubox-node-org/ESPAsyncWebServer) -## Pitfalls +## `AsyncWebSocketMessageBuffer` and `makeBuffer()` -The fork from yubox introduces some breaking API changes compared to the original library, especially regarding the use of `std::shared_ptr>` for WebSocket. -Thanks to this fork, you can handle them by using `ASYNCWEBSERVER_FORK_mathieucarbou`. +The fork from `yubox-node-org` introduces some breaking API changes compared to the original library, especially regarding the use of `std::shared_ptr>` for WebSocket. -Here is an example for serializing a Json document in a websocket message buffer directly. -This code is compatible with both forks. +This fork is compatible with the original library from `me-no-dev` regarding WebSocket, and wraps the optimizations done by `yubox-node-org` in the `AsyncWebSocketMessageBuffer` class. +So you have the choice of which API to use. +I strongly suggest to use the optimized API from `yubox-node-org` as it is much more efficient. + +Here is an example for serializing a Json document in a websocket message buffer. This code is compatible with any forks, but not optimized: + +```cpp +void send(JsonDocument& doc) { + const size_t len = measureJson(doc); + + // original API from me-no-dev + AsyncWebSocketMessageBuffer* buffer = _ws->makeBuffer(len); + assert(buffer); // up to you to keep or remove this + serializeJson(doc, buffer->get(), len); + _ws->textAll(buffer); +} +``` + +Here is an example for serializing a Json document in a more optimized way, and compatible with both forks: ```cpp void send(JsonDocument& doc) { diff --git a/src/AsyncWebSocket.cpp b/src/AsyncWebSocket.cpp index 12be5f83e..443b5f1e0 100644 --- a/src/AsyncWebSocket.cpp +++ b/src/AsyncWebSocket.cpp @@ -123,6 +123,39 @@ size_t webSocketSendFrame(AsyncClient *client, bool final, uint8_t opcode, bool +/* + * AsyncWebSocketMessageBuffer + */ + +AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer() + : _buffer(std::make_shared>(0)) +{ +} + +AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(uint8_t* data, size_t size) + : _buffer(std::make_shared>(size)) +{ + std::memcpy(_buffer->data(), data, size); +} + +AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(size_t size) + : _buffer(std::make_shared>(size)) +{ +} + +AsyncWebSocketMessageBuffer::~AsyncWebSocketMessageBuffer() +{ + _buffer.reset(); +} + +bool AsyncWebSocketMessageBuffer::reserve(size_t size) +{ + if (_buffer->capacity() >= size) + return true; + _buffer->reserve(size); + return _buffer->capacity() >= size; +} + /* * Control Frame */ @@ -644,7 +677,7 @@ size_t AsyncWebSocketClient::printf_P(PGM_P formatP, ...) #endif namespace { -std::shared_ptr> makeBuffer(const uint8_t *message, size_t len) +std::shared_ptr> makeSharedBuffer(const uint8_t *message, size_t len) { auto buffer = std::make_shared>(len); std::memcpy(buffer->data(), message, len); @@ -652,6 +685,12 @@ std::shared_ptr> makeBuffer(const uint8_t *message, size_t } } +void AsyncWebSocketClient::text(AsyncWebSocketMessageBuffer * buffer) +{ + text(std::move(buffer->_buffer)); + delete buffer; +} + void AsyncWebSocketClient::text(std::shared_ptr> buffer) { _queueMessage(buffer); @@ -659,7 +698,7 @@ void AsyncWebSocketClient::text(std::shared_ptr> buffer) void AsyncWebSocketClient::text(const uint8_t *message, size_t len) { - text(makeBuffer(message, len)); + text(makeSharedBuffer(message, len)); } void AsyncWebSocketClient::text(const char *message, size_t len) @@ -698,6 +737,12 @@ void AsyncWebSocketClient::text(const __FlashStringHelper *data) } } +void AsyncWebSocketClient::binary(AsyncWebSocketMessageBuffer * buffer) +{ + binary(std::move(buffer->_buffer)); + delete buffer; +} + void AsyncWebSocketClient::binary(std::shared_ptr> buffer) { _queueMessage(buffer, WS_BINARY); @@ -705,7 +750,7 @@ void AsyncWebSocketClient::binary(std::shared_ptr> buffer) void AsyncWebSocketClient::binary(const uint8_t *message, size_t len) { - binary(makeBuffer(message, len)); + binary(makeSharedBuffer(message, len)); } void AsyncWebSocketClient::binary(const char *message, size_t len) @@ -853,7 +898,7 @@ void AsyncWebSocket::pingAll(const uint8_t *data, size_t len) void AsyncWebSocket::text(uint32_t id, const uint8_t *message, size_t len) { if (AsyncWebSocketClient * c = client(id)) - c->text(makeBuffer(message, len)); + c->text(makeSharedBuffer(message, len)); } void AsyncWebSocket::text(uint32_t id, const char *message, size_t len) { @@ -889,6 +934,12 @@ void AsyncWebSocket::text(uint32_t id, const __FlashStringHelper *data) } } +void AsyncWebSocket::textAll(AsyncWebSocketMessageBuffer * buffer) +{ + textAll(std::move(buffer->_buffer)); + delete buffer; +} + void AsyncWebSocket::textAll(std::shared_ptr> buffer) { for (auto &c : _clients) @@ -897,7 +948,7 @@ void AsyncWebSocket::textAll(std::shared_ptr> buffer) } void AsyncWebSocket::textAll(const uint8_t *message, size_t len) { - textAll(makeBuffer(message, len)); + textAll(makeSharedBuffer(message, len)); } void AsyncWebSocket::textAll(const char * message, size_t len) { @@ -935,7 +986,7 @@ void AsyncWebSocket::textAll(const __FlashStringHelper *data) void AsyncWebSocket::binary(uint32_t id, const uint8_t *message, size_t len) { if (AsyncWebSocketClient *c = client(id)) - c->binary(makeBuffer(message, len)); + c->binary(makeSharedBuffer(message, len)); } void AsyncWebSocket::binary(uint32_t id, const char * message, size_t len) { @@ -961,6 +1012,12 @@ void AsyncWebSocket::binary(uint32_t id, const __FlashStringHelper *data, size_t } } +void AsyncWebSocket::binaryAll(AsyncWebSocketMessageBuffer * buffer) +{ + binaryAll(std::move(buffer->_buffer)); + delete buffer; +} + void AsyncWebSocket::binaryAll(std::shared_ptr> buffer) { for (auto &c : _clients) @@ -970,7 +1027,7 @@ void AsyncWebSocket::binaryAll(std::shared_ptr> buffer) void AsyncWebSocket::binaryAll(const uint8_t *message, size_t len) { - binaryAll(makeBuffer(message, len)); + binaryAll(makeSharedBuffer(message, len)); } void AsyncWebSocket::binaryAll(const char *message, size_t len) @@ -1141,6 +1198,16 @@ void AsyncWebSocket::handleRequest(AsyncWebServerRequest *request) request->send(response); } +AsyncWebSocketMessageBuffer * AsyncWebSocket::makeBuffer(size_t size) +{ + return new AsyncWebSocketMessageBuffer(size); +} + +AsyncWebSocketMessageBuffer * AsyncWebSocket::makeBuffer(uint8_t * data, size_t size) +{ + return new AsyncWebSocketMessageBuffer(data, size); +} + /* * Response to Web Socket request - sends the authorization and detaches the TCP Client from the web server * Authentication code from https://github.com/Links2004/arduinoWebSockets/blob/master/src/WebSockets.cpp#L480 diff --git a/src/AsyncWebSocket.h b/src/AsyncWebSocket.h index 46de34066..ac49d2b82 100644 --- a/src/AsyncWebSocket.h +++ b/src/AsyncWebSocket.h @@ -88,6 +88,23 @@ typedef enum { WS_CONTINUATION, WS_TEXT, WS_BINARY, WS_DISCONNECT = 0x08, WS_PIN typedef enum { WS_MSG_SENDING, WS_MSG_SENT, WS_MSG_ERROR } AwsMessageStatus; typedef enum { WS_EVT_CONNECT, WS_EVT_DISCONNECT, WS_EVT_PONG, WS_EVT_ERROR, WS_EVT_DATA } AwsEventType; +class AsyncWebSocketMessageBuffer { + friend AsyncWebSocket; + friend AsyncWebSocketClient; + + private: + std::shared_ptr> _buffer; + + public: + AsyncWebSocketMessageBuffer(); + AsyncWebSocketMessageBuffer(size_t size); + AsyncWebSocketMessageBuffer(uint8_t* data, size_t size); + ~AsyncWebSocketMessageBuffer(); + bool reserve(size_t size); + uint8_t* get() { return _buffer->data(); } + size_t length() const { return _buffer->size(); } +}; + class AsyncWebSocketMessage { private: @@ -180,6 +197,7 @@ class AsyncWebSocketClient { void text(const char *message); void text(const String &message); void text(const __FlashStringHelper *message); + void text(AsyncWebSocketMessageBuffer *buffer); void binary(std::shared_ptr> buffer); void binary(const uint8_t *message, size_t len); @@ -187,6 +205,7 @@ class AsyncWebSocketClient { void binary(const char * message); void binary(const String &message); void binary(const __FlashStringHelper *message, size_t len); + void binary(AsyncWebSocketMessageBuffer *buffer); bool canSend() const; @@ -245,6 +264,7 @@ class AsyncWebSocket: public AsyncWebHandler { void textAll(const char * message); void textAll(const String &message); void textAll(const __FlashStringHelper *message); // need to convert + void textAll(AsyncWebSocketMessageBuffer *buffer); void binary(uint32_t id, const uint8_t *message, size_t len); void binary(uint32_t id, const char *message, size_t len); @@ -258,6 +278,7 @@ class AsyncWebSocket: public AsyncWebHandler { void binaryAll(const char *message); void binaryAll(const String &message); void binaryAll(const __FlashStringHelper *message, size_t len); + void binaryAll(AsyncWebSocketMessageBuffer *buffer); size_t printf(uint32_t id, const char *format, ...) __attribute__ ((format (printf, 3, 4))); size_t printfAll(const char *format, ...) __attribute__ ((format (printf, 2, 3))); @@ -283,6 +304,11 @@ class AsyncWebSocket: public AsyncWebHandler { virtual bool canHandle(AsyncWebServerRequest *request) override final; virtual void handleRequest(AsyncWebServerRequest *request) override final; + + // messagebuffer functions/objects. + AsyncWebSocketMessageBuffer * makeBuffer(size_t size = 0); + AsyncWebSocketMessageBuffer * makeBuffer(uint8_t * data, size_t size); + const std::list &getClients() const { return _clients; } };