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
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; |
|
}
|
|
|