Compare commits

...

100 Commits

Author SHA1 Message Date
1fa9a1321c more debug 2022-06-24 10:48:51 +02:00
8fd9c5eff1 more debug 2022-06-24 10:35:37 +02:00
57976f7b51 esp8266 build, change config options 2022-06-24 10:23:50 +02:00
5881a99a30 bump version to 2.3.7 2022-04-05 20:07:59 +02:00
52b3aa8ea4 enable debug for CI 2022-04-05 19:40:57 +02:00
28c0cf3094 fix #728 dropNativeClient null ptr 2022-04-05 19:11:11 +02:00
ab9af162b2 fix code style 2022-04-05 19:10:57 +02:00
8d76469e90 update to Arduino IDE 1.8.19 for testing and dropped testing with 1.6.13 2022-04-05 19:03:19 +02:00
61ea44942e Suppress esp32/sha.h warning 2022-04-05 18:55:48 +02:00
28ed615145 Add minimal example for ESP OTA 2021-12-11 11:40:24 +01:00
108090e8cf Update WebSocketsServer.cpp
Created a dummy client in order to be able to drop the TCP Client.
2021-09-10 22:55:37 +02:00
46b2ae1c97 fix #695, #696 2021-07-14 19:28:25 +02:00
27f86a10ad allow access to setExtraHeaders and setReconnectInterval for SocketIOclient
fix: #690
2021-07-09 17:17:42 +02:00
860ac9da69 Prevent Compiler Warning: --> add // falls through
prevent Compiler warning by adding // falls through to imform compiler.
2021-06-20 09:22:41 +02:00
c897f60567 update arduino IDE from 1.8.13 to 1.8.15 2021-06-17 20:04:28 +02:00
b242882ac5 add git submodule update --init to esp8266 build 2021-06-17 19:56:45 +02:00
f8da05aa87 add Socket.IO example for ESP32 2021-06-17 19:43:15 +02:00
72aae52655 set EIO to version 4 in SocketIO examples
see #682
2021-06-17 19:42:09 +02:00
a14b6b73b4 bump version to 2.3.6 2021-03-08 17:22:23 +01:00
ed685e551f fix _fingerprint is set checks for ESP32
see #633 and #632
2021-03-07 13:46:37 +01:00
7c3b1b7408 install libgtk-3-0 2021-03-06 08:59:21 +01:00
738e43fda4 install libgtk-3-0 2021-03-06 08:56:08 +01:00
f55bf8d4ed bump version to 2.3.5 2021-02-09 18:26:21 +01:00
a484da47ed Merge branch 'eio_4_ping_handling' 2021-01-31 20:32:25 +01:00
4355199120 bump version 2.3.4 2021-01-31 20:31:52 +01:00
0a4fcd44c2 EIO=4 ping handling #611 2021-01-31 20:27:56 +01:00
3a2b757155 EIO=4 ping handling #611 2021-01-31 10:28:15 +01:00
900d81e534 make WSclient_t * newClient(WEBSOCKETS_NETWORK_CLASS * TCPclient); public 2021-01-19 16:59:03 +01:00
0ecef8c552 Fixing WebSocket Close see #610 2021-01-19 16:58:37 +01:00
410489f7c5 bump version to 2.3.3 2021-01-07 10:27:42 +01:00
ec22d67c12 version.py can now create WebSocketsVersion.h 2021-01-07 10:24:23 +01:00
39e6a8e709 print version in DEBUG mode 2021-01-07 10:14:30 +01:00
784b7f9cb8 no python3.9 on github actions 2021-01-07 10:05:30 +01:00
fd83d6ad45 add WebSocketsVersion.h and some build checks 2021-01-07 09:59:49 +01:00
0e729cd896 cleanup clone_library 2021-01-06 09:39:19 +01:00
2f21590e55 no need for a full clone 2021-01-06 09:18:27 +01:00
c98baafda7 jobs steps array 2021-01-05 21:54:58 +01:00
983b9801fb Merge branch 'master' of github.com:Links2004/arduinoWebSockets 2021-01-05 21:50:56 +01:00
e70262dab9 add done job 2021-01-05 21:50:40 +01:00
4a05eab627 Update README.md
use github actions as Build Status
2021-01-05 21:30:28 +01:00
822618f606 code style fix 2021-01-05 21:18:30 +01:00
1b4f186fa6 use github actions for build 2021-01-05 21:17:09 +01:00
c68e015322 github actions test3 2021-01-05 18:32:11 +01:00
dc30a2b7bf github actions test2 2021-01-05 18:28:59 +01:00
6bee53a8bd github actions test 2021-01-05 18:11:48 +01:00
ebb87cdc8a + constructors for scalars 2020-11-26 19:33:18 +01:00
e7ab913693 fix clearing _Client[] 2020-11-26 19:33:18 +01:00
52547ec47c fix clients init logic 2020-11-26 19:33:18 +01:00
74411bf729 use native contructor and destructor to initialize WSclient_t 2020-11-26 19:33:18 +01:00
f0cc36dede bump version to 2.3.2 2020-11-21 18:13:22 +01:00
074a674833 bionic is broken for Arduino IDE 1.6.13 so we keep xenial 2020-11-21 16:25:18 +01:00
8239e1625e update travis config 2020-11-21 16:21:04 +01:00
c5900db636 add handling for Socket.IO V3 connection 2020-11-21 16:19:59 +01:00
9470961d85 socket.io improve Cookie handling 2020-11-21 16:19:08 +01:00
04919f848f only try to send socket.io messages in WSC_CONNECTED state
handle sIOtype_CONNECT messages
send 2probe on websocket connect (#574)
2020-11-21 16:17:43 +01:00
25318111a1 add namespace join to socket.io examples 2020-11-21 16:16:59 +01:00
08caf3bfc6 code style 2020-11-21 14:45:53 +01:00
fb26433e75 add error messages if Webserver Hook Functions are not supported by platform 2020-11-21 14:33:25 +01:00
4f52a0f38e cleanup server client init 2020-11-21 14:07:32 +01:00
f20fbbfcd9 code style 2020-11-21 14:07:14 +01:00
28cd929e7e code style 2020-11-21 14:06:30 +01:00
826c6b423a esp8266: share port 80 with regular webserver (#575) 2020-11-21 13:51:16 +01:00
7fea0b8bdf Fix for Newline in Headers when extraHeaders is Empty [577] (#578)
Closes https://github.com/Links2004/arduinoWebSockets/issues/577
2020-11-14 18:17:10 +01:00
4db14451fb clang-format 2020-10-26 17:04:19 +01:00
80bf087cd0 Add SSL client certificate support (#572)
* Add client certificate support

allows WiFiClientSecureBearSSL users to use client certificate and private key for the WebSocker.
also added SSL functions for socket.io
2020-10-26 17:01:31 +01:00
5caff59f7f bump version to 2.3.1 2020-10-04 16:34:25 +02:00
b90fe4c1d7 travis 2020-10-04 16:20:27 +02:00
a086303c87 make old Arduino IDE (1.6.x) happy with CA
```
java.lang.StackOverflowError
	at java.util.regex.Pattern$Loop.match(Pattern.java:4785)
	at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
	at java.util.regex.Pattern$BranchConn.match(Pattern.java:4568)
	at java.util.regex.Pattern$CharProperty.match(Pattern.java:3777)
	at java.util.regex.Pattern$Branch.match(Pattern.java:4604)
	at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
	at java.util.regex.Pattern$Loop.match(Pattern.java:4785)
```
2020-10-04 16:06:55 +02:00
217c7ce4ea Merge pull request #480 from kenkeiras/ssl-with-ca-example
Add example for SSL websocket with CA certificate
2020-10-04 15:41:37 +02:00
13a304a8c9 Merge pull request #569 from Links2004/esp8266_bareSSL_native
ESP8266 bare ssl native
2020-10-04 15:40:43 +02:00
9b873978ba Merge pull request #566 from simap/master
avoid flush on esp32, add/fix debugs, longer yield when waiting for data
2020-10-04 15:31:15 +02:00
ca52afeec0 code style 2020-10-04 15:11:06 +02:00
beba0f88e3 fix README.md 2020-10-04 15:02:09 +02:00
f2265118b4 bump version to 2.3.0 and add note for ESP8266 BareSSL API changes 2020-10-04 14:59:51 +02:00
9982818cfa update README travis from org to com 2020-10-04 14:56:29 +02:00
91b02341ba Native BareSSL support for ESP8266
see #557, #509, #492, #555, #352
2020-10-04 14:49:22 +02:00
a00d3edcb7 Merge pull request #567 from lucalas/master
setFingerprint in WiFiClientSecure required to pass certificate Check
2020-10-03 14:52:10 +02:00
c73c77e988 Add check to set fingerprint; 2020-10-02 09:29:55 +02:00
e1ddbfe1a5 Format Code; 2020-10-01 01:14:01 +02:00
f65e8d9062 call setFingerPrint for ssl object in client with fingerprint received in beginSSL or no connection will start; 2020-10-01 01:12:54 +02:00
508e0fb691 add missing platforms for WEBSOCKETS_YIELD_MORE 2020-09-16 11:11:24 -07:00
083683425f avoid flush on esp32, add/fix debugs, longer yield when waiting for data
flush causes a bunch of reads as we try to close the socket on esp32. I
think flush is broken on that platform. the comments indicate confusion.

added some debug logs for important cases that were missing them, some
missing newlines to exisitng logs.

added a longer yield when waiting for data, in some super busy cases it
could trigger a task watchdog or otherwise starve the system. (yield
alone doesn't always switch to lower priority tasks)

make some other yields conditional to avoid some waste when it would
double-yield.
2020-09-16 11:02:02 -07:00
05ec18e49b Merge pull request #565 from tobiges/master
header response timeout added
2020-09-14 10:25:59 +02:00
e185668a97 header response timeout added 2020-09-12 17:20:03 +02:00
7e34a8b246 Merge pull request #563 from simap/accessClients
add api to check connectedness of clients by num
2020-09-08 21:08:32 +02:00
4acc7eff8a add api to check connectedness of clients by num 2020-09-08 09:54:33 -07:00
b3c5348e9b update arduino IDE version in travis test 2020-07-25 09:07:02 +02:00
19c39d5ea0 typo 2020-07-11 18:38:39 +02:00
ebdc8def4a Merge branch 'master' of github.com:Links2004/arduinoWebSockets 2020-07-11 18:17:05 +02:00
44fb41e3b7 WebSocketClientSocketIOack.ino:150:25: ARDUINOJSON_USE_LONG_LONG fix 2020-07-11 18:16:34 +02:00
cadbd6458f Merge pull request #551 from Links2004/ESP32v4
fix #525 (detect ESP32 version)
2020-07-11 18:13:13 +02:00
f244741caa WebSocketClientSocketIO.ino:113:25: ArduinoJson, you must set ARDUINOJSON_USE_LONG_LONG error 2020-07-11 18:07:16 +02:00
3f1cedb21c fix #541 allow loop() to be called before begin() 2020-07-11 17:58:36 +02:00
ba6275e7fe fix #525 (detect ESP32 vesion) 2020-07-11 17:55:18 +02:00
a71a480676 fix #539 (NODEBUG_WEBSOCKETS warning)
fix warning when using NODEBUG_WEBSOCKETS as compiler option.
2020-05-14 18:04:46 +02:00
1e271b5e12 Merge remote-tracking branch 'origin/master' 2020-05-03 13:07:13 +02:00
0c3b15c5e7 fix #454 2020-05-03 13:05:55 +02:00
7a4160814c fix #454 2020-05-03 13:04:53 +02:00
784088d9cc bump version to 2.2.1 2020-05-03 10:03:53 +02:00
370f217a30 add example for ssl websocket with CA certificate 2019-10-27 21:40:53 +01:00
28 changed files with 2122 additions and 313 deletions

188
.github/workflows/main.yml vendored Normal file
View File

@ -0,0 +1,188 @@
name: CI
on:
schedule:
- cron: '0 0 * * 5'
push:
branches: [ master ]
pull_request:
branches: [ master ]
release:
types: [ published, created, edited ]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
check_version_files:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: check version
run: |
$GITHUB_WORKSPACE/travis/version.py --check
prepare_example_json:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: generate examples
id: set-matrix
run: |
source $GITHUB_WORKSPACE/travis/common.sh
cd $GITHUB_WORKSPACE
echo -en "::set-output name=matrix::"
echo -en "["
get_sketches_json_matrix arduino $GITHUB_WORKSPACE/examples/esp8266 esp8266 1.8.19 esp8266com:esp8266:generic:xtal=80,vt=flash,exception=disabled,stacksmash=disabled,ssl=all,mmu=3232,non32xfer=fast,ResetMethod=nodemcu,CrystalFreq=26,FlashFreq=80,FlashMode=qio,eesz=4M2M,led=2,sdk=nonosdk_190703,ip=lm2f,dbg=Serial1,lvl=SSL,wipe=none,baud=115200
echo -en ","
get_sketches_json_matrix arduino $GITHUB_WORKSPACE/examples/esp8266 esp8266 1.8.19 esp8266com:esp8266:generic:xtal=80,vt=flash,exception=disabled,stacksmash=disabled,ssl=all,mmu=3232,non32xfer=fast,ResetMethod=nodemcu,CrystalFreq=26,FlashFreq=80,FlashMode=qio,eesz=4M2M,led=2,sdk=nonosdk_190703,ip=lm2f,dbg=Disabled,lvl=None____,wipe=none,baud=115200
echo -en ","
get_sketches_json_matrix arduino $GITHUB_WORKSPACE/examples/esp32 esp32 1.8.19 espressif:esp32:esp32:FlashFreq=80
echo -en "]"
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
prepare_ide:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
IDE_VERSION: [1.8.19]
env:
IDE_VERSION: ${{ matrix.IDE_VERSION }}
steps:
- uses: actions/checkout@v2
- name: Get Date
id: get-date
run: |
echo "::set-output name=date::$(/bin/date -u "+%Y%m%d")"
shell: bash
- uses: actions/cache@v2
id: cache_all
with:
path: |
/home/runner/arduino_ide
/home/runner/Arduino
key: ${{ runner.os }}-${{ steps.get-date.outputs.date }}-${{ matrix.IDE_VERSION }}
- name: download IDE
if: steps.cache_all.outputs.cache-hit != 'true'
run: |
wget http://downloads.arduino.cc/arduino-$IDE_VERSION-linux64.tar.xz -q
tar xf arduino-$IDE_VERSION-linux64.tar.xz
mv arduino-$IDE_VERSION $HOME/arduino_ide
- name: download ArduinoJson
if: steps.cache_all.outputs.cache-hit != 'true'
run: |
mkdir -p $HOME/Arduino/libraries
wget https://github.com/bblanchon/ArduinoJson/archive/6.x.zip -q
unzip 6.x.zip
mv ArduinoJson-6.x $HOME/Arduino/libraries/ArduinoJson
- name: download esp8266
if: steps.cache_all.outputs.cache-hit != 'true'
run: |
source $GITHUB_WORKSPACE/travis/common.sh
get_core esp8266
- name: download esp32
if: steps.cache_all.outputs.cache-hit != 'true'
run: |
source $GITHUB_WORKSPACE/travis/common.sh
get_core esp32
build:
needs: [prepare_ide, prepare_example_json]
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include: ${{ fromJson(needs.prepare_example_json.outputs.matrix) }}
env:
CPU: ${{ matrix.cpu }}
BOARD: ${{ matrix.board }}
IDE_VERSION: ${{ matrix.ideversion }}
SKETCH: ${{ matrix.sketch }}
# Steps represent a sequence of tasks that will be executed as part of the job
steps:
- uses: actions/checkout@v2
- name: install libgtk2.0-0
run: |
sudo apt-get install -y libgtk2.0-0
- name: Get Date
id: get-date
run: |
echo "::set-output name=date::$(/bin/date -u "+%Y%m%d")"
shell: bash
- uses: actions/cache@v2
id: cache_all
with:
path: |
/home/runner/arduino_ide
/home/runner/Arduino
key: ${{ runner.os }}-${{ steps.get-date.outputs.date }}-${{ matrix.ideversion }}
- name: install python serial
if: matrix.cpu == 'esp32'
run: |
sudo pip3 install pyserial
sudo pip install pyserial
# sudo apt install python-is-python3
- name: start DISPLAY
run: |
/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_1.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :1 -ac -screen 0 1280x1024x16
export DISPLAY=:1.0
sleep 3
- name: test IDE
run: |
export PATH="$HOME/arduino_ide:$PATH"
which arduino
- name: copy code
run: |
mkdir -p $HOME/Arduino/libraries/
cp -r $GITHUB_WORKSPACE $HOME/Arduino/libraries/arduinoWebSockets
- name: config IDE
run: |
set +x
export DISPLAY=:1.0
export PATH="$HOME/arduino_ide:$PATH"
arduino --board $BOARD --save-prefs
arduino --pref update.check=false --pref build.verbose=false --pref cache.enable=true --pref compiler.cache_core=true --pref compiler.warning_level=default --save-prefs
arduino --get-pref sketchbook.path
arduino --get-pref
- name: build example
timeout-minutes: 20
run: |
set -ex
export DISPLAY=:1.0
export PATH="$HOME/arduino_ide:$PATH"
source $GITHUB_WORKSPACE/travis/common.sh
cd $GITHUB_WORKSPACE
build_sketch arduino $SKETCH
done:
needs: [prepare_ide, prepare_example_json, build, check_version_files]
runs-on: ubuntu-latest
steps:
- name: Done
run: |
echo DONE

View File

@ -10,14 +10,12 @@ os:
- linux
env:
matrix:
- CPU="esp8266" BOARD="esp8266com:esp8266:generic:xtal=80" IDE_VERSION=1.6.5
- CPU="esp8266" BOARD="esp8266com:esp8266:generic:xtal=80,dbg=Serial1" IDE_VERSION=1.6.5
- CPU="esp8266" BOARD="esp8266com:esp8266:generic:xtal=80,eesz=1M,FlashMode=qio,FlashFreq=80" IDE_VERSION=1.8.5
- CPU="esp8266" BOARD="esp8266com:esp8266:generic:xtal=80,eesz=1M,FlashMode=qio,FlashFreq=80" IDE_VERSION=1.8.9
- CPU="esp8266" BOARD="esp8266com:esp8266:generic:xtal=80,eesz=1M,FlashMode=qio,FlashFreq=80" IDE_VERSION=1.8.12
- CPU="esp8266" BOARD="esp8266com:esp8266:generic:xtal=80" IDE_VERSION=1.6.13
- CPU="esp8266" BOARD="esp8266com:esp8266:generic:xtal=80,dbg=Serial1" IDE_VERSION=1.6.13
- CPU="esp8266" BOARD="esp8266com:esp8266:generic:xtal=80,eesz=1M,FlashMode=qio,FlashFreq=80" IDE_VERSION=1.8.13
- CPU="esp32" BOARD="espressif:esp32:esp32:FlashFreq=80" IDE_VERSION=1.8.5
- CPU="esp32" BOARD="espressif:esp32:esp32:FlashFreq=80" IDE_VERSION=1.8.9
- CPU="esp32" BOARD="espressif:esp32:esp32:FlashFreq=80" IDE_VERSION=1.8.12
- CPU="esp32" BOARD="espressif:esp32:esp32:FlashFreq=80" IDE_VERSION=1.8.13
script:
- /sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_1.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :1 -ac -screen 0 1280x1024x16
- export DISPLAY=:1.0
@ -45,9 +43,3 @@ notifications:
email:
on_success: change
on_failure: change
webhooks:
urls:
- https://webhooks.gitter.im/e/1aa78fbe15080b0c2e37
on_success: change # options: [always|never|change] default: always
on_failure: always # options: [always|never|change] default: always
on_start: false # default: false

View File

@ -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++.
@ -79,10 +81,12 @@ Where `WStype_t type` is defined as:
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;
```

View File

@ -0,0 +1,155 @@
/*
* WebSocketClientSocketIOack.ino
*
* Created on: 20.07.2019
*
*/
#include <Arduino.h>
#include <WiFi.h>
#include <WiFiMulti.h>
#include <WiFiClientSecure.h>
#include <ArduinoJson.h>
#include <WebSocketsClient.h>
#include <SocketIOclient.h>
WiFiMulti 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<JsonArray>();
// 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);
break;
case sIOtype_ERROR:
USE_SERIAL.printf("[IOc] get error: %u\n", length);
break;
case sIOtype_BINARY_EVENT:
USE_SERIAL.printf("[IOc] get binary: %u\n", length);
break;
case sIOtype_BINARY_ACK:
USE_SERIAL.printf("[IOc] get binary ack: %u\n", 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");
//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, "/socket.io/?EIO=4");
// 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<JsonArray>();
// 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);
}
}

View File

@ -0,0 +1,27 @@
## Minimal example of WebsocketClientOTA and Python server
Take this as small example, how achieve OTA update on ESP8266 and ESP32.
Python server was wrote from train so take it only as bare example.
It's working, but it's not mean to run in production.
### Usage:
Start server:
```bash
cd python_ota_server
python3 -m venv .venv
source .venv/bin/activate
pip3 install -r requirements.txt
python3 main.py
```
Flash ESP with example sketch and start it.
Change version inside example sketch to higher and compile it and save it to bin file.
Rename it to `mydevice-1.0.1-esp8266.bin` and place it inside new folder firmware (server create it).
When the ESP connect to server, it check if version flashed is equal to fw in firmware folder. If higher FW version is present,
start the flash process.

View File

@ -0,0 +1,263 @@
/*
* WebSocketClientOTA.ino
*
* Created on: 25.10.2021
*
*/
#include <Arduino.h>
#include <ArduinoJson.h>
#ifdef ESP8266
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <Updater.h>
#endif
#ifdef ESP32
#include "WiFi.h"
#include "ESPmDNS.h"
#include <Update.h>
#endif
#include <WiFiUdp.h>
#include <ESP8266WiFiMulti.h>
#include <WebSocketsClient.h>
#include <Hash.h>
ESP8266WiFiMulti WiFiMulti;
WebSocketsClient webSocket;
#define USE_SERIAL Serial
// Variables:
// Settable:
const char *version = "1.0.0";
const char *name = "mydevice";
// Others:
#ifdef ESP8266
const char *chip = "esp8266";
#endif
#ifdef ESP32
const char *chip = "esp32";
#endif
uint32_t maxSketchSpace = 0;
int SketchSize = 0;
bool ws_conn = false;
String IpAddress2String(const IPAddress& ipAddress)
{
return String(ipAddress[0]) + String(".") +
String(ipAddress[1]) + String(".") +
String(ipAddress[2]) + String(".") +
String(ipAddress[3]);
}
void greetings_(){
StaticJsonDocument<200> doc;
doc["type"] = "greetings";
doc["mac"] = WiFi.macAddress();
doc["ip"] = IpAddress2String(WiFi.localIP());
doc["version"] = version;
doc["name"] = name;
doc["chip"] = chip;
char data[200];
serializeJson(doc, data);
webSocket.sendTXT(data);
}
void register_(){
StaticJsonDocument<200> doc;
doc["type"] = "register";
doc["mac"] = WiFi.macAddress();
char data[200];
serializeJson(doc, data);
webSocket.sendTXT(data);
ws_conn = true;
}
typedef void (*CALLBACK_FUNCTION)(JsonDocument &msg);
typedef struct {
char type[50];
CALLBACK_FUNCTION func;
} RESPONSES_STRUCT;
void OTA(JsonDocument &msg){
USE_SERIAL.print(F("[WSc] OTA mode: "));
const char* go = "go";
const char* ok = "ok";
if(strncmp( msg["value"], go, strlen(go)) == 0 ) {
USE_SERIAL.print(F("go\n"));
SketchSize = int(msg["size"]);
maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000;
USE_SERIAL.printf("[WSc] Max sketch size: %u\n", maxSketchSpace);
USE_SERIAL.printf("[WSc] Sketch size: %d\n", SketchSize);
USE_SERIAL.setDebugOutput(true);
if (!Update.begin(maxSketchSpace)) { //start with max available size
Update.printError(Serial);
ESP.restart();
}
} else if (strncmp( msg["value"], ok, strlen(ok)) == 0) {
USE_SERIAL.print(F("OK\n"));
register_();
} else {
USE_SERIAL.print(F("unknown value : "));
USE_SERIAL.print(msg["value"].as<char>());
USE_SERIAL.print(F("\n"));
}
}
void STATE(JsonDocument &msg){
// Do something with message
}
RESPONSES_STRUCT responses[] = {
{"ota", OTA},
{"state", STATE},
};
void text(uint8_t * payload, size_t length){
// Convert mesage to something usable
char msgch[length];
for (unsigned int i = 0; i < length; i++)
{
USE_SERIAL.print((char)payload[i]);
msgch[i] = ((char)payload[i]);
}
msgch[length] = '\0';
// Parse Json
StaticJsonDocument<200> doc_in;
DeserializationError error = deserializeJson(doc_in, msgch);
if (error) {
USE_SERIAL.print(F("deserializeJson() failed: "));
USE_SERIAL.println(error.c_str());
return;
}
// Handle each TYPE of message
int b = 0;
for( b=0 ; strlen(responses[b].type) ; b++ )
{
if( strncmp(doc_in["type"], responses[b].type, strlen(responses[b].type)) == 0 ) {
responses[b].func(doc_in);
}
}
}
void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) {
switch(type) {
case WStype_DISCONNECTED:
USE_SERIAL.printf("[WSc] Disconnected!\n");
break;
case WStype_CONNECTED: {
USE_SERIAL.printf("[WSc] Connected to url: %s\n", payload);
// send message to server when Connected
// webSocket.sendTXT("Connected");
greetings_();
}
break;
case WStype_TEXT:
USE_SERIAL.printf("[WSc] get text: %s\n", payload);
// send message to server
// webSocket.sendTXT("message here");
text(payload, length);
break;
case WStype_BIN:
USE_SERIAL.printf("[WSc] get binary length: %u\n", length);
// hexdump(payload, length);
if (Update.write(payload, length) != length) {
Update.printError(Serial);
ESP.restart();
}
yield();
SketchSize -= length;
USE_SERIAL.printf("[WSc] Sketch size left: %u\n", SketchSize);
if (SketchSize < 1){
if (Update.end(true)) { //true to set the size to the current progress
USE_SERIAL.printf("Update Success: \nRebooting...\n");
delay(5);
yield();
ESP.restart();
} else {
Update.printError(USE_SERIAL);
ESP.restart();
}
USE_SERIAL.setDebugOutput(false);
}
// send data to server
// webSocket.sendBIN(payload, length);
break;
case WStype_PING:
// pong will be send automatically
USE_SERIAL.printf("[WSc] get ping\n");
break;
case WStype_PONG:
// answer to a ping we send
USE_SERIAL.printf("[WSc] get pong\n");
break;
}
}
void setup() {
// USE_SERIAL.begin(921600);
USE_SERIAL.begin(115200);
//Serial.setDebugOutput(true);
USE_SERIAL.setDebugOutput(true);
USE_SERIAL.print(F("\nMAC: "));
USE_SERIAL.println(WiFi.macAddress());
USE_SERIAL.print(F("\nDevice: "));
USE_SERIAL.println(name);
USE_SERIAL.printf("\nVersion: %s\n", version);
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", "PASS");
//WiFi.disconnect();
while(WiFiMulti.run() != WL_CONNECTED) {
delay(100);
}
// server address, port and URL
webSocket.begin("10.0.1.5", 8081, "/");
// event handler
webSocket.onEvent(webSocketEvent);
// use HTTP Basic Authorization this is optional remove if not needed
// webSocket.setAuthorization("USER", "PASS");
// try ever 5000 again if connection has failed
webSocket.setReconnectInterval(5000);
// start heartbeat (optional)
// ping server every 15000 ms
// expect pong from server within 3000 ms
// consider connection disconnected if pong is not received 2 times
webSocket.enableHeartbeat(15000, 3000, 2);
}
void loop() {
webSocket.loop();
}

View File

@ -0,0 +1,235 @@
"""Minimal example of Python websocket server
handling OTA updates for ESP32 amd ESP8266
Check and upload of firmware works.
Register and state function are jus for example.
"""
# pylint: disable=W0703,E1101
import asyncio
import copy
import json
import logging
import subprocess
import threading
import time
from os import listdir
from os.path import join as join_pth
from pathlib import Path
import websockets
from packaging import version
# Logger settings
logging.basicConfig(filename="ws_server.log")
Logger = logging.getLogger('WS-OTA')
Logger.addHandler(logging.StreamHandler())
Logger.setLevel(logging.INFO)
# Path to directory with FW
fw_path = join_pth(Path().absolute(), "firmware")
def create_path(path: str) -> None:
"""Check if path exist or create it"""
Path(path).mkdir(parents=True, exist_ok=True)
def shell(command):
"""Handle execution of shell commands"""
with subprocess.Popen(command, shell=True,
stdout=subprocess.PIPE,
universal_newlines=True
) as process:
for stdout_line in iter(process.stdout.readline, ""):
Logger.debug(stdout_line)
process.stdout.close()
return_code = process.wait()
Logger.debug("Shell returned: %s", return_code)
return process.returncode
return None
async def binary_send(websocket, fw_file):
"""Read firmware file, divide it to chunks and send them"""
with open(fw_file, "rb") as binaryfile:
while True:
chunk = binaryfile.read(2048)
if not chunk:
break
try:
await websocket.send(chunk)
except Exception as exception:
Logger.exception(exception)
return False
time.sleep(0.2)
def version_checker(name, vdev, vapp):
"""Parse and compare FW version"""
if version.parse(vdev) < version.parse(vapp):
Logger.info("Client(%s) version %s is smaller than %s: Go for update", name, vdev, vapp)
return True
Logger.info("Client(%s) version %s is greater or equal to %s: Not updating", name, vdev, vapp)
return False
class WsOtaHandler (threading.Thread):
"""Thread handling ota update
Runing ota directly from message would kill WS
as message bus would timeout.
"""
def __init__(self, name, message, websocket):
threading.Thread.__init__(self, daemon=True)
self.name = name
self.msg = message
self.websocket = websocket
def run(self, ):
try:
asyncio.run(self.start_)
except Exception as exception:
Logger.exception(exception)
finally:
pass
async def start_(self):
"""Start _ota se asyncio future"""
msg_task = asyncio.ensure_future(
self._ota())
done, pending = await asyncio.wait(
[msg_task],
return_when=asyncio.FIRST_COMPLETED,
)
Logger.info("WS Ota Handler done: %s", done)
for task in pending:
task.cancel()
async def _ota(self):
"""Check for new fw and update or pass"""
device_name = self.msg['name']
device_chip = self.msg['chip']
device_version = self.msg['version']
fw_version = ''
fw_name = ''
fw_device = ''
for filename in listdir(fw_path):
fw_info = filename.split("-")
fw_device = fw_info[0]
if fw_device == device_name:
fw_version = fw_info[1]
fw_name = filename
break
if not fw_version:
Logger.info("Client(%s): No fw found!", device_name)
msg = '{"type": "ota", "value":"ok"}'
await self.websocket.send(msg)
return
if not version_checker(device_name, device_version, fw_version):
return
fw_file = join_pth(fw_path, fw_name)
if device_chip == 'esp8266' and not fw_file.endswith('.gz'):
# We can compress fw to make it smaller for upload
fw_cpress = fw_file
fw_file = fw_cpress + ".gz"
cpress = f"gzip -9 {fw_cpress}"
cstate = shell(cpress)
if cstate:
Logger.error("Cannot compress firmware: %s", fw_name)
return
# Get size of fw
size = Path(fw_file).stat().st_size
# Request ota mode
msg = '{"type": "ota", "value":"go", "size":' + str(size) + '}'
await self.websocket.send(msg)
# send file by chunks trough websocket
await binary_send(self.websocket, fw_file)
async def _register(websocket, message):
mac = message.get('mac')
name = message.get('name')
Logger.info("Client(%s) mac: %s", name, mac)
# Some code
response = {'response_type': 'registry', 'state': 'ok'}
await websocket.send(json.dumps(response))
async def _state(websocket, message):
mac = message.get('mac')
name = message.get('name')
Logger.info("Client(%s) mac: %s", name, mac)
# Some code
response = {'response_type': 'state', 'state': 'ok'}
await websocket.send(json.dumps(response))
async def _unhandleld(websocket, msg):
Logger.info("Unhandled message from device: %s", str(msg))
response = {'response_type': 'response', 'state': 'nok'}
await websocket.send(json.dumps(response))
async def _greetings(websocket, message):
WsOtaHandler('thread_ota', copy.deepcopy(message), websocket).start()
async def message_received(websocket, message) -> None:
"""Handle incoming messages
Check if message contain json and run waned function
"""
switcher = {"greetings": _greetings,
"register": _register,
"state": _state
}
if message[0:1] == "{":
try:
msg_json = json.loads(message)
except Exception as exception:
Logger.error(exception)
return
type_ = msg_json.get('type')
name = msg_json.get('name')
func = switcher.get(type_, _unhandleld)
Logger.debug("Client(%s)said: %s", name, type_)
try:
await func(websocket, msg_json)
except Exception as exception:
Logger.error(exception)
# pylint: disable=W0613
async def ws_server(websocket, path) -> None:
"""Run in cycle and wait for new messages"""
async for message in websocket:
await message_received(websocket, message)
async def main():
"""Server starter
Normal user can bind only port nubers greater than 1024
"""
async with websockets.serve(ws_server, "10.0.1.5", 8081):
await asyncio.Future() # run forever
create_path(fw_path)
asyncio.run(main())

View File

@ -0,0 +1,2 @@
packaging
websockets

View File

@ -0,0 +1,103 @@
/*
* WebSocketClientSSLWithCA.ino
*
* Created on: 27.10.2019
*
* note SSL is only possible with the ESP8266
*
*/
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <WebSocketsClient.h>
ESP8266WiFiMulti WiFiMulti;
WebSocketsClient webSocket;
#define USE_SERIAL Serial1
// Can be obtained with:
// openssl s_client -showcerts -connect echo.websocket.org:443 </dev/null
const char ENDPOINT_CA_CERT[] PROGMEM = R"EOF(
-----BEGIN CERTIFICATE-----
MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0NlowSjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT
GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF
q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0
Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWAa6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj
/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG
CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNvbTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k
c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAwVAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC
ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAzMDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu
Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsFAAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo
uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu
X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlGPfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6
KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==
-----END CERTIFICATE-----
)EOF";
void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) {
switch(type) {
case WStype_DISCONNECTED:
USE_SERIAL.printf("[WSc] Disconnected!\n");
break;
case WStype_CONNECTED:
{
USE_SERIAL.printf("[WSc] Connected to url: %s\n", payload);
// send message to server when Connected
webSocket.sendTXT("Connected");
}
break;
case WStype_TEXT:
USE_SERIAL.printf("[WSc] get text: %s\n", payload);
// send message to server
// webSocket.sendTXT("message here");
break;
case WStype_BIN:
USE_SERIAL.printf("[WSc] get binary length: %u\n", length);
hexdump(payload, length);
// send data to server
// webSocket.sendBIN(payload, length);
break;
}
}
void setup() {
USE_SERIAL.begin(115200);
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);
}
//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();
}

View File

@ -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);
@ -85,7 +88,7 @@ void setup() {
USE_SERIAL.printf("[SETUP] WiFi Connected %s\n", ip.c_str());
// server address, port and URL
socketIO.begin("10.11.100.100", 8880);
socketIO.begin("10.11.100.100", 8880, "/socket.io/?EIO=4");
// event handler
socketIO.onEvent(socketIOEvent);
@ -103,20 +106,20 @@ void loop() {
// creat JSON message for Socket.IO (event)
DynamicJsonDocument doc(1024);
JsonArray array = doc.to<JsonArray>();
// add evnet name
// Hint: socket.on('event_name', ....
array.add("event_name");
// add payload (parameters) for the event
JsonObject param1 = array.createNestedObject();
param1["now"] = now;
param1["now"] = (uint32_t) now;
// JSON to String (serializion)
String output;
serializeJson(doc, output);
// Send event
// Send event
socketIO.sendEVENT(output);
// Print JSON for debugging

View File

@ -30,6 +30,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:
{
@ -46,7 +49,7 @@ void socketIOEvent(socketIOmessageType_t type, uint8_t * payload, size_t length)
USE_SERIAL.println(error.c_str());
return;
}
String eventName = doc[0];
USE_SERIAL.printf("[IOc] event name: %s\n", eventName.c_str());
@ -55,7 +58,7 @@ void socketIOEvent(socketIOmessageType_t type, uint8_t * payload, size_t length)
// creat JSON message for Socket.IO (ack)
DynamicJsonDocument docOut(1024);
JsonArray array = docOut.to<JsonArray>();
// add payload (parameters) for the ack (callback function)
JsonObject param1 = array.createNestedObject();
param1["now"] = millis();
@ -65,7 +68,7 @@ void socketIOEvent(socketIOmessageType_t type, uint8_t * payload, size_t length)
output += id;
serializeJson(docOut, output);
// Send event
// Send event
socketIO.send(sIOtype_ACK, output);
}
}
@ -122,7 +125,7 @@ void setup() {
USE_SERIAL.printf("[SETUP] WiFi Connected %s\n", ip.c_str());
// server address, port and URL
socketIO.begin("10.11.100.100", 8880);
socketIO.begin("10.11.100.100", 8880, "/socket.io/?EIO=4");
// event handler
socketIO.onEvent(socketIOEvent);
@ -140,20 +143,20 @@ void loop() {
// creat JSON message for Socket.IO (event)
DynamicJsonDocument doc(1024);
JsonArray array = doc.to<JsonArray>();
// add evnet name
// Hint: socket.on('event_name', ....
array.add("event_name");
// add payload (parameters) for the event
JsonObject param1 = array.createNestedObject();
param1["now"] = now;
param1["now"] = (uint32_t) now;
// JSON to String (serializion)
String output;
serializeJson(doc, output);
// Send event
// Send event
socketIO.sendEVENT(output);
// Print JSON for debugging

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

@ -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.2.0",
"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.7"
}

View File

@ -1,5 +1,5 @@
name=WebSockets
version=2.2.0
version=2.3.7
author=Markus Sattler
maintainer=Markus Sattler
sentence=WebSockets for Arduino (Server + Client)

View File

@ -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);
}
}
/**
@ -37,6 +85,14 @@ bool SocketIOclient::isConnected(void) {
return WebSocketsClient::isConnected();
}
void SocketIOclient::setExtraHeaders(const char * extraHeaders) {
return WebSocketsClient::setExtraHeaders(extraHeaders);
}
void SocketIOclient::setReconnectInterval(unsigned long time) {
return WebSocketsClient::setReconnectInterval(time);
}
/**
* send text data to client
* @param num uint8_t client id
@ -51,7 +107,7 @@ bool SocketIOclient::send(socketIOmessageType_t type, uint8_t * payload, size_t
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);
@ -118,8 +174,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);
}
@ -135,6 +191,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;
@ -165,6 +222,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:

View File

@ -9,6 +9,7 @@
#define SOCKETIOCLIENT_H_
#include "WebSockets.h"
#include "WebSocketsClient.h"
#define EIO_HEARTBEAT_INTERVAL 20000
@ -49,6 +50,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);
@ -65,9 +76,15 @@ class SocketIOclient : protected WebSocketsClient {
bool send(socketIOmessageType_t type, const char * payload, size_t length = 0);
bool send(socketIOmessageType_t type, String & payload);
void setExtraHeaders(const char * extraHeaders = NULL);
void setReconnectInterval(unsigned long time);
void loop(void);
void configureEIOping(bool disableHeartbeat = false);
protected:
bool _disableHeartbeat = false;
uint64_t _lastHeartbeat = 0;
SocketIOclientEvent _cbEvent;
virtual void runIOCbEvent(socketIOmessageType_t type, uint8_t * payload, size_t length) {
@ -76,6 +93,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);

View File

@ -39,7 +39,18 @@ extern "C" {
#ifdef ESP8266
#include <Hash.h>
#elif defined(ESP32)
#include <esp_system.h>
#if ESP_IDF_VERSION_MAJOR >= 4
#if(ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(1, 0, 6))
#include "sha/sha_parallel_engine.h"
#else
#include <esp32/sha.h>
#endif
#else
#include <hwcrypto/sha.h>
#endif
#else
extern "C" {
@ -461,7 +472,7 @@ void WebSockets::handleWebsocketPayloadCb(WSclient_t * client, bool ok, uint8_t
payload[header->payloadLen] = 0x00;
if(header->mask) {
//decode XOR
// decode XOR
for(size_t i = 0; i < header->payloadLen; i++) {
payload[i] = (payload[i] ^ header->maskKey[i % 4]);
}
@ -494,7 +505,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 +514,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;
}
@ -514,7 +526,7 @@ void WebSockets::handleWebsocketPayloadCb(WSclient_t * client, bool ok, uint8_t
// reset input
client->cWsRXsize = 0;
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
//register callback for next message
// register callback for next message
handleWebsocketWaitFor(client, 2);
#endif
@ -623,7 +635,7 @@ bool WebSockets::readCb(WSclient_t * client, uint8_t * out, size_t n, WSreadWait
}
if(!client->tcp->available()) {
WEBSOCKETS_YIELD();
WEBSOCKETS_YIELD_MORE();
continue;
}
@ -632,11 +644,13 @@ bool WebSockets::readCb(WSclient_t * client, uint8_t * out, size_t n, WSreadWait
t = millis();
out += len;
n -= len;
//DEBUG_WEBSOCKETS("Receive %d left %d!\n", len, n);
// DEBUG_WEBSOCKETS("Receive %d left %d!\n", len, n);
} else {
//DEBUG_WEBSOCKETS("Receive %d left %d!\n", len, n);
// DEBUG_WEBSOCKETS("Receive %d left %d!\n", len, n);
}
if(n > 0) {
WEBSOCKETS_YIELD();
}
WEBSOCKETS_YIELD();
}
if(cb) {
cb(client, true);
@ -684,11 +698,13 @@ size_t WebSockets::write(WSclient_t * client, uint8_t * out, size_t n) {
out += len;
n -= len;
total += len;
//DEBUG_WEBSOCKETS("write %d left %d!\n", len, n);
// 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();
}
WEBSOCKETS_YIELD();
}
WEBSOCKETS_YIELD();
return total;

View File

@ -40,9 +40,15 @@
#include <functional>
#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)
@ -63,8 +71,10 @@
#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)
@ -73,15 +83,15 @@
#define WEBSOCKETS_USE_BIG_MEM
#define GET_FREE_HEAP System.freeMemory()
#define WEBSOCKETS_YIELD()
#define WEBSOCKETS_YIELD_MORE()
#else
//atmega328p has only 2KB ram!
// atmega328p has only 2KB ram!
#define WEBSOCKETS_MAX_DATA_SIZE (1024)
// moves all Header strings to Flash
#define WEBSOCKETS_SAVE_RAM
#define WEBSOCKETS_YIELD()
#define WEBSOCKETS_YIELD_MORE()
#endif
#define WEBSOCKETS_TCP_TIMEOUT (5000)
@ -124,6 +134,7 @@
#elif defined(ESP32)
#include <WiFi.h>
#include <WiFiClientSecure.h>
#define SSL_AXTLS
#elif defined(ESP31B)
#include <ESP31BWiFi.h>
#else
@ -143,6 +154,11 @@
#ifdef ESP8266
#include <ESP8266WiFi.h>
#if defined(wificlientbearssl_h) && !defined(USING_AXTLS) && !defined(wificlientsecure_h)
#define SSL_BARESSL
#else
#define SSL_AXTLS
#endif
#else
#include <ESP31BWiFi.h>
#endif
@ -172,6 +188,7 @@
#include <WiFi.h>
#include <WiFiClientSecure.h>
#define SSL_AXTLS
#define WEBSOCKETS_NETWORK_CLASS WiFiClient
#define WEBSOCKETS_NETWORK_SSL_CLASS WiFiClientSecure
#define WEBSOCKETS_NETWORK_SERVER_CLASS WiFiServer
@ -200,6 +217,7 @@
typedef enum {
WSC_NOT_CONNECTED,
WSC_HEADER,
WSC_BODY,
WSC_CONNECTED
} WSclientsStatus_t;
@ -243,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;
@ -279,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

View File

@ -0,0 +1,80 @@
/**
* @file WebSocketsServer.cpp
* @date 28.10.2020
* @author Markus Sattler & esp8266/arduino community
*
* Copyright (c) 2020 Markus Sattler. All rights reserved.
* This file is part of the WebSockets for Arduino.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifndef __WEBSOCKETS4WEBSERVER_H
#define __WEBSOCKETS4WEBSERVER_H
#include <WebSocketsServer.h>
#include <ESP8266WebServer.h>
#if WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266 && WEBSERVER_HAS_HOOK
class WebSockets4WebServer : public WebSocketsServerCore {
public:
WebSockets4WebServer(const String & origin = "", const String & protocol = "arduino")
: WebSocketsServerCore(origin, protocol) {
begin();
}
ESP8266WebServer::HookFunction hookForWebserver(const String & wsRootDir, WebSocketServerEvent event) {
onEvent(event);
return [&, wsRootDir](const String & method, const String & url, WiFiClient * tcpClient, ESP8266WebServer::ContentTypeFunction contentType) {
(void)contentType;
if(!(method == "GET" && url.indexOf(wsRootDir) == 0)) {
return ESP8266WebServer::CLIENT_REQUEST_CAN_CONTINUE;
}
// allocate a WiFiClient copy (like in WebSocketsServer::handleNewClients())
WEBSOCKETS_NETWORK_CLASS * newTcpClient = new WEBSOCKETS_NETWORK_CLASS(*tcpClient);
// Then initialize a new WSclient_t (like in WebSocketsServer::handleNewClient())
WSclient_t * client = handleNewClient(newTcpClient);
if(client) {
// give "GET <url>"
String headerLine;
headerLine.reserve(url.length() + 5);
headerLine = "GET ";
headerLine += url;
handleHeader(client, &headerLine);
}
// tell webserver to not close but forget about this client
return ESP8266WebServer::CLIENT_IS_GIVEN;
};
}
};
#else // WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266 && WEBSERVER_HAS_HOOK
#ifndef WEBSERVER_HAS_HOOK
#error Your current Framework / Arduino core version does not support Webserver Hook Functions
#else
#error Your Hardware Platform does not support Webserver Hook Functions
#endif
#endif // WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266 && WEBSERVER_HAS_HOOK
#endif // __WEBSOCKETS4WEBSERVER_H

View File

@ -31,6 +31,8 @@ WebSocketsClient::WebSocketsClient() {
_client.cIsClient = true;
_client.extraHeaders = WEBSOCKETS_STRING("Origin: file://");
_reconnectInterval = 500;
_port = 0;
_host = "";
}
WebSocketsClient::~WebSocketsClient() {
@ -44,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
@ -83,6 +85,9 @@ void WebSocketsClient::begin(const char * host, uint16_t port, const char * url,
#endif
_lastConnectionFail = 0;
_lastHeaderSent = 0;
DEBUG_WEBSOCKETS("[WS-Client] Websocket Version: " WEBSOCKETS_VERSION "\n");
}
void WebSocketsClient::begin(String host, uint16_t port, String url, String protocol) {
@ -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,9 @@ 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
@ -169,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 {
@ -442,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) {
@ -485,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) {
@ -492,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;
@ -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) {
@ -677,9 +784,11 @@ void WebSocketsClient::handleHeader(WSclient_t * client, String * headerLine) {
if(client->isSocketIO) {
break;
}
// falls through
case 403: ///< Forbidden
// todo handle login
default: ///< Server dont unterstand requrst
// todo handle login
// falls through
default: ///< Server dont unterstand requrst
ok = false;
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] serverCode is not 101 (%d)\n", client->cCode);
clientDisconnect(client);
@ -706,15 +815,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 +869,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 +950,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);
}
}
}

View File

@ -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);
@ -130,11 +154,11 @@ class WebSocketsClient : protected WebSockets {
#endif
/**
* called for sending a Event to the app
* @param type WStype_t
* @param payload uint8_t *
* @param length size_t
*/
* called for sending a Event to the app
* @param type WStype_t
* @param payload uint8_t *
* @param length size_t
*/
virtual void runCbEvent(WStype_t type, uint8_t * payload, size_t length) {
if(_cbEvent) {
_cbEvent(type, payload, length);

View File

@ -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,25 +33,28 @@ 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;
_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();
@ -62,42 +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
client->pingInterval = _pingInterval;
client->pongTimeout = _pongTimeout;
client->disconnectTimeoutCount = _disconnectTimeoutCount;
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
@ -111,43 +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) {
WEBSOCKETS_YIELD();
handleNewClients();
WEBSOCKETS_YIELD();
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;
}
@ -157,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) {
@ -182,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;
}
@ -196,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());
}
@ -219,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) {
@ -238,19 +201,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 +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;
}
@ -273,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);
}
@ -284,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++) {
@ -299,7 +262,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 +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;
}
@ -321,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());
}
@ -331,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++) {
@ -346,14 +309,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 +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;
}
@ -382,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 += ":";
@ -395,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;
}
@ -405,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++) {
@ -419,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)) {
@ -445,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++) {
@ -474,7 +449,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;
@ -486,7 +461,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;
@ -495,11 +470,11 @@ bool WebSocketsServer::newClient(WEBSOCKETS_NETWORK_CLASS * TCPclient) {
client->lastPing = millis();
client->pongReceived = false;
return true;
return client;
break;
}
}
return false;
return nullptr;
}
/**
@ -509,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) {
@ -536,11 +511,35 @@ 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) {
return;
}
if(client->tcp) {
if(client->tcp->connected()) {
#if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) && (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP32)
client->tcp->flush();
#endif
client->tcp->stop();
}
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
client->status = WSC_NOT_CONNECTED;
#else
delete client->tcp;
#endif
client->tcp = NULL;
}
}
/**
* Disconnect an client
* @param client WSclient_t * ptr to the client struct
*/
void 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()) {
@ -553,20 +552,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 = "";
@ -593,7 +579,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;
}
@ -620,6 +606,34 @@ 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
// no client! => create dummy!
WSclient_t dummy = WSclient_t();
client = &dummy;
client->tcp = tcpClient;
dropNativeClient(client);
}
WEBSOCKETS_YIELD();
return client;
}
/**
* Handle incoming Connection Request
*/
@ -627,36 +641,16 @@ 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);
handleNewClient(tcpClient);
if(!ok) {
// 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
tcpClient->stop();
}
WEBSOCKETS_YIELD();
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
}
#endif
@ -665,14 +659,14 @@ 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];
if(clientIsConnected(client)) {
int len = client->tcp->available();
if(len > 0) {
//DEBUG_WEBSOCKETS("[WS-Server][%d][handleClientData] len: %d\n", client->num, len);
// DEBUG_WEBSOCKETS("[WS-Server][%d][handleClientData] len: %d\n", client->num, len);
switch(client->status) {
case WSC_HEADER: {
String headerLine = client->tcp->readStringUntil('\n');
@ -682,6 +676,7 @@ 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;
}
@ -699,7 +694,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;
@ -712,7 +707,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
@ -725,7 +720,7 @@ void WebSocketsServer::handleHeader(WSclient_t * client, String * headerLine) {
// cut URL out
client->cUrl = headerLine->substring(4, headerLine->indexOf(' ', 4));
//reset non-websocket http header validation state for this client
// reset non-websocket http header validation state for this client
client->cHttpHeadersValid = true;
client->cMandatoryHeadersCount = 0;
@ -771,7 +766,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);
@ -868,7 +863,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;
@ -887,7 +882,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;
@ -902,7 +897,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;
@ -910,4 +905,51 @@ void WebSocketsServer::disableHeartbeat() {
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

View File

@ -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<bool(String headerName, String headerValue)> 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,6 +85,8 @@ 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();
@ -99,16 +94,19 @@ 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
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;
@ -120,15 +118,12 @@ class WebSocketsServer : protected WebSockets {
uint32_t _pongTimeout;
uint8_t _disconnectTimeoutCount;
bool newClient(WEBSOCKETS_NETWORK_CLASS * TCPclient);
void messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin);
void clientDisconnect(WSclient_t * client);
bool clientIsConnected(WSclient_t * client);
#if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
void handleNewClients(void);
void handleClientData(void);
#endif
@ -137,10 +132,10 @@ class WebSocketsServer : protected WebSockets {
void handleHBPing(WSclient_t * client); // send ping in specified intervals
/**
* called if a non Websocket connection is coming in.
* Note: can be override
* @param client WSclient_t * ptr to the client struct
*/
* called if a non Websocket connection is coming in.
* Note: can be override
* @param client WSclient_t * ptr to the client struct
*/
virtual void handleNonWebsocketConnection(WSclient_t * client) {
DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] no Websocket connection close.\n", client->num);
client->tcp->write(
@ -156,10 +151,10 @@ class WebSocketsServer : protected WebSockets {
}
/**
* called if a non Authorization connection is coming in.
* Note: can be override
* @param client WSclient_t * ptr to the client struct
*/
* called if a non Authorization connection is coming in.
* Note: can be override
* @param client WSclient_t * ptr to the client struct
*/
virtual void handleAuthorizationFailed(WSclient_t * client) {
client->tcp->write(
"HTTP/1.1 401 Unauthorized\r\n"
@ -175,12 +170,12 @@ class WebSocketsServer : protected WebSockets {
}
/**
* called for sending a Event to the app
* @param num uint8_t
* @param type WStype_t
* @param payload uint8_t *
* @param length size_t
*/
* called for sending a Event to the app
* @param num uint8_t
* @param type WStype_t
* @param payload uint8_t *
* @param length size_t
*/
virtual void runCbEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
if(_cbEvent) {
_cbEvent(num, type, payload, length);
@ -188,28 +183,61 @@ class WebSocketsServer : protected WebSockets {
}
/*
* Called at client socket connect handshake negotiation time for each http header that is not
* a websocket specific http header (not Connection, Upgrade, Sec-WebSocket-*)
* If the custom httpHeaderValidationFunc returns false for any headerName / headerValue passed, the
* socket negotiation is considered invalid and the upgrade to websockets request is denied / rejected
* This mechanism can be used to enable custom authentication schemes e.g. test the value
* of a session cookie to determine if a user is logged on / authenticated
*/
* Called at client socket connect handshake negotiation time for each http header that is not
* a websocket specific http header (not Connection, Upgrade, Sec-WebSocket-*)
* If the custom httpHeaderValidationFunc returns false for any headerName / headerValue passed, the
* socket negotiation is considered invalid and the upgrade to websockets request is denied / rejected
* This mechanism can be used to enable custom authentication schemes e.g. test the value
* of a session cookie to determine if a user is logged on / authenticated
*/
virtual bool execHttpHeaderValidation(String headerName, String headerValue) {
if(_httpHeaderValidationFunc) {
//return the value of the custom http header validation function
// return the value of the custom http header validation function
return _httpHeaderValidationFunc(headerName, headerValue);
}
//no custom http header validation so just assume all is good
// no custom http header validation so just assume all is good
return true;
}
#if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
WSclient_t * handleNewClient(WEBSOCKETS_NETWORK_CLASS * tcpClient);
#endif
/**
* drop native tcp connection (client->tcp)
*/
void dropNativeClient(WSclient_t * client);
private:
/*
* returns an indicator whether the given named header exists in the configured _mandatoryHttpHeaders collection
* @param headerName String ///< the name of the header being checked
*/
* returns an indicator whether the given named header exists in the configured _mandatoryHttpHeaders collection
* @param headerName String ///< the name of the header being checked
*/
bool hasMandatoryHeader(String headerName);
};
class WebSocketsServer : public WebSocketsServerCore {
public:
WebSocketsServer(uint16_t port, const String & origin = "", const String & protocol = "arduino");
virtual ~WebSocketsServer(void);
void begin(void);
void close(void);
#if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
void loop(void); // handle incoming client and client data
#else
// Async interface not need a loop call
void loop(void) __attribute__((deprecated)) {}
#endif
protected:
#if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
void handleNewClients(void);
#endif
uint16_t _port;
WEBSOCKETS_NETWORK_SERVER_CLASS * _server;
};
#endif /* WEBSOCKETSSERVER_H_ */

36
src/WebSocketsVersion.h Normal file
View File

@ -0,0 +1,36 @@
/**
* @file WebSocketsVersion.h
* @date 05.04.2022
* @author Markus Sattler
*
* Copyright (c) 2015 Markus Sattler. All rights reserved.
* This file is part of the WebSockets for Arduino.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifndef WEBSOCKETSVERSION_H_
#define WEBSOCKETSVERSION_H_
#define WEBSOCKETS_VERSION "2.3.7"
#define WEBSOCKETS_VERSION_MAJOR 2
#define WEBSOCKETS_VERSION_MINOR 3
#define WEBSOCKETS_VERSION_PATCH 7
#define WEBSOCKETS_VERSION_INT 2003007
#endif /* WEBSOCKETSVERSION_H_ */

View File

@ -1,5 +1,7 @@
#!/bin/bash
set -x
function build_sketches()
{
local arduino=$1
@ -27,6 +29,64 @@ function build_sketches()
done
}
function build_sketch()
{
local arduino=$1
local sketch=$2
$arduino --verify --verbose $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 +97,38 @@ 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/
git submodule update --init
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
}

132
travis/version.py Executable file
View File

@ -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!')