diff --git a/code/espurna/libs/WebSocketIncomingBuffer.h b/code/espurna/libs/WebSocketIncomingBuffer.h new file mode 100644 index 0000000000..3fd033469d --- /dev/null +++ b/code/espurna/libs/WebSocketIncomingBuffer.h @@ -0,0 +1,86 @@ +/* + +WebSocketIncommingBuffer + +Code by Hermann Kraus (https://bitbucket.org/hermr2d2/) +and slightly modified. + +*/ + +#pragma once + +#include + +struct WebSocketIncomingBuffer { + static constexpr size_t MessageSizeMax { 4096 }; + using Callback = void(*)(AsyncWebSocketClient* client, uint8_t* data, size_t len); + + WebSocketIncomingBuffer() = delete; + WebSocketIncomingBuffer(Callback cb, bool terminate_string, bool cb_on_fragments) : + _cb(cb), + _cb_on_fragments(cb_on_fragments), + _terminate_string(terminate_string) + {} + + explicit WebSocketIncomingBuffer(Callback cb) : + WebSocketIncomingBuffer(cb, true, false) + {} + + void data_event(AsyncWebSocketClient *client, AwsFrameInfo *info, uint8_t *data, size_t len) { + if ((info->final || _cb_on_fragments) + && !_terminate_string + && info->index == 0 + && info->len == len) + { + + /* The whole message is in a single frame and we got all of it's + data therefore we can parse it without copying the data first.*/ + _cb(client, data, len); + return; + } + + if (info->len > MessageSizeMax) { + return; + } + + /* Check if previous fragment was discarded because it was too long. */ + //if (!_cb_on_fragments && info->num > 0 && !_buffer) return; + + if (info->index == 0) { + if (_cb_on_fragments) { + _buffer.reserve(info->len + 1); + } else { + /* The current fragment would lead to a message which is + too long. So discard everything received so far. */ + const size_t reserve = info->len + _buffer.size() + 1; + if (reserve > MessageSizeMax) { + _buffer = Buffer(); + return; + } + + _buffer.reserve(reserve); + } + } + + //assert(_buffer->size() == info->index); + _buffer.insert(_buffer.end(), data, data + len); + if (info->index + len == info->len + && (info->final || _cb_on_fragments)) + { + // Frame/message complete + if (_terminate_string) { + _buffer.push_back(0); + } + _cb(client, _buffer.data(), _buffer.size()); + _buffer.clear(); + } + } + +private: + using Buffer = std::vector; + Buffer _buffer; + + Callback _cb; + bool _cb_on_fragments; + bool _terminate_string; +}; diff --git a/code/espurna/libs/WebSocketIncommingBuffer.h b/code/espurna/libs/WebSocketIncommingBuffer.h deleted file mode 100644 index 3b14df42f0..0000000000 --- a/code/espurna/libs/WebSocketIncommingBuffer.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - -WebSocketIncommingBuffer - -Code by Hermann Kraus (https://bitbucket.org/hermr2d2/) -and slightly modified. - -*/ - -#pragma once - -#define MAX_WS_MSG_SIZE 4000 -typedef std::function AwsMessageHandler; - -class WebSocketIncommingBuffer { - - public: - WebSocketIncommingBuffer(AwsMessageHandler cb, bool terminate_string = true, bool cb_on_fragments = false) : - _cb(cb), - _terminate_string(terminate_string), - _cb_on_fragments(cb_on_fragments), - _buffer(0) - {} - - ~WebSocketIncommingBuffer() { - if (_buffer) delete _buffer; - } - - void data_event(AsyncWebSocketClient *client, AwsFrameInfo *info, uint8_t *data, size_t len) { - - if ((info->final || _cb_on_fragments) - && !_terminate_string - && info->index == 0 - && info->len == len) { - - /* The whole message is in a single frame and we got all of it's - data therefore we can parse it without copying the data first.*/ - _cb(client, data, len); - - } else { - - if (info->len > MAX_WS_MSG_SIZE) return; - - /* Check if previous fragment was discarded because it was too long. */ - //if (!_cb_on_fragments && info->num > 0 && !_buffer) return; - - if (!_buffer) _buffer = new std::vector(); - - if (info->index == 0) { - //New frame => preallocate memory - if (_cb_on_fragments) { - _buffer->reserve(info->len + 1); - } else { - /* The current fragment would lead to a message which is - too long. So discard everything received so far. */ - if (info->len + _buffer->size() > MAX_WS_MSG_SIZE) { - delete _buffer; - _buffer = 0; - return; - } else { - _buffer->reserve(info->len + _buffer->size() + 1); - } - } - } - - //assert(_buffer->size() == info->index); - _buffer->insert(_buffer->end(), data, data+len); - if (info->index + len == info->len - && (info->final || _cb_on_fragments)) { - - // Frame/message complete - if (_terminate_string) _buffer->push_back(0); - _cb(client, _buffer->data(), _buffer->size()); - _buffer->clear(); - - } - } - } - - private: - - AwsMessageHandler _cb; - bool _terminate_string; - bool _cb_on_fragments; - std::vector *_buffer; - -}; diff --git a/code/espurna/ws.cpp b/code/espurna/ws.cpp index fcda3fd071..5e533fe331 100644 --- a/code/espurna/ws.cpp +++ b/code/espurna/ws.cpp @@ -21,7 +21,7 @@ Copyright (C) 2016-2019 by Xose PĂ©rez #include "wifi.h" #include "ws_internal.h" -#include "libs/WebSocketIncommingBuffer.h" +#include "libs/WebSocketIncomingBuffer.h" // ----------------------------------------------------------------------------- // Helpers / utility functions @@ -519,7 +519,7 @@ void _wsPostParse(uint32_t client_id, bool save, bool reload) { void _wsParse(AsyncWebSocketClient *client, uint8_t * payload, size_t length) { - //DEBUG_MSG_P(PSTR("[WEBSOCKET] Parsing: %s\n"), length ? (char*) payload : ""); + //DEBUG_MSG_P(PSTR("[WEBSOCKET] Parsing: %.*s\n"), length, reinterpret_cast(payload)); // Get client ID uint32_t client_id = client->id(); @@ -684,49 +684,60 @@ void _wsConnected(uint32_t client_id) { wsPostSequence(client_id, _ws_callbacks.on_data); } -void _wsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len){ - - if (type == WS_EVT_CONNECT) { - - client->_tempObject = nullptr; - String ip = client->remoteIP().toString(); - +void _wsEvent(AsyncWebSocket* server, AsyncWebSocketClient* client, AwsEventType type, void* arg, uint8_t* data, size_t len) { + switch (type) { + case WS_EVT_CONNECT: + { + const auto ip = client->remoteIP().toString(); #ifndef NOWSAUTH if (!_wsAuth(client)) { - DEBUG_MSG_P(PSTR("[WEBSOCKET] #%u session expired for %s\n"), client->id(), ip.c_str()); + DEBUG_MSG_P(PSTR("[WEBSOCKET] #%u session expired for %s\n"), + client->id(), ip.c_str()); client->close(); return; } #endif - DEBUG_MSG_P(PSTR("[WEBSOCKET] #%u connected, ip: %s, url: %s\n"), client->id(), ip.c_str(), server->url()); + DEBUG_MSG_P(PSTR("[WEBSOCKET] #%u connected, ip: %s, url: %s\n"), + client->id(), ip.c_str(), server->url()); + _wsConnected(client->id()); _wsResetUpdateTimer(); - client->_tempObject = new WebSocketIncommingBuffer(_wsParse, true); - } else if(type == WS_EVT_DISCONNECT) { + client->_tempObject = new WebSocketIncomingBuffer(_wsParse); + break; + } + + case WS_EVT_DISCONNECT: DEBUG_MSG_P(PSTR("[WEBSOCKET] #%u disconnected\n"), client->id()); if (client->_tempObject) { - delete (WebSocketIncommingBuffer *) client->_tempObject; + auto* ptr = reinterpret_cast(client->_tempObject); + delete ptr; client->_tempObject = nullptr; } wifiApCheck(); + break; - } else if(type == WS_EVT_ERROR) { - DEBUG_MSG_P(PSTR("[WEBSOCKET] #%u error(%u): %s\n"), client->id(), *((uint16_t*)arg), (char*)data); + case WS_EVT_ERROR: + { + uint16_t code; + std::memcpy(&code, arg, 2); + DEBUG_MSG_P(PSTR("[WEBSOCKET] #%u error(%hu)\n"), client->id(), code); + break; + } - } else if(type == WS_EVT_PONG) { - DEBUG_MSG_P(PSTR("[WEBSOCKET] #%u pong(%u): %s\n"), client->id(), len, len ? (char*) data : ""); + case WS_EVT_PONG: + break; - } else if(type == WS_EVT_DATA) { - //DEBUG_MSG_P(PSTR("[WEBSOCKET] #%u data(%u): %s\n"), client->id(), len, len ? (char*) data : ""); - if (!client->_tempObject) return; - WebSocketIncommingBuffer *buffer = (WebSocketIncommingBuffer *)client->_tempObject; - AwsFrameInfo * info = (AwsFrameInfo*)arg; - buffer->data_event(client, info, data, len); + case WS_EVT_DATA: + if (client->_tempObject) { + auto *buffer = reinterpret_cast(client->_tempObject); + AwsFrameInfo * info = (AwsFrameInfo*)arg; + buffer->data_event(client, info, data, len); + } + break; } - } void _wsHandlePostponedCallbacks(bool connected) {