From 74b0bf810cd3f344d576aabb8fa558f9e206095a Mon Sep 17 00:00:00 2001 From: anschrammh Date: Sun, 19 Dec 2021 13:14:00 +0100 Subject: [PATCH] Updated the WebSockets librarie because it didn't work with the version 3.0.2 of the sdk anymore due to a change in the SSL classes --- src/libs/WebSockets/README.md | 24 +- .../WebSocketClientSSLWithCA.ino | 103 +++++ .../WebSocketClientSocketIO.ino | 5 +- .../WebSocketClientSocketIOack.ino | 165 ++++++++ .../WebSocketServerHooked.ino | 103 +++++ .../esp8266/WebSocketServerHooked/emu | 20 + .../WebSocketServerHooked/ws-testclient.py | 45 ++ src/libs/WebSockets/library.json | 26 +- src/libs/WebSockets/library.properties | 2 +- src/libs/WebSockets/src/SocketIOclient.cpp | 96 ++++- src/libs/WebSockets/src/SocketIOclient.h | 21 + src/libs/WebSockets/src/WebSockets.cpp | 34 +- src/libs/WebSockets/src/WebSockets.h | 94 +++-- .../WebSockets/src/WebSockets4WebServer.h | 80 ++++ src/libs/WebSockets/src/WebSocketsClient.cpp | 167 ++++++-- src/libs/WebSockets/src/WebSocketsClient.h | 30 +- src/libs/WebSockets/src/WebSocketsServer.cpp | 396 +++++++++++------- src/libs/WebSockets/src/WebSocketsServer.h | 75 +++- src/libs/WebSockets/src/WebSocketsVersion.h | 36 ++ src/libs/WebSockets/travis/common.sh | 86 +++- src/libs/WebSockets/travis/version.py | 132 ++++++ 21 files changed, 1464 insertions(+), 276 deletions(-) create mode 100644 src/libs/WebSockets/examples/esp8266/WebSocketClientSSLWithCA/WebSocketClientSSLWithCA.ino create mode 100644 src/libs/WebSockets/examples/esp8266/WebSocketClientSocketIOack/WebSocketClientSocketIOack.ino create mode 100644 src/libs/WebSockets/examples/esp8266/WebSocketServerHooked/WebSocketServerHooked.ino create mode 100644 src/libs/WebSockets/examples/esp8266/WebSocketServerHooked/emu create mode 100644 src/libs/WebSockets/examples/esp8266/WebSocketServerHooked/ws-testclient.py create mode 100644 src/libs/WebSockets/src/WebSockets4WebServer.h create mode 100644 src/libs/WebSockets/src/WebSocketsVersion.h create mode 100644 src/libs/WebSockets/travis/version.py diff --git a/src/libs/WebSockets/README.md b/src/libs/WebSockets/README.md index 63eef3e..ae2497f 100644 --- a/src/libs/WebSockets/README.md +++ b/src/libs/WebSockets/README.md @@ -1,4 +1,4 @@ -WebSocket Server and Client for Arduino [![Build Status](https://travis-ci.org/Links2004/arduinoWebSockets.svg?branch=master)](https://travis-ci.org/Links2004/arduinoWebSockets) +WebSocket Server and Client for Arduino [![Build Status](https://github.com/Links2004/arduinoWebSockets/workflows/CI/badge.svg?branch=master)](https://github.com/Links2004/arduinoWebSockets/actions?query=workflow%3ACI+branch%3Amaster) =========================================== a WebSocket Server and Client for Arduino based on RFC6455. @@ -34,7 +34,9 @@ a WebSocket Server and Client for Arduino based on RFC6455. ###### Note: ###### - version 2.0 and up is not compatible with AVR/ATmega, check ATmega branch. + version 2.0.0 and up is not compatible with AVR/ATmega, check ATmega branch. + + version 2.3.0 has API changes for the ESP8266 BareSSL (may brakes existing code) Arduino for AVR not supports std namespace of c++. @@ -57,32 +59,34 @@ The mode can be activated in the ```WebSockets.h``` (see WEBSOCKETS_NETWORK_TYPE ### High Level Client API ### - `begin` : Initiate connection sequence to the websocket host. -``` +```c++ 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"); ``` - `onEvent`: Callback to handle for websocket events - ``` + ```c++ void onEvent(WebSocketClientEvent cbEvent); ``` - `WebSocketClientEvent`: Handler for websocket events - ``` + ```c++ void (*WebSocketClientEvent)(WStype_t type, uint8_t * payload, size_t length) ``` Where `WStype_t type` is defined as: - ``` + ```c++ 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_FRAGMENT_TEXT_START, + WStype_FRAGMENT_BIN_START, + WStype_FRAGMENT, + WStype_FRAGMENT_FIN, + WStype_PING, + WStype_PONG, } WStype_t; ``` diff --git a/src/libs/WebSockets/examples/esp8266/WebSocketClientSSLWithCA/WebSocketClientSSLWithCA.ino b/src/libs/WebSockets/examples/esp8266/WebSocketClientSSLWithCA/WebSocketClientSSLWithCA.ino new file mode 100644 index 0000000..214f5e6 --- /dev/null +++ b/src/libs/WebSockets/examples/esp8266/WebSocketClientSSLWithCA/WebSocketClientSSLWithCA.ino @@ -0,0 +1,103 @@ +/* + * WebSocketClientSSLWithCA.ino + * + * Created on: 27.10.2019 + * + * note SSL is only possible with the ESP8266 + * + */ + +#include + +#include +#include + +#include + +ESP8266WiFiMulti WiFiMulti; +WebSocketsClient webSocket; + +#define USE_SERIAL Serial1 + + +// Can be obtained with: +// openssl s_client -showcerts -connect echo.websocket.org:443 0; t--) { + USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t); + USE_SERIAL.flush(); + delay(1000); + } + + WiFiMulti.addAP("SSID", "passpasspass"); + + while(WiFiMulti.run() != WL_CONNECTED) { + delay(100); + } + + //When using BearSSL, client certificate and private key can be set: + //webSocket.setSSLClientCertKey(clientCert, clientPrivateKey); + //clientCert and clientPrivateKey can be of types (const char *, const char *) , or of types (BearSSL::X509List, BearSSL::PrivateKey) + + webSocket.beginSslWithCA("echo.websocket.org", 443, "/", ENDPOINT_CA_CERT); + webSocket.onEvent(webSocketEvent); +} + +void loop() { + webSocket.loop(); +} diff --git a/src/libs/WebSockets/examples/esp8266/WebSocketClientSocketIO/WebSocketClientSocketIO.ino b/src/libs/WebSockets/examples/esp8266/WebSocketClientSocketIO/WebSocketClientSocketIO.ino index a952e5d..b0568f4 100644 --- a/src/libs/WebSockets/examples/esp8266/WebSocketClientSocketIO/WebSocketClientSocketIO.ino +++ b/src/libs/WebSockets/examples/esp8266/WebSocketClientSocketIO/WebSocketClientSocketIO.ino @@ -29,6 +29,9 @@ void socketIOEvent(socketIOmessageType_t type, uint8_t * payload, size_t length) break; case sIOtype_CONNECT: USE_SERIAL.printf("[IOc] Connected to url: %s\n", payload); + + // join default namespace (no auto join in Socket.IO V3) + socketIO.send(sIOtype_CONNECT, "/"); break; case sIOtype_EVENT: USE_SERIAL.printf("[IOc] get event: %s\n", payload); @@ -110,7 +113,7 @@ void loop() { // add payload (parameters) for the event JsonObject param1 = array.createNestedObject(); - param1["now"] = now; + param1["now"] = (uint32_t) now; // JSON to String (serializion) String output; diff --git a/src/libs/WebSockets/examples/esp8266/WebSocketClientSocketIOack/WebSocketClientSocketIOack.ino b/src/libs/WebSockets/examples/esp8266/WebSocketClientSocketIOack/WebSocketClientSocketIOack.ino new file mode 100644 index 0000000..ab9ff68 --- /dev/null +++ b/src/libs/WebSockets/examples/esp8266/WebSocketClientSocketIOack/WebSocketClientSocketIOack.ino @@ -0,0 +1,165 @@ +/* + * WebSocketClientSocketIOack.ino + * + * Created on: 20.07.2019 + * + */ + +#include + +#include +#include + +#include + +#include +#include + +#include + +ESP8266WiFiMulti WiFiMulti; +SocketIOclient socketIO; + +#define USE_SERIAL Serial + + +void socketIOEvent(socketIOmessageType_t type, uint8_t * payload, size_t length) { + switch(type) { + case sIOtype_DISCONNECT: + USE_SERIAL.printf("[IOc] Disconnected!\n"); + break; + case sIOtype_CONNECT: + USE_SERIAL.printf("[IOc] Connected to url: %s\n", payload); + + // join default namespace (no auto join in Socket.IO V3) + socketIO.send(sIOtype_CONNECT, "/"); + break; + case sIOtype_EVENT: + { + char * sptr = NULL; + int id = strtol((char *)payload, &sptr, 10); + USE_SERIAL.printf("[IOc] get event: %s id: %d\n", payload, id); + if(id) { + payload = (uint8_t *)sptr; + } + DynamicJsonDocument doc(1024); + DeserializationError error = deserializeJson(doc, payload, length); + if(error) { + USE_SERIAL.print(F("deserializeJson() failed: ")); + USE_SERIAL.println(error.c_str()); + return; + } + + String eventName = doc[0]; + USE_SERIAL.printf("[IOc] event name: %s\n", eventName.c_str()); + + // Message Includes a ID for a ACK (callback) + if(id) { + // creat JSON message for Socket.IO (ack) + DynamicJsonDocument docOut(1024); + JsonArray array = docOut.to(); + + // add payload (parameters) for the ack (callback function) + JsonObject param1 = array.createNestedObject(); + param1["now"] = millis(); + + // JSON to String (serializion) + String output; + output += id; + serializeJson(docOut, output); + + // Send event + socketIO.send(sIOtype_ACK, output); + } + } + break; + case sIOtype_ACK: + USE_SERIAL.printf("[IOc] get ack: %u\n", length); + hexdump(payload, length); + break; + case sIOtype_ERROR: + USE_SERIAL.printf("[IOc] get error: %u\n", length); + hexdump(payload, length); + break; + case sIOtype_BINARY_EVENT: + USE_SERIAL.printf("[IOc] get binary: %u\n", length); + hexdump(payload, length); + break; + case sIOtype_BINARY_ACK: + USE_SERIAL.printf("[IOc] get binary ack: %u\n", length); + hexdump(payload, length); + break; + } +} + +void setup() { + //USE_SERIAL.begin(921600); + USE_SERIAL.begin(115200); + + //Serial.setDebugOutput(true); + USE_SERIAL.setDebugOutput(true); + + USE_SERIAL.println(); + USE_SERIAL.println(); + USE_SERIAL.println(); + + for(uint8_t t = 4; t > 0; t--) { + USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t); + USE_SERIAL.flush(); + delay(1000); + } + + // disable AP + if(WiFi.getMode() & WIFI_AP) { + WiFi.softAPdisconnect(true); + } + + WiFiMulti.addAP("SSID", "passpasspass"); + + //WiFi.disconnect(); + while(WiFiMulti.run() != WL_CONNECTED) { + delay(100); + } + + String ip = WiFi.localIP().toString(); + USE_SERIAL.printf("[SETUP] WiFi Connected %s\n", ip.c_str()); + + // server address, port and URL + socketIO.begin("10.11.100.100", 8880); + + // event handler + socketIO.onEvent(socketIOEvent); +} + +unsigned long messageTimestamp = 0; +void loop() { + socketIO.loop(); + + uint64_t now = millis(); + + if(now - messageTimestamp > 2000) { + messageTimestamp = now; + + // creat JSON message for Socket.IO (event) + DynamicJsonDocument doc(1024); + JsonArray array = doc.to(); + + // add evnet name + // Hint: socket.on('event_name', .... + array.add("event_name"); + + // add payload (parameters) for the event + JsonObject param1 = array.createNestedObject(); + param1["now"] = (uint32_t) now; + + // JSON to String (serializion) + String output; + serializeJson(doc, output); + + // Send event + socketIO.sendEVENT(output); + + // Print JSON for debugging + USE_SERIAL.println(output); + } +} diff --git a/src/libs/WebSockets/examples/esp8266/WebSocketServerHooked/WebSocketServerHooked.ino b/src/libs/WebSockets/examples/esp8266/WebSocketServerHooked/WebSocketServerHooked.ino new file mode 100644 index 0000000..e762626 --- /dev/null +++ b/src/libs/WebSockets/examples/esp8266/WebSocketServerHooked/WebSocketServerHooked.ino @@ -0,0 +1,103 @@ +/* + * WebSocketServerHooked.ino + * + * Created on: 22.05.2015 + * Hooked on: 28.10.2020 + * + */ + +#include + +#include +#include +#include +#include +#include + +ESP8266WiFiMulti WiFiMulti; + +ESP8266WebServer server(80); +WebSockets4WebServer webSocket; + +#define USE_SERIAL Serial + +void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) { + + switch(type) { + case WStype_DISCONNECTED: + USE_SERIAL.printf("[%u] Disconnected!\n", num); + break; + case WStype_CONNECTED: + { + IPAddress ip = webSocket.remoteIP(num); + USE_SERIAL.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload); + + // send message to client + webSocket.sendTXT(num, "Connected"); + } + break; + case WStype_TEXT: + USE_SERIAL.printf("[%u] get Text: %s\n", num, payload); + + // send message to client + // webSocket.sendTXT(num, "message here"); + + // send data to all connected clients + // webSocket.broadcastTXT("message here"); + break; + case WStype_BIN: + USE_SERIAL.printf("[%u] get binary length: %u\n", num, length); + hexdump(payload, length); + + // send message to client + // webSocket.sendBIN(num, payload, length); + break; + } + +} + +void setup() { + // USE_SERIAL.begin(921600); + USE_SERIAL.begin(115200); + + //Serial.setDebugOutput(true); + USE_SERIAL.setDebugOutput(true); + + USE_SERIAL.println(); + USE_SERIAL.println(); + USE_SERIAL.println(); + + for(uint8_t t = 4; t > 0; t--) { + USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t); + USE_SERIAL.flush(); + delay(1000); + } + + WiFiMulti.addAP("SSID", "passpasspass"); + + while(WiFiMulti.run() != WL_CONNECTED) { + delay(100); + } + + server.on("/", []() { + server.send(200, "text/plain", "I am a regular webserver on port 80!\r\n"); + server.send(200, "text/plain", "I am also a websocket server on '/ws' on the same port 80\r\n"); + }); + + server.addHook(webSocket.hookForWebserver("/ws", webSocketEvent)); + + server.begin(); + Serial.println("HTTP server started on port 80"); + Serial.println("WebSocket server started on the same port"); + Serial.printf("my network address is either 'arduinoWebsockets.local' (mDNS) or '%s'\n", WiFi.localIP().toString().c_str()); + + if (!MDNS.begin("arduinoWebsockets")) { + Serial.println("Error setting up MDNS responder!"); + } +} + +void loop() { + server.handleClient(); + webSocket.loop(); + MDNS.update(); +} diff --git a/src/libs/WebSockets/examples/esp8266/WebSocketServerHooked/emu b/src/libs/WebSockets/examples/esp8266/WebSocketServerHooked/emu new file mode 100644 index 0000000..867a8cd --- /dev/null +++ b/src/libs/WebSockets/examples/esp8266/WebSocketServerHooked/emu @@ -0,0 +1,20 @@ +#!/bin/sh + +# linux script to compile&run arduinoWebSockets in a mock environment + +if [ -z "$ESP8266ARDUINO" ]; then + echo "please set ESP8266ARDUINO env-var to where esp8266/arduino sits" + exit 1 +fi + +set -e + +where=$(pwd) + +cd $ESP8266ARDUINO/tests/host/ + +make -j FORCE32=0 \ + ULIBDIRS=../../libraries/Hash/:~/dev/proj/arduino/libraries/arduinoWebSockets \ + ${where}/WebSocketServerHooked + +valgrind ./bin/WebSocketServerHooked/WebSocketServerHooked -b "$@" diff --git a/src/libs/WebSockets/examples/esp8266/WebSocketServerHooked/ws-testclient.py b/src/libs/WebSockets/examples/esp8266/WebSocketServerHooked/ws-testclient.py new file mode 100644 index 0000000..546c7ff --- /dev/null +++ b/src/libs/WebSockets/examples/esp8266/WebSocketServerHooked/ws-testclient.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 + +# python websocket client to test with +# emulator: server is at ws://127.0.0.1:9080/ws +# esp8266: server is at ws:///ws +# (uncomment the right line below) + +#uri = "ws://127.0.0.1:9080/ws" +uri = "ws://arduinoWebsockets.local/ws" + +import websocket +try: + import thread +except ImportError: + import _thread as thread +import time + +def on_message(ws, message): + print("message"); + print(message) + +def on_error(ws, error): + print("error") + print(error) + +def on_close(ws): + print("### closed ###") + +def on_open(ws): + print("opened") + def run(*args): + for i in range(3): + time.sleep(1) + ws.send("Hello %d" % i) + time.sleep(1) + ws.close() + print("thread terminating...") + thread.start_new_thread(run, ()) + + +if __name__ == "__main__": + websocket.enableTrace(True) + ws = websocket.WebSocketApp(uri, on_message = on_message, on_error = on_error, on_close = on_close) + ws.on_open = on_open + ws.run_forever() diff --git a/src/libs/WebSockets/library.json b/src/libs/WebSockets/library.json index efbc51b..5fd2023 100644 --- a/src/libs/WebSockets/library.json +++ b/src/libs/WebSockets/library.json @@ -1,25 +1,25 @@ { - "name": "WebSockets", - "description": "WebSocket Server and Client for Arduino based on RFC6455", - "keywords": "wifi, http, web, server, client, websocket", "authors": [ { + "maintainer": true, "name": "Markus Sattler", - "url": "https://github.com/Links2004", - "maintainer": true + "url": "https://github.com/Links2004" } ], - "repository": { - "type": "git", - "url": "https://github.com/Links2004/arduinoWebSockets.git" - }, - "version": "2.1.4", - "license": "LGPL-2.1", + "description": "WebSocket Server and Client for Arduino based on RFC6455", "export": { "exclude": [ "tests" ] }, "frameworks": "arduino", - "platforms": "atmelavr, espressif8266, espressif32" -} + "keywords": "wifi, http, web, server, client, websocket", + "license": "LGPL-2.1", + "name": "WebSockets", + "platforms": "atmelavr, espressif8266, espressif32", + "repository": { + "type": "git", + "url": "https://github.com/Links2004/arduinoWebSockets.git" + }, + "version": "2.3.5" +} \ No newline at end of file diff --git a/src/libs/WebSockets/library.properties b/src/libs/WebSockets/library.properties index 2fe174b..db55e15 100644 --- a/src/libs/WebSockets/library.properties +++ b/src/libs/WebSockets/library.properties @@ -1,5 +1,5 @@ name=WebSockets -version=2.1.4 +version=2.3.5 author=Markus Sattler maintainer=Markus Sattler sentence=WebSockets for Arduino (Server + Client) diff --git a/src/libs/WebSockets/src/SocketIOclient.cpp b/src/libs/WebSockets/src/SocketIOclient.cpp index e9467d4..a06efa2 100644 --- a/src/libs/WebSockets/src/SocketIOclient.cpp +++ b/src/libs/WebSockets/src/SocketIOclient.cpp @@ -18,11 +18,59 @@ 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); + } } /** @@ -40,23 +88,24 @@ bool SocketIOclient::isConnected(void) { /** * 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) + * @param headerToPayload bool (see sendFrame for more details) * @return true if ok */ -bool SocketIOclient::sendEVENT(uint8_t * payload, size_t length, bool headerToPayload) { +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)) { + 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, sIOtype_EVENT, 0x00 }; + uint8_t buf[3] = { eIOtype_MESSAGE, type, 0x00 }; ret = WebSocketsClient::write(&_client, buf, 2); } if(ret && payload && length > 0) { @@ -66,12 +115,38 @@ bool SocketIOclient::sendEVENT(uint8_t * payload, size_t length, bool headerToPa } else { // TODO implement } - - // return WebSocketsClient::sendFrame(&_client, WSop_text, payload, length, true, true, headerToPayload); } 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); } @@ -91,8 +166,8 @@ bool SocketIOclient::sendEVENT(String & payload) { void SocketIOclient::loop(void) { WebSocketsClient::loop(); unsigned long t = millis(); - if((t - _lastConnectionFail) > EIO_HEARTBEAT_INTERVAL) { - _lastConnectionFail = t; + if(!_disableHeartbeat && (t - _lastHeartbeat) > EIO_HEARTBEAT_INTERVAL) { + _lastHeartbeat = t; DEBUG_WEBSOCKETS("[wsIOc] send ping\n"); WebSocketsClient::sendTXT(eIOtype_PING); } @@ -108,6 +183,7 @@ void SocketIOclient::handleCbEvent(WStype_t type, uint8_t * payload, size_t leng 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; @@ -138,6 +214,8 @@ void SocketIOclient::handleCbEvent(WStype_t type, uint8_t * payload, size_t leng 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: @@ -161,7 +239,7 @@ void SocketIOclient::handleCbEvent(WStype_t type, uint8_t * payload, size_t leng break; } } break; - + case WStype_ERROR: case WStype_BIN: case WStype_FRAGMENT_TEXT_START: case WStype_FRAGMENT_BIN_START: diff --git a/src/libs/WebSockets/src/SocketIOclient.h b/src/libs/WebSockets/src/SocketIOclient.h index 64b8c87..6deb168 100644 --- a/src/libs/WebSockets/src/SocketIOclient.h +++ b/src/libs/WebSockets/src/SocketIOclient.h @@ -49,6 +49,16 @@ class SocketIOclient : protected WebSocketsClient { 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); @@ -59,9 +69,18 @@ class SocketIOclient : protected WebSocketsClient { 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 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) { @@ -70,6 +89,8 @@ class SocketIOclient : protected WebSocketsClient { } } + void initClient(void); + // Handeling events from websocket layer virtual void runCbEvent(WStype_t type, uint8_t * payload, size_t length) { handleCbEvent(type, payload, length); diff --git a/src/libs/WebSockets/src/WebSockets.cpp b/src/libs/WebSockets/src/WebSockets.cpp index 3a90a40..fc94d3f 100644 --- a/src/libs/WebSockets/src/WebSockets.cpp +++ b/src/libs/WebSockets/src/WebSockets.cpp @@ -39,7 +39,14 @@ extern "C" { #ifdef ESP8266 #include #elif defined(ESP32) +#include + +#if ESP_IDF_VERSION_MAJOR >= 4 +#include +#else #include +#endif + #else extern "C" { @@ -494,7 +501,7 @@ void WebSockets::handleWebsocketPayloadCb(WSclient_t * client, bool ok, uint8_t reasonCode = payload[0] << 8 | payload[1]; } #endif - DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] get ask for close. Code: %d", client->num, reasonCode); + 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 { @@ -503,6 +510,7 @@ void WebSockets::handleWebsocketPayloadCb(WSclient_t * client, bool ok, uint8_t clientDisconnect(client, 1000); } break; default: + DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] got unknown opcode: %d\n", client->num, header->opCode); clientDisconnect(client, 1002); break; } @@ -595,7 +603,7 @@ bool WebSockets::readCb(WSclient_t * client, uint8_t * out, size_t n, WSreadWait #else unsigned long t = millis(); - size_t len; + ssize_t len; DEBUG_WEBSOCKETS("[readCb] n: %zu t: %lu\n", n, t); while(n > 0) { if(client->tcp == NULL) { @@ -623,14 +631,12 @@ bool WebSockets::readCb(WSclient_t * client, uint8_t * out, size_t n, WSreadWait } if(!client->tcp->available()) { -#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) - delay(0); -#endif + WEBSOCKETS_YIELD_MORE(); continue; } len = client->tcp->read((uint8_t *)out, n); - if(len) { + if(len > 0) { t = millis(); out += len; n -= len; @@ -638,13 +644,14 @@ bool WebSockets::readCb(WSclient_t * client, uint8_t * out, size_t n, WSreadWait } else { //DEBUG_WEBSOCKETS("Receive %d left %d!\n", len, n); } -#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) - delay(0); -#endif + if(n > 0) { + WEBSOCKETS_YIELD(); + } } if(cb) { cb(client, true); } + WEBSOCKETS_YIELD(); #endif return true; } @@ -689,12 +696,13 @@ size_t WebSockets::write(WSclient_t * client, uint8_t * out, size_t n) { total += len; //DEBUG_WEBSOCKETS("write %d left %d!\n", len, n); } else { - //DEBUG_WEBSOCKETS("write %d failed left %d!\n", len, n); + DEBUG_WEBSOCKETS("WS write %d failed left %d!\n", len, n); + } + if(n > 0) { + WEBSOCKETS_YIELD(); } -#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) - delay(0); -#endif } + WEBSOCKETS_YIELD(); return total; } diff --git a/src/libs/WebSockets/src/WebSockets.h b/src/libs/WebSockets/src/WebSockets.h index a70768e..db16b0a 100644 --- a/src/libs/WebSockets/src/WebSockets.h +++ b/src/libs/WebSockets/src/WebSockets.h @@ -40,9 +40,15 @@ #include #endif +#include "WebSocketsVersion.h" + #ifndef NODEBUG_WEBSOCKETS #ifdef DEBUG_ESP_PORT -#define DEBUG_WEBSOCKETS(...) DEBUG_ESP_PORT.printf(__VA_ARGS__) +#define DEBUG_WEBSOCKETS(...) \ + { \ + DEBUG_ESP_PORT.printf(__VA_ARGS__); \ + DEBUG_ESP_PORT.flush(); \ + } #else //#define DEBUG_WEBSOCKETS(...) os_printf( __VA_ARGS__ ) #endif @@ -50,8 +56,10 @@ #ifndef DEBUG_WEBSOCKETS #define DEBUG_WEBSOCKETS(...) +#ifndef NODEBUG_WEBSOCKETS #define NODEBUG_WEBSOCKETS #endif +#endif #if defined(ESP8266) || defined(ESP32) @@ -61,22 +69,32 @@ // 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 (2000) +#define WEBSOCKETS_TCP_TIMEOUT (5000) #define NETWORK_ESP8266_ASYNC (0) #define NETWORK_ESP8266 (1) @@ -116,6 +134,7 @@ #elif defined(ESP32) #include #include +#define SSL_AXTLS #elif defined(ESP31B) #include #else @@ -135,6 +154,11 @@ #ifdef ESP8266 #include +#if defined(wificlientbearssl_h) && !defined(USING_AXTLS) && !defined(wificlientsecure_h) +#define SSL_BARESSL +#else +#define SSL_AXTLS +#endif #else #include #endif @@ -164,6 +188,7 @@ #include #include +#define SSL_AXTLS #define WEBSOCKETS_NETWORK_CLASS WiFiClient #define WEBSOCKETS_NETWORK_SSL_CLASS WiFiClientSecure #define WEBSOCKETS_NETWORK_SERVER_CLASS WiFiServer @@ -192,6 +217,7 @@ typedef enum { WSC_NOT_CONNECTED, WSC_HEADER, + WSC_BODY, WSC_CONNECTED } WSclientsStatus_t; @@ -235,34 +261,44 @@ typedef struct { } WSMessageHeader_t; typedef struct { - uint8_t num; ///< connection number + 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; + } - WSclientsStatus_t status; + uint8_t num = 0; ///< connection number - WEBSOCKETS_NETWORK_CLASS * tcp; + WSclientsStatus_t status = WSC_NOT_CONNECTED; - bool isSocketIO; ///< client for socket.io server + WEBSOCKETS_NETWORK_CLASS * tcp = nullptr; + + bool isSocketIO = false; ///< client for socket.io server #if defined(HAS_SSL) - bool isSSL; ///< run in ssl mode + bool isSSL = false; ///< run in ssl mode WEBSOCKETS_NETWORK_SSL_CLASS * ssl; #endif - String cUrl; ///< http url - uint16_t cCode; ///< http code + String cUrl; ///< http url + uint16_t cCode = 0; ///< http code - bool cIsClient = false; ///< will be used for masking - bool cIsUpgrade; ///< Connection == Upgrade - bool cIsWebsocket; ///< Upgrade == websocket + 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; ///< client Sec-WebSocket-Version + 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; ///< State of the RX + uint8_t cWsRXsize = 0; ///< State of the RX uint8_t cWsHeader[WEBSOCKETS_MAX_HEADER_SIZE]; ///< RX WS Message buffer WSMessageHeader_t cWsHeaderDecode; @@ -271,15 +307,15 @@ typedef struct { String extraHeaders; - bool cHttpHeadersValid; ///< non-websocket http header validity indicator - size_t cMandatoryHeadersCount; ///< non-websocket mandatory http headers present count + bool cHttpHeadersValid = false; ///< non-websocket http header validity indicator + size_t cMandatoryHeadersCount; ///< non-websocket mandatory http headers present count - bool pongReceived; - uint32_t pingInterval; // how often ping will be sent, 0 means "heartbeat is not active" - uint32_t lastPing; // millis when last pong has been received - uint32_t pongTimeout; // interval in millis after which pong is considered to timeout - uint8_t disconnectTimeoutCount; // after how many subsequent pong timeouts discconnect will happen, 0 means "do not disconnect" - uint8_t pongTimeoutCount; // current pong timeout 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 diff --git a/src/libs/WebSockets/src/WebSockets4WebServer.h b/src/libs/WebSockets/src/WebSockets4WebServer.h new file mode 100644 index 0000000..a542f1e --- /dev/null +++ b/src/libs/WebSockets/src/WebSockets4WebServer.h @@ -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 +#include + +#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 " + 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 diff --git a/src/libs/WebSockets/src/WebSocketsClient.cpp b/src/libs/WebSockets/src/WebSocketsClient.cpp index 82277ea..a2a4782 100644 --- a/src/libs/WebSockets/src/WebSocketsClient.cpp +++ b/src/libs/WebSockets/src/WebSocketsClient.cpp @@ -30,6 +30,9 @@ WebSocketsClient::WebSocketsClient() { _client.num = 0; _client.cIsClient = true; _client.extraHeaders = WEBSOCKETS_STRING("Origin: file://"); + _reconnectInterval = 500; + _port = 0; + _host = ""; } WebSocketsClient::~WebSocketsClient() { @@ -43,7 +46,7 @@ void WebSocketsClient::begin(const char * host, uint16_t port, const char * url, _host = host; _port = port; #if defined(HAS_SSL) - _fingerprint = ""; + _fingerprint = SSL_FINGERPRINT_NULL; _CA_cert = NULL; #endif @@ -82,7 +85,9 @@ void WebSocketsClient::begin(const char * host, uint16_t port, const char * url, #endif _lastConnectionFail = 0; - _reconnectInterval = 500; + _lastHeaderSent = 0; + + DEBUG_WEBSOCKETS("[WS-Client] Websocket Version: " WEBSOCKETS_VERSION "\n"); } void WebSocketsClient::begin(String host, uint16_t port, String url, String protocol) { @@ -94,6 +99,7 @@ void WebSocketsClient::begin(IPAddress host, uint16_t port, const char * url, co } #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; @@ -108,10 +114,39 @@ void WebSocketsClient::beginSSL(String host, uint16_t port, String url, String f 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 = ""; + _fingerprint = SSL_FINGERPRINT_NULL; _CA_cert = CA_cert; } -#endif +#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); @@ -127,20 +162,35 @@ void WebSocketsClient::beginSocketIOSSL(const char * host, uint16_t port, const begin(host, port, url, protocol); _client.isSocketIO = true; _client.isSSL = true; - _fingerprint = ""; + _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 = ""; - _CA_cert = CA_cert; + _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) @@ -148,6 +198,10 @@ void WebSocketsClient::beginSocketIOSSLWithCA(const char * host, uint16_t port, * 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) { @@ -168,10 +222,25 @@ void WebSocketsClient::loop(void) { DEBUG_WEBSOCKETS("[WS-Client] setting CA certificate"); #if defined(ESP32) _client.ssl->setCACert(_CA_cert); -#elif defined(ESP8266) +#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 { @@ -190,6 +259,7 @@ void WebSocketsClient::loop(void) { 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 @@ -203,7 +273,7 @@ void WebSocketsClient::loop(void) { } } else { handleClientData(); - + WEBSOCKETS_YIELD(); if(_client.status == WSC_CONNECTED) { handleHBPing(); handleHBTimeout(&_client); @@ -440,7 +510,8 @@ void WebSocketsClient::clientDisconnect(WSclient_t * client) { client->cIsWebsocket = false; client->cSessionId = ""; - client->status = WSC_NOT_CONNECTED; + client->status = WSC_NOT_CONNECTED; + _lastConnectionFail = millis(); DEBUG_WEBSOCKETS("[WS-Client] client disconnected.\n"); if(event) { @@ -483,6 +554,13 @@ bool WebSocketsClient::clientIsConnected(WSclient_t * client) { * 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) { @@ -490,6 +568,12 @@ void WebSocketsClient::handleClientData(void) { 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; @@ -498,9 +582,7 @@ void WebSocketsClient::handleClientData(void) { break; } } -#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) - delay(0); -#endif + WEBSOCKETS_YIELD(); } #endif @@ -567,7 +649,7 @@ void WebSocketsClient::sendHeader(WSclient_t * client) { } // add extra headers; by default this includes "Origin: file://" - if(client->extraHeaders) { + if(client->extraHeaders.length() > 0) { handshake += client->extraHeaders + NEW_LINE; } @@ -593,6 +675,7 @@ void WebSocketsClient::sendHeader(WSclient_t * client) { #endif DEBUG_WEBSOCKETS("[WS-Client][sendHeader] sending header... Done (%luus).\n", (micros() - start)); + _lastHeaderSent = millis(); } /** @@ -602,6 +685,22 @@ void WebSocketsClient::sendHeader(WSclient_t * client) { 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()); @@ -635,7 +734,7 @@ void WebSocketsClient::handleHeader(WSclient_t * client, String * headerLine) { } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Version"))) { client->cVersion = headerValue.toInt(); } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Set-Cookie"))) { - if(headerValue.indexOf(WEBSOCKETS_STRING("HttpOnly")) > -1) { + if(headerValue.indexOf(';') > -1) { client->cSessionId = headerValue.substring(headerValue.indexOf('=') + 1, headerValue.indexOf(";")); } else { client->cSessionId = headerValue.substring(headerValue.indexOf('=') + 1); @@ -666,6 +765,14 @@ void WebSocketsClient::handleHeader(WSclient_t * client, String * headerLine) { 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) { @@ -706,15 +813,20 @@ void WebSocketsClient::handleHeader(WSclient_t * client, String * headerLine) { headerDone(client); runCbEvent(WStype_CONNECTED, (uint8_t *)client->cUrl.c_str(), client->cUrl.length()); - } else if(clientIsConnected(client) && client->isSocketIO && client->cSessionId.length() > 0) { - if(_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(); +#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); } - sendHeader(client); +#endif } else { DEBUG_WEBSOCKETS("[WS-Client][handleHeader] no Websocket connection close.\n"); _lastConnectionFail = millis(); @@ -755,14 +867,18 @@ void WebSocketsClient::connectedCb() { #endif #if defined(HAS_SSL) - if(_client.isSSL && _fingerprint.length()) { +#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(wificlientbearssl_h) && !defined(USING_AXTLS) && !defined(wificlientsecure_h) +#if defined(SSL_BARESSL) _client.ssl->setInsecure(); #endif } @@ -832,6 +948,9 @@ void WebSocketsClient::handleHBPing() { if(sendPing()) { _client.lastPing = millis(); _client.pongReceived = false; + } else { + DEBUG_WEBSOCKETS("[WS-Client] sending HB ping failed\n"); + WebSockets::clientDisconnect(&_client, 1000); } } } diff --git a/src/libs/WebSockets/src/WebSocketsClient.h b/src/libs/WebSockets/src/WebSocketsClient.h index b6fe738..6421376 100644 --- a/src/libs/WebSockets/src/WebSocketsClient.h +++ b/src/libs/WebSockets/src/WebSocketsClient.h @@ -43,8 +43,15 @@ class WebSocketsClient : protected WebSockets { void begin(IPAddress host, uint16_t port, const char * url = "/", const char * protocol = "arduino"); #if defined(HAS_SSL) - void beginSSL(const char * host, uint16_t port, const char * url = "/", const char * = "", const char * protocol = "arduino"); +#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 @@ -54,7 +61,11 @@ class WebSocketsClient : protected WebSockets { #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) @@ -91,15 +102,27 @@ class WebSocketsClient : protected WebSockets { void enableHeartbeat(uint32_t pingInterval, uint32_t pongTimeout, uint8_t disconnectTimeoutCount); void disableHeartbeat(); + bool isConnected(void); + protected: String _host; uint16_t _port; - bool isConnected(void); - #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; @@ -107,6 +130,7 @@ class WebSocketsClient : protected WebSockets { 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); diff --git a/src/libs/WebSockets/src/WebSocketsServer.cpp b/src/libs/WebSockets/src/WebSocketsServer.cpp index 633e34c..a32a27d 100644 --- a/src/libs/WebSockets/src/WebSocketsServer.cpp +++ b/src/libs/WebSockets/src/WebSocketsServer.cpp @@ -25,31 +25,36 @@ #include "WebSockets.h" #include "WebSocketsServer.h" -WebSocketsServer::WebSocketsServer(uint16_t port, String origin, String protocol) { - _port = port; - _origin = origin; - _protocol = protocol; - _runnning = false; - - _server = new WEBSOCKETS_NETWORK_SERVER_CLASS(port); - -#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) - _server->onClient([](void * s, AsyncClient * c) { - ((WebSocketsServer *)s)->newClient(new AsyncTCPbuffer(c)); - }, - this); -#endif +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; - - memset(&_clients[0], 0x00, (sizeof(WSclient_t) * WEBSOCKETS_SERVER_CLIENT_MAX)); } -WebSocketsServer::~WebSocketsServer() { +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(); @@ -59,38 +64,20 @@ WebSocketsServer::~WebSocketsServer() { _mandatoryHttpHeaderCount = 0; } +WebSocketsServer::~WebSocketsServer() { +} + /** * called to initialize the Websocket server */ -void WebSocketsServer::begin(void) { - WSclient_t * client; - - // init client storage - for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) { - client = &_clients[i]; - - client->num = i; - client->status = WSC_NOT_CONNECTED; - client->tcp = NULL; -#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) - client->isSSL = false; - client->ssl = NULL; -#endif - client->cUrl = ""; - client->cCode = 0; - client->cKey = ""; - client->cProtocol = ""; - client->cVersion = 0; - client->cIsUpgrade = false; - client->cIsWebsocket = false; - - client->base64Authorization = ""; - - client->cWsRXsize = 0; - -#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) - client->cHttpLine = ""; -#endif +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 @@ -104,41 +91,26 @@ void WebSocketsServer::begin(void) { #endif _runnning = true; - _server->begin(); - DEBUG_WEBSOCKETS("[WS-Server] Server Started.\n"); + DEBUG_WEBSOCKETS("[WS-Server] Websocket Version: " WEBSOCKETS_VERSION "\n"); } -void WebSocketsServer::close(void) { +void WebSocketsServerCore::close(void) { _runnning = false; disconnect(); -#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 WebSocketsServer::loop(void) { - if(_runnning) { - handleNewClients(); - handleClientData(); + // 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(); } } -#endif /** * set callback function * @param cbEvent WebSocketServerEvent */ -void WebSocketsServer::onEvent(WebSocketServerEvent cbEvent) { +void WebSocketsServerCore::onEvent(WebSocketServerEvent cbEvent) { _cbEvent = cbEvent; } @@ -148,7 +120,7 @@ void WebSocketsServer::onEvent(WebSocketServerEvent cbEvent) { * @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 WebSocketsServer::onValidateHttpHeader( +void WebSocketsServerCore::onValidateHttpHeader( WebSocketServerHttpHeaderValFunc validationFunc, const char * mandatoryHttpHeaders[], size_t mandatoryHttpHeaderCount) { @@ -173,7 +145,7 @@ void WebSocketsServer::onValidateHttpHeader( * @param headerToPayload bool (see sendFrame for more details) * @return true if ok */ -bool WebSocketsServer::sendTXT(uint8_t num, uint8_t * payload, size_t length, bool headerToPayload) { +bool WebSocketsServerCore::sendTXT(uint8_t num, uint8_t * payload, size_t length, bool headerToPayload) { if(num >= WEBSOCKETS_SERVER_CLIENT_MAX) { return false; } @@ -187,19 +159,19 @@ bool WebSocketsServer::sendTXT(uint8_t num, uint8_t * payload, size_t length, bo return false; } -bool WebSocketsServer::sendTXT(uint8_t num, const uint8_t * payload, size_t length) { +bool WebSocketsServerCore::sendTXT(uint8_t num, const uint8_t * payload, size_t length) { return sendTXT(num, (uint8_t *)payload, length); } -bool WebSocketsServer::sendTXT(uint8_t num, char * payload, size_t length, bool headerToPayload) { +bool WebSocketsServerCore::sendTXT(uint8_t num, char * payload, size_t length, bool headerToPayload) { return sendTXT(num, (uint8_t *)payload, length, headerToPayload); } -bool WebSocketsServer::sendTXT(uint8_t num, const char * payload, size_t length) { +bool WebSocketsServerCore::sendTXT(uint8_t num, const char * payload, size_t length) { return sendTXT(num, (uint8_t *)payload, length); } -bool WebSocketsServer::sendTXT(uint8_t num, String & payload) { +bool WebSocketsServerCore::sendTXT(uint8_t num, String & payload) { return sendTXT(num, (uint8_t *)payload.c_str(), payload.length()); } @@ -210,7 +182,7 @@ bool WebSocketsServer::sendTXT(uint8_t num, String & payload) { * @param headerToPayload bool (see sendFrame for more details) * @return true if ok */ -bool WebSocketsServer::broadcastTXT(uint8_t * payload, size_t length, bool headerToPayload) { +bool WebSocketsServerCore::broadcastTXT(uint8_t * payload, size_t length, bool headerToPayload) { WSclient_t * client; bool ret = true; if(length == 0) { @@ -224,26 +196,24 @@ bool WebSocketsServer::broadcastTXT(uint8_t * payload, size_t length, bool heade ret = false; } } -#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) - delay(0); -#endif + WEBSOCKETS_YIELD(); } return ret; } -bool WebSocketsServer::broadcastTXT(const uint8_t * payload, size_t length) { +bool WebSocketsServerCore::broadcastTXT(const uint8_t * payload, size_t length) { return broadcastTXT((uint8_t *)payload, length); } -bool WebSocketsServer::broadcastTXT(char * payload, size_t length, bool headerToPayload) { +bool WebSocketsServerCore::broadcastTXT(char * payload, size_t length, bool headerToPayload) { return broadcastTXT((uint8_t *)payload, length, headerToPayload); } -bool WebSocketsServer::broadcastTXT(const char * payload, size_t length) { +bool WebSocketsServerCore::broadcastTXT(const char * payload, size_t length) { return broadcastTXT((uint8_t *)payload, length); } -bool WebSocketsServer::broadcastTXT(String & payload) { +bool WebSocketsServerCore::broadcastTXT(String & payload) { return broadcastTXT((uint8_t *)payload.c_str(), payload.length()); } @@ -255,7 +225,7 @@ bool WebSocketsServer::broadcastTXT(String & payload) { * @param headerToPayload bool (see sendFrame for more details) * @return true if ok */ -bool WebSocketsServer::sendBIN(uint8_t num, uint8_t * payload, size_t length, bool headerToPayload) { +bool WebSocketsServerCore::sendBIN(uint8_t num, uint8_t * payload, size_t length, bool headerToPayload) { if(num >= WEBSOCKETS_SERVER_CLIENT_MAX) { return false; } @@ -266,7 +236,7 @@ bool WebSocketsServer::sendBIN(uint8_t num, uint8_t * payload, size_t length, bo return false; } -bool WebSocketsServer::sendBIN(uint8_t num, const uint8_t * payload, size_t length) { +bool WebSocketsServerCore::sendBIN(uint8_t num, const uint8_t * payload, size_t length) { return sendBIN(num, (uint8_t *)payload, length); } @@ -277,7 +247,7 @@ bool WebSocketsServer::sendBIN(uint8_t num, const uint8_t * payload, size_t leng * @param headerToPayload bool (see sendFrame for more details) * @return true if ok */ -bool WebSocketsServer::broadcastBIN(uint8_t * payload, size_t length, bool headerToPayload) { +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++) { @@ -287,14 +257,12 @@ bool WebSocketsServer::broadcastBIN(uint8_t * payload, size_t length, bool heade ret = false; } } -#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) - delay(0); -#endif + WEBSOCKETS_YIELD(); } return ret; } -bool WebSocketsServer::broadcastBIN(const uint8_t * payload, size_t length) { +bool WebSocketsServerCore::broadcastBIN(const uint8_t * payload, size_t length) { return broadcastBIN((uint8_t *)payload, length); } @@ -305,7 +273,7 @@ bool WebSocketsServer::broadcastBIN(const uint8_t * payload, size_t length) { * @param length size_t * @return true if ping is send out */ -bool WebSocketsServer::sendPing(uint8_t num, uint8_t * payload, size_t length) { +bool WebSocketsServerCore::sendPing(uint8_t num, uint8_t * payload, size_t length) { if(num >= WEBSOCKETS_SERVER_CLIENT_MAX) { return false; } @@ -316,7 +284,7 @@ bool WebSocketsServer::sendPing(uint8_t num, uint8_t * payload, size_t length) { return false; } -bool WebSocketsServer::sendPing(uint8_t num, String & payload) { +bool WebSocketsServerCore::sendPing(uint8_t num, String & payload) { return sendPing(num, (uint8_t *)payload.c_str(), payload.length()); } @@ -326,7 +294,7 @@ bool WebSocketsServer::sendPing(uint8_t num, String & payload) { * @param length size_t * @return true if ping is send out */ -bool WebSocketsServer::broadcastPing(uint8_t * payload, size_t length) { +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++) { @@ -336,21 +304,19 @@ bool WebSocketsServer::broadcastPing(uint8_t * payload, size_t length) { ret = false; } } -#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) - delay(0); -#endif + WEBSOCKETS_YIELD(); } return ret; } -bool WebSocketsServer::broadcastPing(String & payload) { +bool WebSocketsServerCore::broadcastPing(String & payload) { return broadcastPing((uint8_t *)payload.c_str(), payload.length()); } /** * disconnect all clients */ -void WebSocketsServer::disconnect(void) { +void WebSocketsServerCore::disconnect(void) { WSclient_t * client; for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) { client = &_clients[i]; @@ -364,7 +330,7 @@ void WebSocketsServer::disconnect(void) { * disconnect one client * @param num uint8_t client id */ -void WebSocketsServer::disconnect(uint8_t num) { +void WebSocketsServerCore::disconnect(uint8_t num) { if(num >= WEBSOCKETS_SERVER_CLIENT_MAX) { return; } @@ -379,7 +345,7 @@ void WebSocketsServer::disconnect(uint8_t num) { * @param user const char * * @param password const char * */ -void WebSocketsServer::setAuthorization(const char * user, const char * password) { +void WebSocketsServerCore::setAuthorization(const char * user, const char * password) { if(user && password) { String auth = user; auth += ":"; @@ -392,7 +358,7 @@ void WebSocketsServer::setAuthorization(const char * user, const char * password * set the Authorizatio for the http request * @param auth const char * base64 */ -void WebSocketsServer::setAuthorization(const char * auth) { +void WebSocketsServerCore::setAuthorization(const char * auth) { if(auth) { _base64Authorization = auth; } @@ -402,7 +368,7 @@ void WebSocketsServer::setAuthorization(const char * auth) { * count the connected clients (optional ping them) * @param ping bool ping the connected clients */ -int WebSocketsServer::connectedClients(bool ping) { +int WebSocketsServerCore::connectedClients(bool ping) { WSclient_t * client; int count = 0; for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) { @@ -416,13 +382,25 @@ int WebSocketsServer::connectedClients(bool ping) { 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 WebSocketsServer::remoteIP(uint8_t num) { +IPAddress WebSocketsServerCore::remoteIP(uint8_t num) { if(num < WEBSOCKETS_SERVER_CLIENT_MAX) { WSclient_t * client = &_clients[num]; if(clientIsConnected(client)) { @@ -442,7 +420,7 @@ IPAddress WebSocketsServer::remoteIP(uint8_t num) { * handle new client connection * @param client */ -bool WebSocketsServer::newClient(WEBSOCKETS_NETWORK_CLASS * TCPclient) { +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++) { @@ -462,14 +440,16 @@ bool WebSocketsServer::newClient(WEBSOCKETS_NETWORK_CLASS * TCPclient) { #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([](WebSocketsServer * server, AsyncTCPbuffer * obj, WSclient_t * client) -> bool { + 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; @@ -481,14 +461,20 @@ bool WebSocketsServer::newClient(WEBSOCKETS_NETWORK_CLASS * TCPclient) { }, this, std::placeholders::_1, client)); - client->tcp->readStringUntil('\n', &(client->cHttpLine), std::bind(&WebSocketsServer::handleHeader, this, client, &(client->cHttpLine))); + client->tcp->readStringUntil('\n', &(client->cHttpLine), std::bind(&WebSocketsServerCore::handleHeader, this, client, &(client->cHttpLine))); #endif - return true; + client->pingInterval = _pingInterval; + client->pongTimeout = _pongTimeout; + client->disconnectTimeoutCount = _disconnectTimeoutCount; + client->lastPing = millis(); + client->pongReceived = false; + + return client; break; } } - return false; + return nullptr; } /** @@ -498,7 +484,7 @@ bool WebSocketsServer::newClient(WEBSOCKETS_NETWORK_CLASS * TCPclient) { * @param payload uint8_t * * @param length size_t */ -void WebSocketsServer::messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin) { +void WebSocketsServerCore::messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin) { WStype_t type = WStype_ERROR; switch(opcode) { @@ -525,11 +511,32 @@ void WebSocketsServer::messageReceived(WSclient_t * client, WSopcode_t opcode, u 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->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 WebSocketsServer::clientDisconnect(WSclient_t * client) { +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()) { @@ -542,20 +549,7 @@ void WebSocketsServer::clientDisconnect(WSclient_t * client) { } #endif - if(client->tcp) { - if(client->tcp->connected()) { -#if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) - 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; - } + dropNativeClient(client); client->cUrl = ""; client->cKey = ""; @@ -582,7 +576,7 @@ void WebSocketsServer::clientDisconnect(WSclient_t * client) { * @param client WSclient_t * ptr to the client struct * @return true = connected */ -bool WebSocketsServer::clientIsConnected(WSclient_t * client) { +bool WebSocketsServerCore::clientIsConnected(WSclient_t * client) { if(!client->tcp) { return false; } @@ -609,6 +603,30 @@ bool WebSocketsServer::clientIsConnected(WSclient_t * 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 + dropNativeClient(client); + } + + WEBSOCKETS_YIELD(); + + return client; +} + /** * Handle incoming Connection Request */ @@ -616,35 +634,17 @@ void WebSocketsServer::handleNewClients(void) { #if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) while(_server->hasClient()) { #endif - bool ok = false; -#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) // store new connection WEBSOCKETS_NETWORK_CLASS * tcpClient = new WEBSOCKETS_NETWORK_CLASS(_server->available()); -#else - WEBSOCKETS_NETWORK_CLASS * tcpClient = new WEBSOCKETS_NETWORK_CLASS(_server->available()); -#endif - if(!tcpClient) { DEBUG_WEBSOCKETS("[WS-Client] creating Network class failed!"); return; } - ok = newClient(tcpClient); - - if(!ok) { - // no free space to handle client -#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) - IPAddress ip = tcpClient->remoteIP(); - 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 - tcpClient->stop(); - } + handleNewClient(tcpClient); #if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) - delay(0); } #endif } @@ -652,7 +652,7 @@ void WebSocketsServer::handleNewClients(void) { /** * Handel incomming data from Client */ -void WebSocketsServer::handleClientData(void) { +void WebSocketsServerCore::handleClientData(void) { WSclient_t * client; for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) { client = &_clients[i]; @@ -669,14 +669,16 @@ void WebSocketsServer::handleClientData(void) { 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); } -#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) - delay(0); -#endif + WEBSOCKETS_YIELD(); } } #endif @@ -685,7 +687,7 @@ void WebSocketsServer::handleClientData(void) { * 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 WebSocketsServer::hasMandatoryHeader(String headerName) { +bool WebSocketsServerCore::hasMandatoryHeader(String headerName) { for(size_t i = 0; i < _mandatoryHttpHeaderCount; i++) { if(_mandatoryHttpHeaders[i].equalsIgnoreCase(headerName)) return true; @@ -698,7 +700,7 @@ bool WebSocketsServer::hasMandatoryHeader(String headerName) { * @param client WSclient_t * ///< pointer to the client struct * @param headerLine String ///< the header being read / processed */ -void WebSocketsServer::handleHeader(WSclient_t * client, String * headerLine) { +void WebSocketsServerCore::handleHeader(WSclient_t * client, String * headerLine) { static const char * NEW_LINE = "\r\n"; headerLine->trim(); // remove \r @@ -757,7 +759,7 @@ void WebSocketsServer::handleHeader(WSclient_t * client, String * headerLine) { (*headerLine) = ""; #if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) - client->tcp->readStringUntil('\n', &(client->cHttpLine), std::bind(&WebSocketsServer::handleHeader, this, client, &(client->cHttpLine))); + 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); @@ -850,3 +852,97 @@ void WebSocketsServer::handleHeader(WSclient_t * client, String * headerLine) { } } } + +/** + * 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 diff --git a/src/libs/WebSockets/src/WebSocketsServer.h b/src/libs/WebSockets/src/WebSocketsServer.h index 60144a1..e0fa759 100644 --- a/src/libs/WebSockets/src/WebSocketsServer.h +++ b/src/libs/WebSockets/src/WebSocketsServer.h @@ -31,8 +31,14 @@ #define WEBSOCKETS_SERVER_CLIENT_MAX (5) #endif -class WebSocketsServer : protected WebSockets { +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); @@ -41,19 +47,6 @@ class WebSocketsServer : protected WebSockets { typedef std::function WebSocketServerHttpHeaderValFunc; #endif - WebSocketsServer(uint16_t port, String origin = "", String protocol = "arduino"); - virtual ~WebSocketsServer(void); - - void begin(void); - void close(void); - -#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(WebSocketServerEvent cbEvent); void onValidateHttpHeader( WebSocketServerHttpHeaderValFunc validationFunc, @@ -92,20 +85,28 @@ class WebSocketsServer : protected WebSockets { 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: - uint16_t _port; String _origin; String _protocol; String _base64Authorization; ///< Base64 encoded Auth request String * _mandatoryHttpHeaders; size_t _mandatoryHttpHeaderCount; - WEBSOCKETS_NETWORK_SERVER_CLASS * _server; - WSclient_t _clients[WEBSOCKETS_SERVER_CLIENT_MAX]; WebSocketServerEvent _cbEvent; @@ -113,7 +114,9 @@ class WebSocketsServer : protected WebSockets { bool _runnning; - bool newClient(WEBSOCKETS_NETWORK_CLASS * TCPclient); + 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); @@ -121,12 +124,13 @@ class WebSocketsServer : protected WebSockets { bool clientIsConnected(WSclient_t * client); #if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) - void handleNewClients(void); 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 @@ -195,6 +199,15 @@ class WebSocketsServer : protected WebSockets { 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 @@ -203,4 +216,28 @@ class WebSocketsServer : protected WebSockets { 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_ */ diff --git a/src/libs/WebSockets/src/WebSocketsVersion.h b/src/libs/WebSockets/src/WebSocketsVersion.h new file mode 100644 index 0000000..3e1aa0c --- /dev/null +++ b/src/libs/WebSockets/src/WebSocketsVersion.h @@ -0,0 +1,36 @@ +/** + * @file WebSocketsVersion.h + * @date 09.02.2021 + * @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.5" + +#define WEBSOCKETS_VERSION_MAJOR 2 +#define WEBSOCKETS_VERSION_MINOR 3 +#define WEBSOCKETS_VERSION_PATCH 5 + +#define WEBSOCKETS_VERSION_INT 2003005 + +#endif /* WEBSOCKETSVERSION_H_ */ diff --git a/src/libs/WebSockets/travis/common.sh b/src/libs/WebSockets/travis/common.sh index be959fa..234b1f6 100644 --- a/src/libs/WebSockets/travis/common.sh +++ b/src/libs/WebSockets/travis/common.sh @@ -27,6 +27,64 @@ function build_sketches() done } +function build_sketch() +{ + local arduino=$1 + local sketch=$2 + $arduino --verify $sketch; + local result=$? + if [ $result -ne 0 ]; then + echo "Build failed ($sketch) build verbose..." + $arduino --verify --verbose --preserve-temp-files $sketch + result=$? + fi + if [ $result -ne 0 ]; then + echo "Build failed ($1) $sketch" + return $result + fi +} + +function get_sketches_json() +{ + local arduino=$1 + local srcpath=$2 + local platform=$3 + local sketches=($(find $srcpath -name *.ino)) + echo -en "[" + for sketch in "${sketches[@]}" ; do + local sketchdir=$(dirname $sketch) + if [[ -f "$sketchdir/.$platform.skip" ]]; then + continue + fi + echo -en "\"$sketch\"" + if [[ $sketch != ${sketches[-1]} ]] ; then + echo -en "," + fi + + done + echo -en "]" +} + +function get_sketches_json_matrix() +{ + local arduino=$1 + local srcpath=$2 + local platform=$3 + local ideversion=$4 + local board=$5 + local sketches=($(find $srcpath -name *.ino)) + for sketch in "${sketches[@]}" ; do + local sketchdir=$(dirname $sketch) + local sketchname=$(basename $sketch) + if [[ -f "$sketchdir/.$platform.skip" ]]; then + continue + fi + echo -en "{\"name\":\"$sketchname\",\"board\":\"$board\",\"ideversion\":\"$ideversion\",\"cpu\":\"$platform\",\"sketch\":\"$sketch\"}" + if [[ $sketch != ${sketches[-1]} ]] ; then + echo -en "," + fi + done +} function get_core() { @@ -37,17 +95,37 @@ function get_core() if [ "$1" = "esp8266" ] ; then mkdir esp8266com cd esp8266com - git clone https://github.com/esp8266/Arduino.git esp8266 - cd esp8266/tools + git clone --depth 1 https://github.com/esp8266/Arduino.git esp8266 + cd esp8266/ + rm -rf .git + cd tools python get.py fi if [ "$1" = "esp32" ] ; then mkdir espressif cd espressif - git clone https://github.com/espressif/arduino-esp32.git esp32 - cd esp32/tools + git clone --depth 1 https://github.com/espressif/arduino-esp32.git esp32 + cd esp32/ + rm -rf .git + cd tools python get.py fi } + +function clone_library() { + local url=$1 + echo clone $(basename $url) + mkdir -p $HOME/Arduino/libraries + cd $HOME/Arduino/libraries + git clone --depth 1 $url + rm -rf */.git + rm -rf */.github + rm -rf */examples +} + +function hash_library_names() { + cd $HOME/Arduino/libraries + ls | sha1sum -z | cut -c1-5 +} \ No newline at end of file diff --git a/src/libs/WebSockets/travis/version.py b/src/libs/WebSockets/travis/version.py new file mode 100644 index 0000000..71454ab --- /dev/null +++ b/src/libs/WebSockets/travis/version.py @@ -0,0 +1,132 @@ +#!/usr/bin/python3 + +import json +import configparser +import argparse +import re +import os +import datetime + +travis_dir = os.path.dirname(os.path.abspath(__file__)) +base_dir = os.path.abspath(travis_dir + "/../") + +def write_header_file(version): + hvs = version.split('.') + intversion = int(hvs[0]) * 1000000 + int(hvs[1]) * 1000 + int(hvs[2]) + now = datetime.datetime.now() + + text = f'''/** + * @file WebSocketsVersion.h + * @date {now.strftime("%d.%m.%Y")} + * @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 "{version}" + +#define WEBSOCKETS_VERSION_MAJOR {hvs[0]} +#define WEBSOCKETS_VERSION_MINOR {hvs[1]} +#define WEBSOCKETS_VERSION_PATCH {hvs[2]} + +#define WEBSOCKETS_VERSION_INT {intversion} + +#endif /* WEBSOCKETSVERSION_H_ */ +''' + with open(f'{base_dir}/src/WebSocketsVersion.h', 'w') as f: + f.write(text) + + +def get_library_properties_version(): + library_properties = {} + with open(f'{base_dir}/library.properties', 'r') as f: + library_properties = configparser.ConfigParser() + library_properties.read_string('[root]\n' + f.read()) + return library_properties['root']['version'] + + +def get_library_json_version(): + library_json = {} + with open(f'{base_dir}/library.json', 'r') as f: + library_json = json.load(f) + return library_json['version'] + + +def get_header_versions(): + data = {} + define = re.compile('^#define WEBSOCKETS_VERSION_?(.*) "?([0-9\.]*)"?$') + with open(f'{base_dir}/src/WebSocketsVersion.h', 'r') as f: + for line in f: + m = define.match(line) + if m: + name = m[1] + if name == "": + name = "VERSION" + data[name] = m[2] + return data + + +parser = argparse.ArgumentParser(description='Checks and update Version files') +parser.add_argument( + '--update', action='store_true', default=False) +parser.add_argument( + '--check', action='store_true', default=True) + +args = parser.parse_args() + +if args.update: + library_properties_version = get_library_properties_version() + + with open(f'{base_dir}/library.json', 'r') as f: + library_json = json.load(f) + + library_json['version'] = library_properties_version + + with open(f'{base_dir}/library.json', 'w') as f: + json.dump(library_json, f, indent=4, sort_keys=True) + + write_header_file(library_properties_version) + + +library_json_version = get_library_json_version() +library_properties_version = get_library_properties_version() +header_version = get_header_versions() + +print("WebSocketsVersion.h", header_version) +print(f"library.json: {library_json_version}") +print(f"library.properties: {library_properties_version}") + +if args.check: + if library_json_version != library_properties_version or header_version['VERSION'] != library_properties_version: + raise Exception('versions did not match!') + + hvs = header_version['VERSION'].split('.') + if header_version['MAJOR'] != hvs[0]: + raise Exception('header MAJOR version wrong!') + if header_version['MINOR'] != hvs[1]: + raise Exception('header MINOR version wrong!') + if header_version['PATCH'] != hvs[2]: + raise Exception('header PATCH version wrong!') + + intversion = int(hvs[0]) * 1000000 + int(hvs[1]) * 1000 + int(hvs[2]) + if int(header_version['INT']) != intversion: + raise Exception('header INT version wrong!')