esp8266: share port 80 with regular webserver (#575)

This commit is contained in:
david gauchard
2020-11-21 13:51:16 +01:00
committed by GitHub
parent 7fea0b8bdf
commit 826c6b423a
7 changed files with 459 additions and 137 deletions

View File

@ -0,0 +1,103 @@
/*
* WebSocketServerHooked.ino
*
* Created on: 22.05.2015
* Hooked on: 28.10.2020
*
*/
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <WebSockets4WebServer.h>
#include <Hash.h>
#include <ESP8266mDNS.h>
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();
}

View File

@ -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 "$@"

View File

@ -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()

View File

@ -42,7 +42,7 @@
#ifndef NODEBUG_WEBSOCKETS #ifndef NODEBUG_WEBSOCKETS
#ifdef DEBUG_ESP_PORT #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 #else
//#define DEBUG_WEBSOCKETS(...) os_printf( __VA_ARGS__ ) //#define DEBUG_WEBSOCKETS(...) os_printf( __VA_ARGS__ )
#endif #endif

View File

@ -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 <WebSocketsServer.h>
#include <ESP8266WebServer.h>
#if WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266 && WEBSERVER_HAS_HOOK
class WebSockets4WebServer: public WebSocketsServerCore {
public:
WebSockets4WebServer(const String& origin = "", const String& protocol = "arduino"):
WebSocketsServerCore(origin, protocol)
{
begin();
}
ESP8266WebServer::HookFunction hookForWebserver (const String& wsRootDir, WebSocketServerEvent event)
{
onEvent(event);
return [&, wsRootDir](const String & method, const String & url, WiFiClient * tcpClient, ESP8266WebServer::ContentTypeFunction contentType)
{
if (!(method == "GET" && url.indexOf(wsRootDir) == 0)) {
return ESP8266WebServer::CLIENT_REQUEST_CAN_CONTINUE;
}
// allocate a WiFiClient copy (like in WebSocketsServer::handleNewClients())
WEBSOCKETS_NETWORK_CLASS * newTcpClient = new WEBSOCKETS_NETWORK_CLASS(*tcpClient);
// Then initialize a new WSclient_t (like in WebSocketsServer::handleNewClient())
WSclient_t * client = handleNewClient(newTcpClient);
if (client)
{
// give "GET <url>"
String headerLine;
headerLine.reserve(url.length() + 5);
headerLine = "GET ";
headerLine += url;
handleHeader(client, &headerLine);
}
// tell webserver to not close but forget about this client
return ESP8266WebServer::CLIENT_IS_GIVEN;
};
}
};
#endif // WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266 && WEBSERVER_HAS_HOOK
#endif // __WEBSOCKETS4WEBSERVER_H

View File

@ -25,8 +25,7 @@
#include "WebSockets.h" #include "WebSockets.h"
#include "WebSocketsServer.h" #include "WebSocketsServer.h"
WebSocketsServer::WebSocketsServer(uint16_t port, String origin, String protocol) { WebSocketsServerCore::WebSocketsServerCore(const String& origin, const String& protocol) {
_port = port;
_origin = origin; _origin = origin;
_protocol = protocol; _protocol = protocol;
_runnning = false; _runnning = false;
@ -34,15 +33,6 @@ WebSocketsServer::WebSocketsServer(uint16_t port, String origin, String protocol
_pongTimeout = 0; _pongTimeout = 0;
_disconnectTimeoutCount = 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; _cbEvent = NULL;
_httpHeaderValidationFunc = 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)); 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 // disconnect all clients
close(); close();
@ -62,10 +66,13 @@ WebSocketsServer::~WebSocketsServer() {
_mandatoryHttpHeaderCount = 0; _mandatoryHttpHeaderCount = 0;
} }
WebSocketsServer::~WebSocketsServer() {
}
/** /**
* called to initialize the Websocket server * called to initialize the Websocket server
*/ */
void WebSocketsServer::begin(void) { void WebSocketsServerCore::begin(void) {
WSclient_t * client; WSclient_t * client;
// init client storage // init client storage
@ -82,12 +89,16 @@ void WebSocketsServer::begin(void) {
client->cUrl = ""; client->cUrl = "";
client->cCode = 0; client->cCode = 0;
client->cKey = ""; client->cKey = "";
client->cAccept = "";
client->cProtocol = ""; client->cProtocol = "";
client->cExtensions = "";
client->cVersion = 0; client->cVersion = 0;
client->cIsUpgrade = false; client->cIsUpgrade = false;
client->cIsWebsocket = false; client->cIsWebsocket = false;
client->base64Authorization = ""; client->base64Authorization = "";
client->plainAuthorization = "";
client->extraHeaders = "";
client->cWsRXsize = 0; client->cWsRXsize = 0;
@ -111,43 +122,18 @@ void WebSocketsServer::begin(void) {
#endif #endif
_runnning = true; _runnning = true;
_server->begin();
DEBUG_WEBSOCKETS("[WS-Server] Server Started.\n");
} }
void WebSocketsServer::close(void) { void WebSocketsServerCore::close(void) {
_runnning = false; _runnning = false;
disconnect(); 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 * set callback function
* @param cbEvent WebSocketServerEvent * @param cbEvent WebSocketServerEvent
*/ */
void WebSocketsServer::onEvent(WebSocketServerEvent cbEvent) { void WebSocketsServerCore::onEvent(WebSocketServerEvent cbEvent) {
_cbEvent = 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 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 * @param mandatoryHttpHeaderCount size_t ///< the number of items in the mandatoryHttpHeaders array
*/ */
void WebSocketsServer::onValidateHttpHeader( void WebSocketsServerCore::onValidateHttpHeader(
WebSocketServerHttpHeaderValFunc validationFunc, WebSocketServerHttpHeaderValFunc validationFunc,
const char * mandatoryHttpHeaders[], const char * mandatoryHttpHeaders[],
size_t mandatoryHttpHeaderCount) { size_t mandatoryHttpHeaderCount) {
@ -182,7 +168,7 @@ void WebSocketsServer::onValidateHttpHeader(
* @param headerToPayload bool (see sendFrame for more details) * @param headerToPayload bool (see sendFrame for more details)
* @return true if ok * @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) { if(num >= WEBSOCKETS_SERVER_CLIENT_MAX) {
return false; return false;
} }
@ -196,19 +182,19 @@ bool WebSocketsServer::sendTXT(uint8_t num, uint8_t * payload, size_t length, bo
return false; 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); 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); 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); 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()); 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) * @param headerToPayload bool (see sendFrame for more details)
* @return true if ok * @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; WSclient_t * client;
bool ret = true; bool ret = true;
if(length == 0) { if(length == 0) {
@ -238,19 +224,19 @@ bool WebSocketsServer::broadcastTXT(uint8_t * payload, size_t length, bool heade
return ret; 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); 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); 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); 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()); 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) * @param headerToPayload bool (see sendFrame for more details)
* @return true if ok * @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) { if(num >= WEBSOCKETS_SERVER_CLIENT_MAX) {
return false; return false;
} }
@ -273,7 +259,7 @@ bool WebSocketsServer::sendBIN(uint8_t num, uint8_t * payload, size_t length, bo
return false; 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); 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) * @param headerToPayload bool (see sendFrame for more details)
* @return true if ok * @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; WSclient_t * client;
bool ret = true; bool ret = true;
for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) { 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; 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); return broadcastBIN((uint8_t *)payload, length);
} }
@ -310,7 +296,7 @@ bool WebSocketsServer::broadcastBIN(const uint8_t * payload, size_t length) {
* @param length size_t * @param length size_t
* @return true if ping is send out * @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) { if(num >= WEBSOCKETS_SERVER_CLIENT_MAX) {
return false; return false;
} }
@ -321,7 +307,7 @@ bool WebSocketsServer::sendPing(uint8_t num, uint8_t * payload, size_t length) {
return false; 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()); 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 * @param length size_t
* @return true if ping is send out * @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; WSclient_t * client;
bool ret = true; bool ret = true;
for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) { 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; return ret;
} }
bool WebSocketsServer::broadcastPing(String & payload) { bool WebSocketsServerCore::broadcastPing(String & payload) {
return broadcastPing((uint8_t *)payload.c_str(), payload.length()); return broadcastPing((uint8_t *)payload.c_str(), payload.length());
} }
/** /**
* disconnect all clients * disconnect all clients
*/ */
void WebSocketsServer::disconnect(void) { void WebSocketsServerCore::disconnect(void) {
WSclient_t * client; WSclient_t * client;
for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) { for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
client = &_clients[i]; client = &_clients[i];
@ -367,7 +353,7 @@ void WebSocketsServer::disconnect(void) {
* disconnect one client * disconnect one client
* @param num uint8_t client id * @param num uint8_t client id
*/ */
void WebSocketsServer::disconnect(uint8_t num) { void WebSocketsServerCore::disconnect(uint8_t num) {
if(num >= WEBSOCKETS_SERVER_CLIENT_MAX) { if(num >= WEBSOCKETS_SERVER_CLIENT_MAX) {
return; return;
} }
@ -382,7 +368,7 @@ void WebSocketsServer::disconnect(uint8_t num) {
* @param user const char * * @param user const char *
* @param password 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) { if(user && password) {
String auth = user; String auth = user;
auth += ":"; auth += ":";
@ -395,7 +381,7 @@ void WebSocketsServer::setAuthorization(const char * user, const char * password
* set the Authorizatio for the http request * set the Authorizatio for the http request
* @param auth const char * base64 * @param auth const char * base64
*/ */
void WebSocketsServer::setAuthorization(const char * auth) { void WebSocketsServerCore::setAuthorization(const char * auth) {
if(auth) { if(auth) {
_base64Authorization = auth; _base64Authorization = auth;
} }
@ -405,7 +391,7 @@ void WebSocketsServer::setAuthorization(const char * auth) {
* count the connected clients (optional ping them) * count the connected clients (optional ping them)
* @param ping bool ping the connected clients * @param ping bool ping the connected clients
*/ */
int WebSocketsServer::connectedClients(bool ping) { int WebSocketsServerCore::connectedClients(bool ping) {
WSclient_t * client; WSclient_t * client;
int count = 0; int count = 0;
for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) { 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 * see if one client is connected
* @param num uint8_t client id * @param num uint8_t client id
*/ */
bool WebSocketsServer::clientIsConnected(uint8_t num) { bool WebSocketsServerCore::clientIsConnected(uint8_t num) {
if(num >= WEBSOCKETS_SERVER_CLIENT_MAX) { if(num >= WEBSOCKETS_SERVER_CLIENT_MAX) {
return false; return false;
} }
@ -437,7 +423,7 @@ bool WebSocketsServer::clientIsConnected(uint8_t num) {
* @param num uint8_t client id * @param num uint8_t client id
* @return IPAddress * @return IPAddress
*/ */
IPAddress WebSocketsServer::remoteIP(uint8_t num) { IPAddress WebSocketsServerCore::remoteIP(uint8_t num) {
if(num < WEBSOCKETS_SERVER_CLIENT_MAX) { if(num < WEBSOCKETS_SERVER_CLIENT_MAX) {
WSclient_t * client = &_clients[num]; WSclient_t * client = &_clients[num];
if(clientIsConnected(client)) { if(clientIsConnected(client)) {
@ -457,7 +443,7 @@ IPAddress WebSocketsServer::remoteIP(uint8_t num) {
* handle new client connection * handle new client connection
* @param client * @param client
*/ */
bool WebSocketsServer::newClient(WEBSOCKETS_NETWORK_CLASS * TCPclient) { WSclient_t * WebSocketsServerCore::newClient(WEBSOCKETS_NETWORK_CLASS * TCPclient) {
WSclient_t * client; WSclient_t * client;
// search free list entry for client // search free list entry for client
for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) { for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
@ -486,7 +472,7 @@ bool WebSocketsServer::newClient(WEBSOCKETS_NETWORK_CLASS * TCPclient) {
#endif #endif
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) #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); DEBUG_WEBSOCKETS("[WS-Server][%d] Disconnect client\n", client->num);
AsyncTCPbuffer ** sl = &server->_clients[client->num].tcp; AsyncTCPbuffer ** sl = &server->_clients[client->num].tcp;
@ -498,7 +484,7 @@ bool WebSocketsServer::newClient(WEBSOCKETS_NETWORK_CLASS * TCPclient) {
}, },
this, std::placeholders::_1, client)); 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 #endif
client->pingInterval = _pingInterval; client->pingInterval = _pingInterval;
@ -507,11 +493,11 @@ bool WebSocketsServer::newClient(WEBSOCKETS_NETWORK_CLASS * TCPclient) {
client->lastPing = millis(); client->lastPing = millis();
client->pongReceived = false; client->pongReceived = false;
return true; return client;
break; break;
} }
} }
return false; return nullptr;
} }
/** /**
@ -521,7 +507,7 @@ bool WebSocketsServer::newClient(WEBSOCKETS_NETWORK_CLASS * TCPclient) {
* @param payload uint8_t * * @param payload uint8_t *
* @param length size_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; WStype_t type = WStype_ERROR;
switch(opcode) { switch(opcode) {
@ -549,22 +535,11 @@ void WebSocketsServer::messageReceived(WSclient_t * client, WSopcode_t opcode, u
} }
/** /**
* Disconnect an client * Discard a native client
* @param client WSclient_t * ptr to the client struct * @param client WSclient_t * ptr to the client struct contaning the native client "->tcp"
*/ */
void WebSocketsServer::clientDisconnect(WSclient_t * client) { void WebSocketsServerCore::dropNativeClient (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
if(client->tcp) { if(client->tcp) {
if(client->tcp->connected()) { if(client->tcp->connected()) {
#if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) && (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP32) #if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) && (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP32)
@ -579,6 +554,26 @@ void WebSocketsServer::clientDisconnect(WSclient_t * client) {
#endif #endif
client->tcp = NULL; 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->cUrl = "";
client->cKey = ""; client->cKey = "";
@ -605,7 +600,7 @@ void WebSocketsServer::clientDisconnect(WSclient_t * client) {
* @param client WSclient_t * ptr to the client struct * @param client WSclient_t * ptr to the client struct
* @return true = connected * @return true = connected
*/ */
bool WebSocketsServer::clientIsConnected(WSclient_t * client) { bool WebSocketsServerCore::clientIsConnected(WSclient_t * client) {
if(!client->tcp) { if(!client->tcp) {
return false; return false;
} }
@ -635,27 +630,11 @@ bool WebSocketsServer::clientIsConnected(WSclient_t * client) {
/** /**
* Handle incoming Connection Request * Handle incoming Connection Request
*/ */
void WebSocketsServer::handleNewClients(void) { WSclient_t * WebSocketsServerCore::handleNewClient(WEBSOCKETS_NETWORK_CLASS * tcpClient) {
#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) WSclient_t * client = newClient(tcpClient);
// 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) { if(!client) {
DEBUG_WEBSOCKETS("[WS-Client] creating Network class failed!");
return;
}
ok = newClient(tcpClient);
if(!ok) {
// no free space to handle client // no free space to handle client
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) #if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
#ifndef NODEBUG_WEBSOCKETS #ifndef NODEBUG_WEBSOCKETS
@ -663,12 +642,33 @@ void WebSocketsServer::handleNewClients(void) {
#endif #endif
DEBUG_WEBSOCKETS("[WS-Server] no free space new client from %d.%d.%d.%d\n", ip[0], ip[1], ip[2], ip[3]); DEBUG_WEBSOCKETS("[WS-Server] no free space new client from %d.%d.%d.%d\n", ip[0], ip[1], ip[2], ip[3]);
#else #else
DEBUG_WEBSOCKETS("[WS-Server] no free space new client\n"); DEBUG_WEBSOCKETS("[WS-Server] no free space new client\n");
#endif #endif
tcpClient->stop(); dropNativeClient(client);
} }
WEBSOCKETS_YIELD(); 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) #if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
} }
#endif #endif
@ -677,7 +677,7 @@ void WebSocketsServer::handleNewClients(void) {
/** /**
* Handel incomming data from Client * Handel incomming data from Client
*/ */
void WebSocketsServer::handleClientData(void) { void WebSocketsServerCore::handleClientData(void) {
WSclient_t * client; WSclient_t * client;
for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) { for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
client = &_clients[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 * returns an indicator whether the given named header exists in the configured _mandatoryHttpHeaders collection
* @param headerName String ///< the name of the header being checked * @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++) { for(size_t i = 0; i < _mandatoryHttpHeaderCount; i++) {
if(_mandatoryHttpHeaders[i].equalsIgnoreCase(headerName)) if(_mandatoryHttpHeaders[i].equalsIgnoreCase(headerName))
return true; return true;
@ -725,7 +725,7 @@ bool WebSocketsServer::hasMandatoryHeader(String headerName) {
* @param client WSclient_t * ///< pointer to the client struct * @param client WSclient_t * ///< pointer to the client struct
* @param headerLine String ///< the header being read / processed * @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"; static const char * NEW_LINE = "\r\n";
headerLine->trim(); // remove \r headerLine->trim(); // remove \r
@ -784,7 +784,7 @@ void WebSocketsServer::handleHeader(WSclient_t * client, String * headerLine) {
(*headerLine) = ""; (*headerLine) = "";
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) #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 #endif
} else { } else {
DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] Header read fin.\n", client->num); 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 * send heartbeat ping to server in set intervals
*/ */
void WebSocketsServer::handleHBPing(WSclient_t * client) { void WebSocketsServerCore::handleHBPing(WSclient_t * client) {
if(client->pingInterval == 0) if(client->pingInterval == 0)
return; return;
uint32_t pi = millis() - client->lastPing; 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 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 * @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; _pingInterval = pingInterval;
_pongTimeout = pongTimeout; _pongTimeout = pongTimeout;
_disconnectTimeoutCount = disconnectTimeoutCount; _disconnectTimeoutCount = disconnectTimeoutCount;
@ -915,7 +915,7 @@ void WebSocketsServer::enableHeartbeat(uint32_t pingInterval, uint32_t pongTimeo
/** /**
* disable ping/pong heartbeat process * disable ping/pong heartbeat process
*/ */
void WebSocketsServer::disableHeartbeat() { void WebSocketsServerCore::disableHeartbeat() {
_pingInterval = 0; _pingInterval = 0;
WSclient_t * client; WSclient_t * client;
@ -924,3 +924,51 @@ void WebSocketsServer::disableHeartbeat() {
client->pingInterval = 0; 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) {
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

View File

@ -31,8 +31,15 @@
#define WEBSOCKETS_SERVER_CLIENT_MAX (5) #define WEBSOCKETS_SERVER_CLIENT_MAX (5)
#endif #endif
class WebSocketsServer : protected WebSockets { class WebSocketsServerCore : protected WebSockets {
public: public:
WebSocketsServerCore(const String& origin = "", const String& protocol = "arduino");
virtual ~WebSocketsServerCore(void);
void begin(void);
void close(void);
#ifdef __AVR__ #ifdef __AVR__
typedef void (*WebSocketServerEvent)(uint8_t num, WStype_t type, uint8_t * payload, size_t length); typedef void (*WebSocketServerEvent)(uint8_t num, WStype_t type, uint8_t * payload, size_t length);
typedef bool (*WebSocketServerHttpHeaderValFunc)(String headerName, String headerValue); typedef bool (*WebSocketServerHttpHeaderValFunc)(String headerName, String headerValue);
@ -41,19 +48,6 @@ class WebSocketsServer : protected WebSockets {
typedef std::function<bool(String headerName, String headerValue)> WebSocketServerHttpHeaderValFunc; typedef std::function<bool(String headerName, String headerValue)> WebSocketServerHttpHeaderValFunc;
#endif #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 onEvent(WebSocketServerEvent cbEvent);
void onValidateHttpHeader( void onValidateHttpHeader(
WebSocketServerHttpHeaderValFunc validationFunc, WebSocketServerHttpHeaderValFunc validationFunc,
@ -101,16 +95,17 @@ class WebSocketsServer : protected WebSockets {
IPAddress remoteIP(uint8_t num); IPAddress remoteIP(uint8_t num);
#endif #endif
#if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
void loop(void); // handle client data only
#endif
protected: protected:
uint16_t _port;
String _origin; String _origin;
String _protocol; String _protocol;
String _base64Authorization; ///< Base64 encoded Auth request String _base64Authorization; ///< Base64 encoded Auth request
String * _mandatoryHttpHeaders; String * _mandatoryHttpHeaders;
size_t _mandatoryHttpHeaderCount; size_t _mandatoryHttpHeaderCount;
WEBSOCKETS_NETWORK_SERVER_CLASS * _server;
WSclient_t _clients[WEBSOCKETS_SERVER_CLIENT_MAX]; WSclient_t _clients[WEBSOCKETS_SERVER_CLIENT_MAX];
WebSocketServerEvent _cbEvent; WebSocketServerEvent _cbEvent;
@ -122,7 +117,7 @@ class WebSocketsServer : protected WebSockets {
uint32_t _pongTimeout; uint32_t _pongTimeout;
uint8_t _disconnectTimeoutCount; 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); 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); bool clientIsConnected(WSclient_t * client);
#if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) #if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
void handleNewClients(void);
void handleClientData(void); void handleClientData(void);
#endif #endif
@ -206,12 +200,48 @@ class WebSocketsServer : protected WebSockets {
return true; 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: private:
/* /*
* returns an indicator whether the given named header exists in the configured _mandatoryHttpHeaders collection * returns an indicator whether the given named header exists in the configured _mandatoryHttpHeaders collection
* @param headerName String ///< the name of the header being checked * @param headerName String ///< the name of the header being checked
*/ */
bool hasMandatoryHeader(String headerName); 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_ */ #endif /* WEBSOCKETSSERVER_H_ */