From 826c6b423a0f607a01b4cb69f42690dcb9291cf1 Mon Sep 17 00:00:00 2001 From: david gauchard Date: Sat, 21 Nov 2020 13:51:16 +0100 Subject: [PATCH] esp8266: share port 80 with regular webserver (#575) --- .../WebSocketServerHooked.ino | 103 +++++++ examples/esp8266/WebSocketServerHooked/emu | 20 ++ .../WebSocketServerHooked/ws-testclient.py | 45 +++ src/WebSockets.h | 2 +- src/WebSockets4WebServer.h | 76 +++++ src/WebSocketsServer.cpp | 282 ++++++++++-------- src/WebSocketsServer.h | 68 +++-- 7 files changed, 459 insertions(+), 137 deletions(-) create mode 100644 examples/esp8266/WebSocketServerHooked/WebSocketServerHooked.ino create mode 100755 examples/esp8266/WebSocketServerHooked/emu create mode 100755 examples/esp8266/WebSocketServerHooked/ws-testclient.py create mode 100644 src/WebSockets4WebServer.h diff --git a/examples/esp8266/WebSocketServerHooked/WebSocketServerHooked.ino b/examples/esp8266/WebSocketServerHooked/WebSocketServerHooked.ino new file mode 100644 index 0000000..1a74f8d --- /dev/null +++ b/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/examples/esp8266/WebSocketServerHooked/emu b/examples/esp8266/WebSocketServerHooked/emu new file mode 100755 index 0000000..867a8cd --- /dev/null +++ b/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/examples/esp8266/WebSocketServerHooked/ws-testclient.py b/examples/esp8266/WebSocketServerHooked/ws-testclient.py new file mode 100755 index 0000000..546c7ff --- /dev/null +++ b/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/WebSockets.h b/src/WebSockets.h index a858c84..4fed8a0 100644 --- a/src/WebSockets.h +++ b/src/WebSockets.h @@ -42,7 +42,7 @@ #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 diff --git a/src/WebSockets4WebServer.h b/src/WebSockets4WebServer.h new file mode 100644 index 0000000..514abbb --- /dev/null +++ b/src/WebSockets4WebServer.h @@ -0,0 +1,76 @@ +/** + * @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) + { + 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; + }; + } +}; + +#endif // WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266 && WEBSERVER_HAS_HOOK + +#endif // __WEBSOCKETS4WEBSERVER_H diff --git a/src/WebSocketsServer.cpp b/src/WebSocketsServer.cpp index a614350..74b4228 100644 --- a/src/WebSocketsServer.cpp +++ b/src/WebSocketsServer.cpp @@ -25,8 +25,7 @@ #include "WebSockets.h" #include "WebSocketsServer.h" -WebSocketsServer::WebSocketsServer(uint16_t port, String origin, String protocol) { - _port = port; +WebSocketsServerCore::WebSocketsServerCore(const String& origin, const String& protocol) { _origin = origin; _protocol = protocol; _runnning = false; @@ -34,15 +33,6 @@ WebSocketsServer::WebSocketsServer(uint16_t port, String origin, String protocol _pongTimeout = 0; _disconnectTimeoutCount = 0; - _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 - _cbEvent = NULL; _httpHeaderValidationFunc = NULL; @@ -52,7 +42,21 @@ WebSocketsServer::WebSocketsServer(uint16_t port, String origin, String protocol 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(); @@ -62,10 +66,13 @@ WebSocketsServer::~WebSocketsServer() { _mandatoryHttpHeaderCount = 0; } +WebSocketsServer::~WebSocketsServer() { +} + /** * called to initialize the Websocket server */ -void WebSocketsServer::begin(void) { +void WebSocketsServerCore::begin(void) { WSclient_t * client; // init client storage @@ -82,12 +89,16 @@ void WebSocketsServer::begin(void) { client->cUrl = ""; client->cCode = 0; client->cKey = ""; + client->cAccept = ""; client->cProtocol = ""; + client->cExtensions = ""; client->cVersion = 0; client->cIsUpgrade = false; client->cIsWebsocket = false; client->base64Authorization = ""; + client->plainAuthorization = ""; + client->extraHeaders = ""; client->cWsRXsize = 0; @@ -111,43 +122,18 @@ void WebSocketsServer::begin(void) { #endif _runnning = true; - _server->begin(); - - DEBUG_WEBSOCKETS("[WS-Server] Server Started.\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) { - WEBSOCKETS_YIELD(); - handleNewClients(); - WEBSOCKETS_YIELD(); - handleClientData(); - } -} -#endif - /** * set callback function * @param cbEvent WebSocketServerEvent */ -void WebSocketsServer::onEvent(WebSocketServerEvent cbEvent) { +void WebSocketsServerCore::onEvent(WebSocketServerEvent cbEvent) { _cbEvent = cbEvent; } @@ -157,7 +143,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) { @@ -182,7 +168,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; } @@ -196,19 +182,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()); } @@ -219,7 +205,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) { @@ -238,19 +224,19 @@ bool WebSocketsServer::broadcastTXT(uint8_t * payload, size_t length, bool heade 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()); } @@ -262,7 +248,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; } @@ -273,7 +259,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); } @@ -284,7 +270,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++) { @@ -299,7 +285,7 @@ bool WebSocketsServer::broadcastBIN(uint8_t * payload, size_t length, bool heade 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); } @@ -310,7 +296,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; } @@ -321,7 +307,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()); } @@ -331,7 +317,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++) { @@ -346,14 +332,14 @@ bool WebSocketsServer::broadcastPing(uint8_t * payload, size_t length) { 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]; @@ -367,7 +353,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; } @@ -382,7 +368,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 += ":"; @@ -395,7 +381,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; } @@ -405,7 +391,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++) { @@ -423,7 +409,7 @@ int WebSocketsServer::connectedClients(bool ping) { * see if one client is connected * @param num uint8_t client id */ -bool WebSocketsServer::clientIsConnected(uint8_t num) { +bool WebSocketsServerCore::clientIsConnected(uint8_t num) { if(num >= WEBSOCKETS_SERVER_CLIENT_MAX) { return false; } @@ -437,7 +423,7 @@ bool WebSocketsServer::clientIsConnected(uint8_t num) { * @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)) { @@ -457,7 +443,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++) { @@ -486,7 +472,7 @@ bool WebSocketsServer::newClient(WEBSOCKETS_NETWORK_CLASS * TCPclient) { #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; @@ -498,7 +484,7 @@ 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 client->pingInterval = _pingInterval; @@ -507,11 +493,11 @@ bool WebSocketsServer::newClient(WEBSOCKETS_NETWORK_CLASS * TCPclient) { client->lastPing = millis(); client->pongReceived = false; - return true; + return client; break; } } - return false; + return nullptr; } /** @@ -521,7 +507,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) { @@ -549,22 +535,11 @@ void WebSocketsServer::messageReceived(WSclient_t * client, WSopcode_t opcode, u } /** - * Disconnect an client - * @param client WSclient_t * ptr to the client struct + * Discard a native client + * @param client WSclient_t * ptr to the client struct contaning the native client "->tcp" */ -void WebSocketsServer::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 - +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) @@ -579,6 +554,26 @@ void WebSocketsServer::clientDisconnect(WSclient_t * client) { #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 = ""; @@ -605,7 +600,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; } @@ -635,27 +630,11 @@ bool WebSocketsServer::clientIsConnected(WSclient_t * client) { /** * Handle incoming Connection Request */ -void WebSocketsServer::handleNewClients(void) { -#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) - while(_server->hasClient()) { -#endif - bool ok = false; +WSclient_t * WebSocketsServerCore::handleNewClient(WEBSOCKETS_NETWORK_CLASS * tcpClient) { -#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 + WSclient_t * client = newClient(tcpClient); - if(!tcpClient) { - DEBUG_WEBSOCKETS("[WS-Client] creating Network class failed!"); - return; - } - - ok = newClient(tcpClient); - - if(!ok) { + if(!client) { // no free space to handle client #if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) #ifndef NODEBUG_WEBSOCKETS @@ -663,12 +642,33 @@ void WebSocketsServer::handleNewClients(void) { #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"); + DEBUG_WEBSOCKETS("[WS-Server] no free space new client\n"); #endif - tcpClient->stop(); + 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 @@ -677,7 +677,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]; @@ -712,7 +712,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; @@ -725,7 +725,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 @@ -784,7 +784,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); @@ -881,7 +881,7 @@ void WebSocketsServer::handleHeader(WSclient_t * client, String * headerLine) { /** * send heartbeat ping to server in set intervals */ -void WebSocketsServer::handleHBPing(WSclient_t * client) { +void WebSocketsServerCore::handleHBPing(WSclient_t * client) { if(client->pingInterval == 0) return; uint32_t pi = millis() - client->lastPing; @@ -900,7 +900,7 @@ void WebSocketsServer::handleHBPing(WSclient_t * client) { * @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 WebSocketsServer::enableHeartbeat(uint32_t pingInterval, uint32_t pongTimeout, uint8_t disconnectTimeoutCount) { +void WebSocketsServerCore::enableHeartbeat(uint32_t pingInterval, uint32_t pongTimeout, uint8_t disconnectTimeoutCount) { _pingInterval = pingInterval; _pongTimeout = pongTimeout; _disconnectTimeoutCount = disconnectTimeoutCount; @@ -915,7 +915,7 @@ void WebSocketsServer::enableHeartbeat(uint32_t pingInterval, uint32_t pongTimeo /** * disable ping/pong heartbeat process */ -void WebSocketsServer::disableHeartbeat() { +void WebSocketsServerCore::disableHeartbeat() { _pingInterval = 0; WSclient_t * client; @@ -923,4 +923,52 @@ void WebSocketsServer::disableHeartbeat() { client = &_clients[i]; client->pingInterval = 0; } -} \ No newline at end of file +} + +//////////////////// +// 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) { + WebSocketsServer::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/WebSocketsServer.h b/src/WebSocketsServer.h index 2541788..8089c18 100644 --- a/src/WebSocketsServer.h +++ b/src/WebSocketsServer.h @@ -31,8 +31,15 @@ #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 +48,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, @@ -101,16 +95,17 @@ class WebSocketsServer : protected WebSockets { IPAddress remoteIP(uint8_t num); #endif +#if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) + void loop(void); // handle client data only +#endif + 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; @@ -122,7 +117,7 @@ class WebSocketsServer : protected WebSockets { uint32_t _pongTimeout; uint8_t _disconnectTimeoutCount; - bool newClient(WEBSOCKETS_NETWORK_CLASS * TCPclient); + WSclient_t * newClient(WEBSOCKETS_NETWORK_CLASS * TCPclient); void messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin); @@ -130,7 +125,6 @@ class WebSocketsServer : protected WebSockets { bool clientIsConnected(WSclient_t * client); #if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) - void handleNewClients(void); void handleClientData(void); #endif @@ -206,12 +200,48 @@ 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 * @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_ */