mirror of
https://github.com/IoTManagerProject/IoTManager.git
synced 2026-03-27 06:32:19 +03:00
перенос библиотеки сокетов в тело проекта
This commit is contained in:
260
lib/WebSockets/src/SocketIOclient.cpp
Normal file
260
lib/WebSockets/src/SocketIOclient.cpp
Normal file
@@ -0,0 +1,260 @@
|
||||
/*
|
||||
* SocketIOclient.cpp
|
||||
*
|
||||
* Created on: May 12, 2018
|
||||
* Author: links
|
||||
*/
|
||||
|
||||
#include "WebSockets.h"
|
||||
#include "WebSocketsClient.h"
|
||||
#include "SocketIOclient.h"
|
||||
|
||||
SocketIOclient::SocketIOclient() {
|
||||
}
|
||||
|
||||
SocketIOclient::~SocketIOclient() {
|
||||
}
|
||||
|
||||
void SocketIOclient::begin(const char * host, uint16_t port, const char * url, const char * protocol) {
|
||||
WebSocketsClient::beginSocketIO(host, port, url, protocol);
|
||||
WebSocketsClient::enableHeartbeat(60 * 1000, 90 * 1000, 5);
|
||||
initClient();
|
||||
}
|
||||
|
||||
void SocketIOclient::begin(String host, uint16_t port, String url, String protocol) {
|
||||
WebSocketsClient::beginSocketIO(host, port, url, protocol);
|
||||
WebSocketsClient::enableHeartbeat(60 * 1000, 90 * 1000, 5);
|
||||
initClient();
|
||||
}
|
||||
#if defined(HAS_SSL)
|
||||
void SocketIOclient::beginSSL(const char * host, uint16_t port, const char * url, const char * protocol) {
|
||||
WebSocketsClient::beginSocketIOSSL(host, port, url, protocol);
|
||||
WebSocketsClient::enableHeartbeat(60 * 1000, 90 * 1000, 5);
|
||||
initClient();
|
||||
}
|
||||
|
||||
void SocketIOclient::beginSSL(String host, uint16_t port, String url, String protocol) {
|
||||
WebSocketsClient::beginSocketIOSSL(host, port, url, protocol);
|
||||
WebSocketsClient::enableHeartbeat(60 * 1000, 90 * 1000, 5);
|
||||
initClient();
|
||||
}
|
||||
#if defined(SSL_BARESSL)
|
||||
void SocketIOclient::beginSSLWithCA(const char * host, uint16_t port, const char * url, const char * CA_cert, const char * protocol) {
|
||||
WebSocketsClient::beginSocketIOSSLWithCA(host, port, url, CA_cert, protocol);
|
||||
WebSocketsClient::enableHeartbeat(60 * 1000, 90 * 1000, 5);
|
||||
initClient();
|
||||
}
|
||||
|
||||
void SocketIOclient::beginSSLWithCA(const char * host, uint16_t port, const char * url, BearSSL::X509List * CA_cert, const char * protocol) {
|
||||
WebSocketsClient::beginSocketIOSSLWithCA(host, port, url, CA_cert, protocol);
|
||||
WebSocketsClient::enableHeartbeat(60 * 1000, 90 * 1000, 5);
|
||||
initClient();
|
||||
}
|
||||
|
||||
void SocketIOclient::setSSLClientCertKey(const char * clientCert, const char * clientPrivateKey) {
|
||||
WebSocketsClient::setSSLClientCertKey(clientCert, clientPrivateKey);
|
||||
}
|
||||
|
||||
void SocketIOclient::setSSLClientCertKey(BearSSL::X509List * clientCert, BearSSL::PrivateKey * clientPrivateKey) {
|
||||
WebSocketsClient::setSSLClientCertKey(clientCert, clientPrivateKey);
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
void SocketIOclient::configureEIOping(bool disableHeartbeat) {
|
||||
_disableHeartbeat = disableHeartbeat;
|
||||
}
|
||||
|
||||
void SocketIOclient::initClient(void) {
|
||||
if(_client.cUrl.indexOf("EIO=4") != -1) {
|
||||
DEBUG_WEBSOCKETS("[wsIOc] found EIO=4 disable EIO ping on client\n");
|
||||
configureEIOping(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* set callback function
|
||||
* @param cbEvent SocketIOclientEvent
|
||||
*/
|
||||
void SocketIOclient::onEvent(SocketIOclientEvent cbEvent) {
|
||||
_cbEvent = cbEvent;
|
||||
}
|
||||
|
||||
bool SocketIOclient::isConnected(void) {
|
||||
return WebSocketsClient::isConnected();
|
||||
}
|
||||
|
||||
void SocketIOclient::setExtraHeaders(const char * extraHeaders) {
|
||||
return WebSocketsClient::setExtraHeaders(extraHeaders);
|
||||
}
|
||||
|
||||
void SocketIOclient::setReconnectInterval(unsigned long time) {
|
||||
return WebSocketsClient::setReconnectInterval(time);
|
||||
}
|
||||
|
||||
/**
|
||||
* send text data to client
|
||||
* @param num uint8_t client id
|
||||
* @param type socketIOmessageType_t
|
||||
* @param payload uint8_t *
|
||||
* @param length size_t
|
||||
* @param headerToPayload bool (see sendFrame for more details)
|
||||
* @return true if ok
|
||||
*/
|
||||
bool SocketIOclient::send(socketIOmessageType_t type, uint8_t * payload, size_t length, bool headerToPayload) {
|
||||
bool ret = false;
|
||||
if(length == 0) {
|
||||
length = strlen((const char *)payload);
|
||||
}
|
||||
if(clientIsConnected(&_client) && _client.status == WSC_CONNECTED) {
|
||||
if(!headerToPayload) {
|
||||
// webSocket Header
|
||||
ret = WebSocketsClient::sendFrameHeader(&_client, WSop_text, length + 2, true);
|
||||
// Engine.IO / Socket.IO Header
|
||||
if(ret) {
|
||||
uint8_t buf[3] = { eIOtype_MESSAGE, type, 0x00 };
|
||||
ret = WebSocketsClient::write(&_client, buf, 2);
|
||||
}
|
||||
if(ret && payload && length > 0) {
|
||||
ret = WebSocketsClient::write(&_client, payload, length);
|
||||
}
|
||||
return ret;
|
||||
} else {
|
||||
// TODO implement
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SocketIOclient::send(socketIOmessageType_t type, const uint8_t * payload, size_t length) {
|
||||
return send(type, (uint8_t *)payload, length);
|
||||
}
|
||||
|
||||
bool SocketIOclient::send(socketIOmessageType_t type, char * payload, size_t length, bool headerToPayload) {
|
||||
return send(type, (uint8_t *)payload, length, headerToPayload);
|
||||
}
|
||||
|
||||
bool SocketIOclient::send(socketIOmessageType_t type, const char * payload, size_t length) {
|
||||
return send(type, (uint8_t *)payload, length);
|
||||
}
|
||||
|
||||
bool SocketIOclient::send(socketIOmessageType_t type, String & payload) {
|
||||
return send(type, (uint8_t *)payload.c_str(), payload.length());
|
||||
}
|
||||
|
||||
/**
|
||||
* send text data to client
|
||||
* @param num uint8_t client id
|
||||
* @param payload uint8_t *
|
||||
* @param length size_t
|
||||
* @param headerToPayload bool (see sendFrame for more details)
|
||||
* @return true if ok
|
||||
*/
|
||||
bool SocketIOclient::sendEVENT(uint8_t * payload, size_t length, bool headerToPayload) {
|
||||
return send(sIOtype_EVENT, payload, length, headerToPayload);
|
||||
}
|
||||
|
||||
bool SocketIOclient::sendEVENT(const uint8_t * payload, size_t length) {
|
||||
return sendEVENT((uint8_t *)payload, length);
|
||||
}
|
||||
|
||||
bool SocketIOclient::sendEVENT(char * payload, size_t length, bool headerToPayload) {
|
||||
return sendEVENT((uint8_t *)payload, length, headerToPayload);
|
||||
}
|
||||
|
||||
bool SocketIOclient::sendEVENT(const char * payload, size_t length) {
|
||||
return sendEVENT((uint8_t *)payload, length);
|
||||
}
|
||||
|
||||
bool SocketIOclient::sendEVENT(String & payload) {
|
||||
return sendEVENT((uint8_t *)payload.c_str(), payload.length());
|
||||
}
|
||||
|
||||
void SocketIOclient::loop(void) {
|
||||
WebSocketsClient::loop();
|
||||
unsigned long t = millis();
|
||||
if(!_disableHeartbeat && (t - _lastHeartbeat) > EIO_HEARTBEAT_INTERVAL) {
|
||||
_lastHeartbeat = t;
|
||||
DEBUG_WEBSOCKETS("[wsIOc] send ping\n");
|
||||
WebSocketsClient::sendTXT(eIOtype_PING);
|
||||
}
|
||||
}
|
||||
|
||||
void SocketIOclient::handleCbEvent(WStype_t type, uint8_t * payload, size_t length) {
|
||||
switch(type) {
|
||||
case WStype_DISCONNECTED:
|
||||
runIOCbEvent(sIOtype_DISCONNECT, NULL, 0);
|
||||
DEBUG_WEBSOCKETS("[wsIOc] Disconnected!\n");
|
||||
break;
|
||||
case WStype_CONNECTED: {
|
||||
DEBUG_WEBSOCKETS("[wsIOc] Connected to url: %s\n", payload);
|
||||
// send message to server when Connected
|
||||
// Engine.io upgrade confirmation message (required)
|
||||
WebSocketsClient::sendTXT("2probe");
|
||||
WebSocketsClient::sendTXT(eIOtype_UPGRADE);
|
||||
runIOCbEvent(sIOtype_CONNECT, payload, length);
|
||||
} break;
|
||||
case WStype_TEXT: {
|
||||
if(length < 1) {
|
||||
break;
|
||||
}
|
||||
|
||||
engineIOmessageType_t eType = (engineIOmessageType_t)payload[0];
|
||||
switch(eType) {
|
||||
case eIOtype_PING:
|
||||
payload[0] = eIOtype_PONG;
|
||||
DEBUG_WEBSOCKETS("[wsIOc] get ping send pong (%s)\n", payload);
|
||||
WebSocketsClient::sendTXT(payload, length, false);
|
||||
break;
|
||||
case eIOtype_PONG:
|
||||
DEBUG_WEBSOCKETS("[wsIOc] get pong\n");
|
||||
break;
|
||||
case eIOtype_MESSAGE: {
|
||||
if(length < 2) {
|
||||
break;
|
||||
}
|
||||
socketIOmessageType_t ioType = (socketIOmessageType_t)payload[1];
|
||||
uint8_t * data = &payload[2];
|
||||
size_t lData = length - 2;
|
||||
switch(ioType) {
|
||||
case sIOtype_EVENT:
|
||||
DEBUG_WEBSOCKETS("[wsIOc] get event (%d): %s\n", lData, data);
|
||||
break;
|
||||
case sIOtype_CONNECT:
|
||||
DEBUG_WEBSOCKETS("[wsIOc] connected (%d): %s\n", lData, data);
|
||||
return;
|
||||
case sIOtype_DISCONNECT:
|
||||
case sIOtype_ACK:
|
||||
case sIOtype_ERROR:
|
||||
case sIOtype_BINARY_EVENT:
|
||||
case sIOtype_BINARY_ACK:
|
||||
default:
|
||||
DEBUG_WEBSOCKETS("[wsIOc] Socket.IO Message Type %c (%02X) is not implemented\n", ioType, ioType);
|
||||
DEBUG_WEBSOCKETS("[wsIOc] get text: %s\n", payload);
|
||||
break;
|
||||
}
|
||||
|
||||
runIOCbEvent(ioType, data, lData);
|
||||
} break;
|
||||
case eIOtype_OPEN:
|
||||
case eIOtype_CLOSE:
|
||||
case eIOtype_UPGRADE:
|
||||
case eIOtype_NOOP:
|
||||
default:
|
||||
DEBUG_WEBSOCKETS("[wsIOc] Engine.IO Message Type %c (%02X) is not implemented\n", eType, eType);
|
||||
DEBUG_WEBSOCKETS("[wsIOc] get text: %s\n", payload);
|
||||
break;
|
||||
}
|
||||
} break;
|
||||
case WStype_ERROR:
|
||||
case WStype_BIN:
|
||||
case WStype_FRAGMENT_TEXT_START:
|
||||
case WStype_FRAGMENT_BIN_START:
|
||||
case WStype_FRAGMENT:
|
||||
case WStype_FRAGMENT_FIN:
|
||||
case WStype_PING:
|
||||
case WStype_PONG:
|
||||
break;
|
||||
}
|
||||
}
|
||||
105
lib/WebSockets/src/SocketIOclient.h
Normal file
105
lib/WebSockets/src/SocketIOclient.h
Normal file
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
* SocketIOclient.h
|
||||
*
|
||||
* Created on: May 12, 2018
|
||||
* Author: links
|
||||
*/
|
||||
|
||||
#ifndef SOCKETIOCLIENT_H_
|
||||
#define SOCKETIOCLIENT_H_
|
||||
|
||||
#include "WebSockets.h"
|
||||
#include "WebSocketsClient.h"
|
||||
|
||||
#define EIO_HEARTBEAT_INTERVAL 20000
|
||||
|
||||
#define EIO_MAX_HEADER_SIZE (WEBSOCKETS_MAX_HEADER_SIZE + 1)
|
||||
#define SIO_MAX_HEADER_SIZE (EIO_MAX_HEADER_SIZE + 1)
|
||||
|
||||
typedef enum {
|
||||
eIOtype_OPEN = '0', ///< Sent from the server when a new transport is opened (recheck)
|
||||
eIOtype_CLOSE = '1', ///< Request the close of this transport but does not shutdown the connection itself.
|
||||
eIOtype_PING = '2', ///< Sent by the client. Server should answer with a pong packet containing the same data
|
||||
eIOtype_PONG = '3', ///< Sent by the server to respond to ping packets.
|
||||
eIOtype_MESSAGE = '4', ///< actual message, client and server should call their callbacks with the data
|
||||
eIOtype_UPGRADE = '5', ///< Before engine.io switches a transport, it tests, if server and client can communicate over this transport. If this test succeed, the client sends an upgrade packets which requests the server to flush its cache on the old transport and switch to the new transport.
|
||||
eIOtype_NOOP = '6', ///< A noop packet. Used primarily to force a poll cycle when an incoming websocket connection is received.
|
||||
} engineIOmessageType_t;
|
||||
|
||||
typedef enum {
|
||||
sIOtype_CONNECT = '0',
|
||||
sIOtype_DISCONNECT = '1',
|
||||
sIOtype_EVENT = '2',
|
||||
sIOtype_ACK = '3',
|
||||
sIOtype_ERROR = '4',
|
||||
sIOtype_BINARY_EVENT = '5',
|
||||
sIOtype_BINARY_ACK = '6',
|
||||
} socketIOmessageType_t;
|
||||
|
||||
class SocketIOclient : protected WebSocketsClient {
|
||||
public:
|
||||
#ifdef __AVR__
|
||||
typedef void (*SocketIOclientEvent)(socketIOmessageType_t type, uint8_t * payload, size_t length);
|
||||
#else
|
||||
typedef std::function<void(socketIOmessageType_t type, uint8_t * payload, size_t length)> SocketIOclientEvent;
|
||||
#endif
|
||||
|
||||
SocketIOclient(void);
|
||||
virtual ~SocketIOclient(void);
|
||||
|
||||
void begin(const char * host, uint16_t port, const char * url = "/socket.io/?EIO=3", const char * protocol = "arduino");
|
||||
void begin(String host, uint16_t port, String url = "/socket.io/?EIO=3", String protocol = "arduino");
|
||||
|
||||
#ifdef HAS_SSL
|
||||
void beginSSL(const char * host, uint16_t port, const char * url = "/socket.io/?EIO=3", const char * protocol = "arduino");
|
||||
void beginSSL(String host, uint16_t port, String url = "/socket.io/?EIO=3", String protocol = "arduino");
|
||||
#ifndef SSL_AXTLS
|
||||
void beginSSLWithCA(const char * host, uint16_t port, const char * url = "/socket.io/?EIO=3", const char * CA_cert = NULL, const char * protocol = "arduino");
|
||||
void beginSSLWithCA(const char * host, uint16_t port, const char * url = "/socket.io/?EIO=3", BearSSL::X509List * CA_cert = NULL, const char * protocol = "arduino");
|
||||
void setSSLClientCertKey(const char * clientCert = NULL, const char * clientPrivateKey = NULL);
|
||||
void setSSLClientCertKey(BearSSL::X509List * clientCert = NULL, BearSSL::PrivateKey * clientPrivateKey = NULL);
|
||||
#endif
|
||||
#endif
|
||||
bool isConnected(void);
|
||||
|
||||
void onEvent(SocketIOclientEvent cbEvent);
|
||||
|
||||
bool sendEVENT(uint8_t * payload, size_t length = 0, bool headerToPayload = false);
|
||||
bool sendEVENT(const uint8_t * payload, size_t length = 0);
|
||||
bool sendEVENT(char * payload, size_t length = 0, bool headerToPayload = false);
|
||||
bool sendEVENT(const char * payload, size_t length = 0);
|
||||
bool sendEVENT(String & payload);
|
||||
|
||||
bool send(socketIOmessageType_t type, uint8_t * payload, size_t length = 0, bool headerToPayload = false);
|
||||
bool send(socketIOmessageType_t type, const uint8_t * payload, size_t length = 0);
|
||||
bool send(socketIOmessageType_t type, char * payload, size_t length = 0, bool headerToPayload = false);
|
||||
bool send(socketIOmessageType_t type, const char * payload, size_t length = 0);
|
||||
bool send(socketIOmessageType_t type, String & payload);
|
||||
|
||||
void setExtraHeaders(const char * extraHeaders = NULL);
|
||||
void setReconnectInterval(unsigned long time);
|
||||
|
||||
void loop(void);
|
||||
|
||||
void configureEIOping(bool disableHeartbeat = false);
|
||||
|
||||
protected:
|
||||
bool _disableHeartbeat = false;
|
||||
uint64_t _lastHeartbeat = 0;
|
||||
SocketIOclientEvent _cbEvent;
|
||||
virtual void runIOCbEvent(socketIOmessageType_t type, uint8_t * payload, size_t length) {
|
||||
if(_cbEvent) {
|
||||
_cbEvent(type, payload, length);
|
||||
}
|
||||
}
|
||||
|
||||
void initClient(void);
|
||||
|
||||
// Handeling events from websocket layer
|
||||
virtual void runCbEvent(WStype_t type, uint8_t * payload, size_t length) {
|
||||
handleCbEvent(type, payload, length);
|
||||
}
|
||||
void handleCbEvent(WStype_t type, uint8_t * payload, size_t length);
|
||||
};
|
||||
|
||||
#endif /* SOCKETIOCLIENT_H_ */
|
||||
761
lib/WebSockets/src/WebSockets.cpp
Normal file
761
lib/WebSockets/src/WebSockets.cpp
Normal file
@@ -0,0 +1,761 @@
|
||||
/**
|
||||
* @file WebSockets.cpp
|
||||
* @date 20.05.2015
|
||||
* @author Markus Sattler
|
||||
*
|
||||
* Copyright (c) 2015 Markus Sattler. All rights reserved.
|
||||
* This file is part of the WebSockets for Arduino.
|
||||
*
|
||||
* 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 "WebSockets.h"
|
||||
|
||||
#ifdef ESP8266
|
||||
#include <core_esp8266_features.h>
|
||||
#endif
|
||||
|
||||
extern "C" {
|
||||
#ifdef CORE_HAS_LIBB64
|
||||
#include <libb64/cencode.h>
|
||||
#else
|
||||
#include "libb64/cencode_inc.h"
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef ESP8266
|
||||
#include <Hash.h>
|
||||
#elif defined(ESP32)
|
||||
#include <esp_system.h>
|
||||
|
||||
#if ESP_IDF_VERSION_MAJOR >= 4
|
||||
#if(ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(1, 0, 6))
|
||||
#include "sha/sha_parallel_engine.h"
|
||||
#else
|
||||
#include <esp32/sha.h>
|
||||
#endif
|
||||
#else
|
||||
#include <hwcrypto/sha.h>
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
extern "C" {
|
||||
#include "libsha1/libsha1.h"
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
*
|
||||
* @param client WSclient_t * ptr to the client struct
|
||||
* @param code uint16_t see RFC
|
||||
* @param reason ptr to the disconnect reason message
|
||||
* @param reasonLen length of the disconnect reason message
|
||||
*/
|
||||
void WebSockets::clientDisconnect(WSclient_t * client, uint16_t code, char * reason, size_t reasonLen) {
|
||||
DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] clientDisconnect code: %u\n", client->num, code);
|
||||
if(client->status == WSC_CONNECTED && code) {
|
||||
if(reason) {
|
||||
sendFrame(client, WSop_close, (uint8_t *)reason, reasonLen);
|
||||
} else {
|
||||
uint8_t buffer[2];
|
||||
buffer[0] = ((code >> 8) & 0xFF);
|
||||
buffer[1] = (code & 0xFF);
|
||||
sendFrame(client, WSop_close, &buffer[0], 2);
|
||||
}
|
||||
}
|
||||
clientDisconnect(client);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param buf uint8_t * ptr to the buffer for writing
|
||||
* @param opcode WSopcode_t
|
||||
* @param length size_t length of the payload
|
||||
* @param mask bool add dummy mask to the frame (needed for web browser)
|
||||
* @param maskkey uint8_t[4] key used for payload
|
||||
* @param fin bool can be used to send data in more then one frame (set fin on the last frame)
|
||||
*/
|
||||
uint8_t WebSockets::createHeader(uint8_t * headerPtr, WSopcode_t opcode, size_t length, bool mask, uint8_t maskKey[4], bool fin) {
|
||||
uint8_t headerSize;
|
||||
// calculate header Size
|
||||
if(length < 126) {
|
||||
headerSize = 2;
|
||||
} else if(length < 0xFFFF) {
|
||||
headerSize = 4;
|
||||
} else {
|
||||
headerSize = 10;
|
||||
}
|
||||
|
||||
if(mask) {
|
||||
headerSize += 4;
|
||||
}
|
||||
|
||||
// create header
|
||||
|
||||
// byte 0
|
||||
*headerPtr = 0x00;
|
||||
if(fin) {
|
||||
*headerPtr |= bit(7); ///< set Fin
|
||||
}
|
||||
*headerPtr |= opcode; ///< set opcode
|
||||
headerPtr++;
|
||||
|
||||
// byte 1
|
||||
*headerPtr = 0x00;
|
||||
if(mask) {
|
||||
*headerPtr |= bit(7); ///< set mask
|
||||
}
|
||||
|
||||
if(length < 126) {
|
||||
*headerPtr |= length;
|
||||
headerPtr++;
|
||||
} else if(length < 0xFFFF) {
|
||||
*headerPtr |= 126;
|
||||
headerPtr++;
|
||||
*headerPtr = ((length >> 8) & 0xFF);
|
||||
headerPtr++;
|
||||
*headerPtr = (length & 0xFF);
|
||||
headerPtr++;
|
||||
} else {
|
||||
// Normally we never get here (to less memory)
|
||||
*headerPtr |= 127;
|
||||
headerPtr++;
|
||||
*headerPtr = 0x00;
|
||||
headerPtr++;
|
||||
*headerPtr = 0x00;
|
||||
headerPtr++;
|
||||
*headerPtr = 0x00;
|
||||
headerPtr++;
|
||||
*headerPtr = 0x00;
|
||||
headerPtr++;
|
||||
*headerPtr = ((length >> 24) & 0xFF);
|
||||
headerPtr++;
|
||||
*headerPtr = ((length >> 16) & 0xFF);
|
||||
headerPtr++;
|
||||
*headerPtr = ((length >> 8) & 0xFF);
|
||||
headerPtr++;
|
||||
*headerPtr = (length & 0xFF);
|
||||
headerPtr++;
|
||||
}
|
||||
|
||||
if(mask) {
|
||||
*headerPtr = maskKey[0];
|
||||
headerPtr++;
|
||||
*headerPtr = maskKey[1];
|
||||
headerPtr++;
|
||||
*headerPtr = maskKey[2];
|
||||
headerPtr++;
|
||||
*headerPtr = maskKey[3];
|
||||
headerPtr++;
|
||||
}
|
||||
return headerSize;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param client WSclient_t * ptr to the client struct
|
||||
* @param opcode WSopcode_t
|
||||
* @param length size_t length of the payload
|
||||
* @param fin bool can be used to send data in more then one frame (set fin on the last frame)
|
||||
* @return true if ok
|
||||
*/
|
||||
bool WebSockets::sendFrameHeader(WSclient_t * client, WSopcode_t opcode, size_t length, bool fin) {
|
||||
uint8_t maskKey[4] = { 0x00, 0x00, 0x00, 0x00 };
|
||||
uint8_t buffer[WEBSOCKETS_MAX_HEADER_SIZE] = { 0 };
|
||||
|
||||
uint8_t headerSize = createHeader(&buffer[0], opcode, length, client->cIsClient, maskKey, fin);
|
||||
|
||||
if(write(client, &buffer[0], headerSize) != headerSize) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param client WSclient_t * ptr to the client struct
|
||||
* @param opcode WSopcode_t
|
||||
* @param payload uint8_t * ptr to the payload
|
||||
* @param length size_t length of the payload
|
||||
* @param fin bool can be used to send data in more then one frame (set fin on the last frame)
|
||||
* @param headerToPayload bool set true if the payload has reserved 14 Byte at the beginning to dynamically add the Header (payload neet to be in RAM!)
|
||||
* @return true if ok
|
||||
*/
|
||||
bool WebSockets::sendFrame(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin, bool headerToPayload) {
|
||||
if(client->tcp && !client->tcp->connected()) {
|
||||
DEBUG_WEBSOCKETS("[WS][%d][sendFrame] not Connected!?\n", client->num);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(client->status != WSC_CONNECTED) {
|
||||
DEBUG_WEBSOCKETS("[WS][%d][sendFrame] not in WSC_CONNECTED state!?\n", client->num);
|
||||
return false;
|
||||
}
|
||||
|
||||
DEBUG_WEBSOCKETS("[WS][%d][sendFrame] ------- send message frame -------\n", client->num);
|
||||
DEBUG_WEBSOCKETS("[WS][%d][sendFrame] fin: %u opCode: %u mask: %u length: %u headerToPayload: %u\n", client->num, fin, opcode, client->cIsClient, length, headerToPayload);
|
||||
|
||||
if(opcode == WSop_text) {
|
||||
DEBUG_WEBSOCKETS("[WS][%d][sendFrame] text: %s\n", client->num, (payload + (headerToPayload ? 14 : 0)));
|
||||
}
|
||||
|
||||
uint8_t maskKey[4] = { 0x00, 0x00, 0x00, 0x00 };
|
||||
uint8_t buffer[WEBSOCKETS_MAX_HEADER_SIZE] = { 0 };
|
||||
|
||||
uint8_t headerSize;
|
||||
uint8_t * headerPtr;
|
||||
uint8_t * payloadPtr = payload;
|
||||
bool useInternBuffer = false;
|
||||
bool ret = true;
|
||||
|
||||
// calculate header Size
|
||||
if(length < 126) {
|
||||
headerSize = 2;
|
||||
} else if(length < 0xFFFF) {
|
||||
headerSize = 4;
|
||||
} else {
|
||||
headerSize = 10;
|
||||
}
|
||||
|
||||
if(client->cIsClient) {
|
||||
headerSize += 4;
|
||||
}
|
||||
|
||||
#ifdef WEBSOCKETS_USE_BIG_MEM
|
||||
// only for ESP since AVR has less HEAP
|
||||
// try to send data in one TCP package (only if some free Heap is there)
|
||||
if(!headerToPayload && ((length > 0) && (length < 1400)) && (GET_FREE_HEAP > 6000)) {
|
||||
DEBUG_WEBSOCKETS("[WS][%d][sendFrame] pack to one TCP package...\n", client->num);
|
||||
uint8_t * dataPtr = (uint8_t *)malloc(length + WEBSOCKETS_MAX_HEADER_SIZE);
|
||||
if(dataPtr) {
|
||||
memcpy((dataPtr + WEBSOCKETS_MAX_HEADER_SIZE), payload, length);
|
||||
headerToPayload = true;
|
||||
useInternBuffer = true;
|
||||
payloadPtr = dataPtr;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// set Header Pointer
|
||||
if(headerToPayload) {
|
||||
// calculate offset in payload
|
||||
headerPtr = (payloadPtr + (WEBSOCKETS_MAX_HEADER_SIZE - headerSize));
|
||||
} else {
|
||||
headerPtr = &buffer[0];
|
||||
}
|
||||
|
||||
if(client->cIsClient && useInternBuffer) {
|
||||
// if we use a Intern Buffer we can modify the data
|
||||
// by this fact its possible the do the masking
|
||||
for(uint8_t x = 0; x < sizeof(maskKey); x++) {
|
||||
maskKey[x] = random(0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
createHeader(headerPtr, opcode, length, client->cIsClient, maskKey, fin);
|
||||
|
||||
if(client->cIsClient && useInternBuffer) {
|
||||
uint8_t * dataMaskPtr;
|
||||
|
||||
if(headerToPayload) {
|
||||
dataMaskPtr = (payloadPtr + WEBSOCKETS_MAX_HEADER_SIZE);
|
||||
} else {
|
||||
dataMaskPtr = payloadPtr;
|
||||
}
|
||||
|
||||
for(size_t x = 0; x < length; x++) {
|
||||
dataMaskPtr[x] = (dataMaskPtr[x] ^ maskKey[x % 4]);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef NODEBUG_WEBSOCKETS
|
||||
unsigned long start = micros();
|
||||
#endif
|
||||
|
||||
if(headerToPayload) {
|
||||
// header has be added to payload
|
||||
// payload is forced to reserved 14 Byte but we may not need all based on the length and mask settings
|
||||
// offset in payload is calculatetd 14 - headerSize
|
||||
if(write(client, &payloadPtr[(WEBSOCKETS_MAX_HEADER_SIZE - headerSize)], (length + headerSize)) != (length + headerSize)) {
|
||||
ret = false;
|
||||
}
|
||||
} else {
|
||||
// send header
|
||||
if(write(client, &buffer[0], headerSize) != headerSize) {
|
||||
ret = false;
|
||||
}
|
||||
|
||||
if(payloadPtr && length > 0) {
|
||||
// send payload
|
||||
if(write(client, &payloadPtr[0], length) != length) {
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_WEBSOCKETS("[WS][%d][sendFrame] sending Frame Done (%luus).\n", client->num, (micros() - start));
|
||||
|
||||
#ifdef WEBSOCKETS_USE_BIG_MEM
|
||||
if(useInternBuffer && payloadPtr) {
|
||||
free(payloadPtr);
|
||||
}
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* callen when HTTP header is done
|
||||
* @param client WSclient_t * ptr to the client struct
|
||||
*/
|
||||
void WebSockets::headerDone(WSclient_t * client) {
|
||||
client->status = WSC_CONNECTED;
|
||||
client->cWsRXsize = 0;
|
||||
DEBUG_WEBSOCKETS("[WS][%d][headerDone] Header Handling Done.\n", client->num);
|
||||
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
|
||||
client->cHttpLine = "";
|
||||
handleWebsocket(client);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* handle the WebSocket stream
|
||||
* @param client WSclient_t * ptr to the client struct
|
||||
*/
|
||||
void WebSockets::handleWebsocket(WSclient_t * client) {
|
||||
if(client->cWsRXsize == 0) {
|
||||
handleWebsocketCb(client);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wait for
|
||||
* @param client
|
||||
* @param size
|
||||
*/
|
||||
bool WebSockets::handleWebsocketWaitFor(WSclient_t * client, size_t size) {
|
||||
if(!client->tcp || !client->tcp->connected()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(size > WEBSOCKETS_MAX_HEADER_SIZE) {
|
||||
DEBUG_WEBSOCKETS("[WS][%d][handleWebsocketWaitFor] size: %d too big!\n", client->num, size);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(client->cWsRXsize >= size) {
|
||||
return true;
|
||||
}
|
||||
|
||||
DEBUG_WEBSOCKETS("[WS][%d][handleWebsocketWaitFor] size: %d cWsRXsize: %d\n", client->num, size, client->cWsRXsize);
|
||||
readCb(client, &client->cWsHeader[client->cWsRXsize], (size - client->cWsRXsize), std::bind([](WebSockets * server, size_t size, WSclient_t * client, bool ok) {
|
||||
DEBUG_WEBSOCKETS("[WS][%d][handleWebsocketWaitFor][readCb] size: %d ok: %d\n", client->num, size, ok);
|
||||
if(ok) {
|
||||
client->cWsRXsize = size;
|
||||
server->handleWebsocketCb(client);
|
||||
} else {
|
||||
DEBUG_WEBSOCKETS("[WS][%d][readCb] failed.\n", client->num);
|
||||
client->cWsRXsize = 0;
|
||||
// timeout or error
|
||||
server->clientDisconnect(client, 1002);
|
||||
}
|
||||
},
|
||||
this, size, std::placeholders::_1, std::placeholders::_2));
|
||||
return false;
|
||||
}
|
||||
|
||||
void WebSockets::handleWebsocketCb(WSclient_t * client) {
|
||||
if(!client->tcp || !client->tcp->connected()) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t * buffer = client->cWsHeader;
|
||||
|
||||
WSMessageHeader_t * header = &client->cWsHeaderDecode;
|
||||
uint8_t * payload = NULL;
|
||||
|
||||
uint8_t headerLen = 2;
|
||||
|
||||
if(!handleWebsocketWaitFor(client, headerLen)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// split first 2 bytes in the data
|
||||
header->fin = ((*buffer >> 7) & 0x01);
|
||||
header->rsv1 = ((*buffer >> 6) & 0x01);
|
||||
header->rsv2 = ((*buffer >> 5) & 0x01);
|
||||
header->rsv3 = ((*buffer >> 4) & 0x01);
|
||||
header->opCode = (WSopcode_t)(*buffer & 0x0F);
|
||||
buffer++;
|
||||
|
||||
header->mask = ((*buffer >> 7) & 0x01);
|
||||
header->payloadLen = (WSopcode_t)(*buffer & 0x7F);
|
||||
buffer++;
|
||||
|
||||
if(header->payloadLen == 126) {
|
||||
headerLen += 2;
|
||||
if(!handleWebsocketWaitFor(client, headerLen)) {
|
||||
return;
|
||||
}
|
||||
header->payloadLen = buffer[0] << 8 | buffer[1];
|
||||
buffer += 2;
|
||||
} else if(header->payloadLen == 127) {
|
||||
headerLen += 8;
|
||||
// read 64bit integer as length
|
||||
if(!handleWebsocketWaitFor(client, headerLen)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(buffer[0] != 0 || buffer[1] != 0 || buffer[2] != 0 || buffer[3] != 0) {
|
||||
// really too big!
|
||||
header->payloadLen = 0xFFFFFFFF;
|
||||
} else {
|
||||
header->payloadLen = buffer[4] << 24 | buffer[5] << 16 | buffer[6] << 8 | buffer[7];
|
||||
}
|
||||
buffer += 8;
|
||||
}
|
||||
|
||||
DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] ------- read massage frame -------\n", client->num);
|
||||
DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] fin: %u rsv1: %u rsv2: %u rsv3 %u opCode: %u\n", client->num, header->fin, header->rsv1, header->rsv2, header->rsv3, header->opCode);
|
||||
DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] mask: %u payloadLen: %u\n", client->num, header->mask, header->payloadLen);
|
||||
|
||||
if(header->payloadLen > WEBSOCKETS_MAX_DATA_SIZE) {
|
||||
DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] payload too big! (%u)\n", client->num, header->payloadLen);
|
||||
clientDisconnect(client, 1009);
|
||||
return;
|
||||
}
|
||||
|
||||
if(header->mask) {
|
||||
headerLen += 4;
|
||||
if(!handleWebsocketWaitFor(client, headerLen)) {
|
||||
return;
|
||||
}
|
||||
header->maskKey = buffer;
|
||||
buffer += 4;
|
||||
}
|
||||
|
||||
if(header->payloadLen > 0) {
|
||||
// if text data we need one more
|
||||
payload = (uint8_t *)malloc(header->payloadLen + 1);
|
||||
|
||||
if(!payload) {
|
||||
DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] to less memory to handle payload %d!\n", client->num, header->payloadLen);
|
||||
clientDisconnect(client, 1011);
|
||||
return;
|
||||
}
|
||||
readCb(client, payload, header->payloadLen, std::bind(&WebSockets::handleWebsocketPayloadCb, this, std::placeholders::_1, std::placeholders::_2, payload));
|
||||
} else {
|
||||
handleWebsocketPayloadCb(client, true, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void WebSockets::handleWebsocketPayloadCb(WSclient_t * client, bool ok, uint8_t * payload) {
|
||||
WSMessageHeader_t * header = &client->cWsHeaderDecode;
|
||||
if(ok) {
|
||||
if(header->payloadLen > 0) {
|
||||
payload[header->payloadLen] = 0x00;
|
||||
|
||||
if(header->mask) {
|
||||
// decode XOR
|
||||
for(size_t i = 0; i < header->payloadLen; i++) {
|
||||
payload[i] = (payload[i] ^ header->maskKey[i % 4]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch(header->opCode) {
|
||||
case WSop_text:
|
||||
DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] text: %s\n", client->num, payload);
|
||||
// no break here!
|
||||
case WSop_binary:
|
||||
case WSop_continuation:
|
||||
messageReceived(client, header->opCode, payload, header->payloadLen, header->fin);
|
||||
break;
|
||||
case WSop_ping:
|
||||
// send pong back
|
||||
DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] ping received (%s)\n", client->num, payload ? (const char *)payload : "");
|
||||
sendFrame(client, WSop_pong, payload, header->payloadLen);
|
||||
messageReceived(client, header->opCode, payload, header->payloadLen, header->fin);
|
||||
break;
|
||||
case WSop_pong:
|
||||
DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] get pong (%s)\n", client->num, payload ? (const char *)payload : "");
|
||||
client->pongReceived = true;
|
||||
messageReceived(client, header->opCode, payload, header->payloadLen, header->fin);
|
||||
break;
|
||||
case WSop_close: {
|
||||
#ifndef NODEBUG_WEBSOCKETS
|
||||
uint16_t reasonCode = 1000;
|
||||
if(header->payloadLen >= 2) {
|
||||
reasonCode = payload[0] << 8 | payload[1];
|
||||
}
|
||||
#endif
|
||||
DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] get ask for close. Code: %d\n", client->num, reasonCode);
|
||||
if(header->payloadLen > 2) {
|
||||
DEBUG_WEBSOCKETS(" (%s)\n", (payload + 2));
|
||||
} else {
|
||||
DEBUG_WEBSOCKETS("\n");
|
||||
}
|
||||
clientDisconnect(client, 1000);
|
||||
} break;
|
||||
default:
|
||||
DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] got unknown opcode: %d\n", client->num, header->opCode);
|
||||
clientDisconnect(client, 1002);
|
||||
break;
|
||||
}
|
||||
|
||||
if(payload) {
|
||||
free(payload);
|
||||
}
|
||||
|
||||
// reset input
|
||||
client->cWsRXsize = 0;
|
||||
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
|
||||
// register callback for next message
|
||||
handleWebsocketWaitFor(client, 2);
|
||||
#endif
|
||||
|
||||
} else {
|
||||
DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] missing data!\n", client->num);
|
||||
free(payload);
|
||||
clientDisconnect(client, 1002);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* generate the key for Sec-WebSocket-Accept
|
||||
* @param clientKey String
|
||||
* @return String Accept Key
|
||||
*/
|
||||
String WebSockets::acceptKey(String & clientKey) {
|
||||
uint8_t sha1HashBin[20] = { 0 };
|
||||
#ifdef ESP8266
|
||||
sha1(clientKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11", &sha1HashBin[0]);
|
||||
#elif defined(ESP32)
|
||||
String data = clientKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
||||
esp_sha(SHA1, (unsigned char *)data.c_str(), data.length(), &sha1HashBin[0]);
|
||||
#else
|
||||
clientKey += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
||||
SHA1_CTX ctx;
|
||||
SHA1Init(&ctx);
|
||||
SHA1Update(&ctx, (const unsigned char *)clientKey.c_str(), clientKey.length());
|
||||
SHA1Final(&sha1HashBin[0], &ctx);
|
||||
#endif
|
||||
|
||||
String key = base64_encode(sha1HashBin, 20);
|
||||
key.trim();
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* base64_encode
|
||||
* @param data uint8_t *
|
||||
* @param length size_t
|
||||
* @return base64 encoded String
|
||||
*/
|
||||
String WebSockets::base64_encode(uint8_t * data, size_t length) {
|
||||
size_t size = ((length * 1.6f) + 1);
|
||||
char * buffer = (char *)malloc(size);
|
||||
if(buffer) {
|
||||
base64_encodestate _state;
|
||||
base64_init_encodestate(&_state);
|
||||
int len = base64_encode_block((const char *)&data[0], length, &buffer[0], &_state);
|
||||
len = base64_encode_blockend((buffer + len), &_state);
|
||||
|
||||
String base64 = String(buffer);
|
||||
free(buffer);
|
||||
return base64;
|
||||
}
|
||||
return String("-FAIL-");
|
||||
}
|
||||
|
||||
/**
|
||||
* read x byte from tcp or get timeout
|
||||
* @param client WSclient_t *
|
||||
* @param out uint8_t * data buffer
|
||||
* @param n size_t byte count
|
||||
* @return true if ok
|
||||
*/
|
||||
bool WebSockets::readCb(WSclient_t * client, uint8_t * out, size_t n, WSreadWaitCb cb) {
|
||||
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
|
||||
if(!client->tcp || !client->tcp->connected()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
client->tcp->readBytes(out, n, std::bind([](WSclient_t * client, bool ok, WSreadWaitCb cb) {
|
||||
if(cb) {
|
||||
cb(client, ok);
|
||||
}
|
||||
},
|
||||
client, std::placeholders::_1, cb));
|
||||
|
||||
#else
|
||||
unsigned long t = millis();
|
||||
ssize_t len;
|
||||
DEBUG_WEBSOCKETS("[readCb] n: %zu t: %lu\n", n, t);
|
||||
while(n > 0) {
|
||||
if(client->tcp == NULL) {
|
||||
DEBUG_WEBSOCKETS("[readCb] tcp is null!\n");
|
||||
if(cb) {
|
||||
cb(client, false);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!client->tcp->connected()) {
|
||||
DEBUG_WEBSOCKETS("[readCb] not connected!\n");
|
||||
if(cb) {
|
||||
cb(client, false);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if((millis() - t) > WEBSOCKETS_TCP_TIMEOUT) {
|
||||
DEBUG_WEBSOCKETS("[readCb] receive TIMEOUT! %lu\n", (millis() - t));
|
||||
if(cb) {
|
||||
cb(client, false);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!client->tcp->available()) {
|
||||
WEBSOCKETS_YIELD_MORE();
|
||||
continue;
|
||||
}
|
||||
|
||||
len = client->tcp->read((uint8_t *)out, n);
|
||||
if(len > 0) {
|
||||
t = millis();
|
||||
out += len;
|
||||
n -= len;
|
||||
// DEBUG_WEBSOCKETS("Receive %d left %d!\n", len, n);
|
||||
} else {
|
||||
// DEBUG_WEBSOCKETS("Receive %d left %d!\n", len, n);
|
||||
}
|
||||
if(n > 0) {
|
||||
WEBSOCKETS_YIELD();
|
||||
}
|
||||
}
|
||||
if(cb) {
|
||||
cb(client, true);
|
||||
}
|
||||
WEBSOCKETS_YIELD();
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* write x byte to tcp or get timeout
|
||||
* @param client WSclient_t *
|
||||
* @param out uint8_t * data buffer
|
||||
* @param n size_t byte count
|
||||
* @return bytes send
|
||||
*/
|
||||
size_t WebSockets::write(WSclient_t * client, uint8_t * out, size_t n) {
|
||||
if(out == NULL)
|
||||
return 0;
|
||||
if(client == NULL)
|
||||
return 0;
|
||||
unsigned long t = millis();
|
||||
size_t len = 0;
|
||||
size_t total = 0;
|
||||
DEBUG_WEBSOCKETS("[write] n: %zu t: %lu\n", n, t);
|
||||
while(n > 0) {
|
||||
if(client->tcp == NULL) {
|
||||
DEBUG_WEBSOCKETS("[write] tcp is null!\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if(!client->tcp->connected()) {
|
||||
DEBUG_WEBSOCKETS("[write] not connected!\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if((millis() - t) > WEBSOCKETS_TCP_TIMEOUT) {
|
||||
DEBUG_WEBSOCKETS("[write] write TIMEOUT! %lu\n", (millis() - t));
|
||||
break;
|
||||
}
|
||||
|
||||
len = client->tcp->write((const uint8_t *)out, n);
|
||||
if(len) {
|
||||
t = millis();
|
||||
out += len;
|
||||
n -= len;
|
||||
total += len;
|
||||
// DEBUG_WEBSOCKETS("write %d left %d!\n", len, n);
|
||||
} else {
|
||||
DEBUG_WEBSOCKETS("WS write %d failed left %d!\n", len, n);
|
||||
}
|
||||
if(n > 0) {
|
||||
WEBSOCKETS_YIELD();
|
||||
}
|
||||
}
|
||||
WEBSOCKETS_YIELD();
|
||||
return total;
|
||||
}
|
||||
|
||||
size_t WebSockets::write(WSclient_t * client, const char * out) {
|
||||
if(client == NULL)
|
||||
return 0;
|
||||
if(out == NULL)
|
||||
return 0;
|
||||
return write(client, (uint8_t *)out, strlen(out));
|
||||
}
|
||||
|
||||
/**
|
||||
* enable ping/pong heartbeat process
|
||||
* @param client WSclient_t *
|
||||
* @param pingInterval uint32_t how often ping will be sent
|
||||
* @param pongTimeout uint32_t millis after which pong should timout if not received
|
||||
* @param disconnectTimeoutCount uint8_t how many timeouts before disconnect, 0=> do not disconnect
|
||||
*/
|
||||
void WebSockets::enableHeartbeat(WSclient_t * client, uint32_t pingInterval, uint32_t pongTimeout, uint8_t disconnectTimeoutCount) {
|
||||
if(client == NULL)
|
||||
return;
|
||||
client->pingInterval = pingInterval;
|
||||
client->pongTimeout = pongTimeout;
|
||||
client->disconnectTimeoutCount = disconnectTimeoutCount;
|
||||
client->pongReceived = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* handle ping/pong heartbeat timeout process
|
||||
* @param client WSclient_t *
|
||||
*/
|
||||
void WebSockets::handleHBTimeout(WSclient_t * client) {
|
||||
if(client->pingInterval) { // if heartbeat is enabled
|
||||
uint32_t pi = millis() - client->lastPing;
|
||||
|
||||
if(client->pongReceived) {
|
||||
client->pongTimeoutCount = 0;
|
||||
} else {
|
||||
if(pi > client->pongTimeout) { // pong not received in time
|
||||
client->pongTimeoutCount++;
|
||||
client->lastPing = millis() - client->pingInterval - 500; // force ping on the next run
|
||||
|
||||
DEBUG_WEBSOCKETS("[HBtimeout] pong TIMEOUT! lp=%d millis=%d pi=%d count=%d\n", client->lastPing, millis(), pi, client->pongTimeoutCount);
|
||||
|
||||
if(client->disconnectTimeoutCount && client->pongTimeoutCount >= client->disconnectTimeoutCount) {
|
||||
DEBUG_WEBSOCKETS("[HBtimeout] count=%d, DISCONNECTING\n", client->pongTimeoutCount);
|
||||
clientDisconnect(client);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
367
lib/WebSockets/src/WebSockets.h
Normal file
367
lib/WebSockets/src/WebSockets.h
Normal file
@@ -0,0 +1,367 @@
|
||||
/**
|
||||
* @file WebSockets.h
|
||||
* @date 20.05.2015
|
||||
* @author Markus Sattler
|
||||
*
|
||||
* Copyright (c) 2015 Markus Sattler. All rights reserved.
|
||||
* This file is part of the WebSockets for Arduino.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef WEBSOCKETS_H_
|
||||
#define WEBSOCKETS_H_
|
||||
|
||||
#ifdef STM32_DEVICE
|
||||
#include <application.h>
|
||||
#define bit(b) (1UL << (b)) // Taken directly from Arduino.h
|
||||
#else
|
||||
#include <Arduino.h>
|
||||
#include <IPAddress.h>
|
||||
#endif
|
||||
|
||||
#ifdef ARDUINO_ARCH_AVR
|
||||
#error Version 2.x.x currently does not support Arduino with AVR since there is no support for std namespace of c++.
|
||||
#error Use Version 1.x.x. (ATmega branch)
|
||||
#else
|
||||
#include <functional>
|
||||
#endif
|
||||
|
||||
#include "WebSocketsVersion.h"
|
||||
|
||||
#ifndef NODEBUG_WEBSOCKETS
|
||||
#ifdef DEBUG_ESP_PORT
|
||||
#define DEBUG_WEBSOCKETS(...) \
|
||||
{ \
|
||||
DEBUG_ESP_PORT.printf(__VA_ARGS__); \
|
||||
DEBUG_ESP_PORT.flush(); \
|
||||
}
|
||||
#else
|
||||
//#define DEBUG_WEBSOCKETS(...) os_printf( __VA_ARGS__ )
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef DEBUG_WEBSOCKETS
|
||||
#define DEBUG_WEBSOCKETS(...)
|
||||
#ifndef NODEBUG_WEBSOCKETS
|
||||
#define NODEBUG_WEBSOCKETS
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(ESP8266) || defined(ESP32)
|
||||
|
||||
#define WEBSOCKETS_MAX_DATA_SIZE (15 * 1024)
|
||||
#define WEBSOCKETS_USE_BIG_MEM
|
||||
#define GET_FREE_HEAP ESP.getFreeHeap()
|
||||
// moves all Header strings to Flash (~300 Byte)
|
||||
//#define WEBSOCKETS_SAVE_RAM
|
||||
|
||||
#if defined(ESP8266)
|
||||
#define WEBSOCKETS_YIELD() delay(0)
|
||||
#define WEBSOCKETS_YIELD_MORE() delay(1)
|
||||
#elif defined(ESP32)
|
||||
#define WEBSOCKETS_YIELD() yield()
|
||||
#define WEBSOCKETS_YIELD_MORE() delay(1)
|
||||
#endif
|
||||
|
||||
#elif defined(STM32_DEVICE)
|
||||
|
||||
#define WEBSOCKETS_MAX_DATA_SIZE (15 * 1024)
|
||||
#define WEBSOCKETS_USE_BIG_MEM
|
||||
#define GET_FREE_HEAP System.freeMemory()
|
||||
#define WEBSOCKETS_YIELD()
|
||||
#define WEBSOCKETS_YIELD_MORE()
|
||||
#else
|
||||
|
||||
// atmega328p has only 2KB ram!
|
||||
#define WEBSOCKETS_MAX_DATA_SIZE (1024)
|
||||
// moves all Header strings to Flash
|
||||
#define WEBSOCKETS_SAVE_RAM
|
||||
#define WEBSOCKETS_YIELD()
|
||||
#define WEBSOCKETS_YIELD_MORE()
|
||||
#endif
|
||||
|
||||
#define WEBSOCKETS_TCP_TIMEOUT (5000)
|
||||
|
||||
#define NETWORK_ESP8266_ASYNC (0)
|
||||
#define NETWORK_ESP8266 (1)
|
||||
#define NETWORK_W5100 (2)
|
||||
#define NETWORK_ENC28J60 (3)
|
||||
#define NETWORK_ESP32 (4)
|
||||
#define NETWORK_ESP32_ETH (5)
|
||||
|
||||
// max size of the WS Message Header
|
||||
#define WEBSOCKETS_MAX_HEADER_SIZE (14)
|
||||
|
||||
#if !defined(WEBSOCKETS_NETWORK_TYPE)
|
||||
// select Network type based
|
||||
#if defined(ESP8266) || defined(ESP31B)
|
||||
#define WEBSOCKETS_NETWORK_TYPE NETWORK_ESP8266
|
||||
//#define WEBSOCKETS_NETWORK_TYPE NETWORK_ESP8266_ASYNC
|
||||
//#define WEBSOCKETS_NETWORK_TYPE NETWORK_W5100
|
||||
|
||||
#elif defined(ESP32)
|
||||
#define WEBSOCKETS_NETWORK_TYPE NETWORK_ESP32
|
||||
//#define WEBSOCKETS_NETWORK_TYPE NETWORK_ESP32_ETH
|
||||
#else
|
||||
#define WEBSOCKETS_NETWORK_TYPE NETWORK_W5100
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Includes and defined based on Network Type
|
||||
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
|
||||
|
||||
// Note:
|
||||
// No SSL/WSS support for client in Async mode
|
||||
// TLS lib need a sync interface!
|
||||
|
||||
#if defined(ESP8266)
|
||||
#include <ESP8266WiFi.h>
|
||||
#elif defined(ESP32)
|
||||
#include <WiFi.h>
|
||||
#include <WiFiClientSecure.h>
|
||||
#define SSL_AXTLS
|
||||
#elif defined(ESP31B)
|
||||
#include <ESP31BWiFi.h>
|
||||
#else
|
||||
#error "network type ESP8266 ASYNC only possible on the ESP mcu!"
|
||||
#endif
|
||||
|
||||
#include <ESPAsyncTCP.h>
|
||||
#include <ESPAsyncTCPbuffer.h>
|
||||
#define WEBSOCKETS_NETWORK_CLASS AsyncTCPbuffer
|
||||
#define WEBSOCKETS_NETWORK_SERVER_CLASS AsyncServer
|
||||
|
||||
#elif(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
||||
|
||||
#if !defined(ESP8266) && !defined(ESP31B)
|
||||
#error "network type ESP8266 only possible on the ESP mcu!"
|
||||
#endif
|
||||
|
||||
#ifdef ESP8266
|
||||
#include <ESP8266WiFi.h>
|
||||
#if defined(wificlientbearssl_h) && !defined(USING_AXTLS) && !defined(wificlientsecure_h)
|
||||
#define SSL_BARESSL
|
||||
#else
|
||||
#define SSL_AXTLS
|
||||
#endif
|
||||
#else
|
||||
#include <ESP31BWiFi.h>
|
||||
#endif
|
||||
#define WEBSOCKETS_NETWORK_CLASS WiFiClient
|
||||
#define WEBSOCKETS_NETWORK_SSL_CLASS WiFiClientSecure
|
||||
#define WEBSOCKETS_NETWORK_SERVER_CLASS WiFiServer
|
||||
|
||||
#elif(WEBSOCKETS_NETWORK_TYPE == NETWORK_W5100)
|
||||
|
||||
#ifdef STM32_DEVICE
|
||||
#define WEBSOCKETS_NETWORK_CLASS TCPClient
|
||||
#define WEBSOCKETS_NETWORK_SERVER_CLASS TCPServer
|
||||
#else
|
||||
#include <Ethernet.h>
|
||||
#include <SPI.h>
|
||||
#define WEBSOCKETS_NETWORK_CLASS EthernetClient
|
||||
#define WEBSOCKETS_NETWORK_SERVER_CLASS EthernetServer
|
||||
#endif
|
||||
|
||||
#elif(WEBSOCKETS_NETWORK_TYPE == NETWORK_ENC28J60)
|
||||
|
||||
#include <UIPEthernet.h>
|
||||
#define WEBSOCKETS_NETWORK_CLASS UIPClient
|
||||
#define WEBSOCKETS_NETWORK_SERVER_CLASS UIPServer
|
||||
|
||||
#elif(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
|
||||
|
||||
#include <WiFi.h>
|
||||
#include <WiFiClientSecure.h>
|
||||
#define SSL_AXTLS
|
||||
#define WEBSOCKETS_NETWORK_CLASS WiFiClient
|
||||
#define WEBSOCKETS_NETWORK_SSL_CLASS WiFiClientSecure
|
||||
#define WEBSOCKETS_NETWORK_SERVER_CLASS WiFiServer
|
||||
|
||||
#elif(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32_ETH)
|
||||
|
||||
#include <ETH.h>
|
||||
#define WEBSOCKETS_NETWORK_CLASS WiFiClient
|
||||
#define WEBSOCKETS_NETWORK_SERVER_CLASS WiFiServer
|
||||
|
||||
#else
|
||||
#error "no network type selected!"
|
||||
#endif
|
||||
|
||||
#ifdef WEBSOCKETS_NETWORK_SSL_CLASS
|
||||
#define HAS_SSL
|
||||
#endif
|
||||
|
||||
// moves all Header strings to Flash (~300 Byte)
|
||||
#ifdef WEBSOCKETS_SAVE_RAM
|
||||
#define WEBSOCKETS_STRING(var) F(var)
|
||||
#else
|
||||
#define WEBSOCKETS_STRING(var) var
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
WSC_NOT_CONNECTED,
|
||||
WSC_HEADER,
|
||||
WSC_BODY,
|
||||
WSC_CONNECTED
|
||||
} WSclientsStatus_t;
|
||||
|
||||
typedef enum {
|
||||
WStype_ERROR,
|
||||
WStype_DISCONNECTED,
|
||||
WStype_CONNECTED,
|
||||
WStype_TEXT,
|
||||
WStype_BIN,
|
||||
WStype_FRAGMENT_TEXT_START,
|
||||
WStype_FRAGMENT_BIN_START,
|
||||
WStype_FRAGMENT,
|
||||
WStype_FRAGMENT_FIN,
|
||||
WStype_PING,
|
||||
WStype_PONG,
|
||||
} WStype_t;
|
||||
|
||||
typedef enum {
|
||||
WSop_continuation = 0x00, ///< %x0 denotes a continuation frame
|
||||
WSop_text = 0x01, ///< %x1 denotes a text frame
|
||||
WSop_binary = 0x02, ///< %x2 denotes a binary frame
|
||||
///< %x3-7 are reserved for further non-control frames
|
||||
WSop_close = 0x08, ///< %x8 denotes a connection close
|
||||
WSop_ping = 0x09, ///< %x9 denotes a ping
|
||||
WSop_pong = 0x0A ///< %xA denotes a pong
|
||||
///< %xB-F are reserved for further control frames
|
||||
} WSopcode_t;
|
||||
|
||||
typedef struct {
|
||||
bool fin;
|
||||
bool rsv1;
|
||||
bool rsv2;
|
||||
bool rsv3;
|
||||
|
||||
WSopcode_t opCode;
|
||||
bool mask;
|
||||
|
||||
size_t payloadLen;
|
||||
|
||||
uint8_t * maskKey;
|
||||
} WSMessageHeader_t;
|
||||
|
||||
typedef struct {
|
||||
void init(uint8_t num,
|
||||
uint32_t pingInterval,
|
||||
uint32_t pongTimeout,
|
||||
uint8_t disconnectTimeoutCount) {
|
||||
this->num = num;
|
||||
this->pingInterval = pingInterval;
|
||||
this->pongTimeout = pongTimeout;
|
||||
this->disconnectTimeoutCount = disconnectTimeoutCount;
|
||||
}
|
||||
|
||||
uint8_t num = 0; ///< connection number
|
||||
|
||||
WSclientsStatus_t status = WSC_NOT_CONNECTED;
|
||||
|
||||
WEBSOCKETS_NETWORK_CLASS * tcp = nullptr;
|
||||
|
||||
bool isSocketIO = false; ///< client for socket.io server
|
||||
|
||||
#if defined(HAS_SSL)
|
||||
bool isSSL = false; ///< run in ssl mode
|
||||
WEBSOCKETS_NETWORK_SSL_CLASS * ssl;
|
||||
#endif
|
||||
|
||||
String cUrl; ///< http url
|
||||
uint16_t cCode = 0; ///< http code
|
||||
|
||||
bool cIsClient = false; ///< will be used for masking
|
||||
bool cIsUpgrade = false; ///< Connection == Upgrade
|
||||
bool cIsWebsocket = false; ///< Upgrade == websocket
|
||||
|
||||
String cSessionId; ///< client Set-Cookie (session id)
|
||||
String cKey; ///< client Sec-WebSocket-Key
|
||||
String cAccept; ///< client Sec-WebSocket-Accept
|
||||
String cProtocol; ///< client Sec-WebSocket-Protocol
|
||||
String cExtensions; ///< client Sec-WebSocket-Extensions
|
||||
uint16_t cVersion = 0; ///< client Sec-WebSocket-Version
|
||||
|
||||
uint8_t cWsRXsize = 0; ///< State of the RX
|
||||
uint8_t cWsHeader[WEBSOCKETS_MAX_HEADER_SIZE]; ///< RX WS Message buffer
|
||||
WSMessageHeader_t cWsHeaderDecode;
|
||||
|
||||
String base64Authorization; ///< Base64 encoded Auth request
|
||||
String plainAuthorization; ///< Base64 encoded Auth request
|
||||
|
||||
String extraHeaders;
|
||||
|
||||
bool cHttpHeadersValid = false; ///< non-websocket http header validity indicator
|
||||
size_t cMandatoryHeadersCount; ///< non-websocket mandatory http headers present count
|
||||
|
||||
bool pongReceived = false;
|
||||
uint32_t pingInterval = 0; // how often ping will be sent, 0 means "heartbeat is not active"
|
||||
uint32_t lastPing = 0; // millis when last pong has been received
|
||||
uint32_t pongTimeout = 0; // interval in millis after which pong is considered to timeout
|
||||
uint8_t disconnectTimeoutCount = 0; // after how many subsequent pong timeouts discconnect will happen, 0 means "do not disconnect"
|
||||
uint8_t pongTimeoutCount = 0; // current pong timeout count
|
||||
|
||||
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
|
||||
String cHttpLine; ///< HTTP header lines
|
||||
#endif
|
||||
|
||||
} WSclient_t;
|
||||
|
||||
class WebSockets {
|
||||
protected:
|
||||
#ifdef __AVR__
|
||||
typedef void (*WSreadWaitCb)(WSclient_t * client, bool ok);
|
||||
#else
|
||||
typedef std::function<void(WSclient_t * client, bool ok)> WSreadWaitCb;
|
||||
#endif
|
||||
|
||||
virtual void clientDisconnect(WSclient_t * client) = 0;
|
||||
virtual bool clientIsConnected(WSclient_t * client) = 0;
|
||||
|
||||
void clientDisconnect(WSclient_t * client, uint16_t code, char * reason = NULL, size_t reasonLen = 0);
|
||||
|
||||
virtual void messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin) = 0;
|
||||
|
||||
uint8_t createHeader(uint8_t * buf, WSopcode_t opcode, size_t length, bool mask, uint8_t maskKey[4], bool fin);
|
||||
bool sendFrameHeader(WSclient_t * client, WSopcode_t opcode, size_t length = 0, bool fin = true);
|
||||
bool sendFrame(WSclient_t * client, WSopcode_t opcode, uint8_t * payload = NULL, size_t length = 0, bool fin = true, bool headerToPayload = false);
|
||||
|
||||
void headerDone(WSclient_t * client);
|
||||
|
||||
void handleWebsocket(WSclient_t * client);
|
||||
|
||||
bool handleWebsocketWaitFor(WSclient_t * client, size_t size);
|
||||
void handleWebsocketCb(WSclient_t * client);
|
||||
void handleWebsocketPayloadCb(WSclient_t * client, bool ok, uint8_t * payload);
|
||||
|
||||
String acceptKey(String & clientKey);
|
||||
String base64_encode(uint8_t * data, size_t length);
|
||||
|
||||
bool readCb(WSclient_t * client, uint8_t * out, size_t n, WSreadWaitCb cb);
|
||||
virtual size_t write(WSclient_t * client, uint8_t * out, size_t n);
|
||||
size_t write(WSclient_t * client, const char * out);
|
||||
|
||||
void enableHeartbeat(WSclient_t * client, uint32_t pingInterval, uint32_t pongTimeout, uint8_t disconnectTimeoutCount);
|
||||
void handleHBTimeout(WSclient_t * client);
|
||||
};
|
||||
|
||||
#ifndef UNUSED
|
||||
#define UNUSED(var) (void)(var)
|
||||
#endif
|
||||
#endif /* WEBSOCKETS_H_ */
|
||||
80
lib/WebSockets/src/WebSockets4WebServer.h
Normal file
80
lib/WebSockets/src/WebSockets4WebServer.h
Normal file
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* @file WebSocketsServer.cpp
|
||||
* @date 28.10.2020
|
||||
* @author Markus Sattler & esp8266/arduino community
|
||||
*
|
||||
* Copyright (c) 2020 Markus Sattler. All rights reserved.
|
||||
* This file is part of the WebSockets for Arduino.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __WEBSOCKETS4WEBSERVER_H
|
||||
#define __WEBSOCKETS4WEBSERVER_H
|
||||
|
||||
#include <WebSocketsServer.h>
|
||||
#include <ESP8266WebServer.h>
|
||||
|
||||
#if WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266 && WEBSERVER_HAS_HOOK
|
||||
|
||||
class WebSockets4WebServer : public WebSocketsServerCore {
|
||||
public:
|
||||
WebSockets4WebServer(const String & origin = "", const String & protocol = "arduino")
|
||||
: WebSocketsServerCore(origin, protocol) {
|
||||
begin();
|
||||
}
|
||||
|
||||
ESP8266WebServer::HookFunction hookForWebserver(const String & wsRootDir, WebSocketServerEvent event) {
|
||||
onEvent(event);
|
||||
|
||||
return [&, wsRootDir](const String & method, const String & url, WiFiClient * tcpClient, ESP8266WebServer::ContentTypeFunction contentType) {
|
||||
(void)contentType;
|
||||
|
||||
if(!(method == "GET" && url.indexOf(wsRootDir) == 0)) {
|
||||
return ESP8266WebServer::CLIENT_REQUEST_CAN_CONTINUE;
|
||||
}
|
||||
|
||||
// allocate a WiFiClient copy (like in WebSocketsServer::handleNewClients())
|
||||
WEBSOCKETS_NETWORK_CLASS * newTcpClient = new WEBSOCKETS_NETWORK_CLASS(*tcpClient);
|
||||
|
||||
// Then initialize a new WSclient_t (like in WebSocketsServer::handleNewClient())
|
||||
WSclient_t * client = handleNewClient(newTcpClient);
|
||||
|
||||
if(client) {
|
||||
// give "GET <url>"
|
||||
String headerLine;
|
||||
headerLine.reserve(url.length() + 5);
|
||||
headerLine = "GET ";
|
||||
headerLine += url;
|
||||
handleHeader(client, &headerLine);
|
||||
}
|
||||
|
||||
// tell webserver to not close but forget about this client
|
||||
return ESP8266WebServer::CLIENT_IS_GIVEN;
|
||||
};
|
||||
}
|
||||
};
|
||||
#else // WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266 && WEBSERVER_HAS_HOOK
|
||||
|
||||
#ifndef WEBSERVER_HAS_HOOK
|
||||
#error Your current Framework / Arduino core version does not support Webserver Hook Functions
|
||||
#else
|
||||
#error Your Hardware Platform does not support Webserver Hook Functions
|
||||
#endif
|
||||
|
||||
#endif // WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266 && WEBSERVER_HAS_HOOK
|
||||
|
||||
#endif // __WEBSOCKETS4WEBSERVER_H
|
||||
975
lib/WebSockets/src/WebSocketsClient.cpp
Normal file
975
lib/WebSockets/src/WebSocketsClient.cpp
Normal file
@@ -0,0 +1,975 @@
|
||||
/**
|
||||
* @file WebSocketsClient.cpp
|
||||
* @date 20.05.2015
|
||||
* @author Markus Sattler
|
||||
*
|
||||
* Copyright (c) 2015 Markus Sattler. All rights reserved.
|
||||
* This file is part of the WebSockets for Arduino.
|
||||
*
|
||||
* 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 "WebSockets.h"
|
||||
#include "WebSocketsClient.h"
|
||||
|
||||
WebSocketsClient::WebSocketsClient() {
|
||||
_cbEvent = NULL;
|
||||
_client.num = 0;
|
||||
_client.cIsClient = true;
|
||||
_client.extraHeaders = WEBSOCKETS_STRING("Origin: file://");
|
||||
_reconnectInterval = 500;
|
||||
_port = 0;
|
||||
_host = "";
|
||||
}
|
||||
|
||||
WebSocketsClient::~WebSocketsClient() {
|
||||
disconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
* calles to init the Websockets server
|
||||
*/
|
||||
void WebSocketsClient::begin(const char * host, uint16_t port, const char * url, const char * protocol) {
|
||||
_host = host;
|
||||
_port = port;
|
||||
#if defined(HAS_SSL)
|
||||
_fingerprint = SSL_FINGERPRINT_NULL;
|
||||
_CA_cert = NULL;
|
||||
#endif
|
||||
|
||||
_client.num = 0;
|
||||
_client.status = WSC_NOT_CONNECTED;
|
||||
_client.tcp = NULL;
|
||||
#if defined(HAS_SSL)
|
||||
_client.isSSL = false;
|
||||
_client.ssl = NULL;
|
||||
#endif
|
||||
_client.cUrl = url;
|
||||
_client.cCode = 0;
|
||||
_client.cIsUpgrade = false;
|
||||
_client.cIsWebsocket = true;
|
||||
_client.cKey = "";
|
||||
_client.cAccept = "";
|
||||
_client.cProtocol = protocol;
|
||||
_client.cExtensions = "";
|
||||
_client.cVersion = 0;
|
||||
_client.base64Authorization = "";
|
||||
_client.plainAuthorization = "";
|
||||
_client.isSocketIO = false;
|
||||
|
||||
_client.lastPing = 0;
|
||||
_client.pongReceived = false;
|
||||
_client.pongTimeoutCount = 0;
|
||||
|
||||
#ifdef ESP8266
|
||||
randomSeed(RANDOM_REG32);
|
||||
#else
|
||||
// todo find better seed
|
||||
randomSeed(millis());
|
||||
#endif
|
||||
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
|
||||
asyncConnect();
|
||||
#endif
|
||||
|
||||
_lastConnectionFail = 0;
|
||||
_lastHeaderSent = 0;
|
||||
|
||||
DEBUG_WEBSOCKETS("[WS-Client] Websocket Version: " WEBSOCKETS_VERSION "\n");
|
||||
}
|
||||
|
||||
void WebSocketsClient::begin(String host, uint16_t port, String url, String protocol) {
|
||||
begin(host.c_str(), port, url.c_str(), protocol.c_str());
|
||||
}
|
||||
|
||||
void WebSocketsClient::begin(IPAddress host, uint16_t port, const char * url, const char * protocol) {
|
||||
return begin(host.toString().c_str(), port, url, protocol);
|
||||
}
|
||||
|
||||
#if defined(HAS_SSL)
|
||||
#if defined(SSL_AXTLS)
|
||||
void WebSocketsClient::beginSSL(const char * host, uint16_t port, const char * url, const char * fingerprint, const char * protocol) {
|
||||
begin(host, port, url, protocol);
|
||||
_client.isSSL = true;
|
||||
_fingerprint = fingerprint;
|
||||
_CA_cert = NULL;
|
||||
}
|
||||
|
||||
void WebSocketsClient::beginSSL(String host, uint16_t port, String url, String fingerprint, String protocol) {
|
||||
beginSSL(host.c_str(), port, url.c_str(), fingerprint.c_str(), protocol.c_str());
|
||||
}
|
||||
|
||||
void WebSocketsClient::beginSslWithCA(const char * host, uint16_t port, const char * url, const char * CA_cert, const char * protocol) {
|
||||
begin(host, port, url, protocol);
|
||||
_client.isSSL = true;
|
||||
_fingerprint = SSL_FINGERPRINT_NULL;
|
||||
_CA_cert = CA_cert;
|
||||
}
|
||||
#else
|
||||
void WebSocketsClient::beginSSL(const char * host, uint16_t port, const char * url, const uint8_t * fingerprint, const char * protocol) {
|
||||
begin(host, port, url, protocol);
|
||||
_client.isSSL = true;
|
||||
_fingerprint = fingerprint;
|
||||
_CA_cert = NULL;
|
||||
}
|
||||
|
||||
void WebSocketsClient::beginSslWithCA(const char * host, uint16_t port, const char * url, BearSSL::X509List * CA_cert, const char * protocol) {
|
||||
begin(host, port, url, protocol);
|
||||
_client.isSSL = true;
|
||||
_fingerprint = SSL_FINGERPRINT_NULL;
|
||||
_CA_cert = CA_cert;
|
||||
}
|
||||
|
||||
void WebSocketsClient::beginSslWithCA(const char * host, uint16_t port, const char * url, const char * CA_cert, const char * protocol) {
|
||||
beginSslWithCA(host, port, url, new BearSSL::X509List(CA_cert), protocol);
|
||||
}
|
||||
|
||||
void WebSocketsClient::setSSLClientCertKey(BearSSL::X509List * clientCert, BearSSL::PrivateKey * clientPrivateKey) {
|
||||
_client_cert = clientCert;
|
||||
_client_key = clientPrivateKey;
|
||||
}
|
||||
|
||||
void WebSocketsClient::setSSLClientCertKey(const char * clientCert, const char * clientPrivateKey) {
|
||||
setSSLClientCertKey(new BearSSL::X509List(clientCert), new BearSSL::PrivateKey(clientPrivateKey));
|
||||
}
|
||||
|
||||
#endif // SSL_AXTLS
|
||||
#endif // HAS_SSL
|
||||
|
||||
void WebSocketsClient::beginSocketIO(const char * host, uint16_t port, const char * url, const char * protocol) {
|
||||
begin(host, port, url, protocol);
|
||||
_client.isSocketIO = true;
|
||||
}
|
||||
|
||||
void WebSocketsClient::beginSocketIO(String host, uint16_t port, String url, String protocol) {
|
||||
beginSocketIO(host.c_str(), port, url.c_str(), protocol.c_str());
|
||||
}
|
||||
|
||||
#if defined(HAS_SSL)
|
||||
void WebSocketsClient::beginSocketIOSSL(const char * host, uint16_t port, const char * url, const char * protocol) {
|
||||
begin(host, port, url, protocol);
|
||||
_client.isSocketIO = true;
|
||||
_client.isSSL = true;
|
||||
_fingerprint = SSL_FINGERPRINT_NULL;
|
||||
}
|
||||
|
||||
void WebSocketsClient::beginSocketIOSSL(String host, uint16_t port, String url, String protocol) {
|
||||
beginSocketIOSSL(host.c_str(), port, url.c_str(), protocol.c_str());
|
||||
}
|
||||
|
||||
#if defined(SSL_BARESSL)
|
||||
void WebSocketsClient::beginSocketIOSSLWithCA(const char * host, uint16_t port, const char * url, BearSSL::X509List * CA_cert, const char * protocol) {
|
||||
begin(host, port, url, protocol);
|
||||
_client.isSocketIO = true;
|
||||
_client.isSSL = true;
|
||||
_fingerprint = SSL_FINGERPRINT_NULL;
|
||||
_CA_cert = CA_cert;
|
||||
}
|
||||
#endif
|
||||
|
||||
void WebSocketsClient::beginSocketIOSSLWithCA(const char * host, uint16_t port, const char * url, const char * CA_cert, const char * protocol) {
|
||||
begin(host, port, url, protocol);
|
||||
_client.isSocketIO = true;
|
||||
_client.isSSL = true;
|
||||
_fingerprint = SSL_FINGERPRINT_NULL;
|
||||
#if defined(SSL_BARESSL)
|
||||
_CA_cert = new BearSSL::X509List(CA_cert);
|
||||
#else
|
||||
_CA_cert = CA_cert;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
|
||||
/**
|
||||
* called in arduino loop
|
||||
*/
|
||||
void WebSocketsClient::loop(void) {
|
||||
if(_port == 0) {
|
||||
return;
|
||||
}
|
||||
WEBSOCKETS_YIELD();
|
||||
if(!clientIsConnected(&_client)) {
|
||||
// do not flood the server
|
||||
if((millis() - _lastConnectionFail) < _reconnectInterval) {
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(HAS_SSL)
|
||||
if(_client.isSSL) {
|
||||
DEBUG_WEBSOCKETS("[WS-Client] connect wss...\n");
|
||||
if(_client.ssl) {
|
||||
delete _client.ssl;
|
||||
_client.ssl = NULL;
|
||||
_client.tcp = NULL;
|
||||
}
|
||||
_client.ssl = new WEBSOCKETS_NETWORK_SSL_CLASS();
|
||||
_client.tcp = _client.ssl;
|
||||
if(_CA_cert) {
|
||||
DEBUG_WEBSOCKETS("[WS-Client] setting CA certificate");
|
||||
#if defined(ESP32)
|
||||
_client.ssl->setCACert(_CA_cert);
|
||||
#elif defined(ESP8266) && defined(SSL_AXTLS)
|
||||
_client.ssl->setCACert((const uint8_t *)_CA_cert, strlen(_CA_cert) + 1);
|
||||
#elif defined(ESP8266) && defined(SSL_BARESSL)
|
||||
_client.ssl->setTrustAnchors(_CA_cert);
|
||||
#else
|
||||
#error setCACert not implemented
|
||||
#endif
|
||||
#if defined(ESP32)
|
||||
} else if(!SSL_FINGERPRINT_IS_SET) {
|
||||
_client.ssl->setInsecure();
|
||||
#elif defined(SSL_BARESSL)
|
||||
} else if(SSL_FINGERPRINT_IS_SET) {
|
||||
_client.ssl->setFingerprint(_fingerprint);
|
||||
} else {
|
||||
_client.ssl->setInsecure();
|
||||
}
|
||||
if(_client_cert && _client_key) {
|
||||
_client.ssl->setClientRSACert(_client_cert, _client_key);
|
||||
DEBUG_WEBSOCKETS("[WS-Client] setting client certificate and key");
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
DEBUG_WEBSOCKETS("[WS-Client] connect ws...\n");
|
||||
if(_client.tcp) {
|
||||
delete _client.tcp;
|
||||
_client.tcp = NULL;
|
||||
}
|
||||
_client.tcp = new WEBSOCKETS_NETWORK_CLASS();
|
||||
}
|
||||
#else
|
||||
_client.tcp = new WEBSOCKETS_NETWORK_CLASS();
|
||||
#endif
|
||||
|
||||
if(!_client.tcp) {
|
||||
DEBUG_WEBSOCKETS("[WS-Client] creating Network class failed!");
|
||||
return;
|
||||
}
|
||||
WEBSOCKETS_YIELD();
|
||||
#if defined(ESP32)
|
||||
if(_client.tcp->connect(_host.c_str(), _port, WEBSOCKETS_TCP_TIMEOUT)) {
|
||||
#else
|
||||
if(_client.tcp->connect(_host.c_str(), _port)) {
|
||||
#endif
|
||||
connectedCb();
|
||||
_lastConnectionFail = 0;
|
||||
} else {
|
||||
connectFailedCb();
|
||||
_lastConnectionFail = millis();
|
||||
}
|
||||
} else {
|
||||
handleClientData();
|
||||
WEBSOCKETS_YIELD();
|
||||
if(_client.status == WSC_CONNECTED) {
|
||||
handleHBPing();
|
||||
handleHBTimeout(&_client);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* set callback function
|
||||
* @param cbEvent WebSocketServerEvent
|
||||
*/
|
||||
void WebSocketsClient::onEvent(WebSocketClientEvent cbEvent) {
|
||||
_cbEvent = cbEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* send text data to client
|
||||
* @param num uint8_t client id
|
||||
* @param payload uint8_t *
|
||||
* @param length size_t
|
||||
* @param headerToPayload bool (see sendFrame for more details)
|
||||
* @return true if ok
|
||||
*/
|
||||
bool WebSocketsClient::sendTXT(uint8_t * payload, size_t length, bool headerToPayload) {
|
||||
if(length == 0) {
|
||||
length = strlen((const char *)payload);
|
||||
}
|
||||
if(clientIsConnected(&_client)) {
|
||||
return sendFrame(&_client, WSop_text, payload, length, true, headerToPayload);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WebSocketsClient::sendTXT(const uint8_t * payload, size_t length) {
|
||||
return sendTXT((uint8_t *)payload, length);
|
||||
}
|
||||
|
||||
bool WebSocketsClient::sendTXT(char * payload, size_t length, bool headerToPayload) {
|
||||
return sendTXT((uint8_t *)payload, length, headerToPayload);
|
||||
}
|
||||
|
||||
bool WebSocketsClient::sendTXT(const char * payload, size_t length) {
|
||||
return sendTXT((uint8_t *)payload, length);
|
||||
}
|
||||
|
||||
bool WebSocketsClient::sendTXT(String & payload) {
|
||||
return sendTXT((uint8_t *)payload.c_str(), payload.length());
|
||||
}
|
||||
|
||||
bool WebSocketsClient::sendTXT(char payload) {
|
||||
uint8_t buf[WEBSOCKETS_MAX_HEADER_SIZE + 2] = { 0x00 };
|
||||
buf[WEBSOCKETS_MAX_HEADER_SIZE] = payload;
|
||||
return sendTXT(buf, 1, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* send binary data to client
|
||||
* @param num uint8_t client id
|
||||
* @param payload uint8_t *
|
||||
* @param length size_t
|
||||
* @param headerToPayload bool (see sendFrame for more details)
|
||||
* @return true if ok
|
||||
*/
|
||||
bool WebSocketsClient::sendBIN(uint8_t * payload, size_t length, bool headerToPayload) {
|
||||
if(clientIsConnected(&_client)) {
|
||||
return sendFrame(&_client, WSop_binary, payload, length, true, headerToPayload);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WebSocketsClient::sendBIN(const uint8_t * payload, size_t length) {
|
||||
return sendBIN((uint8_t *)payload, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* sends a WS ping to Server
|
||||
* @param payload uint8_t *
|
||||
* @param length size_t
|
||||
* @return true if ping is send out
|
||||
*/
|
||||
bool WebSocketsClient::sendPing(uint8_t * payload, size_t length) {
|
||||
if(clientIsConnected(&_client)) {
|
||||
bool sent = sendFrame(&_client, WSop_ping, payload, length);
|
||||
if(sent)
|
||||
_client.lastPing = millis();
|
||||
return sent;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WebSocketsClient::sendPing(String & payload) {
|
||||
return sendPing((uint8_t *)payload.c_str(), payload.length());
|
||||
}
|
||||
|
||||
/**
|
||||
* disconnect one client
|
||||
* @param num uint8_t client id
|
||||
*/
|
||||
void WebSocketsClient::disconnect(void) {
|
||||
if(clientIsConnected(&_client)) {
|
||||
WebSockets::clientDisconnect(&_client, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* set the Authorizatio for the http request
|
||||
* @param user const char *
|
||||
* @param password const char *
|
||||
*/
|
||||
void WebSocketsClient::setAuthorization(const char * user, const char * password) {
|
||||
if(user && password) {
|
||||
String auth = user;
|
||||
auth += ":";
|
||||
auth += password;
|
||||
_client.base64Authorization = base64_encode((uint8_t *)auth.c_str(), auth.length());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* set the Authorizatio for the http request
|
||||
* @param auth const char * base64
|
||||
*/
|
||||
void WebSocketsClient::setAuthorization(const char * auth) {
|
||||
if(auth) {
|
||||
//_client.base64Authorization = auth;
|
||||
_client.plainAuthorization = auth;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* set extra headers for the http request;
|
||||
* separate headers by "\r\n"
|
||||
* @param extraHeaders const char * extraHeaders
|
||||
*/
|
||||
void WebSocketsClient::setExtraHeaders(const char * extraHeaders) {
|
||||
_client.extraHeaders = extraHeaders;
|
||||
}
|
||||
|
||||
/**
|
||||
* set the reconnect Interval
|
||||
* how long to wait after a connection initiate failed
|
||||
* @param time in ms
|
||||
*/
|
||||
void WebSocketsClient::setReconnectInterval(unsigned long time) {
|
||||
_reconnectInterval = time;
|
||||
}
|
||||
|
||||
bool WebSocketsClient::isConnected(void) {
|
||||
return (_client.status == WSC_CONNECTED);
|
||||
}
|
||||
|
||||
//#################################################################################
|
||||
//#################################################################################
|
||||
//#################################################################################
|
||||
|
||||
/**
|
||||
*
|
||||
* @param client WSclient_t * ptr to the client struct
|
||||
* @param opcode WSopcode_t
|
||||
* @param payload uint8_t *
|
||||
* @param length size_t
|
||||
*/
|
||||
void WebSocketsClient::messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin) {
|
||||
WStype_t type = WStype_ERROR;
|
||||
|
||||
UNUSED(client);
|
||||
|
||||
switch(opcode) {
|
||||
case WSop_text:
|
||||
type = fin ? WStype_TEXT : WStype_FRAGMENT_TEXT_START;
|
||||
break;
|
||||
case WSop_binary:
|
||||
type = fin ? WStype_BIN : WStype_FRAGMENT_BIN_START;
|
||||
break;
|
||||
case WSop_continuation:
|
||||
type = fin ? WStype_FRAGMENT_FIN : WStype_FRAGMENT;
|
||||
break;
|
||||
case WSop_ping:
|
||||
type = WStype_PING;
|
||||
break;
|
||||
case WSop_pong:
|
||||
type = WStype_PONG;
|
||||
break;
|
||||
case WSop_close:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
runCbEvent(type, payload, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnect an client
|
||||
* @param client WSclient_t * ptr to the client struct
|
||||
*/
|
||||
void WebSocketsClient::clientDisconnect(WSclient_t * client) {
|
||||
bool event = false;
|
||||
|
||||
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
|
||||
if(client->isSSL && client->ssl) {
|
||||
if(client->ssl->connected()) {
|
||||
client->ssl->flush();
|
||||
client->ssl->stop();
|
||||
}
|
||||
event = true;
|
||||
delete client->ssl;
|
||||
client->ssl = NULL;
|
||||
client->tcp = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
if(client->tcp) {
|
||||
if(client->tcp->connected()) {
|
||||
#if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
|
||||
client->tcp->flush();
|
||||
#endif
|
||||
client->tcp->stop();
|
||||
}
|
||||
event = true;
|
||||
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
|
||||
client->status = WSC_NOT_CONNECTED;
|
||||
#else
|
||||
delete client->tcp;
|
||||
#endif
|
||||
client->tcp = NULL;
|
||||
}
|
||||
|
||||
client->cCode = 0;
|
||||
client->cKey = "";
|
||||
client->cAccept = "";
|
||||
client->cVersion = 0;
|
||||
client->cIsUpgrade = false;
|
||||
client->cIsWebsocket = false;
|
||||
client->cSessionId = "";
|
||||
|
||||
client->status = WSC_NOT_CONNECTED;
|
||||
_lastConnectionFail = millis();
|
||||
|
||||
DEBUG_WEBSOCKETS("[WS-Client] client disconnected.\n");
|
||||
if(event) {
|
||||
runCbEvent(WStype_DISCONNECTED, NULL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get client state
|
||||
* @param client WSclient_t * ptr to the client struct
|
||||
* @return true = conneted
|
||||
*/
|
||||
bool WebSocketsClient::clientIsConnected(WSclient_t * client) {
|
||||
if(!client->tcp) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(client->tcp->connected()) {
|
||||
if(client->status != WSC_NOT_CONNECTED) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
// client lost
|
||||
if(client->status != WSC_NOT_CONNECTED) {
|
||||
DEBUG_WEBSOCKETS("[WS-Client] connection lost.\n");
|
||||
// do cleanup
|
||||
clientDisconnect(client);
|
||||
}
|
||||
}
|
||||
|
||||
if(client->tcp) {
|
||||
// do cleanup
|
||||
clientDisconnect(client);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
|
||||
/**
|
||||
* Handel incomming data from Client
|
||||
*/
|
||||
void WebSocketsClient::handleClientData(void) {
|
||||
if((_client.status == WSC_HEADER || _client.status == WSC_BODY) && _lastHeaderSent + WEBSOCKETS_TCP_TIMEOUT < millis()) {
|
||||
DEBUG_WEBSOCKETS("[WS-Client][handleClientData] header response timeout.. disconnecting!\n");
|
||||
clientDisconnect(&_client);
|
||||
WEBSOCKETS_YIELD();
|
||||
return;
|
||||
}
|
||||
|
||||
int len = _client.tcp->available();
|
||||
if(len > 0) {
|
||||
switch(_client.status) {
|
||||
case WSC_HEADER: {
|
||||
String headerLine = _client.tcp->readStringUntil('\n');
|
||||
handleHeader(&_client, &headerLine);
|
||||
} break;
|
||||
case WSC_BODY: {
|
||||
char buf[256] = { 0 };
|
||||
_client.tcp->readBytes(&buf[0], std::min((size_t)len, sizeof(buf)));
|
||||
String bodyLine = buf;
|
||||
handleHeader(&_client, &bodyLine);
|
||||
} break;
|
||||
case WSC_CONNECTED:
|
||||
WebSockets::handleWebsocket(&_client);
|
||||
break;
|
||||
default:
|
||||
WebSockets::clientDisconnect(&_client, 1002);
|
||||
break;
|
||||
}
|
||||
}
|
||||
WEBSOCKETS_YIELD();
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* send the WebSocket header to Server
|
||||
* @param client WSclient_t * ptr to the client struct
|
||||
*/
|
||||
void WebSocketsClient::sendHeader(WSclient_t * client) {
|
||||
static const char * NEW_LINE = "\r\n";
|
||||
|
||||
DEBUG_WEBSOCKETS("[WS-Client][sendHeader] sending header...\n");
|
||||
|
||||
uint8_t randomKey[16] = { 0 };
|
||||
|
||||
for(uint8_t i = 0; i < sizeof(randomKey); i++) {
|
||||
randomKey[i] = random(0xFF);
|
||||
}
|
||||
|
||||
client->cKey = base64_encode(&randomKey[0], 16);
|
||||
|
||||
#ifndef NODEBUG_WEBSOCKETS
|
||||
unsigned long start = micros();
|
||||
#endif
|
||||
|
||||
String handshake;
|
||||
bool ws_header = true;
|
||||
String url = client->cUrl;
|
||||
|
||||
if(client->isSocketIO) {
|
||||
if(client->cSessionId.length() == 0) {
|
||||
url += WEBSOCKETS_STRING("&transport=polling");
|
||||
ws_header = false;
|
||||
} else {
|
||||
url += WEBSOCKETS_STRING("&transport=websocket&sid=");
|
||||
url += client->cSessionId;
|
||||
}
|
||||
}
|
||||
|
||||
handshake = WEBSOCKETS_STRING("GET ");
|
||||
handshake += url + WEBSOCKETS_STRING(
|
||||
" HTTP/1.1\r\n"
|
||||
"Host: ");
|
||||
handshake += _host + ":" + _port + NEW_LINE;
|
||||
|
||||
if(ws_header) {
|
||||
handshake += WEBSOCKETS_STRING(
|
||||
"Connection: Upgrade\r\n"
|
||||
"Upgrade: websocket\r\n"
|
||||
"Sec-WebSocket-Version: 13\r\n"
|
||||
"Sec-WebSocket-Key: ");
|
||||
handshake += client->cKey + NEW_LINE;
|
||||
|
||||
if(client->cProtocol.length() > 0) {
|
||||
handshake += WEBSOCKETS_STRING("Sec-WebSocket-Protocol: ");
|
||||
handshake += client->cProtocol + NEW_LINE;
|
||||
}
|
||||
|
||||
if(client->cExtensions.length() > 0) {
|
||||
handshake += WEBSOCKETS_STRING("Sec-WebSocket-Extensions: ");
|
||||
handshake += client->cExtensions + NEW_LINE;
|
||||
}
|
||||
} else {
|
||||
handshake += WEBSOCKETS_STRING("Connection: keep-alive\r\n");
|
||||
}
|
||||
|
||||
// add extra headers; by default this includes "Origin: file://"
|
||||
if(client->extraHeaders.length() > 0) {
|
||||
handshake += client->extraHeaders + NEW_LINE;
|
||||
}
|
||||
|
||||
handshake += WEBSOCKETS_STRING("User-Agent: arduino-WebSocket-Client\r\n");
|
||||
|
||||
if(client->base64Authorization.length() > 0) {
|
||||
handshake += WEBSOCKETS_STRING("Authorization: Basic ");
|
||||
handshake += client->base64Authorization + NEW_LINE;
|
||||
}
|
||||
|
||||
if(client->plainAuthorization.length() > 0) {
|
||||
handshake += WEBSOCKETS_STRING("Authorization: ");
|
||||
handshake += client->plainAuthorization + NEW_LINE;
|
||||
}
|
||||
|
||||
handshake += NEW_LINE;
|
||||
|
||||
DEBUG_WEBSOCKETS("[WS-Client][sendHeader] handshake %s", (uint8_t *)handshake.c_str());
|
||||
write(client, (uint8_t *)handshake.c_str(), handshake.length());
|
||||
|
||||
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
|
||||
client->tcp->readStringUntil('\n', &(client->cHttpLine), std::bind(&WebSocketsClient::handleHeader, this, client, &(client->cHttpLine)));
|
||||
#endif
|
||||
|
||||
DEBUG_WEBSOCKETS("[WS-Client][sendHeader] sending header... Done (%luus).\n", (micros() - start));
|
||||
_lastHeaderSent = millis();
|
||||
}
|
||||
|
||||
/**
|
||||
* handle the WebSocket header reading
|
||||
* @param client WSclient_t * ptr to the client struct
|
||||
*/
|
||||
void WebSocketsClient::handleHeader(WSclient_t * client, String * headerLine) {
|
||||
headerLine->trim(); // remove \r
|
||||
|
||||
// this code handels the http body for Socket.IO V3 requests
|
||||
if(headerLine->length() > 0 && client->isSocketIO && client->status == WSC_BODY && client->cSessionId.length() == 0) {
|
||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] socket.io json: %s\n", headerLine->c_str());
|
||||
String sid_begin = WEBSOCKETS_STRING("\"sid\":\"");
|
||||
if(headerLine->indexOf(sid_begin) > -1) {
|
||||
int start = headerLine->indexOf(sid_begin) + sid_begin.length();
|
||||
int end = headerLine->indexOf('"', start);
|
||||
client->cSessionId = headerLine->substring(start, end);
|
||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cSessionId: %s\n", client->cSessionId.c_str());
|
||||
|
||||
// Trigger websocket connection code path
|
||||
*headerLine = "";
|
||||
}
|
||||
}
|
||||
|
||||
// headle HTTP header
|
||||
if(headerLine->length() > 0) {
|
||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] RX: %s\n", headerLine->c_str());
|
||||
|
||||
if(headerLine->startsWith(WEBSOCKETS_STRING("HTTP/1."))) {
|
||||
// "HTTP/1.1 101 Switching Protocols"
|
||||
client->cCode = headerLine->substring(9, headerLine->indexOf(' ', 9)).toInt();
|
||||
} else if(headerLine->indexOf(':') >= 0) {
|
||||
String headerName = headerLine->substring(0, headerLine->indexOf(':'));
|
||||
String headerValue = headerLine->substring(headerLine->indexOf(':') + 1);
|
||||
|
||||
// remove space in the beginning (RFC2616)
|
||||
if(headerValue[0] == ' ') {
|
||||
headerValue.remove(0, 1);
|
||||
}
|
||||
|
||||
if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Connection"))) {
|
||||
if(headerValue.equalsIgnoreCase(WEBSOCKETS_STRING("upgrade"))) {
|
||||
client->cIsUpgrade = true;
|
||||
}
|
||||
} else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Upgrade"))) {
|
||||
if(headerValue.equalsIgnoreCase(WEBSOCKETS_STRING("websocket"))) {
|
||||
client->cIsWebsocket = true;
|
||||
}
|
||||
} else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Accept"))) {
|
||||
client->cAccept = headerValue;
|
||||
client->cAccept.trim(); // see rfc6455
|
||||
} else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Protocol"))) {
|
||||
client->cProtocol = headerValue;
|
||||
} else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Extensions"))) {
|
||||
client->cExtensions = headerValue;
|
||||
} else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Version"))) {
|
||||
client->cVersion = headerValue.toInt();
|
||||
} else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Set-Cookie"))) {
|
||||
if(headerValue.indexOf(';') > -1) {
|
||||
client->cSessionId = headerValue.substring(headerValue.indexOf('=') + 1, headerValue.indexOf(";"));
|
||||
} else {
|
||||
client->cSessionId = headerValue.substring(headerValue.indexOf('=') + 1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Header error (%s)\n", headerLine->c_str());
|
||||
}
|
||||
|
||||
(*headerLine) = "";
|
||||
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
|
||||
client->tcp->readStringUntil('\n', &(client->cHttpLine), std::bind(&WebSocketsClient::handleHeader, this, client, &(client->cHttpLine)));
|
||||
#endif
|
||||
} else {
|
||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Header read fin.\n");
|
||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Client settings:\n");
|
||||
|
||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cURL: %s\n", client->cUrl.c_str());
|
||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cKey: %s\n", client->cKey.c_str());
|
||||
|
||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Server header:\n");
|
||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cCode: %d\n", client->cCode);
|
||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cIsUpgrade: %d\n", client->cIsUpgrade);
|
||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cIsWebsocket: %d\n", client->cIsWebsocket);
|
||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cAccept: %s\n", client->cAccept.c_str());
|
||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cProtocol: %s\n", client->cProtocol.c_str());
|
||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cExtensions: %s\n", client->cExtensions.c_str());
|
||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cVersion: %d\n", client->cVersion);
|
||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cSessionId: %s\n", client->cSessionId.c_str());
|
||||
|
||||
if(client->isSocketIO && client->cSessionId.length() == 0 && clientIsConnected(client)) {
|
||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] still missing cSessionId try socket.io V3\n");
|
||||
client->status = WSC_BODY;
|
||||
return;
|
||||
} else {
|
||||
client->status = WSC_HEADER;
|
||||
}
|
||||
|
||||
bool ok = (client->cIsUpgrade && client->cIsWebsocket);
|
||||
|
||||
if(ok) {
|
||||
switch(client->cCode) {
|
||||
case 101: ///< Switching Protocols
|
||||
|
||||
break;
|
||||
case 200:
|
||||
if(client->isSocketIO) {
|
||||
break;
|
||||
}
|
||||
// falls through
|
||||
case 403: ///< Forbidden
|
||||
// todo handle login
|
||||
// falls through
|
||||
default: ///< Server dont unterstand requrst
|
||||
ok = false;
|
||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] serverCode is not 101 (%d)\n", client->cCode);
|
||||
clientDisconnect(client);
|
||||
_lastConnectionFail = millis();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(ok) {
|
||||
if(client->cAccept.length() == 0) {
|
||||
ok = false;
|
||||
} else {
|
||||
// generate Sec-WebSocket-Accept key for check
|
||||
String sKey = acceptKey(client->cKey);
|
||||
if(sKey != client->cAccept) {
|
||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Sec-WebSocket-Accept is wrong\n");
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(ok) {
|
||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Websocket connection init done.\n");
|
||||
headerDone(client);
|
||||
|
||||
runCbEvent(WStype_CONNECTED, (uint8_t *)client->cUrl.c_str(), client->cUrl.length());
|
||||
#if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
|
||||
} else if(client->isSocketIO) {
|
||||
if(client->cSessionId.length() > 0) {
|
||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] found cSessionId\n");
|
||||
if(clientIsConnected(client) && _client.tcp->available()) {
|
||||
// read not needed data
|
||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] still data in buffer (%d), clean up.\n", _client.tcp->available());
|
||||
while(_client.tcp->available() > 0) {
|
||||
_client.tcp->read();
|
||||
}
|
||||
}
|
||||
sendHeader(client);
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] no Websocket connection close.\n");
|
||||
_lastConnectionFail = millis();
|
||||
if(clientIsConnected(client)) {
|
||||
write(client, "This is a webSocket client!");
|
||||
}
|
||||
clientDisconnect(client);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WebSocketsClient::connectedCb() {
|
||||
DEBUG_WEBSOCKETS("[WS-Client] connected to %s:%u.\n", _host.c_str(), _port);
|
||||
|
||||
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
|
||||
_client.tcp->onDisconnect(std::bind([](WebSocketsClient * c, AsyncTCPbuffer * obj, WSclient_t * client) -> bool {
|
||||
DEBUG_WEBSOCKETS("[WS-Server][%d] Disconnect client\n", client->num);
|
||||
client->status = WSC_NOT_CONNECTED;
|
||||
client->tcp = NULL;
|
||||
|
||||
// reconnect
|
||||
c->asyncConnect();
|
||||
|
||||
return true;
|
||||
},
|
||||
this, std::placeholders::_1, &_client));
|
||||
#endif
|
||||
|
||||
_client.status = WSC_HEADER;
|
||||
|
||||
#if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
|
||||
// set Timeout for readBytesUntil and readStringUntil
|
||||
_client.tcp->setTimeout(WEBSOCKETS_TCP_TIMEOUT);
|
||||
#endif
|
||||
|
||||
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
|
||||
_client.tcp->setNoDelay(true);
|
||||
#endif
|
||||
|
||||
#if defined(HAS_SSL)
|
||||
#if defined(SSL_AXTLS) || defined(ESP32)
|
||||
if(_client.isSSL && SSL_FINGERPRINT_IS_SET) {
|
||||
if(!_client.ssl->verify(_fingerprint.c_str(), _host.c_str())) {
|
||||
DEBUG_WEBSOCKETS("[WS-Client] certificate mismatch\n");
|
||||
WebSockets::clientDisconnect(&_client, 1000);
|
||||
return;
|
||||
}
|
||||
#else
|
||||
if(_client.isSSL && SSL_FINGERPRINT_IS_SET) {
|
||||
#endif
|
||||
} else if(_client.isSSL && !_CA_cert) {
|
||||
#if defined(SSL_BARESSL)
|
||||
_client.ssl->setInsecure();
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
// send Header to Server
|
||||
sendHeader(&_client);
|
||||
}
|
||||
|
||||
void WebSocketsClient::connectFailedCb() {
|
||||
DEBUG_WEBSOCKETS("[WS-Client] connection to %s:%u Failed\n", _host.c_str(), _port);
|
||||
}
|
||||
|
||||
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
|
||||
|
||||
void WebSocketsClient::asyncConnect() {
|
||||
DEBUG_WEBSOCKETS("[WS-Client] asyncConnect...\n");
|
||||
|
||||
AsyncClient * tcpclient = new AsyncClient();
|
||||
|
||||
if(!tcpclient) {
|
||||
DEBUG_WEBSOCKETS("[WS-Client] creating AsyncClient class failed!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
tcpclient->onDisconnect([](void * obj, AsyncClient * c) {
|
||||
c->free();
|
||||
delete c;
|
||||
});
|
||||
|
||||
tcpclient->onConnect(std::bind([](WebSocketsClient * ws, AsyncClient * tcp) {
|
||||
ws->_client.tcp = new AsyncTCPbuffer(tcp);
|
||||
if(!ws->_client.tcp) {
|
||||
DEBUG_WEBSOCKETS("[WS-Client] creating Network class failed!\n");
|
||||
ws->connectFailedCb();
|
||||
return;
|
||||
}
|
||||
ws->connectedCb();
|
||||
},
|
||||
this, std::placeholders::_2));
|
||||
|
||||
tcpclient->onError(std::bind([](WebSocketsClient * ws, AsyncClient * tcp) {
|
||||
ws->connectFailedCb();
|
||||
|
||||
// reconnect
|
||||
ws->asyncConnect();
|
||||
},
|
||||
this, std::placeholders::_2));
|
||||
|
||||
if(!tcpclient->connect(_host.c_str(), _port)) {
|
||||
connectFailedCb();
|
||||
delete tcpclient;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* send heartbeat ping to server in set intervals
|
||||
*/
|
||||
void WebSocketsClient::handleHBPing() {
|
||||
if(_client.pingInterval == 0)
|
||||
return;
|
||||
uint32_t pi = millis() - _client.lastPing;
|
||||
if(pi > _client.pingInterval) {
|
||||
DEBUG_WEBSOCKETS("[WS-Client] sending HB ping\n");
|
||||
if(sendPing()) {
|
||||
_client.lastPing = millis();
|
||||
_client.pongReceived = false;
|
||||
} else {
|
||||
DEBUG_WEBSOCKETS("[WS-Client] sending HB ping failed\n");
|
||||
WebSockets::clientDisconnect(&_client, 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* enable ping/pong heartbeat process
|
||||
* @param pingInterval uint32_t how often ping will be sent
|
||||
* @param pongTimeout uint32_t millis after which pong should timout if not received
|
||||
* @param disconnectTimeoutCount uint8_t how many timeouts before disconnect, 0=> do not disconnect
|
||||
*/
|
||||
void WebSocketsClient::enableHeartbeat(uint32_t pingInterval, uint32_t pongTimeout, uint8_t disconnectTimeoutCount) {
|
||||
WebSockets::enableHeartbeat(&_client, pingInterval, pongTimeout, disconnectTimeoutCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* disable ping/pong heartbeat process
|
||||
*/
|
||||
void WebSocketsClient::disableHeartbeat() {
|
||||
_client.pingInterval = 0;
|
||||
}
|
||||
169
lib/WebSockets/src/WebSocketsClient.h
Normal file
169
lib/WebSockets/src/WebSocketsClient.h
Normal file
@@ -0,0 +1,169 @@
|
||||
/**
|
||||
* @file WebSocketsClient.h
|
||||
* @date 20.05.2015
|
||||
* @author Markus Sattler
|
||||
*
|
||||
* Copyright (c) 2015 Markus Sattler. All rights reserved.
|
||||
* This file is part of the WebSockets for Arduino.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef WEBSOCKETSCLIENT_H_
|
||||
#define WEBSOCKETSCLIENT_H_
|
||||
|
||||
#include "WebSockets.h"
|
||||
|
||||
class WebSocketsClient : protected WebSockets {
|
||||
public:
|
||||
#ifdef __AVR__
|
||||
typedef void (*WebSocketClientEvent)(WStype_t type, uint8_t * payload, size_t length);
|
||||
#else
|
||||
typedef std::function<void(WStype_t type, uint8_t * payload, size_t length)> WebSocketClientEvent;
|
||||
#endif
|
||||
|
||||
WebSocketsClient(void);
|
||||
virtual ~WebSocketsClient(void);
|
||||
|
||||
void begin(const char * host, uint16_t port, const char * url = "/", const char * protocol = "arduino");
|
||||
void begin(String host, uint16_t port, String url = "/", String protocol = "arduino");
|
||||
void begin(IPAddress host, uint16_t port, const char * url = "/", const char * protocol = "arduino");
|
||||
|
||||
#if defined(HAS_SSL)
|
||||
#ifdef SSL_AXTLS
|
||||
void beginSSL(const char * host, uint16_t port, const char * url = "/", const char * fingerprint = "", const char * protocol = "arduino");
|
||||
void beginSSL(String host, uint16_t port, String url = "/", String fingerprint = "", String protocol = "arduino");
|
||||
#else
|
||||
void beginSSL(const char * host, uint16_t port, const char * url = "/", const uint8_t * fingerprint = NULL, const char * protocol = "arduino");
|
||||
void beginSslWithCA(const char * host, uint16_t port, const char * url = "/", BearSSL::X509List * CA_cert = NULL, const char * protocol = "arduino");
|
||||
void setSSLClientCertKey(BearSSL::X509List * clientCert = NULL, BearSSL::PrivateKey * clientPrivateKey = NULL);
|
||||
void setSSLClientCertKey(const char * clientCert = NULL, const char * clientPrivateKey = NULL);
|
||||
#endif
|
||||
void beginSslWithCA(const char * host, uint16_t port, const char * url = "/", const char * CA_cert = NULL, const char * protocol = "arduino");
|
||||
#endif
|
||||
|
||||
void beginSocketIO(const char * host, uint16_t port, const char * url = "/socket.io/?EIO=3", const char * protocol = "arduino");
|
||||
void beginSocketIO(String host, uint16_t port, String url = "/socket.io/?EIO=3", String protocol = "arduino");
|
||||
|
||||
#if defined(HAS_SSL)
|
||||
void beginSocketIOSSL(const char * host, uint16_t port, const char * url = "/socket.io/?EIO=3", const char * protocol = "arduino");
|
||||
void beginSocketIOSSL(String host, uint16_t port, String url = "/socket.io/?EIO=3", String protocol = "arduino");
|
||||
|
||||
void beginSocketIOSSLWithCA(const char * host, uint16_t port, const char * url = "/socket.io/?EIO=3", const char * CA_cert = NULL, const char * protocol = "arduino");
|
||||
#if defined(SSL_BARESSL)
|
||||
void beginSocketIOSSLWithCA(const char * host, uint16_t port, const char * url = "/socket.io/?EIO=3", BearSSL::X509List * CA_cert = NULL, const char * protocol = "arduino");
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
|
||||
void loop(void);
|
||||
#else
|
||||
// Async interface not need a loop call
|
||||
void loop(void) __attribute__((deprecated)) {}
|
||||
#endif
|
||||
|
||||
void onEvent(WebSocketClientEvent cbEvent);
|
||||
|
||||
bool sendTXT(uint8_t * payload, size_t length = 0, bool headerToPayload = false);
|
||||
bool sendTXT(const uint8_t * payload, size_t length = 0);
|
||||
bool sendTXT(char * payload, size_t length = 0, bool headerToPayload = false);
|
||||
bool sendTXT(const char * payload, size_t length = 0);
|
||||
bool sendTXT(String & payload);
|
||||
bool sendTXT(char payload);
|
||||
|
||||
bool sendBIN(uint8_t * payload, size_t length, bool headerToPayload = false);
|
||||
bool sendBIN(const uint8_t * payload, size_t length);
|
||||
|
||||
bool sendPing(uint8_t * payload = NULL, size_t length = 0);
|
||||
bool sendPing(String & payload);
|
||||
|
||||
void disconnect(void);
|
||||
|
||||
void setAuthorization(const char * user, const char * password);
|
||||
void setAuthorization(const char * auth);
|
||||
|
||||
void setExtraHeaders(const char * extraHeaders = NULL);
|
||||
|
||||
void setReconnectInterval(unsigned long time);
|
||||
|
||||
void enableHeartbeat(uint32_t pingInterval, uint32_t pongTimeout, uint8_t disconnectTimeoutCount);
|
||||
void disableHeartbeat();
|
||||
|
||||
bool isConnected(void);
|
||||
|
||||
protected:
|
||||
String _host;
|
||||
uint16_t _port;
|
||||
|
||||
#if defined(HAS_SSL)
|
||||
#ifdef SSL_AXTLS
|
||||
String _fingerprint;
|
||||
const char * _CA_cert;
|
||||
#define SSL_FINGERPRINT_IS_SET (_fingerprint.length())
|
||||
#define SSL_FINGERPRINT_NULL ""
|
||||
#else
|
||||
const uint8_t * _fingerprint;
|
||||
BearSSL::X509List * _CA_cert;
|
||||
BearSSL::X509List * _client_cert;
|
||||
BearSSL::PrivateKey * _client_key;
|
||||
#define SSL_FINGERPRINT_IS_SET (_fingerprint != NULL)
|
||||
#define SSL_FINGERPRINT_NULL NULL
|
||||
#endif
|
||||
|
||||
#endif
|
||||
WSclient_t _client;
|
||||
|
||||
WebSocketClientEvent _cbEvent;
|
||||
|
||||
unsigned long _lastConnectionFail;
|
||||
unsigned long _reconnectInterval;
|
||||
unsigned long _lastHeaderSent;
|
||||
|
||||
void messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin);
|
||||
|
||||
void clientDisconnect(WSclient_t * client);
|
||||
bool clientIsConnected(WSclient_t * client);
|
||||
|
||||
#if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
|
||||
void handleClientData(void);
|
||||
#endif
|
||||
|
||||
void sendHeader(WSclient_t * client);
|
||||
void handleHeader(WSclient_t * client, String * headerLine);
|
||||
|
||||
void connectedCb();
|
||||
void connectFailedCb();
|
||||
|
||||
void handleHBPing(); // send ping in specified intervals
|
||||
|
||||
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
|
||||
void asyncConnect();
|
||||
#endif
|
||||
|
||||
/**
|
||||
* called for sending a Event to the app
|
||||
* @param type WStype_t
|
||||
* @param payload uint8_t *
|
||||
* @param length size_t
|
||||
*/
|
||||
virtual void runCbEvent(WStype_t type, uint8_t * payload, size_t length) {
|
||||
if(_cbEvent) {
|
||||
_cbEvent(type, payload, length);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* WEBSOCKETSCLIENT_H_ */
|
||||
955
lib/WebSockets/src/WebSocketsServer.cpp
Normal file
955
lib/WebSockets/src/WebSocketsServer.cpp
Normal file
@@ -0,0 +1,955 @@
|
||||
/**
|
||||
* @file WebSocketsServer.cpp
|
||||
* @date 20.05.2015
|
||||
* @author Markus Sattler
|
||||
*
|
||||
* Copyright (c) 2015 Markus Sattler. All rights reserved.
|
||||
* This file is part of the WebSockets for Arduino.
|
||||
*
|
||||
* 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 "WebSockets.h"
|
||||
#include "WebSocketsServer.h"
|
||||
|
||||
WebSocketsServerCore::WebSocketsServerCore(const String & origin, const String & protocol) {
|
||||
_origin = origin;
|
||||
_protocol = protocol;
|
||||
_runnning = false;
|
||||
_pingInterval = 0;
|
||||
_pongTimeout = 0;
|
||||
_disconnectTimeoutCount = 0;
|
||||
|
||||
_cbEvent = NULL;
|
||||
|
||||
_httpHeaderValidationFunc = NULL;
|
||||
_mandatoryHttpHeaders = NULL;
|
||||
_mandatoryHttpHeaderCount = 0;
|
||||
}
|
||||
|
||||
WebSocketsServer::WebSocketsServer(uint16_t port, const String & origin, const String & protocol)
|
||||
: WebSocketsServerCore(origin, protocol) {
|
||||
_port = port;
|
||||
|
||||
_server = new WEBSOCKETS_NETWORK_SERVER_CLASS(port);
|
||||
|
||||
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
|
||||
_server->onClient([](void * s, AsyncClient * c) {
|
||||
((WebSocketsServerCore *)s)->newClient(new AsyncTCPbuffer(c));
|
||||
},
|
||||
this);
|
||||
#endif
|
||||
}
|
||||
|
||||
WebSocketsServerCore::~WebSocketsServerCore() {
|
||||
// disconnect all clients
|
||||
close();
|
||||
|
||||
if(_mandatoryHttpHeaders)
|
||||
delete[] _mandatoryHttpHeaders;
|
||||
|
||||
_mandatoryHttpHeaderCount = 0;
|
||||
}
|
||||
|
||||
WebSocketsServer::~WebSocketsServer() {
|
||||
}
|
||||
|
||||
/**
|
||||
* called to initialize the Websocket server
|
||||
*/
|
||||
void WebSocketsServerCore::begin(void) {
|
||||
// adjust clients storage:
|
||||
// _clients[i]'s constructor are already called,
|
||||
// all its members are initialized to their default value,
|
||||
// except the ones explicitly detailed in WSclient_t() constructor.
|
||||
// Then we need to initialize some members to non-trivial values:
|
||||
for(int i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
|
||||
_clients[i].init(i, _pingInterval, _pongTimeout, _disconnectTimeoutCount);
|
||||
}
|
||||
|
||||
#ifdef ESP8266
|
||||
randomSeed(RANDOM_REG32);
|
||||
#elif defined(ESP32)
|
||||
#define DR_REG_RNG_BASE 0x3ff75144
|
||||
randomSeed(READ_PERI_REG(DR_REG_RNG_BASE));
|
||||
#else
|
||||
// TODO find better seed
|
||||
randomSeed(millis());
|
||||
#endif
|
||||
|
||||
_runnning = true;
|
||||
|
||||
DEBUG_WEBSOCKETS("[WS-Server] Websocket Version: " WEBSOCKETS_VERSION "\n");
|
||||
}
|
||||
|
||||
void WebSocketsServerCore::close(void) {
|
||||
_runnning = false;
|
||||
disconnect();
|
||||
|
||||
// restore _clients[] to their initial state
|
||||
// before next call to ::begin()
|
||||
for(int i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
|
||||
_clients[i] = WSclient_t();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* set callback function
|
||||
* @param cbEvent WebSocketServerEvent
|
||||
*/
|
||||
void WebSocketsServerCore::onEvent(WebSocketServerEvent cbEvent) {
|
||||
_cbEvent = cbEvent;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets the custom http header validator function
|
||||
* @param httpHeaderValidationFunc WebSocketServerHttpHeaderValFunc ///< pointer to the custom http header validation function
|
||||
* @param mandatoryHttpHeaders[] const char* ///< the array of named http headers considered to be mandatory / must be present in order for websocket upgrade to succeed
|
||||
* @param mandatoryHttpHeaderCount size_t ///< the number of items in the mandatoryHttpHeaders array
|
||||
*/
|
||||
void WebSocketsServerCore::onValidateHttpHeader(
|
||||
WebSocketServerHttpHeaderValFunc validationFunc,
|
||||
const char * mandatoryHttpHeaders[],
|
||||
size_t mandatoryHttpHeaderCount) {
|
||||
_httpHeaderValidationFunc = validationFunc;
|
||||
|
||||
if(_mandatoryHttpHeaders)
|
||||
delete[] _mandatoryHttpHeaders;
|
||||
|
||||
_mandatoryHttpHeaderCount = mandatoryHttpHeaderCount;
|
||||
_mandatoryHttpHeaders = new String[_mandatoryHttpHeaderCount];
|
||||
|
||||
for(size_t i = 0; i < _mandatoryHttpHeaderCount; i++) {
|
||||
_mandatoryHttpHeaders[i] = mandatoryHttpHeaders[i];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* send text data to client
|
||||
* @param num uint8_t client id
|
||||
* @param payload uint8_t *
|
||||
* @param length size_t
|
||||
* @param headerToPayload bool (see sendFrame for more details)
|
||||
* @return true if ok
|
||||
*/
|
||||
bool WebSocketsServerCore::sendTXT(uint8_t num, uint8_t * payload, size_t length, bool headerToPayload) {
|
||||
if(num >= WEBSOCKETS_SERVER_CLIENT_MAX) {
|
||||
return false;
|
||||
}
|
||||
if(length == 0) {
|
||||
length = strlen((const char *)payload);
|
||||
}
|
||||
WSclient_t * client = &_clients[num];
|
||||
if(clientIsConnected(client)) {
|
||||
return sendFrame(client, WSop_text, payload, length, true, headerToPayload);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WebSocketsServerCore::sendTXT(uint8_t num, const uint8_t * payload, size_t length) {
|
||||
return sendTXT(num, (uint8_t *)payload, length);
|
||||
}
|
||||
|
||||
bool WebSocketsServerCore::sendTXT(uint8_t num, char * payload, size_t length, bool headerToPayload) {
|
||||
return sendTXT(num, (uint8_t *)payload, length, headerToPayload);
|
||||
}
|
||||
|
||||
bool WebSocketsServerCore::sendTXT(uint8_t num, const char * payload, size_t length) {
|
||||
return sendTXT(num, (uint8_t *)payload, length);
|
||||
}
|
||||
|
||||
bool WebSocketsServerCore::sendTXT(uint8_t num, String & payload) {
|
||||
return sendTXT(num, (uint8_t *)payload.c_str(), payload.length());
|
||||
}
|
||||
|
||||
/**
|
||||
* send text data to client all
|
||||
* @param payload uint8_t *
|
||||
* @param length size_t
|
||||
* @param headerToPayload bool (see sendFrame for more details)
|
||||
* @return true if ok
|
||||
*/
|
||||
bool WebSocketsServerCore::broadcastTXT(uint8_t * payload, size_t length, bool headerToPayload) {
|
||||
WSclient_t * client;
|
||||
bool ret = true;
|
||||
if(length == 0) {
|
||||
length = strlen((const char *)payload);
|
||||
}
|
||||
|
||||
for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
|
||||
client = &_clients[i];
|
||||
if(clientIsConnected(client)) {
|
||||
if(!sendFrame(client, WSop_text, payload, length, true, headerToPayload)) {
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
WEBSOCKETS_YIELD();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool WebSocketsServerCore::broadcastTXT(const uint8_t * payload, size_t length) {
|
||||
return broadcastTXT((uint8_t *)payload, length);
|
||||
}
|
||||
|
||||
bool WebSocketsServerCore::broadcastTXT(char * payload, size_t length, bool headerToPayload) {
|
||||
return broadcastTXT((uint8_t *)payload, length, headerToPayload);
|
||||
}
|
||||
|
||||
bool WebSocketsServerCore::broadcastTXT(const char * payload, size_t length) {
|
||||
return broadcastTXT((uint8_t *)payload, length);
|
||||
}
|
||||
|
||||
bool WebSocketsServerCore::broadcastTXT(String & payload) {
|
||||
return broadcastTXT((uint8_t *)payload.c_str(), payload.length());
|
||||
}
|
||||
|
||||
/**
|
||||
* send binary data to client
|
||||
* @param num uint8_t client id
|
||||
* @param payload uint8_t *
|
||||
* @param length size_t
|
||||
* @param headerToPayload bool (see sendFrame for more details)
|
||||
* @return true if ok
|
||||
*/
|
||||
bool WebSocketsServerCore::sendBIN(uint8_t num, uint8_t * payload, size_t length, bool headerToPayload) {
|
||||
if(num >= WEBSOCKETS_SERVER_CLIENT_MAX) {
|
||||
return false;
|
||||
}
|
||||
WSclient_t * client = &_clients[num];
|
||||
if(clientIsConnected(client)) {
|
||||
return sendFrame(client, WSop_binary, payload, length, true, headerToPayload);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WebSocketsServerCore::sendBIN(uint8_t num, const uint8_t * payload, size_t length) {
|
||||
return sendBIN(num, (uint8_t *)payload, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* send binary data to client all
|
||||
* @param payload uint8_t *
|
||||
* @param length size_t
|
||||
* @param headerToPayload bool (see sendFrame for more details)
|
||||
* @return true if ok
|
||||
*/
|
||||
bool WebSocketsServerCore::broadcastBIN(uint8_t * payload, size_t length, bool headerToPayload) {
|
||||
WSclient_t * client;
|
||||
bool ret = true;
|
||||
for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
|
||||
client = &_clients[i];
|
||||
if(clientIsConnected(client)) {
|
||||
if(!sendFrame(client, WSop_binary, payload, length, true, headerToPayload)) {
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
WEBSOCKETS_YIELD();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool WebSocketsServerCore::broadcastBIN(const uint8_t * payload, size_t length) {
|
||||
return broadcastBIN((uint8_t *)payload, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* sends a WS ping to Client
|
||||
* @param num uint8_t client id
|
||||
* @param payload uint8_t *
|
||||
* @param length size_t
|
||||
* @return true if ping is send out
|
||||
*/
|
||||
bool WebSocketsServerCore::sendPing(uint8_t num, uint8_t * payload, size_t length) {
|
||||
if(num >= WEBSOCKETS_SERVER_CLIENT_MAX) {
|
||||
return false;
|
||||
}
|
||||
WSclient_t * client = &_clients[num];
|
||||
if(clientIsConnected(client)) {
|
||||
return sendFrame(client, WSop_ping, payload, length);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WebSocketsServerCore::sendPing(uint8_t num, String & payload) {
|
||||
return sendPing(num, (uint8_t *)payload.c_str(), payload.length());
|
||||
}
|
||||
|
||||
/**
|
||||
* sends a WS ping to all Client
|
||||
* @param payload uint8_t *
|
||||
* @param length size_t
|
||||
* @return true if ping is send out
|
||||
*/
|
||||
bool WebSocketsServerCore::broadcastPing(uint8_t * payload, size_t length) {
|
||||
WSclient_t * client;
|
||||
bool ret = true;
|
||||
for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
|
||||
client = &_clients[i];
|
||||
if(clientIsConnected(client)) {
|
||||
if(!sendFrame(client, WSop_ping, payload, length)) {
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
WEBSOCKETS_YIELD();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool WebSocketsServerCore::broadcastPing(String & payload) {
|
||||
return broadcastPing((uint8_t *)payload.c_str(), payload.length());
|
||||
}
|
||||
|
||||
/**
|
||||
* disconnect all clients
|
||||
*/
|
||||
void WebSocketsServerCore::disconnect(void) {
|
||||
WSclient_t * client;
|
||||
for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
|
||||
client = &_clients[i];
|
||||
if(clientIsConnected(client)) {
|
||||
WebSockets::clientDisconnect(client, 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* disconnect one client
|
||||
* @param num uint8_t client id
|
||||
*/
|
||||
void WebSocketsServerCore::disconnect(uint8_t num) {
|
||||
if(num >= WEBSOCKETS_SERVER_CLIENT_MAX) {
|
||||
return;
|
||||
}
|
||||
WSclient_t * client = &_clients[num];
|
||||
if(clientIsConnected(client)) {
|
||||
WebSockets::clientDisconnect(client, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* set the Authorization for the http request
|
||||
* @param user const char *
|
||||
* @param password const char *
|
||||
*/
|
||||
void WebSocketsServerCore::setAuthorization(const char * user, const char * password) {
|
||||
if(user && password) {
|
||||
String auth = user;
|
||||
auth += ":";
|
||||
auth += password;
|
||||
_base64Authorization = base64_encode((uint8_t *)auth.c_str(), auth.length());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* set the Authorizatio for the http request
|
||||
* @param auth const char * base64
|
||||
*/
|
||||
void WebSocketsServerCore::setAuthorization(const char * auth) {
|
||||
if(auth) {
|
||||
_base64Authorization = auth;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* count the connected clients (optional ping them)
|
||||
* @param ping bool ping the connected clients
|
||||
*/
|
||||
int WebSocketsServerCore::connectedClients(bool ping) {
|
||||
WSclient_t * client;
|
||||
int count = 0;
|
||||
for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
|
||||
client = &_clients[i];
|
||||
if(client->status == WSC_CONNECTED) {
|
||||
if(ping != true || sendPing(i)) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* see if one client is connected
|
||||
* @param num uint8_t client id
|
||||
*/
|
||||
bool WebSocketsServerCore::clientIsConnected(uint8_t num) {
|
||||
if(num >= WEBSOCKETS_SERVER_CLIENT_MAX) {
|
||||
return false;
|
||||
}
|
||||
WSclient_t * client = &_clients[num];
|
||||
return clientIsConnected(client);
|
||||
}
|
||||
|
||||
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
|
||||
/**
|
||||
* get an IP for a client
|
||||
* @param num uint8_t client id
|
||||
* @return IPAddress
|
||||
*/
|
||||
IPAddress WebSocketsServerCore::remoteIP(uint8_t num) {
|
||||
if(num < WEBSOCKETS_SERVER_CLIENT_MAX) {
|
||||
WSclient_t * client = &_clients[num];
|
||||
if(clientIsConnected(client)) {
|
||||
return client->tcp->remoteIP();
|
||||
}
|
||||
}
|
||||
|
||||
return IPAddress();
|
||||
}
|
||||
#endif
|
||||
|
||||
//#################################################################################
|
||||
//#################################################################################
|
||||
//#################################################################################
|
||||
|
||||
/**
|
||||
* handle new client connection
|
||||
* @param client
|
||||
*/
|
||||
WSclient_t * WebSocketsServerCore::newClient(WEBSOCKETS_NETWORK_CLASS * TCPclient) {
|
||||
WSclient_t * client;
|
||||
// search free list entry for client
|
||||
for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
|
||||
client = &_clients[i];
|
||||
|
||||
// state is not connected or tcp connection is lost
|
||||
if(!clientIsConnected(client)) {
|
||||
client->tcp = TCPclient;
|
||||
|
||||
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
|
||||
client->isSSL = false;
|
||||
client->tcp->setNoDelay(true);
|
||||
#endif
|
||||
#if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
|
||||
// set Timeout for readBytesUntil and readStringUntil
|
||||
client->tcp->setTimeout(WEBSOCKETS_TCP_TIMEOUT);
|
||||
#endif
|
||||
client->status = WSC_HEADER;
|
||||
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
|
||||
#ifndef NODEBUG_WEBSOCKETS
|
||||
IPAddress ip = client->tcp->remoteIP();
|
||||
#endif
|
||||
DEBUG_WEBSOCKETS("[WS-Server][%d] new client from %d.%d.%d.%d\n", client->num, ip[0], ip[1], ip[2], ip[3]);
|
||||
#else
|
||||
DEBUG_WEBSOCKETS("[WS-Server][%d] new client\n", client->num);
|
||||
#endif
|
||||
|
||||
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
|
||||
client->tcp->onDisconnect(std::bind([](WebSocketsServerCore * server, AsyncTCPbuffer * obj, WSclient_t * client) -> bool {
|
||||
DEBUG_WEBSOCKETS("[WS-Server][%d] Disconnect client\n", client->num);
|
||||
|
||||
AsyncTCPbuffer ** sl = &server->_clients[client->num].tcp;
|
||||
if(*sl == obj) {
|
||||
client->status = WSC_NOT_CONNECTED;
|
||||
*sl = NULL;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
this, std::placeholders::_1, client));
|
||||
|
||||
client->tcp->readStringUntil('\n', &(client->cHttpLine), std::bind(&WebSocketsServerCore::handleHeader, this, client, &(client->cHttpLine)));
|
||||
#endif
|
||||
|
||||
client->pingInterval = _pingInterval;
|
||||
client->pongTimeout = _pongTimeout;
|
||||
client->disconnectTimeoutCount = _disconnectTimeoutCount;
|
||||
client->lastPing = millis();
|
||||
client->pongReceived = false;
|
||||
|
||||
return client;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param client WSclient_t * ptr to the client struct
|
||||
* @param opcode WSopcode_t
|
||||
* @param payload uint8_t *
|
||||
* @param length size_t
|
||||
*/
|
||||
void WebSocketsServerCore::messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin) {
|
||||
WStype_t type = WStype_ERROR;
|
||||
|
||||
switch(opcode) {
|
||||
case WSop_text:
|
||||
type = fin ? WStype_TEXT : WStype_FRAGMENT_TEXT_START;
|
||||
break;
|
||||
case WSop_binary:
|
||||
type = fin ? WStype_BIN : WStype_FRAGMENT_BIN_START;
|
||||
break;
|
||||
case WSop_continuation:
|
||||
type = fin ? WStype_FRAGMENT_FIN : WStype_FRAGMENT;
|
||||
break;
|
||||
case WSop_ping:
|
||||
type = WStype_PING;
|
||||
break;
|
||||
case WSop_pong:
|
||||
type = WStype_PONG;
|
||||
break;
|
||||
case WSop_close:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
runCbEvent(client->num, type, payload, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Discard a native client
|
||||
* @param client WSclient_t * ptr to the client struct contaning the native client "->tcp"
|
||||
*/
|
||||
void WebSocketsServerCore::dropNativeClient(WSclient_t * client) {
|
||||
if(!client) {
|
||||
return;
|
||||
}
|
||||
if(client->tcp) {
|
||||
if(client->tcp->connected()) {
|
||||
#if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) && (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP32)
|
||||
client->tcp->flush();
|
||||
#endif
|
||||
client->tcp->stop();
|
||||
}
|
||||
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
|
||||
client->status = WSC_NOT_CONNECTED;
|
||||
#else
|
||||
delete client->tcp;
|
||||
#endif
|
||||
client->tcp = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnect an client
|
||||
* @param client WSclient_t * ptr to the client struct
|
||||
*/
|
||||
void WebSocketsServerCore::clientDisconnect(WSclient_t * client) {
|
||||
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
|
||||
if(client->isSSL && client->ssl) {
|
||||
if(client->ssl->connected()) {
|
||||
client->ssl->flush();
|
||||
client->ssl->stop();
|
||||
}
|
||||
delete client->ssl;
|
||||
client->ssl = NULL;
|
||||
client->tcp = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
dropNativeClient(client);
|
||||
|
||||
client->cUrl = "";
|
||||
client->cKey = "";
|
||||
client->cProtocol = "";
|
||||
client->cVersion = 0;
|
||||
client->cIsUpgrade = false;
|
||||
client->cIsWebsocket = false;
|
||||
|
||||
client->cWsRXsize = 0;
|
||||
|
||||
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
|
||||
client->cHttpLine = "";
|
||||
#endif
|
||||
|
||||
client->status = WSC_NOT_CONNECTED;
|
||||
|
||||
DEBUG_WEBSOCKETS("[WS-Server][%d] client disconnected.\n", client->num);
|
||||
|
||||
runCbEvent(client->num, WStype_DISCONNECTED, NULL, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* get client state
|
||||
* @param client WSclient_t * ptr to the client struct
|
||||
* @return true = connected
|
||||
*/
|
||||
bool WebSocketsServerCore::clientIsConnected(WSclient_t * client) {
|
||||
if(!client->tcp) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(client->tcp->connected()) {
|
||||
if(client->status != WSC_NOT_CONNECTED) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
// client lost
|
||||
if(client->status != WSC_NOT_CONNECTED) {
|
||||
DEBUG_WEBSOCKETS("[WS-Server][%d] client connection lost.\n", client->num);
|
||||
// do cleanup
|
||||
clientDisconnect(client);
|
||||
}
|
||||
}
|
||||
|
||||
if(client->tcp) {
|
||||
// do cleanup
|
||||
DEBUG_WEBSOCKETS("[WS-Server][%d] client list cleanup.\n", client->num);
|
||||
clientDisconnect(client);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
|
||||
/**
|
||||
* Handle incoming Connection Request
|
||||
*/
|
||||
WSclient_t * WebSocketsServerCore::handleNewClient(WEBSOCKETS_NETWORK_CLASS * tcpClient) {
|
||||
WSclient_t * client = newClient(tcpClient);
|
||||
|
||||
if(!client) {
|
||||
// no free space to handle client
|
||||
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
|
||||
#ifndef NODEBUG_WEBSOCKETS
|
||||
IPAddress ip = tcpClient->remoteIP();
|
||||
#endif
|
||||
DEBUG_WEBSOCKETS("[WS-Server] no free space new client from %d.%d.%d.%d\n", ip[0], ip[1], ip[2], ip[3]);
|
||||
#else
|
||||
DEBUG_WEBSOCKETS("[WS-Server] no free space new client\n");
|
||||
#endif
|
||||
// no client! => create dummy!
|
||||
WSclient_t dummy = WSclient_t();
|
||||
client = &dummy;
|
||||
client->tcp = tcpClient;
|
||||
dropNativeClient(client);
|
||||
}
|
||||
|
||||
WEBSOCKETS_YIELD();
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle incoming Connection Request
|
||||
*/
|
||||
void WebSocketsServer::handleNewClients(void) {
|
||||
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
|
||||
while(_server->hasClient()) {
|
||||
#endif
|
||||
|
||||
// store new connection
|
||||
WEBSOCKETS_NETWORK_CLASS * tcpClient = new WEBSOCKETS_NETWORK_CLASS(_server->available());
|
||||
if(!tcpClient) {
|
||||
DEBUG_WEBSOCKETS("[WS-Client] creating Network class failed!");
|
||||
return;
|
||||
}
|
||||
|
||||
handleNewClient(tcpClient);
|
||||
|
||||
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Handel incomming data from Client
|
||||
*/
|
||||
void WebSocketsServerCore::handleClientData(void) {
|
||||
WSclient_t * client;
|
||||
for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
|
||||
client = &_clients[i];
|
||||
if(clientIsConnected(client)) {
|
||||
int len = client->tcp->available();
|
||||
if(len > 0) {
|
||||
// DEBUG_WEBSOCKETS("[WS-Server][%d][handleClientData] len: %d\n", client->num, len);
|
||||
switch(client->status) {
|
||||
case WSC_HEADER: {
|
||||
String headerLine = client->tcp->readStringUntil('\n');
|
||||
handleHeader(client, &headerLine);
|
||||
} break;
|
||||
case WSC_CONNECTED:
|
||||
WebSockets::handleWebsocket(client);
|
||||
break;
|
||||
default:
|
||||
DEBUG_WEBSOCKETS("[WS-Server][%d][handleClientData] unknown client status %d\n", client->num, client->status);
|
||||
WebSockets::clientDisconnect(client, 1002);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
handleHBPing(client);
|
||||
handleHBTimeout(client);
|
||||
}
|
||||
WEBSOCKETS_YIELD();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* returns an indicator whether the given named header exists in the configured _mandatoryHttpHeaders collection
|
||||
* @param headerName String ///< the name of the header being checked
|
||||
*/
|
||||
bool WebSocketsServerCore::hasMandatoryHeader(String headerName) {
|
||||
for(size_t i = 0; i < _mandatoryHttpHeaderCount; i++) {
|
||||
if(_mandatoryHttpHeaders[i].equalsIgnoreCase(headerName))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* handles http header reading for WebSocket upgrade
|
||||
* @param client WSclient_t * ///< pointer to the client struct
|
||||
* @param headerLine String ///< the header being read / processed
|
||||
*/
|
||||
void WebSocketsServerCore::handleHeader(WSclient_t * client, String * headerLine) {
|
||||
static const char * NEW_LINE = "\r\n";
|
||||
|
||||
headerLine->trim(); // remove \r
|
||||
|
||||
if(headerLine->length() > 0) {
|
||||
DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] RX: %s\n", client->num, headerLine->c_str());
|
||||
|
||||
// websocket requests always start with GET see rfc6455
|
||||
if(headerLine->startsWith("GET ")) {
|
||||
// cut URL out
|
||||
client->cUrl = headerLine->substring(4, headerLine->indexOf(' ', 4));
|
||||
|
||||
// reset non-websocket http header validation state for this client
|
||||
client->cHttpHeadersValid = true;
|
||||
client->cMandatoryHeadersCount = 0;
|
||||
|
||||
} else if(headerLine->indexOf(':') >= 0) {
|
||||
String headerName = headerLine->substring(0, headerLine->indexOf(':'));
|
||||
String headerValue = headerLine->substring(headerLine->indexOf(':') + 1);
|
||||
|
||||
// remove space in the beginning (RFC2616)
|
||||
if(headerValue[0] == ' ') {
|
||||
headerValue.remove(0, 1);
|
||||
}
|
||||
|
||||
if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Connection"))) {
|
||||
headerValue.toLowerCase();
|
||||
if(headerValue.indexOf(WEBSOCKETS_STRING("upgrade")) >= 0) {
|
||||
client->cIsUpgrade = true;
|
||||
}
|
||||
} else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Upgrade"))) {
|
||||
if(headerValue.equalsIgnoreCase(WEBSOCKETS_STRING("websocket"))) {
|
||||
client->cIsWebsocket = true;
|
||||
}
|
||||
} else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Version"))) {
|
||||
client->cVersion = headerValue.toInt();
|
||||
} else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Key"))) {
|
||||
client->cKey = headerValue;
|
||||
client->cKey.trim(); // see rfc6455
|
||||
} else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Protocol"))) {
|
||||
client->cProtocol = headerValue;
|
||||
} else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Extensions"))) {
|
||||
client->cExtensions = headerValue;
|
||||
} else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Authorization"))) {
|
||||
client->base64Authorization = headerValue;
|
||||
} else {
|
||||
client->cHttpHeadersValid &= execHttpHeaderValidation(headerName, headerValue);
|
||||
if(_mandatoryHttpHeaderCount > 0 && hasMandatoryHeader(headerName)) {
|
||||
client->cMandatoryHeadersCount++;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Header error (%s)\n", headerLine->c_str());
|
||||
}
|
||||
|
||||
(*headerLine) = "";
|
||||
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
|
||||
client->tcp->readStringUntil('\n', &(client->cHttpLine), std::bind(&WebSocketsServerCore::handleHeader, this, client, &(client->cHttpLine)));
|
||||
#endif
|
||||
} else {
|
||||
DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] Header read fin.\n", client->num);
|
||||
DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cURL: %s\n", client->num, client->cUrl.c_str());
|
||||
DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cIsUpgrade: %d\n", client->num, client->cIsUpgrade);
|
||||
DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cIsWebsocket: %d\n", client->num, client->cIsWebsocket);
|
||||
DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cKey: %s\n", client->num, client->cKey.c_str());
|
||||
DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cProtocol: %s\n", client->num, client->cProtocol.c_str());
|
||||
DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cExtensions: %s\n", client->num, client->cExtensions.c_str());
|
||||
DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cVersion: %d\n", client->num, client->cVersion);
|
||||
DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - base64Authorization: %s\n", client->num, client->base64Authorization.c_str());
|
||||
DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cHttpHeadersValid: %d\n", client->num, client->cHttpHeadersValid);
|
||||
DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cMandatoryHeadersCount: %d\n", client->num, client->cMandatoryHeadersCount);
|
||||
|
||||
bool ok = (client->cIsUpgrade && client->cIsWebsocket);
|
||||
|
||||
if(ok) {
|
||||
if(client->cUrl.length() == 0) {
|
||||
ok = false;
|
||||
}
|
||||
if(client->cKey.length() == 0) {
|
||||
ok = false;
|
||||
}
|
||||
if(client->cVersion != 13) {
|
||||
ok = false;
|
||||
}
|
||||
if(!client->cHttpHeadersValid) {
|
||||
ok = false;
|
||||
}
|
||||
if(client->cMandatoryHeadersCount != _mandatoryHttpHeaderCount) {
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
|
||||
if(_base64Authorization.length() > 0) {
|
||||
String auth = WEBSOCKETS_STRING("Basic ");
|
||||
auth += _base64Authorization;
|
||||
if(auth != client->base64Authorization) {
|
||||
DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] HTTP Authorization failed!\n", client->num);
|
||||
handleAuthorizationFailed(client);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(ok) {
|
||||
DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] Websocket connection incoming.\n", client->num);
|
||||
|
||||
// generate Sec-WebSocket-Accept key
|
||||
String sKey = acceptKey(client->cKey);
|
||||
|
||||
DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - sKey: %s\n", client->num, sKey.c_str());
|
||||
|
||||
client->status = WSC_CONNECTED;
|
||||
|
||||
String handshake = WEBSOCKETS_STRING(
|
||||
"HTTP/1.1 101 Switching Protocols\r\n"
|
||||
"Server: arduino-WebSocketsServer\r\n"
|
||||
"Upgrade: websocket\r\n"
|
||||
"Connection: Upgrade\r\n"
|
||||
"Sec-WebSocket-Version: 13\r\n"
|
||||
"Sec-WebSocket-Accept: ");
|
||||
handshake += sKey + NEW_LINE;
|
||||
|
||||
if(_origin.length() > 0) {
|
||||
handshake += WEBSOCKETS_STRING("Access-Control-Allow-Origin: ");
|
||||
handshake += _origin + NEW_LINE;
|
||||
}
|
||||
|
||||
if(client->cProtocol.length() > 0) {
|
||||
handshake += WEBSOCKETS_STRING("Sec-WebSocket-Protocol: ");
|
||||
handshake += _protocol + NEW_LINE;
|
||||
}
|
||||
|
||||
// header end
|
||||
handshake += NEW_LINE;
|
||||
|
||||
DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] handshake %s", client->num, (uint8_t *)handshake.c_str());
|
||||
|
||||
write(client, (uint8_t *)handshake.c_str(), handshake.length());
|
||||
|
||||
headerDone(client);
|
||||
|
||||
// send ping
|
||||
WebSockets::sendFrame(client, WSop_ping);
|
||||
|
||||
runCbEvent(client->num, WStype_CONNECTED, (uint8_t *)client->cUrl.c_str(), client->cUrl.length());
|
||||
|
||||
} else {
|
||||
handleNonWebsocketConnection(client);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* send heartbeat ping to server in set intervals
|
||||
*/
|
||||
void WebSocketsServerCore::handleHBPing(WSclient_t * client) {
|
||||
if(client->pingInterval == 0)
|
||||
return;
|
||||
uint32_t pi = millis() - client->lastPing;
|
||||
if(pi > client->pingInterval) {
|
||||
DEBUG_WEBSOCKETS("[WS-Server][%d] sending HB ping\n", client->num);
|
||||
if(sendPing(client->num)) {
|
||||
client->lastPing = millis();
|
||||
client->pongReceived = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* enable ping/pong heartbeat process
|
||||
* @param pingInterval uint32_t how often ping will be sent
|
||||
* @param pongTimeout uint32_t millis after which pong should timout if not received
|
||||
* @param disconnectTimeoutCount uint8_t how many timeouts before disconnect, 0=> do not disconnect
|
||||
*/
|
||||
void WebSocketsServerCore::enableHeartbeat(uint32_t pingInterval, uint32_t pongTimeout, uint8_t disconnectTimeoutCount) {
|
||||
_pingInterval = pingInterval;
|
||||
_pongTimeout = pongTimeout;
|
||||
_disconnectTimeoutCount = disconnectTimeoutCount;
|
||||
|
||||
WSclient_t * client;
|
||||
for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
|
||||
client = &_clients[i];
|
||||
WebSockets::enableHeartbeat(client, pingInterval, pongTimeout, disconnectTimeoutCount);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* disable ping/pong heartbeat process
|
||||
*/
|
||||
void WebSocketsServerCore::disableHeartbeat() {
|
||||
_pingInterval = 0;
|
||||
|
||||
WSclient_t * client;
|
||||
for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
|
||||
client = &_clients[i];
|
||||
client->pingInterval = 0;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////
|
||||
// WebSocketServer
|
||||
|
||||
/**
|
||||
* called to initialize the Websocket server
|
||||
*/
|
||||
void WebSocketsServer::begin(void) {
|
||||
WebSocketsServerCore::begin();
|
||||
_server->begin();
|
||||
|
||||
DEBUG_WEBSOCKETS("[WS-Server] Server Started.\n");
|
||||
}
|
||||
|
||||
void WebSocketsServer::close(void) {
|
||||
WebSocketsServerCore::close();
|
||||
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
||||
_server->close();
|
||||
#elif(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
|
||||
_server->end();
|
||||
#else
|
||||
// TODO how to close server?
|
||||
#endif
|
||||
}
|
||||
|
||||
#if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
|
||||
/**
|
||||
* called in arduino loop
|
||||
*/
|
||||
void WebSocketsServerCore::loop(void) {
|
||||
if(_runnning) {
|
||||
WEBSOCKETS_YIELD();
|
||||
handleClientData();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* called in arduino loop
|
||||
*/
|
||||
void WebSocketsServer::loop(void) {
|
||||
if(_runnning) {
|
||||
WEBSOCKETS_YIELD();
|
||||
handleNewClients();
|
||||
WebSocketsServerCore::loop();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
243
lib/WebSockets/src/WebSocketsServer.h
Normal file
243
lib/WebSockets/src/WebSocketsServer.h
Normal file
@@ -0,0 +1,243 @@
|
||||
/**
|
||||
* @file WebSocketsServer.h
|
||||
* @date 20.05.2015
|
||||
* @author Markus Sattler
|
||||
*
|
||||
* Copyright (c) 2015 Markus Sattler. All rights reserved.
|
||||
* This file is part of the WebSockets for Arduino.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef WEBSOCKETSSERVER_H_
|
||||
#define WEBSOCKETSSERVER_H_
|
||||
|
||||
#include "WebSockets.h"
|
||||
|
||||
#ifndef WEBSOCKETS_SERVER_CLIENT_MAX
|
||||
#define WEBSOCKETS_SERVER_CLIENT_MAX (5)
|
||||
#endif
|
||||
|
||||
class WebSocketsServerCore : protected WebSockets {
|
||||
public:
|
||||
WebSocketsServerCore(const String & origin = "", const String & protocol = "arduino");
|
||||
virtual ~WebSocketsServerCore(void);
|
||||
|
||||
void begin(void);
|
||||
void close(void);
|
||||
|
||||
#ifdef __AVR__
|
||||
typedef void (*WebSocketServerEvent)(uint8_t num, WStype_t type, uint8_t * payload, size_t length);
|
||||
typedef bool (*WebSocketServerHttpHeaderValFunc)(String headerName, String headerValue);
|
||||
#else
|
||||
typedef std::function<void(uint8_t num, WStype_t type, uint8_t * payload, size_t length)> WebSocketServerEvent;
|
||||
typedef std::function<bool(String headerName, String headerValue)> WebSocketServerHttpHeaderValFunc;
|
||||
#endif
|
||||
|
||||
void onEvent(WebSocketServerEvent cbEvent);
|
||||
void onValidateHttpHeader(
|
||||
WebSocketServerHttpHeaderValFunc validationFunc,
|
||||
const char * mandatoryHttpHeaders[],
|
||||
size_t mandatoryHttpHeaderCount);
|
||||
|
||||
bool sendTXT(uint8_t num, uint8_t * payload, size_t length = 0, bool headerToPayload = false);
|
||||
bool sendTXT(uint8_t num, const uint8_t * payload, size_t length = 0);
|
||||
bool sendTXT(uint8_t num, char * payload, size_t length = 0, bool headerToPayload = false);
|
||||
bool sendTXT(uint8_t num, const char * payload, size_t length = 0);
|
||||
bool sendTXT(uint8_t num, String & payload);
|
||||
|
||||
bool broadcastTXT(uint8_t * payload, size_t length = 0, bool headerToPayload = false);
|
||||
bool broadcastTXT(const uint8_t * payload, size_t length = 0);
|
||||
bool broadcastTXT(char * payload, size_t length = 0, bool headerToPayload = false);
|
||||
bool broadcastTXT(const char * payload, size_t length = 0);
|
||||
bool broadcastTXT(String & payload);
|
||||
|
||||
bool sendBIN(uint8_t num, uint8_t * payload, size_t length, bool headerToPayload = false);
|
||||
bool sendBIN(uint8_t num, const uint8_t * payload, size_t length);
|
||||
|
||||
bool broadcastBIN(uint8_t * payload, size_t length, bool headerToPayload = false);
|
||||
bool broadcastBIN(const uint8_t * payload, size_t length);
|
||||
|
||||
bool sendPing(uint8_t num, uint8_t * payload = NULL, size_t length = 0);
|
||||
bool sendPing(uint8_t num, String & payload);
|
||||
|
||||
bool broadcastPing(uint8_t * payload = NULL, size_t length = 0);
|
||||
bool broadcastPing(String & payload);
|
||||
|
||||
void disconnect(void);
|
||||
void disconnect(uint8_t num);
|
||||
|
||||
void setAuthorization(const char * user, const char * password);
|
||||
void setAuthorization(const char * auth);
|
||||
|
||||
int connectedClients(bool ping = false);
|
||||
|
||||
bool clientIsConnected(uint8_t num);
|
||||
|
||||
void enableHeartbeat(uint32_t pingInterval, uint32_t pongTimeout, uint8_t disconnectTimeoutCount);
|
||||
void disableHeartbeat();
|
||||
|
||||
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
|
||||
IPAddress remoteIP(uint8_t num);
|
||||
#endif
|
||||
|
||||
#if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
|
||||
void loop(void); // handle client data only
|
||||
#endif
|
||||
|
||||
WSclient_t * newClient(WEBSOCKETS_NETWORK_CLASS * TCPclient);
|
||||
|
||||
protected:
|
||||
String _origin;
|
||||
String _protocol;
|
||||
String _base64Authorization; ///< Base64 encoded Auth request
|
||||
String * _mandatoryHttpHeaders;
|
||||
size_t _mandatoryHttpHeaderCount;
|
||||
|
||||
WSclient_t _clients[WEBSOCKETS_SERVER_CLIENT_MAX];
|
||||
|
||||
WebSocketServerEvent _cbEvent;
|
||||
WebSocketServerHttpHeaderValFunc _httpHeaderValidationFunc;
|
||||
|
||||
bool _runnning;
|
||||
|
||||
uint32_t _pingInterval;
|
||||
uint32_t _pongTimeout;
|
||||
uint8_t _disconnectTimeoutCount;
|
||||
|
||||
void messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin);
|
||||
|
||||
void clientDisconnect(WSclient_t * client);
|
||||
bool clientIsConnected(WSclient_t * client);
|
||||
|
||||
#if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
|
||||
void handleClientData(void);
|
||||
#endif
|
||||
|
||||
void handleHeader(WSclient_t * client, String * headerLine);
|
||||
|
||||
void handleHBPing(WSclient_t * client); // send ping in specified intervals
|
||||
|
||||
/**
|
||||
* called if a non Websocket connection is coming in.
|
||||
* Note: can be override
|
||||
* @param client WSclient_t * ptr to the client struct
|
||||
*/
|
||||
virtual void handleNonWebsocketConnection(WSclient_t * client) {
|
||||
DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] no Websocket connection close.\n", client->num);
|
||||
client->tcp->write(
|
||||
"HTTP/1.1 400 Bad Request\r\n"
|
||||
"Server: arduino-WebSocket-Server\r\n"
|
||||
"Content-Type: text/plain\r\n"
|
||||
"Content-Length: 32\r\n"
|
||||
"Connection: close\r\n"
|
||||
"Sec-WebSocket-Version: 13\r\n"
|
||||
"\r\n"
|
||||
"This is a Websocket server only!");
|
||||
clientDisconnect(client);
|
||||
}
|
||||
|
||||
/**
|
||||
* called if a non Authorization connection is coming in.
|
||||
* Note: can be override
|
||||
* @param client WSclient_t * ptr to the client struct
|
||||
*/
|
||||
virtual void handleAuthorizationFailed(WSclient_t * client) {
|
||||
client->tcp->write(
|
||||
"HTTP/1.1 401 Unauthorized\r\n"
|
||||
"Server: arduino-WebSocket-Server\r\n"
|
||||
"Content-Type: text/plain\r\n"
|
||||
"Content-Length: 45\r\n"
|
||||
"Connection: close\r\n"
|
||||
"Sec-WebSocket-Version: 13\r\n"
|
||||
"WWW-Authenticate: Basic realm=\"WebSocket Server\""
|
||||
"\r\n"
|
||||
"This Websocket server requires Authorization!");
|
||||
clientDisconnect(client);
|
||||
}
|
||||
|
||||
/**
|
||||
* called for sending a Event to the app
|
||||
* @param num uint8_t
|
||||
* @param type WStype_t
|
||||
* @param payload uint8_t *
|
||||
* @param length size_t
|
||||
*/
|
||||
virtual void runCbEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
|
||||
if(_cbEvent) {
|
||||
_cbEvent(num, type, payload, length);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Called at client socket connect handshake negotiation time for each http header that is not
|
||||
* a websocket specific http header (not Connection, Upgrade, Sec-WebSocket-*)
|
||||
* If the custom httpHeaderValidationFunc returns false for any headerName / headerValue passed, the
|
||||
* socket negotiation is considered invalid and the upgrade to websockets request is denied / rejected
|
||||
* This mechanism can be used to enable custom authentication schemes e.g. test the value
|
||||
* of a session cookie to determine if a user is logged on / authenticated
|
||||
*/
|
||||
virtual bool execHttpHeaderValidation(String headerName, String headerValue) {
|
||||
if(_httpHeaderValidationFunc) {
|
||||
// return the value of the custom http header validation function
|
||||
return _httpHeaderValidationFunc(headerName, headerValue);
|
||||
}
|
||||
// no custom http header validation so just assume all is good
|
||||
return true;
|
||||
}
|
||||
|
||||
#if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
|
||||
WSclient_t * handleNewClient(WEBSOCKETS_NETWORK_CLASS * tcpClient);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* drop native tcp connection (client->tcp)
|
||||
*/
|
||||
void dropNativeClient(WSclient_t * client);
|
||||
|
||||
private:
|
||||
/*
|
||||
* returns an indicator whether the given named header exists in the configured _mandatoryHttpHeaders collection
|
||||
* @param headerName String ///< the name of the header being checked
|
||||
*/
|
||||
bool hasMandatoryHeader(String headerName);
|
||||
};
|
||||
|
||||
class WebSocketsServer : public WebSocketsServerCore {
|
||||
public:
|
||||
WebSocketsServer(uint16_t port, const String & origin = "", const String & protocol = "arduino");
|
||||
virtual ~WebSocketsServer(void);
|
||||
|
||||
void begin(void);
|
||||
void close(void);
|
||||
|
||||
#if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
|
||||
void loop(void); // handle incoming client and client data
|
||||
#else
|
||||
// Async interface not need a loop call
|
||||
void loop(void) __attribute__((deprecated)) {}
|
||||
#endif
|
||||
|
||||
protected:
|
||||
#if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
|
||||
void handleNewClients(void);
|
||||
#endif
|
||||
|
||||
uint16_t _port;
|
||||
WEBSOCKETS_NETWORK_SERVER_CLASS * _server;
|
||||
};
|
||||
|
||||
#endif /* WEBSOCKETSSERVER_H_ */
|
||||
36
lib/WebSockets/src/WebSocketsVersion.h
Normal file
36
lib/WebSockets/src/WebSocketsVersion.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* @file WebSocketsVersion.h
|
||||
* @date 05.04.2022
|
||||
* @author Markus Sattler
|
||||
*
|
||||
* Copyright (c) 2015 Markus Sattler. All rights reserved.
|
||||
* This file is part of the WebSockets for Arduino.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef WEBSOCKETSVERSION_H_
|
||||
#define WEBSOCKETSVERSION_H_
|
||||
|
||||
#define WEBSOCKETS_VERSION "2.3.7"
|
||||
|
||||
#define WEBSOCKETS_VERSION_MAJOR 2
|
||||
#define WEBSOCKETS_VERSION_MINOR 3
|
||||
#define WEBSOCKETS_VERSION_PATCH 7
|
||||
|
||||
#define WEBSOCKETS_VERSION_INT 2003007
|
||||
|
||||
#endif /* WEBSOCKETSVERSION_H_ */
|
||||
7
lib/WebSockets/src/libb64/AUTHORS
Normal file
7
lib/WebSockets/src/libb64/AUTHORS
Normal file
@@ -0,0 +1,7 @@
|
||||
libb64: Base64 Encoding/Decoding Routines
|
||||
======================================
|
||||
|
||||
Authors:
|
||||
-------
|
||||
|
||||
Chris Venter chris.venter@gmail.com http://rocketpod.blogspot.com
|
||||
29
lib/WebSockets/src/libb64/LICENSE
Normal file
29
lib/WebSockets/src/libb64/LICENSE
Normal file
@@ -0,0 +1,29 @@
|
||||
Copyright-Only Dedication (based on United States law)
|
||||
or Public Domain Certification
|
||||
|
||||
The person or persons who have associated work with this document (the
|
||||
"Dedicator" or "Certifier") hereby either (a) certifies that, to the best of
|
||||
his knowledge, the work of authorship identified is in the public domain of the
|
||||
country from which the work is published, or (b) hereby dedicates whatever
|
||||
copyright the dedicators holds in the work of authorship identified below (the
|
||||
"Work") to the public domain. A certifier, moreover, dedicates any copyright
|
||||
interest he may have in the associated work, and for these purposes, is
|
||||
described as a "dedicator" below.
|
||||
|
||||
A certifier has taken reasonable steps to verify the copyright status of this
|
||||
work. Certifier recognizes that his good faith efforts may not shield him from
|
||||
liability if in fact the work certified is not in the public domain.
|
||||
|
||||
Dedicator makes this dedication for the benefit of the public at large and to
|
||||
the detriment of the Dedicator's heirs and successors. Dedicator intends this
|
||||
dedication to be an overt act of relinquishment in perpetuity of all present
|
||||
and future rights under copyright law, whether vested or contingent, in the
|
||||
Work. Dedicator understands that such relinquishment of all rights includes
|
||||
the relinquishment of all rights to enforce (by lawsuit or otherwise) those
|
||||
copyrights in the Work.
|
||||
|
||||
Dedicator recognizes that, once placed in the public domain, the Work may be
|
||||
freely reproduced, distributed, transmitted, used, modified, built upon, or
|
||||
otherwise exploited by anyone for any purpose, commercial or non-commercial,
|
||||
and in any way, including by methods that have not yet been invented or
|
||||
conceived.
|
||||
98
lib/WebSockets/src/libb64/cdecode.c
Normal file
98
lib/WebSockets/src/libb64/cdecode.c
Normal file
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
cdecoder.c - c source to a base64 decoding algorithm implementation
|
||||
|
||||
This is part of the libb64 project, and has been placed in the public domain.
|
||||
For details, see http://sourceforge.net/projects/libb64
|
||||
*/
|
||||
|
||||
#ifdef ESP8266
|
||||
#include <core_esp8266_features.h>
|
||||
#endif
|
||||
|
||||
#if defined(ESP32)
|
||||
#define CORE_HAS_LIBB64
|
||||
#endif
|
||||
|
||||
#ifndef CORE_HAS_LIBB64
|
||||
#include "cdecode_inc.h"
|
||||
|
||||
int base64_decode_value(char value_in)
|
||||
{
|
||||
static const char decoding[] = {62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-2,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51};
|
||||
static const char decoding_size = sizeof(decoding);
|
||||
value_in -= 43;
|
||||
if (value_in < 0 || value_in > decoding_size) return -1;
|
||||
return decoding[(int)value_in];
|
||||
}
|
||||
|
||||
void base64_init_decodestate(base64_decodestate* state_in)
|
||||
{
|
||||
state_in->step = step_a;
|
||||
state_in->plainchar = 0;
|
||||
}
|
||||
|
||||
int base64_decode_block(const char* code_in, const int length_in, char* plaintext_out, base64_decodestate* state_in)
|
||||
{
|
||||
const char* codechar = code_in;
|
||||
char* plainchar = plaintext_out;
|
||||
char fragment;
|
||||
|
||||
*plainchar = state_in->plainchar;
|
||||
|
||||
switch (state_in->step)
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
case step_a:
|
||||
do {
|
||||
if (codechar == code_in+length_in)
|
||||
{
|
||||
state_in->step = step_a;
|
||||
state_in->plainchar = *plainchar;
|
||||
return plainchar - plaintext_out;
|
||||
}
|
||||
fragment = (char)base64_decode_value(*codechar++);
|
||||
} while (fragment < 0);
|
||||
*plainchar = (fragment & 0x03f) << 2;
|
||||
case step_b:
|
||||
do {
|
||||
if (codechar == code_in+length_in)
|
||||
{
|
||||
state_in->step = step_b;
|
||||
state_in->plainchar = *plainchar;
|
||||
return plainchar - plaintext_out;
|
||||
}
|
||||
fragment = (char)base64_decode_value(*codechar++);
|
||||
} while (fragment < 0);
|
||||
*plainchar++ |= (fragment & 0x030) >> 4;
|
||||
*plainchar = (fragment & 0x00f) << 4;
|
||||
case step_c:
|
||||
do {
|
||||
if (codechar == code_in+length_in)
|
||||
{
|
||||
state_in->step = step_c;
|
||||
state_in->plainchar = *plainchar;
|
||||
return plainchar - plaintext_out;
|
||||
}
|
||||
fragment = (char)base64_decode_value(*codechar++);
|
||||
} while (fragment < 0);
|
||||
*plainchar++ |= (fragment & 0x03c) >> 2;
|
||||
*plainchar = (fragment & 0x003) << 6;
|
||||
case step_d:
|
||||
do {
|
||||
if (codechar == code_in+length_in)
|
||||
{
|
||||
state_in->step = step_d;
|
||||
state_in->plainchar = *plainchar;
|
||||
return plainchar - plaintext_out;
|
||||
}
|
||||
fragment = (char)base64_decode_value(*codechar++);
|
||||
} while (fragment < 0);
|
||||
*plainchar++ |= (fragment & 0x03f);
|
||||
}
|
||||
}
|
||||
/* control should not reach here */
|
||||
return plainchar - plaintext_out;
|
||||
}
|
||||
|
||||
#endif
|
||||
28
lib/WebSockets/src/libb64/cdecode_inc.h
Normal file
28
lib/WebSockets/src/libb64/cdecode_inc.h
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
cdecode.h - c header for a base64 decoding algorithm
|
||||
|
||||
This is part of the libb64 project, and has been placed in the public domain.
|
||||
For details, see http://sourceforge.net/projects/libb64
|
||||
*/
|
||||
|
||||
#ifndef BASE64_CDECODE_H
|
||||
#define BASE64_CDECODE_H
|
||||
|
||||
typedef enum
|
||||
{
|
||||
step_a, step_b, step_c, step_d
|
||||
} base64_decodestep;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
base64_decodestep step;
|
||||
char plainchar;
|
||||
} base64_decodestate;
|
||||
|
||||
void base64_init_decodestate(base64_decodestate* state_in);
|
||||
|
||||
int base64_decode_value(char value_in);
|
||||
|
||||
int base64_decode_block(const char* code_in, const int length_in, char* plaintext_out, base64_decodestate* state_in);
|
||||
|
||||
#endif /* BASE64_CDECODE_H */
|
||||
119
lib/WebSockets/src/libb64/cencode.c
Normal file
119
lib/WebSockets/src/libb64/cencode.c
Normal file
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
cencoder.c - c source to a base64 encoding algorithm implementation
|
||||
|
||||
This is part of the libb64 project, and has been placed in the public domain.
|
||||
For details, see http://sourceforge.net/projects/libb64
|
||||
*/
|
||||
|
||||
#ifdef ESP8266
|
||||
#include <core_esp8266_features.h>
|
||||
#endif
|
||||
|
||||
#if defined(ESP32)
|
||||
#define CORE_HAS_LIBB64
|
||||
#endif
|
||||
|
||||
#ifndef CORE_HAS_LIBB64
|
||||
#include "cencode_inc.h"
|
||||
|
||||
const int CHARS_PER_LINE = 72;
|
||||
|
||||
void base64_init_encodestate(base64_encodestate* state_in)
|
||||
{
|
||||
state_in->step = step_A;
|
||||
state_in->result = 0;
|
||||
state_in->stepcount = 0;
|
||||
}
|
||||
|
||||
char base64_encode_value(char value_in)
|
||||
{
|
||||
static const char* encoding = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
if (value_in > 63) return '=';
|
||||
return encoding[(int)value_in];
|
||||
}
|
||||
|
||||
int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, base64_encodestate* state_in)
|
||||
{
|
||||
const char* plainchar = plaintext_in;
|
||||
const char* const plaintextend = plaintext_in + length_in;
|
||||
char* codechar = code_out;
|
||||
char result;
|
||||
char fragment;
|
||||
|
||||
result = state_in->result;
|
||||
|
||||
switch (state_in->step)
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
case step_A:
|
||||
if (plainchar == plaintextend)
|
||||
{
|
||||
state_in->result = result;
|
||||
state_in->step = step_A;
|
||||
return codechar - code_out;
|
||||
}
|
||||
fragment = *plainchar++;
|
||||
result = (fragment & 0x0fc) >> 2;
|
||||
*codechar++ = base64_encode_value(result);
|
||||
result = (fragment & 0x003) << 4;
|
||||
case step_B:
|
||||
if (plainchar == plaintextend)
|
||||
{
|
||||
state_in->result = result;
|
||||
state_in->step = step_B;
|
||||
return codechar - code_out;
|
||||
}
|
||||
fragment = *plainchar++;
|
||||
result |= (fragment & 0x0f0) >> 4;
|
||||
*codechar++ = base64_encode_value(result);
|
||||
result = (fragment & 0x00f) << 2;
|
||||
case step_C:
|
||||
if (plainchar == plaintextend)
|
||||
{
|
||||
state_in->result = result;
|
||||
state_in->step = step_C;
|
||||
return codechar - code_out;
|
||||
}
|
||||
fragment = *plainchar++;
|
||||
result |= (fragment & 0x0c0) >> 6;
|
||||
*codechar++ = base64_encode_value(result);
|
||||
result = (fragment & 0x03f) >> 0;
|
||||
*codechar++ = base64_encode_value(result);
|
||||
|
||||
++(state_in->stepcount);
|
||||
if (state_in->stepcount == CHARS_PER_LINE/4)
|
||||
{
|
||||
*codechar++ = '\n';
|
||||
state_in->stepcount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* control should not reach here */
|
||||
return codechar - code_out;
|
||||
}
|
||||
|
||||
int base64_encode_blockend(char* code_out, base64_encodestate* state_in)
|
||||
{
|
||||
char* codechar = code_out;
|
||||
|
||||
switch (state_in->step)
|
||||
{
|
||||
case step_B:
|
||||
*codechar++ = base64_encode_value(state_in->result);
|
||||
*codechar++ = '=';
|
||||
*codechar++ = '=';
|
||||
break;
|
||||
case step_C:
|
||||
*codechar++ = base64_encode_value(state_in->result);
|
||||
*codechar++ = '=';
|
||||
break;
|
||||
case step_A:
|
||||
break;
|
||||
}
|
||||
*codechar++ = 0x00;
|
||||
|
||||
return codechar - code_out;
|
||||
}
|
||||
|
||||
#endif
|
||||
31
lib/WebSockets/src/libb64/cencode_inc.h
Normal file
31
lib/WebSockets/src/libb64/cencode_inc.h
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
cencode.h - c header for a base64 encoding algorithm
|
||||
|
||||
This is part of the libb64 project, and has been placed in the public domain.
|
||||
For details, see http://sourceforge.net/projects/libb64
|
||||
*/
|
||||
|
||||
#ifndef BASE64_CENCODE_H
|
||||
#define BASE64_CENCODE_H
|
||||
|
||||
typedef enum
|
||||
{
|
||||
step_A, step_B, step_C
|
||||
} base64_encodestep;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
base64_encodestep step;
|
||||
char result;
|
||||
int stepcount;
|
||||
} base64_encodestate;
|
||||
|
||||
void base64_init_encodestate(base64_encodestate* state_in);
|
||||
|
||||
char base64_encode_value(char value_in);
|
||||
|
||||
int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, base64_encodestate* state_in);
|
||||
|
||||
int base64_encode_blockend(char* code_out, base64_encodestate* state_in);
|
||||
|
||||
#endif /* BASE64_CENCODE_H */
|
||||
202
lib/WebSockets/src/libsha1/libsha1.c
Normal file
202
lib/WebSockets/src/libsha1/libsha1.c
Normal file
@@ -0,0 +1,202 @@
|
||||
/* from valgrind tests */
|
||||
|
||||
/* ================ sha1.c ================ */
|
||||
/*
|
||||
SHA-1 in C
|
||||
By Steve Reid <steve@edmweb.com>
|
||||
100% Public Domain
|
||||
|
||||
Test Vectors (from FIPS PUB 180-1)
|
||||
"abc"
|
||||
A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
|
||||
"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
|
||||
84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
|
||||
A million repetitions of "a"
|
||||
34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
|
||||
*/
|
||||
|
||||
/* #define LITTLE_ENDIAN * This should be #define'd already, if true. */
|
||||
/* #define SHA1HANDSOFF * Copies data before messing with it. */
|
||||
|
||||
#if !defined(ESP8266) && !defined(ESP32)
|
||||
|
||||
#define SHA1HANDSOFF
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "libsha1.h"
|
||||
|
||||
|
||||
#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
|
||||
|
||||
/* blk0() and blk() perform the initial expand. */
|
||||
/* I got the idea of expanding during the round function from SSLeay */
|
||||
#if BYTE_ORDER == LITTLE_ENDIAN
|
||||
#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
|
||||
|(rol(block->l[i],8)&0x00FF00FF))
|
||||
#elif BYTE_ORDER == BIG_ENDIAN
|
||||
#define blk0(i) block->l[i]
|
||||
#else
|
||||
#error "Endianness not defined!"
|
||||
#endif
|
||||
#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
|
||||
^block->l[(i+2)&15]^block->l[i&15],1))
|
||||
|
||||
/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
|
||||
#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
|
||||
#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
|
||||
#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
|
||||
#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
|
||||
#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
|
||||
|
||||
|
||||
/* Hash a single 512-bit block. This is the core of the algorithm. */
|
||||
|
||||
void SHA1Transform(uint32_t state[5], const unsigned char buffer[64])
|
||||
{
|
||||
uint32_t a, b, c, d, e;
|
||||
typedef union {
|
||||
unsigned char c[64];
|
||||
uint32_t l[16];
|
||||
} CHAR64LONG16;
|
||||
#ifdef SHA1HANDSOFF
|
||||
CHAR64LONG16 block[1]; /* use array to appear as a pointer */
|
||||
memcpy(block, buffer, 64);
|
||||
#else
|
||||
/* The following had better never be used because it causes the
|
||||
* pointer-to-const buffer to be cast into a pointer to non-const.
|
||||
* And the result is written through. I threw a "const" in, hoping
|
||||
* this will cause a diagnostic.
|
||||
*/
|
||||
CHAR64LONG16* block = (const CHAR64LONG16*)buffer;
|
||||
#endif
|
||||
/* Copy context->state[] to working vars */
|
||||
a = state[0];
|
||||
b = state[1];
|
||||
c = state[2];
|
||||
d = state[3];
|
||||
e = state[4];
|
||||
/* 4 rounds of 20 operations each. Loop unrolled. */
|
||||
R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
|
||||
R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
|
||||
R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
|
||||
R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
|
||||
R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
|
||||
R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
|
||||
R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
|
||||
R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
|
||||
R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
|
||||
R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
|
||||
R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
|
||||
R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
|
||||
R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
|
||||
R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
|
||||
R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
|
||||
R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
|
||||
R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
|
||||
R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
|
||||
R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
|
||||
R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
|
||||
/* Add the working vars back into context.state[] */
|
||||
state[0] += a;
|
||||
state[1] += b;
|
||||
state[2] += c;
|
||||
state[3] += d;
|
||||
state[4] += e;
|
||||
/* Wipe variables */
|
||||
a = b = c = d = e = 0;
|
||||
#ifdef SHA1HANDSOFF
|
||||
memset(block, '\0', sizeof(block));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/* SHA1Init - Initialize new context */
|
||||
|
||||
void SHA1Init(SHA1_CTX* context)
|
||||
{
|
||||
/* SHA1 initialization constants */
|
||||
context->state[0] = 0x67452301;
|
||||
context->state[1] = 0xEFCDAB89;
|
||||
context->state[2] = 0x98BADCFE;
|
||||
context->state[3] = 0x10325476;
|
||||
context->state[4] = 0xC3D2E1F0;
|
||||
context->count[0] = context->count[1] = 0;
|
||||
}
|
||||
|
||||
|
||||
/* Run your data through this. */
|
||||
|
||||
void SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len)
|
||||
{
|
||||
uint32_t i, j;
|
||||
|
||||
j = context->count[0];
|
||||
if ((context->count[0] += len << 3) < j)
|
||||
context->count[1]++;
|
||||
context->count[1] += (len>>29);
|
||||
j = (j >> 3) & 63;
|
||||
if ((j + len) > 63) {
|
||||
memcpy(&context->buffer[j], data, (i = 64-j));
|
||||
SHA1Transform(context->state, context->buffer);
|
||||
for ( ; i + 63 < len; i += 64) {
|
||||
SHA1Transform(context->state, &data[i]);
|
||||
}
|
||||
j = 0;
|
||||
}
|
||||
else i = 0;
|
||||
memcpy(&context->buffer[j], &data[i], len - i);
|
||||
}
|
||||
|
||||
|
||||
/* Add padding and return the message digest. */
|
||||
|
||||
void SHA1Final(unsigned char digest[20], SHA1_CTX* context)
|
||||
{
|
||||
unsigned i;
|
||||
unsigned char finalcount[8];
|
||||
unsigned char c;
|
||||
|
||||
#if 0 /* untested "improvement" by DHR */
|
||||
/* Convert context->count to a sequence of bytes
|
||||
* in finalcount. Second element first, but
|
||||
* big-endian order within element.
|
||||
* But we do it all backwards.
|
||||
*/
|
||||
unsigned char *fcp = &finalcount[8];
|
||||
|
||||
for (i = 0; i < 2; i++)
|
||||
{
|
||||
uint32_t t = context->count[i];
|
||||
int j;
|
||||
|
||||
for (j = 0; j < 4; t >>= 8, j++)
|
||||
*--fcp = (unsigned char) t;
|
||||
}
|
||||
#else
|
||||
for (i = 0; i < 8; i++) {
|
||||
finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
|
||||
>> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */
|
||||
}
|
||||
#endif
|
||||
c = 0200;
|
||||
SHA1Update(context, &c, 1);
|
||||
while ((context->count[0] & 504) != 448) {
|
||||
c = 0000;
|
||||
SHA1Update(context, &c, 1);
|
||||
}
|
||||
SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */
|
||||
for (i = 0; i < 20; i++) {
|
||||
digest[i] = (unsigned char)
|
||||
((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
|
||||
}
|
||||
/* Wipe variables */
|
||||
memset(context, '\0', sizeof(*context));
|
||||
memset(&finalcount, '\0', sizeof(finalcount));
|
||||
}
|
||||
/* ================ end of sha1.c ================ */
|
||||
|
||||
|
||||
#endif
|
||||
21
lib/WebSockets/src/libsha1/libsha1.h
Normal file
21
lib/WebSockets/src/libsha1/libsha1.h
Normal file
@@ -0,0 +1,21 @@
|
||||
/* ================ sha1.h ================ */
|
||||
/*
|
||||
SHA-1 in C
|
||||
By Steve Reid <steve@edmweb.com>
|
||||
100% Public Domain
|
||||
*/
|
||||
|
||||
#if !defined(ESP8266) && !defined(ESP32)
|
||||
|
||||
typedef struct {
|
||||
uint32_t state[5];
|
||||
uint32_t count[2];
|
||||
unsigned char buffer[64];
|
||||
} SHA1_CTX;
|
||||
|
||||
void SHA1Transform(uint32_t state[5], const unsigned char buffer[64]);
|
||||
void SHA1Init(SHA1_CTX* context);
|
||||
void SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len);
|
||||
void SHA1Final(unsigned char digest[20], SHA1_CTX* context);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user