Monitoring system for electric vehicles (log various sensors, such as consumed power, solar production, speed, slope, apparent wind, etc.)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1294 lines
33 KiB

/*
Asynchronous WebServer library for Espressif MCUs
Copyright (c) 2016 Hristo Gochkov. All rights reserved.
This file is part of the esp8266 core for Arduino environment.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "Arduino.h"
#include "AsyncWebSocket.h"
#include <libb64/cencode.h>
#ifndef ESP8266
#include "mbedtls/sha1.h"
#else
#include <Hash.h>
#endif
#define MAX_PRINTF_LEN 64
size_t webSocketSendFrameWindow(AsyncClient *client){
if(!client->canSend())
return 0;
size_t space = client->space();
if(space < 9)
return 0;
return space - 8;
}
size_t webSocketSendFrame(AsyncClient *client, bool final, uint8_t opcode, bool mask, uint8_t *data, size_t len){
if(!client->canSend())
return 0;
size_t space = client->space();
if(space < 2)
return 0;
uint8_t mbuf[4] = {0,0,0,0};
uint8_t headLen = 2;
if(len && mask){
headLen += 4;
mbuf[0] = rand() % 0xFF;
mbuf[1] = rand() % 0xFF;
mbuf[2] = rand() % 0xFF;
mbuf[3] = rand() % 0xFF;
}
if(len > 125)
headLen += 2;
if(space < headLen)
return 0;
space -= headLen;
if(len > space) len = space;
uint8_t *buf = (uint8_t*)malloc(headLen);
if(buf == NULL){
//os_printf("could not malloc %u bytes for frame header\n", headLen);
return 0;
}
buf[0] = opcode & 0x0F;
if(final)
buf[0] |= 0x80;
if(len < 126)
buf[1] = len & 0x7F;
else {
buf[1] = 126;
buf[2] = (uint8_t)((len >> 8) & 0xFF);
buf[3] = (uint8_t)(len & 0xFF);
}
if(len && mask){
buf[1] |= 0x80;
memcpy(buf + (headLen - 4), mbuf, 4);
}
if(client->add((const char *)buf, headLen) != headLen){
//os_printf("error adding %lu header bytes\n", headLen);
free(buf);
return 0;
}
free(buf);
if(len){
if(len && mask){
size_t i;
for(i=0;i<len;i++)
data[i] = data[i] ^ mbuf[i%4];
}
if(client->add((const char *)data, len) != len){
//os_printf("error adding %lu data bytes\n", len);
return 0;
}
}
if(!client->send()){
//os_printf("error sending frame: %lu\n", headLen+len);
return 0;
}
return len;
}
/*
* AsyncWebSocketMessageBuffer
*/
AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer()
:_data(nullptr)
,_len(0)
,_lock(false)
,_count(0)
{
}
AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(uint8_t * data, size_t size)
:_data(nullptr)
,_len(size)
,_lock(false)
,_count(0)
{
if (!data) {
return;
}
_data = new uint8_t[_len + 1];
if (_data) {
memcpy(_data, data, _len);
_data[_len] = 0;
}
}
AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(size_t size)
:_data(nullptr)
,_len(size)
,_lock(false)
,_count(0)
{
_data = new uint8_t[_len + 1];
if (_data) {
_data[_len] = 0;
}
}
AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(const AsyncWebSocketMessageBuffer & copy)
:_data(nullptr)
,_len(0)
,_lock(false)
,_count(0)
{
_len = copy._len;
_lock = copy._lock;
_count = 0;
if (_len) {
_data = new uint8_t[_len + 1];
_data[_len] = 0;
}
if (_data) {
memcpy(_data, copy._data, _len);
_data[_len] = 0;
}
}
AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(AsyncWebSocketMessageBuffer && copy)
:_data(nullptr)
,_len(0)
,_lock(false)
,_count(0)
{
_len = copy._len;
_lock = copy._lock;
_count = 0;
if (copy._data) {
_data = copy._data;
copy._data = nullptr;
}
}
AsyncWebSocketMessageBuffer::~AsyncWebSocketMessageBuffer()
{
if (_data) {
delete[] _data;
}
}
bool AsyncWebSocketMessageBuffer::reserve(size_t size)
{
_len = size;
if (_data) {
delete[] _data;
_data = nullptr;
}
_data = new uint8_t[_len + 1];
if (_data) {
_data[_len] = 0;
return true;
} else {
return false;
}
}
/*
* Control Frame
*/
class AsyncWebSocketControl {
private:
uint8_t _opcode;
uint8_t *_data;
size_t _len;
bool _mask;
bool _finished;
public:
AsyncWebSocketControl(uint8_t opcode, uint8_t *data=NULL, size_t len=0, bool mask=false)
:_opcode(opcode)
,_len(len)
,_mask(len && mask)
,_finished(false)
{
if(data == NULL)
_len = 0;
if(_len){
if(_len > 125)
_len = 125;
_data = (uint8_t*)malloc(_len);
if(_data == NULL)
_len = 0;
else memcpy(_data, data, len);
} else _data = NULL;
}
virtual ~AsyncWebSocketControl(){
if(_data != NULL)
free(_data);
}
virtual bool finished() const { return _finished; }
uint8_t opcode(){ return _opcode; }
uint8_t len(){ return _len + 2; }
size_t send(AsyncClient *client){
_finished = true;
return webSocketSendFrame(client, true, _opcode & 0x0F, _mask, _data, _len);
}
};
/*
* Basic Buffered Message
*/
AsyncWebSocketBasicMessage::AsyncWebSocketBasicMessage(const char * data, size_t len, uint8_t opcode, bool mask)
:_len(len)
,_sent(0)
,_ack(0)
,_acked(0)
{
_opcode = opcode & 0x07;
_mask = mask;
_data = (uint8_t*)malloc(_len+1);
if(_data == NULL){
_len = 0;
_status = WS_MSG_ERROR;
} else {
_status = WS_MSG_SENDING;
memcpy(_data, data, _len);
_data[_len] = 0;
}
}
AsyncWebSocketBasicMessage::AsyncWebSocketBasicMessage(uint8_t opcode, bool mask)
:_len(0)
,_sent(0)
,_ack(0)
,_acked(0)
,_data(NULL)
{
_opcode = opcode & 0x07;
_mask = mask;
}
AsyncWebSocketBasicMessage::~AsyncWebSocketBasicMessage() {
if(_data != NULL)
free(_data);
}
void AsyncWebSocketBasicMessage::ack(size_t len, uint32_t time) {
(void)time;
_acked += len;
if(_sent == _len && _acked == _ack){
_status = WS_MSG_SENT;
}
}
size_t AsyncWebSocketBasicMessage::send(AsyncClient *client) {
if(_status != WS_MSG_SENDING)
return 0;
if(_acked < _ack){
return 0;
}
if(_sent == _len){
if(_acked == _ack)
_status = WS_MSG_SENT;
return 0;
}
if(_sent > _len){
_status = WS_MSG_ERROR;
return 0;
}
size_t toSend = _len - _sent;
size_t window = webSocketSendFrameWindow(client);
if(window < toSend) {
toSend = window;
}
_sent += toSend;
_ack += toSend + ((toSend < 126)?2:4) + (_mask * 4);
bool final = (_sent == _len);
uint8_t* dPtr = (uint8_t*)(_data + (_sent - toSend));
uint8_t opCode = (toSend && _sent == toSend)?_opcode:(uint8_t)WS_CONTINUATION;
size_t sent = webSocketSendFrame(client, final, opCode, _mask, dPtr, toSend);
_status = WS_MSG_SENDING;
if(toSend && sent != toSend){
_sent -= (toSend - sent);
_ack -= (toSend - sent);
}
return sent;
}
// bool AsyncWebSocketBasicMessage::reserve(size_t size) {
// if (size) {
// _data = (uint8_t*)malloc(size +1);
// if (_data) {
// memset(_data, 0, size);
// _len = size;
// _status = WS_MSG_SENDING;
// return true;
// }
// }
// return false;
// }
/*
* AsyncWebSocketMultiMessage Message
*/
AsyncWebSocketMultiMessage::AsyncWebSocketMultiMessage(AsyncWebSocketMessageBuffer * buffer, uint8_t opcode, bool mask)
:_len(0)
,_sent(0)
,_ack(0)
,_acked(0)
,_WSbuffer(nullptr)
{
_opcode = opcode & 0x07;
_mask = mask;
if (buffer) {
_WSbuffer = buffer;
(*_WSbuffer)++;
_data = buffer->get();
_len = buffer->length();
_status = WS_MSG_SENDING;
//ets_printf("M: %u\n", _len);
} else {
_status = WS_MSG_ERROR;
}
}
AsyncWebSocketMultiMessage::~AsyncWebSocketMultiMessage() {
if (_WSbuffer) {
(*_WSbuffer)--; // decreases the counter.
}
}
void AsyncWebSocketMultiMessage::ack(size_t len, uint32_t time) {
(void)time;
_acked += len;
if(_sent >= _len && _acked >= _ack){
_status = WS_MSG_SENT;
}
//ets_printf("A: %u\n", len);
}
size_t AsyncWebSocketMultiMessage::send(AsyncClient *client) {
if(_status != WS_MSG_SENDING)
return 0;
if(_acked < _ack){
return 0;
}
if(_sent == _len){
_status = WS_MSG_SENT;
return 0;
}
if(_sent > _len){
_status = WS_MSG_ERROR;
//ets_printf("E: %u > %u\n", _sent, _len);
return 0;
}
size_t toSend = _len - _sent;
size_t window = webSocketSendFrameWindow(client);
if(window < toSend) {
toSend = window;
}
_sent += toSend;
_ack += toSend + ((toSend < 126)?2:4) + (_mask * 4);
//ets_printf("W: %u %u\n", _sent - toSend, toSend);
bool final = (_sent == _len);
uint8_t* dPtr = (uint8_t*)(_data + (_sent - toSend));
uint8_t opCode = (toSend && _sent == toSend)?_opcode:(uint8_t)WS_CONTINUATION;
size_t sent = webSocketSendFrame(client, final, opCode, _mask, dPtr, toSend);
_status = WS_MSG_SENDING;
if(toSend && sent != toSend){
//ets_printf("E: %u != %u\n", toSend, sent);
_sent -= (toSend - sent);
_ack -= (toSend - sent);
}
//ets_printf("S: %u %u\n", _sent, sent);
return sent;
}
/*
* Async WebSocket Client
*/
const char * AWSC_PING_PAYLOAD = "ESPAsyncWebServer-PING";
const size_t AWSC_PING_PAYLOAD_LEN = 22;
AsyncWebSocketClient::AsyncWebSocketClient(AsyncWebServerRequest *request, AsyncWebSocket *server)
: _controlQueue(LinkedList<AsyncWebSocketControl *>([](AsyncWebSocketControl *c){ delete c; }))
, _messageQueue(LinkedList<AsyncWebSocketMessage *>([](AsyncWebSocketMessage *m){ delete m; }))
, _tempObject(NULL)
{
_client = request->client();
_server = server;
_clientId = _server->_getNextId();
_status = WS_CONNECTED;
_pstate = 0;
_lastMessageTime = millis();
_keepAlivePeriod = 0;
_client->setRxTimeout(0);
_client->onError([](void *r, AsyncClient* c, int8_t error){ (void)c; ((AsyncWebSocketClient*)(r))->_onError(error); }, this);
_client->onAck([](void *r, AsyncClient* c, size_t len, uint32_t time){ (void)c; ((AsyncWebSocketClient*)(r))->_onAck(len, time); }, this);
_client->onDisconnect([](void *r, AsyncClient* c){ ((AsyncWebSocketClient*)(r))->_onDisconnect(); delete c; }, this);
_client->onTimeout([](void *r, AsyncClient* c, uint32_t time){ (void)c; ((AsyncWebSocketClient*)(r))->_onTimeout(time); }, this);
_client->onData([](void *r, AsyncClient* c, void *buf, size_t len){ (void)c; ((AsyncWebSocketClient*)(r))->_onData(buf, len); }, this);
_client->onPoll([](void *r, AsyncClient* c){ (void)c; ((AsyncWebSocketClient*)(r))->_onPoll(); }, this);
_server->_addClient(this);
_server->_handleEvent(this, WS_EVT_CONNECT, request, NULL, 0);
delete request;
}
AsyncWebSocketClient::~AsyncWebSocketClient(){
_messageQueue.free();
_controlQueue.free();
_server->_handleEvent(this, WS_EVT_DISCONNECT, NULL, NULL, 0);
}
void AsyncWebSocketClient::_onAck(size_t len, uint32_t time){
_lastMessageTime = millis();
if(!_controlQueue.isEmpty()){
auto head = _controlQueue.front();
if(head->finished()){
len -= head->len();
if(_status == WS_DISCONNECTING && head->opcode() == WS_DISCONNECT){
_controlQueue.remove(head);
_status = WS_DISCONNECTED;
_client->close(true);
return;
}
_controlQueue.remove(head);
}
}
if(len && !_messageQueue.isEmpty()){
_messageQueue.front()->ack(len, time);
}
_server->_cleanBuffers();
_runQueue();
}
void AsyncWebSocketClient::_onPoll(){
if(_client->canSend() && (!_controlQueue.isEmpty() || !_messageQueue.isEmpty())){
_runQueue();
} else if(_keepAlivePeriod > 0 && _controlQueue.isEmpty() && _messageQueue.isEmpty() && (millis() - _lastMessageTime) >= _keepAlivePeriod){
ping((uint8_t *)AWSC_PING_PAYLOAD, AWSC_PING_PAYLOAD_LEN);
}
}
void AsyncWebSocketClient::_runQueue(){
while(!_messageQueue.isEmpty() && _messageQueue.front()->finished()){
_messageQueue.remove(_messageQueue.front());
}
if(!_controlQueue.isEmpty() && (_messageQueue.isEmpty() || _messageQueue.front()->betweenFrames()) && webSocketSendFrameWindow(_client) > (size_t)(_controlQueue.front()->len() - 1)){
_controlQueue.front()->send(_client);
} else if(!_messageQueue.isEmpty() && _messageQueue.front()->betweenFrames() && webSocketSendFrameWindow(_client)){
_messageQueue.front()->send(_client);
}
}
bool AsyncWebSocketClient::queueIsFull(){
if((_messageQueue.length() >= WS_MAX_QUEUED_MESSAGES) || (_status != WS_CONNECTED) ) return true;
return false;
}
void AsyncWebSocketClient::_queueMessage(AsyncWebSocketMessage *dataMessage){
if(dataMessage == NULL)
return;
if(_status != WS_CONNECTED){
delete dataMessage;
return;
}
if(_messageQueue.length() >= WS_MAX_QUEUED_MESSAGES){
ets_printf("ERROR: Too many messages queued\n");
delete dataMessage;
} else {
_messageQueue.add(dataMessage);
}
if(_client->canSend())
_runQueue();
}
void AsyncWebSocketClient::_queueControl(AsyncWebSocketControl *controlMessage){
if(controlMessage == NULL)
return;
_controlQueue.add(controlMessage);
if(_client->canSend())
_runQueue();
}
void AsyncWebSocketClient::close(uint16_t code, const char * message){
if(_status != WS_CONNECTED)
return;
if(code){
uint8_t packetLen = 2;
if(message != NULL){
size_t mlen = strlen(message);
if(mlen > 123) mlen = 123;
packetLen += mlen;
}
char * buf = (char*)malloc(packetLen);
if(buf != NULL){
buf[0] = (uint8_t)(code >> 8);
buf[1] = (uint8_t)(code & 0xFF);
if(message != NULL){
memcpy(buf+2, message, packetLen -2);
}
_queueControl(new AsyncWebSocketControl(WS_DISCONNECT,(uint8_t*)buf,packetLen));
free(buf);
return;
}
}
_queueControl(new AsyncWebSocketControl(WS_DISCONNECT));
}
void AsyncWebSocketClient::ping(uint8_t *data, size_t len){
if(_status == WS_CONNECTED)
_queueControl(new AsyncWebSocketControl(WS_PING, data, len));
}
void AsyncWebSocketClient::_onError(int8_t){}
void AsyncWebSocketClient::_onTimeout(uint32_t time){
(void)time;
_client->close(true);
}
void AsyncWebSocketClient::_onDisconnect(){
_client = NULL;
_server->_handleDisconnect(this);
}
void AsyncWebSocketClient::_onData(void *pbuf, size_t plen){
_lastMessageTime = millis();
uint8_t *data = (uint8_t*)pbuf;
while(plen > 0){
if(!_pstate){
const uint8_t *fdata = data;
_pinfo.index = 0;
_pinfo.final = (fdata[0] & 0x80) != 0;
_pinfo.opcode = fdata[0] & 0x0F;
_pinfo.masked = (fdata[1] & 0x80) != 0;
_pinfo.len = fdata[1] & 0x7F;
data += 2;
plen -= 2;
if(_pinfo.len == 126){
_pinfo.len = fdata[3] | (uint16_t)(fdata[2]) << 8;
data += 2;
plen -= 2;
} else if(_pinfo.len == 127){
_pinfo.len = fdata[9] | (uint16_t)(fdata[8]) << 8 | (uint32_t)(fdata[7]) << 16 | (uint32_t)(fdata[6]) << 24 | (uint64_t)(fdata[5]) << 32 | (uint64_t)(fdata[4]) << 40 | (uint64_t)(fdata[3]) << 48 | (uint64_t)(fdata[2]) << 56;
data += 8;
plen -= 8;
}
if(_pinfo.masked){
memcpy(_pinfo.mask, data, 4);
data += 4;
plen -= 4;
}
}
const size_t datalen = std::min((size_t)(_pinfo.len - _pinfo.index), plen);
const auto datalast = data[datalen];
if(_pinfo.masked){
for(size_t i=0;i<datalen;i++)
data[i] ^= _pinfo.mask[(_pinfo.index+i)%4];
}
if((datalen + _pinfo.index) < _pinfo.len){
_pstate = 1;
if(_pinfo.index == 0){
if(_pinfo.opcode){
_pinfo.message_opcode = _pinfo.opcode;
_pinfo.num = 0;
} else _pinfo.num += 1;
}
_server->_handleEvent(this, WS_EVT_DATA, (void *)&_pinfo, (uint8_t*)data, datalen);
_pinfo.index += datalen;
} else if((datalen + _pinfo.index) == _pinfo.len){
_pstate = 0;
if(_pinfo.opcode == WS_DISCONNECT){
if(datalen){
uint16_t reasonCode = (uint16_t)(data[0] << 8) + data[1];
char * reasonString = (char*)(data+2);
if(reasonCode > 1001){
_server->_handleEvent(this, WS_EVT_ERROR, (void *)&reasonCode, (uint8_t*)reasonString, strlen(reasonString));
}
}
if(_status == WS_DISCONNECTING){
_status = WS_DISCONNECTED;
_client->close(true);
} else {
_status = WS_DISCONNECTING;
_client->ackLater();
_queueControl(new AsyncWebSocketControl(WS_DISCONNECT, data, datalen));
}
} else if(_pinfo.opcode == WS_PING){
_queueControl(new AsyncWebSocketControl(WS_PONG, data, datalen));
} else if(_pinfo.opcode == WS_PONG){
if(datalen != AWSC_PING_PAYLOAD_LEN || memcmp(AWSC_PING_PAYLOAD, data, AWSC_PING_PAYLOAD_LEN) != 0)
_server->_handleEvent(this, WS_EVT_PONG, NULL, data, datalen);
} else if(_pinfo.opcode < 8){//continuation or text/binary frame
_server->_handleEvent(this, WS_EVT_DATA, (void *)&_pinfo, data, datalen);
}
} else {
//os_printf("frame error: len: %u, index: %llu, total: %llu\n", datalen, _pinfo.index, _pinfo.len);
//what should we do?
break;
}
// restore byte as _handleEvent may have added a null terminator i.e., data[len] = 0;
if (datalen > 0)
data[datalen] = datalast;
data += datalen;
plen -= datalen;
}
}
size_t AsyncWebSocketClient::printf(const char *format, ...) {
va_list arg;
va_start(arg, format);
char* temp = new char[MAX_PRINTF_LEN];
if(!temp){
va_end(arg);
return 0;
}
char* buffer = temp;
size_t len = vsnprintf(temp, MAX_PRINTF_LEN, format, arg);
va_end(arg);
if (len > (MAX_PRINTF_LEN - 1)) {
buffer = new char[len + 1];
if (!buffer) {
delete[] temp;
return 0;
}
va_start(arg, format);
vsnprintf(buffer, len + 1, format, arg);
va_end(arg);
}
text(buffer, len);
if (buffer != temp) {
delete[] buffer;
}
delete[] temp;
return len;
}
#ifndef ESP32
size_t AsyncWebSocketClient::printf_P(PGM_P formatP, ...) {
va_list arg;
va_start(arg, formatP);
char* temp = new char[MAX_PRINTF_LEN];
if(!temp){
va_end(arg);
return 0;
}
char* buffer = temp;
size_t len = vsnprintf_P(temp, MAX_PRINTF_LEN, formatP, arg);
va_end(arg);
if (len > (MAX_PRINTF_LEN - 1)) {
buffer = new char[len + 1];
if (!buffer) {
delete[] temp;
return 0;
}
va_start(arg, formatP);
vsnprintf_P(buffer, len + 1, formatP, arg);
va_end(arg);
}
text(buffer, len);
if (buffer != temp) {
delete[] buffer;
}
delete[] temp;
return len;
}
#endif
void AsyncWebSocketClient::text(const char * message, size_t len){
_queueMessage(new AsyncWebSocketBasicMessage(message, len));
}
void AsyncWebSocketClient::text(const char * message){
text(message, strlen(message));
}
void AsyncWebSocketClient::text(uint8_t * message, size_t len){
text((const char *)message, len);
}
void AsyncWebSocketClient::text(char * message){
text(message, strlen(message));
}
void AsyncWebSocketClient::text(const String &message){
text(message.c_str(), message.length());
}
void AsyncWebSocketClient::text(const __FlashStringHelper *data){
PGM_P p = reinterpret_cast<PGM_P>(data);
size_t n = 0;
while (1) {
if (pgm_read_byte(p+n) == 0) break;
n += 1;
}
char * message = (char*) malloc(n+1);
if(message){
for(size_t b=0; b<n; b++)
message[b] = pgm_read_byte(p++);
message[n] = 0;
text(message, n);
free(message);
}
}
void AsyncWebSocketClient::text(AsyncWebSocketMessageBuffer * buffer)
{
_queueMessage(new AsyncWebSocketMultiMessage(buffer));
}
void AsyncWebSocketClient::binary(const char * message, size_t len){
_queueMessage(new AsyncWebSocketBasicMessage(message, len, WS_BINARY));
}
void AsyncWebSocketClient::binary(const char * message){
binary(message, strlen(message));
}
void AsyncWebSocketClient::binary(uint8_t * message, size_t len){
binary((const char *)message, len);
}
void AsyncWebSocketClient::binary(char * message){
binary(message, strlen(message));
}
void AsyncWebSocketClient::binary(const String &message){
binary(message.c_str(), message.length());
}
void AsyncWebSocketClient::binary(const __FlashStringHelper *data, size_t len){
PGM_P p = reinterpret_cast<PGM_P>(data);
char * message = (char*) malloc(len);
if(message){
for(size_t b=0; b<len; b++)
message[b] = pgm_read_byte(p++);
binary(message, len);
free(message);
}
}
void AsyncWebSocketClient::binary(AsyncWebSocketMessageBuffer * buffer)
{
_queueMessage(new AsyncWebSocketMultiMessage(buffer, WS_BINARY));
}
IPAddress AsyncWebSocketClient::remoteIP() {
if(!_client) {
return IPAddress(0U);
}
return _client->remoteIP();
}
uint16_t AsyncWebSocketClient::remotePort() {
if(!_client) {
return 0;
}
return _client->remotePort();
}
/*
* Async Web Socket - Each separate socket location
*/
AsyncWebSocket::AsyncWebSocket(const String& url)
:_url(url)
,_clients(LinkedList<AsyncWebSocketClient *>([](AsyncWebSocketClient *c){ delete c; }))
,_cNextId(1)
,_enabled(true)
,_buffers(LinkedList<AsyncWebSocketMessageBuffer *>([](AsyncWebSocketMessageBuffer *b){ delete b; }))
{
_eventHandler = NULL;
}
AsyncWebSocket::~AsyncWebSocket(){}
void AsyncWebSocket::_handleEvent(AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len){
if(_eventHandler != NULL){
_eventHandler(this, client, type, arg, data, len);
}
}
void AsyncWebSocket::_addClient(AsyncWebSocketClient * client){
_clients.add(client);
}
void AsyncWebSocket::_handleDisconnect(AsyncWebSocketClient * client){
_clients.remove_first([=](AsyncWebSocketClient * c){
return c->id() == client->id();
});
}
bool AsyncWebSocket::availableForWriteAll(){
for(const auto& c: _clients){
if(c->queueIsFull()) return false;
}
return true;
}
bool AsyncWebSocket::availableForWrite(uint32_t id){
for(const auto& c: _clients){
if(c->queueIsFull() && (c->id() == id )) return false;
}
return true;
}
size_t AsyncWebSocket::count() const {
return _clients.count_if([](AsyncWebSocketClient * c){
return c->status() == WS_CONNECTED;
});
}
AsyncWebSocketClient * AsyncWebSocket::client(uint32_t id){
for(const auto &c: _clients){
if(c->id() == id && c->status() == WS_CONNECTED){
return c;
}
}
return nullptr;
}
void AsyncWebSocket::close(uint32_t id, uint16_t code, const char * message){
AsyncWebSocketClient * c = client(id);
if(c)
c->close(code, message);
}
void AsyncWebSocket::closeAll(uint16_t code, const char * message){
for(const auto& c: _clients){
if(c->status() == WS_CONNECTED)
c->close(code, message);
}
}
void AsyncWebSocket::cleanupClients(uint16_t maxClients)
{
if (count() > maxClients){
_clients.front()->close();
}
}
void AsyncWebSocket::ping(uint32_t id, uint8_t *data, size_t len){
AsyncWebSocketClient * c = client(id);
if(c)
c->ping(data, len);
}
void AsyncWebSocket::pingAll(uint8_t *data, size_t len){
for(const auto& c: _clients){
if(c->status() == WS_CONNECTED)
c->ping(data, len);
}
}
void AsyncWebSocket::text(uint32_t id, const char * message, size_t len){
AsyncWebSocketClient * c = client(id);
if(c)
c->text(message, len);
}
void AsyncWebSocket::textAll(AsyncWebSocketMessageBuffer * buffer){
if (!buffer) return;
buffer->lock();
for(const auto& c: _clients){
if(c->status() == WS_CONNECTED){
c->text(buffer);
}
}
buffer->unlock();
_cleanBuffers();
}
void AsyncWebSocket::textAll(const char * message, size_t len){
AsyncWebSocketMessageBuffer * WSBuffer = makeBuffer((uint8_t *)message, len);
textAll(WSBuffer);
}
void AsyncWebSocket::binary(uint32_t id, const char * message, size_t len){
AsyncWebSocketClient * c = client(id);
if(c)
c->binary(message, len);
}
void AsyncWebSocket::binaryAll(const char * message, size_t len){
AsyncWebSocketMessageBuffer * buffer = makeBuffer((uint8_t *)message, len);
binaryAll(buffer);
}
void AsyncWebSocket::binaryAll(AsyncWebSocketMessageBuffer * buffer)
{
if (!buffer) return;
buffer->lock();
for(const auto& c: _clients){
if(c->status() == WS_CONNECTED)
c->binary(buffer);
}
buffer->unlock();
_cleanBuffers();
}
void AsyncWebSocket::message(uint32_t id, AsyncWebSocketMessage *message){
AsyncWebSocketClient * c = client(id);
if(c)
c->message(message);
}
void AsyncWebSocket::messageAll(AsyncWebSocketMultiMessage *message){
for(const auto& c: _clients){
if(c->status() == WS_CONNECTED)
c->message(message);
}
_cleanBuffers();
}
size_t AsyncWebSocket::printf(uint32_t id, const char *format, ...){
AsyncWebSocketClient * c = client(id);
if(c){
va_list arg;
va_start(arg, format);
size_t len = c->printf(format, arg);
va_end(arg);
return len;
}
return 0;
}
size_t AsyncWebSocket::printfAll(const char *format, ...) {
va_list arg;
char* temp = new char[MAX_PRINTF_LEN];
if(!temp){
return 0;
}
va_start(arg, format);
size_t len = vsnprintf(temp, MAX_PRINTF_LEN, format, arg);
va_end(arg);
delete[] temp;
AsyncWebSocketMessageBuffer * buffer = makeBuffer(len);
if (!buffer) {
return 0;
}
va_start(arg, format);
vsnprintf( (char *)buffer->get(), len + 1, format, arg);
va_end(arg);
textAll(buffer);
return len;
}
#ifndef ESP32
size_t AsyncWebSocket::printf_P(uint32_t id, PGM_P formatP, ...){
AsyncWebSocketClient * c = client(id);
if(c != NULL){
va_list arg;
va_start(arg, formatP);
size_t len = c->printf_P(formatP, arg);
va_end(arg);
return len;
}
return 0;
}
#endif
size_t AsyncWebSocket::printfAll_P(PGM_P formatP, ...) {
va_list arg;
char* temp = new char[MAX_PRINTF_LEN];
if(!temp){
return 0;
}
va_start(arg, formatP);
size_t len = vsnprintf_P(temp, MAX_PRINTF_LEN, formatP, arg);
va_end(arg);
delete[] temp;
AsyncWebSocketMessageBuffer * buffer = makeBuffer(len + 1);
if (!buffer) {
return 0;
}
va_start(arg, formatP);
vsnprintf_P((char *)buffer->get(), len + 1, formatP, arg);
va_end(arg);
textAll(buffer);
return len;
}
void AsyncWebSocket::text(uint32_t id, const char * message){
text(id, message, strlen(message));
}
void AsyncWebSocket::text(uint32_t id, uint8_t * message, size_t len){
text(id, (const char *)message, len);
}
void AsyncWebSocket::text(uint32_t id, char * message){
text(id, message, strlen(message));
}
void AsyncWebSocket::text(uint32_t id, const String &message){
text(id, message.c_str(), message.length());
}
void AsyncWebSocket::text(uint32_t id, const __FlashStringHelper *message){
AsyncWebSocketClient * c = client(id);
if(c != NULL)
c->text(message);
}
void AsyncWebSocket::textAll(const char * message){
textAll(message, strlen(message));
}
void AsyncWebSocket::textAll(uint8_t * message, size_t len){
textAll((const char *)message, len);
}
void AsyncWebSocket::textAll(char * message){
textAll(message, strlen(message));
}
void AsyncWebSocket::textAll(const String &message){
textAll(message.c_str(), message.length());
}
void AsyncWebSocket::textAll(const __FlashStringHelper *message){
for(const auto& c: _clients){
if(c->status() == WS_CONNECTED)
c->text(message);
}
}
void AsyncWebSocket::binary(uint32_t id, const char * message){
binary(id, message, strlen(message));
}
void AsyncWebSocket::binary(uint32_t id, uint8_t * message, size_t len){
binary(id, (const char *)message, len);
}
void AsyncWebSocket::binary(uint32_t id, char * message){
binary(id, message, strlen(message));
}
void AsyncWebSocket::binary(uint32_t id, const String &message){
binary(id, message.c_str(), message.length());
}
void AsyncWebSocket::binary(uint32_t id, const __FlashStringHelper *message, size_t len){
AsyncWebSocketClient * c = client(id);
if(c != NULL)
c-> binary(message, len);
}
void AsyncWebSocket::binaryAll(const char * message){
binaryAll(message, strlen(message));
}
void AsyncWebSocket::binaryAll(uint8_t * message, size_t len){
binaryAll((const char *)message, len);
}
void AsyncWebSocket::binaryAll(char * message){
binaryAll(message, strlen(message));
}
void AsyncWebSocket::binaryAll(const String &message){
binaryAll(message.c_str(), message.length());
}
void AsyncWebSocket::binaryAll(const __FlashStringHelper *message, size_t len){
for(const auto& c: _clients){
if(c->status() == WS_CONNECTED)
c-> binary(message, len);
}
}
const char * WS_STR_CONNECTION = "Connection";
const char * WS_STR_UPGRADE = "Upgrade";
const char * WS_STR_ORIGIN = "Origin";
const char * WS_STR_VERSION = "Sec-WebSocket-Version";
const char * WS_STR_KEY = "Sec-WebSocket-Key";
const char * WS_STR_PROTOCOL = "Sec-WebSocket-Protocol";
const char * WS_STR_ACCEPT = "Sec-WebSocket-Accept";
const char * WS_STR_UUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
bool AsyncWebSocket::canHandle(AsyncWebServerRequest *request){
if(!_enabled)
return false;
if(request->method() != HTTP_GET || !request->url().equals(_url) || !request->isExpectedRequestedConnType(RCT_WS))
return false;
request->addInterestingHeader(WS_STR_CONNECTION);
request->addInterestingHeader(WS_STR_UPGRADE);
request->addInterestingHeader(WS_STR_ORIGIN);
request->addInterestingHeader(WS_STR_VERSION);
request->addInterestingHeader(WS_STR_KEY);
request->addInterestingHeader(WS_STR_PROTOCOL);
return true;
}
void AsyncWebSocket::handleRequest(AsyncWebServerRequest *request){
if(!request->hasHeader(WS_STR_VERSION) || !request->hasHeader(WS_STR_KEY)){
request->send(400);
return;
}
if((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str())){
return request->requestAuthentication();
}
AsyncWebHeader* version = request->getHeader(WS_STR_VERSION);
if(version->value().toInt() != 13){
AsyncWebServerResponse *response = request->beginResponse(400);
response->addHeader(WS_STR_VERSION,"13");
request->send(response);
return;
}
AsyncWebHeader* key = request->getHeader(WS_STR_KEY);
AsyncWebServerResponse *response = new AsyncWebSocketResponse(key->value(), this);
if(request->hasHeader(WS_STR_PROTOCOL)){
AsyncWebHeader* protocol = request->getHeader(WS_STR_PROTOCOL);
//ToDo: check protocol
response->addHeader(WS_STR_PROTOCOL, protocol->value());
}
request->send(response);
}
AsyncWebSocketMessageBuffer * AsyncWebSocket::makeBuffer(size_t size)
{
AsyncWebSocketMessageBuffer * buffer = new AsyncWebSocketMessageBuffer(size);
if (buffer) {
AsyncWebLockGuard l(_lock);
_buffers.add(buffer);
}
return buffer;
}
AsyncWebSocketMessageBuffer * AsyncWebSocket::makeBuffer(uint8_t * data, size_t size)
{
AsyncWebSocketMessageBuffer * buffer = new AsyncWebSocketMessageBuffer(data, size);
if (buffer) {
AsyncWebLockGuard l(_lock);
_buffers.add(buffer);
}
return buffer;
}
void AsyncWebSocket::_cleanBuffers()
{
AsyncWebLockGuard l(_lock);
for(AsyncWebSocketMessageBuffer * c: _buffers){
if(c && c->canDelete()){
_buffers.remove(c);
}
}
}
AsyncWebSocket::AsyncWebSocketClientLinkedList AsyncWebSocket::getClients() const {
return _clients;
}
/*
* 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
*/
AsyncWebSocketResponse::AsyncWebSocketResponse(const String& key, AsyncWebSocket *server){
_server = server;
_code = 101;
_sendContentLength = false;
uint8_t * hash = (uint8_t*)malloc(20);
if(hash == NULL){
_state = RESPONSE_FAILED;
return;
}
char * buffer = (char *) malloc(33);
if(buffer == NULL){
free(hash);
_state = RESPONSE_FAILED;
return;
}
#ifdef ESP8266
sha1(key + WS_STR_UUID, hash);
#else
(String&)key += WS_STR_UUID;
mbedtls_sha1_context ctx;
mbedtls_sha1_init(&ctx);
mbedtls_sha1_starts_ret(&ctx);
mbedtls_sha1_update_ret(&ctx, (const unsigned char*)key.c_str(), key.length());
mbedtls_sha1_finish_ret(&ctx, hash);
mbedtls_sha1_free(&ctx);
#endif
base64_encodestate _state;
base64_init_encodestate(&_state);
int len = base64_encode_block((const char *) hash, 20, buffer, &_state);
len = base64_encode_blockend((buffer + len), &_state);
addHeader(WS_STR_CONNECTION, WS_STR_UPGRADE);
addHeader(WS_STR_UPGRADE, "websocket");
addHeader(WS_STR_ACCEPT,buffer);
free(buffer);
free(hash);
}
void AsyncWebSocketResponse::_respond(AsyncWebServerRequest *request){
if(_state == RESPONSE_FAILED){
request->client()->close(true);
return;
}
String out = _assembleHead(request->version());
request->client()->write(out.c_str(), _headLength);
_state = RESPONSE_WAIT_ACK;
}
size_t AsyncWebSocketResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time){
(void)time;
if(len){
new AsyncWebSocketClient(request, _server);
}
return 0;
}