mirror of
https://github.com/Links2004/arduinoWebSockets.git
synced 2025-06-25 15:01:36 +02:00
Compare commits
221 Commits
2.0.4
...
esp8266_ba
Author | SHA1 | Date | |
---|---|---|---|
ca52afeec0 | |||
beba0f88e3 | |||
f2265118b4 | |||
9982818cfa | |||
91b02341ba | |||
a00d3edcb7 | |||
c73c77e988 | |||
e1ddbfe1a5 | |||
f65e8d9062 | |||
05ec18e49b | |||
e185668a97 | |||
7e34a8b246 | |||
4acc7eff8a | |||
b3c5348e9b | |||
19c39d5ea0 | |||
ebdc8def4a | |||
44fb41e3b7 | |||
cadbd6458f | |||
f244741caa | |||
3f1cedb21c | |||
ba6275e7fe | |||
a71a480676 | |||
1e271b5e12 | |||
0c3b15c5e7 | |||
7a4160814c | |||
784088d9cc | |||
9048ef9ee4 | |||
80b0867786 | |||
b9e1336826 | |||
ac9b2ccdf6 | |||
12684582aa | |||
420cc55960 | |||
64296cced7 | |||
c3f01e43fc | |||
04e4be03fe | |||
ebd7a528ac | |||
516911900b | |||
674a6e98c9 | |||
4ee0ba5630 | |||
c038f100d6 | |||
0444a78441 | |||
8cc20b6e4b | |||
40152be197 | |||
e2669c1c5e | |||
771831c57e | |||
06a7bb177b | |||
bd158c9c5c | |||
9a803e1fb3 | |||
c64f2b81e5 | |||
4ef8d733dc | |||
7816bb3fa4 | |||
adf2b07f3b | |||
d3fde2ba34 | |||
c164c47f08 | |||
6a76efa791 | |||
5d26a65df4 | |||
007b5ba9a8 | |||
dbca2ab420 | |||
dfa35fa9ae | |||
794163cec9 | |||
294436840c | |||
b1b21d188f | |||
3063ad27ae | |||
e8df841b7f | |||
0aa07421a6 | |||
df3ef524b6 | |||
8ec27b0468 | |||
c361895a4b | |||
31bade1530 | |||
4223a9e41f | |||
72731beb10 | |||
1b6b42b877 | |||
d6dd7be987 | |||
d325bd338e | |||
d693437908 | |||
9b6ce7563a | |||
68800e2e7a | |||
eaef4f0801 | |||
4db22fe5e4 | |||
50e2e366cc | |||
e8249f76e6 | |||
a70d644f5d | |||
a3079f5645 | |||
2626b26315 | |||
38a881a86a | |||
32dd01b91b | |||
db8167f9c7 | |||
388683d4cd | |||
38c401b8af | |||
f7a7ab6ab4 | |||
bdee8b9744 | |||
50903cd410 | |||
f95c014342 | |||
8967356af3 | |||
d2043bf8ef | |||
7ddcdc2bd3 | |||
bde97179bf | |||
9ce044e550 | |||
a796079b0c | |||
e8c0d775fb | |||
5636b02b46 | |||
4b33575af1 | |||
486a612693 | |||
ea8e81c6ce | |||
8a187b523b | |||
ac8d806085 | |||
02e0128802 | |||
01e1fdfb50 | |||
d02932ef21 | |||
5bf8b2407e | |||
50d9a8d6e5 | |||
7443f9b1dc | |||
4b04f6fa53 | |||
a67a8dd0e0 | |||
4759a66a62 | |||
48e690a42a | |||
b059d0711c | |||
a09b8c48ee | |||
94eb4b12e5 | |||
2df62558f1 | |||
86074c90ae | |||
d73e3ecc9c | |||
f62aa6478d | |||
d340c89b09 | |||
05f4cfd7bf | |||
46cff38287 | |||
0b35e32b2a | |||
255f40eaa7 | |||
5e1f9b1c26 | |||
30b6deedd2 | |||
0e6e1c76ee | |||
887683fabb | |||
0b10299549 | |||
1d300084e0 | |||
d9e1e97cf6 | |||
642750e232 | |||
a0c714bf10 | |||
c5461d5779 | |||
b87bce4466 | |||
9608c2d210 | |||
4baf4896cc | |||
4fd2e9d993 | |||
fa580a568f | |||
a93a845780 | |||
83a1539e1e | |||
1bd4638621 | |||
f40a390ab1 | |||
b6c27f74cd | |||
c64a082270 | |||
522a67bc1b | |||
29df9c30f7 | |||
4c1c5378cb | |||
dbabc1025b | |||
1d40160d99 | |||
a388676b40 | |||
8c19d7ba48 | |||
4fc80871a6 | |||
0ca020bbd2 | |||
cc3ce02c67 | |||
96c266101e | |||
64c8908b05 | |||
87c6c80c67 | |||
adb255bda0 | |||
d0ab6c4fd1 | |||
fcb623ce91 | |||
669f0b489e | |||
32cb052f23 | |||
42ab3168c5 | |||
86d2e2400a | |||
ad07f3c665 | |||
a4533a028c | |||
ae3dd103a1 | |||
40f84c19f4 | |||
f68d9d8030 | |||
c911776860 | |||
adb52b11e9 | |||
81e567b248 | |||
1afe317c7d | |||
177ec8239d | |||
e675c7590e | |||
615926ae1e | |||
acd0a603eb | |||
26140be6c9 | |||
3b1dabbe1e | |||
55bc7db7ad | |||
1a6f46c67d | |||
210f2e8fa1 | |||
34a2d282e4 | |||
e93a323e56 | |||
6da0bc97e8 | |||
ae2ba7effe | |||
dc426c9a61 | |||
60e3d1080e | |||
26fc61f7a2 | |||
604a781122 | |||
f52d718cf2 | |||
6757b8b74c | |||
bef2541ede | |||
dac71c4c23 | |||
60903a2fa5 | |||
0aaf50f87f | |||
2add886219 | |||
ab3b5bae46 | |||
45bb7dbe23 | |||
92032289ec | |||
5cd68c5304 | |||
ddaeea0e4b | |||
a097f0defd | |||
689e3ef921 | |||
75133dfa6d | |||
d2719573d4 | |||
0d9aa043f0 | |||
7810d0d0b3 | |||
1defe6d8e1 | |||
d036dcd8e2 | |||
529a86cc26 | |||
958ab08a6b | |||
7361e2b1b6 | |||
1961ddc159 | |||
bf3cfa6237 | |||
f8a5acc9b7 |
63
.clang-format
Normal file
63
.clang-format
Normal file
@ -0,0 +1,63 @@
|
||||
---
|
||||
BasedOnStyle: Google
|
||||
AccessModifierOffset: '-2'
|
||||
AlignAfterOpenBracket: DontAlign
|
||||
AlignConsecutiveAssignments: 'true'
|
||||
AlignConsecutiveDeclarations: 'false'
|
||||
AlignEscapedNewlines: Left
|
||||
AlignTrailingComments: 'true'
|
||||
AllowAllParametersOfDeclarationOnNextLine: 'false'
|
||||
AllowShortBlocksOnASingleLine: 'false'
|
||||
AllowShortCaseLabelsOnASingleLine: 'false'
|
||||
AllowShortFunctionsOnASingleLine: InlineOnly
|
||||
AllowShortIfStatementsOnASingleLine: 'true'
|
||||
AllowShortLoopsOnASingleLine: 'true'
|
||||
AlwaysBreakAfterDefinitionReturnType: None
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: 'true'
|
||||
AlwaysBreakTemplateDeclarations: 'false'
|
||||
BinPackParameters: 'true'
|
||||
BreakAfterJavaFieldAnnotations: 'false'
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeBraces: Attach
|
||||
BreakBeforeInheritanceComma: 'false'
|
||||
BreakBeforeTernaryOperators: 'false'
|
||||
BreakConstructorInitializers: BeforeColon
|
||||
BreakStringLiterals: 'false'
|
||||
ColumnLimit: '0'
|
||||
CompactNamespaces: 'true'
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: 'true'
|
||||
ConstructorInitializerIndentWidth: '4'
|
||||
ContinuationIndentWidth: '4'
|
||||
Cpp11BracedListStyle: 'false'
|
||||
DerivePointerAlignment: 'false'
|
||||
FixNamespaceComments: 'true'
|
||||
IndentCaseLabels: 'true'
|
||||
IndentWidth: '4'
|
||||
IndentWrappedFunctionNames: 'false'
|
||||
JavaScriptQuotes: Single
|
||||
JavaScriptWrapImports: 'false'
|
||||
KeepEmptyLinesAtTheStartOfBlocks: 'false'
|
||||
MaxEmptyLinesToKeep: '1'
|
||||
NamespaceIndentation: All
|
||||
ObjCBlockIndentWidth: '4'
|
||||
ObjCSpaceAfterProperty: 'false'
|
||||
ObjCSpaceBeforeProtocolList: 'false'
|
||||
PointerAlignment: Middle
|
||||
SortIncludes: 'false'
|
||||
SortUsingDeclarations: 'true'
|
||||
SpaceAfterCStyleCast: 'false'
|
||||
SpaceAfterTemplateKeyword: 'false'
|
||||
SpaceBeforeAssignmentOperators: 'true'
|
||||
SpaceBeforeParens: Never
|
||||
SpaceInEmptyParentheses: 'false'
|
||||
SpacesBeforeTrailingComments: '4'
|
||||
SpacesInAngles: 'false'
|
||||
SpacesInCStyleCastParentheses: 'false'
|
||||
SpacesInContainerLiterals: 'false'
|
||||
SpacesInParentheses: 'false'
|
||||
SpacesInSquareBrackets: 'false'
|
||||
TabWidth: '4'
|
||||
UseTab: Never
|
||||
|
||||
...
|
8
.gitignore
vendored
8
.gitignore
vendored
@ -27,3 +27,11 @@
|
||||
*.out
|
||||
*.app
|
||||
/tests/webSocketServer/node_modules
|
||||
|
||||
# IDE
|
||||
.vscode
|
||||
.cproject
|
||||
.project
|
||||
.settings
|
||||
*.swp
|
||||
|
||||
|
51
.travis.yml
Normal file
51
.travis.yml
Normal file
@ -0,0 +1,51 @@
|
||||
sudo: false
|
||||
dist:
|
||||
- xenial
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- xvfb
|
||||
language: bash
|
||||
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.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.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
|
||||
- sleep 3
|
||||
- wget http://downloads.arduino.cc/arduino-$IDE_VERSION-linux64.tar.xz
|
||||
- tar xf arduino-$IDE_VERSION-linux64.tar.xz
|
||||
- mv arduino-$IDE_VERSION $HOME/arduino_ide
|
||||
- export PATH="$HOME/arduino_ide:$PATH"
|
||||
- which arduino
|
||||
- mkdir -p $HOME/Arduino/libraries
|
||||
|
||||
- wget https://github.com/bblanchon/ArduinoJson/archive/6.x.zip
|
||||
- unzip 6.x.zip
|
||||
- mv ArduinoJson-6.x $HOME/Arduino/libraries/ArduinoJson
|
||||
- cp -r $TRAVIS_BUILD_DIR $HOME/Arduino/libraries/arduinoWebSockets
|
||||
- source $TRAVIS_BUILD_DIR/travis/common.sh
|
||||
- get_core $CPU
|
||||
- cd $TRAVIS_BUILD_DIR
|
||||
- arduino --board $BOARD --save-prefs
|
||||
- arduino --get-pref sketchbook.path
|
||||
- arduino --pref update.check=false
|
||||
- build_sketches arduino $HOME/Arduino/libraries/arduinoWebSockets/examples/$CPU $CPU
|
||||
|
||||
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
|
76
README.md
76
README.md
@ -1,57 +1,95 @@
|
||||
WebSocket Server and Client for Arduino
|
||||
WebSocket Server and Client for Arduino [](https://travis-ci.com/Links2004/arduinoWebSockets)
|
||||
===========================================
|
||||
|
||||
a WebSocket Server and Client for Arduino based on RFC6455.
|
||||
|
||||
|
||||
|
||||
##### Supported features of RFC6455 #####
|
||||
- text frame
|
||||
- binary frame
|
||||
- connection close
|
||||
- ping
|
||||
- pong
|
||||
|
||||
##### Not supported features of RFC6455 #####
|
||||
- continuation frame
|
||||
|
||||
|
||||
##### Limitations #####
|
||||
- max input length is limited to the ram size and the ```WEBSOCKETS_MAX_DATA_SIZE``` define
|
||||
- max output length has no limit (the hardware is the limit)
|
||||
- Client send big frames with mask 0x00000000 (on AVR all frames)
|
||||
|
||||
- continuation frame reassembly need to be handled in the application code
|
||||
|
||||
##### Limitations for Async #####
|
||||
- Functions called from within the context of the websocket event might not honor `yield()` and/or `delay()`. See [this issue](https://github.com/Links2004/arduinoWebSockets/issues/58#issuecomment-192376395) for more info and a potential workaround.
|
||||
- wss / SSL is not possible.
|
||||
|
||||
|
||||
##### Supported Hardware #####
|
||||
- ESP8266 [Arduino for ESP8266](https://github.com/Links2004/Arduino)
|
||||
- ESP8266 [Arduino for ESP8266](https://github.com/esp8266/Arduino/)
|
||||
- ESP32 [Arduino for ESP32](https://github.com/espressif/arduino-esp32)
|
||||
- ESP31B
|
||||
- ATmega328 with Ethernet Shield (ATmega branch)
|
||||
- ATmega328 with enc28j60 (ATmega branch)
|
||||
- ATmega2560 with Ethernet Shield (ATmega branch)
|
||||
- ATmega2560 with enc28j60 (ATmega branch)
|
||||
|
||||
- Particle with STM32 ARM Cortex M3
|
||||
- ATmega328 with Ethernet Shield (ATmega branch)
|
||||
- ATmega328 with enc28j60 (ATmega branch)
|
||||
- ATmega2560 with Ethernet Shield (ATmega branch)
|
||||
- ATmega2560 with enc28j60 (ATmega branch)
|
||||
|
||||
###### 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++.
|
||||
|
||||
|
||||
### wss / SSL ###
|
||||
supported for:
|
||||
- wss client on the ESP8266
|
||||
- wss / SSL is not natively supported in WebSocketsServer however it is possible to achieve secure websockets
|
||||
by running the device behind an SSL proxy. See [Nginx](examples/Nginx/esp8266.ssl.reverse.proxy.conf) for a
|
||||
sample Nginx server configuration file to enable this.
|
||||
|
||||
sample Nginx server configuration file to enable this.
|
||||
|
||||
### ESP Async TCP ###
|
||||
|
||||
This libary can run in Async TCP mode on the ESP.
|
||||
|
||||
The mode can be aktivated in the ```WebSockets.h``` (see WEBSOCKETS_NETWORK_TYPE define).
|
||||
The mode can be activated in the ```WebSockets.h``` (see WEBSOCKETS_NETWORK_TYPE define).
|
||||
|
||||
[ESPAsyncTCP](https://github.com/me-no-dev/ESPAsyncTCP) libary is required.
|
||||
|
||||
|
||||
### High Level Client API ###
|
||||
|
||||
- `begin` : Initiate connection sequence to the websocket host.
|
||||
```c++
|
||||
void begin(const char *host, uint16_t port, const char * url = "/", const char * protocol = "arduino");
|
||||
void begin(String host, uint16_t port, String url = "/", String protocol = "arduino");
|
||||
```
|
||||
- `onEvent`: Callback to handle for websocket events
|
||||
|
||||
```c++
|
||||
void onEvent(WebSocketClientEvent cbEvent);
|
||||
```
|
||||
|
||||
- `WebSocketClientEvent`: Handler for websocket events
|
||||
```c++
|
||||
void (*WebSocketClientEvent)(WStype_t type, uint8_t * payload, size_t length)
|
||||
```
|
||||
Where `WStype_t type` is defined as:
|
||||
```c++
|
||||
typedef enum {
|
||||
WStype_ERROR,
|
||||
WStype_DISCONNECTED,
|
||||
WStype_CONNECTED,
|
||||
WStype_TEXT,
|
||||
WStype_BIN,
|
||||
WStype_FRAGMENT_TEXT_START,
|
||||
WStype_FRAGMENT_BIN_START,
|
||||
WStype_FRAGMENT,
|
||||
WStype_FRAGMENT_FIN,
|
||||
WStype_PING,
|
||||
WStype_PONG,
|
||||
} WStype_t;
|
||||
```
|
||||
|
||||
### Issues ###
|
||||
Submit issues to: https://github.com/Links2004/arduinoWebSockets/issues
|
||||
|
||||
|
110
examples/esp32/WebSocketClient/WebSocketClient.ino
Normal file
110
examples/esp32/WebSocketClient/WebSocketClient.ino
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* WebSocketClient.ino
|
||||
*
|
||||
* Created on: 24.05.2015
|
||||
*
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include <WiFi.h>
|
||||
#include <WiFiMulti.h>
|
||||
#include <WiFiClientSecure.h>
|
||||
|
||||
#include <WebSocketsClient.h>
|
||||
|
||||
|
||||
WiFiMulti WiFiMulti;
|
||||
WebSocketsClient webSocket;
|
||||
|
||||
#define USE_SERIAL Serial1
|
||||
|
||||
void hexdump(const void *mem, uint32_t len, uint8_t cols = 16) {
|
||||
const uint8_t* src = (const uint8_t*) mem;
|
||||
USE_SERIAL.printf("\n[HEXDUMP] Address: 0x%08X len: 0x%X (%d)", (ptrdiff_t)src, len, len);
|
||||
for(uint32_t i = 0; i < len; i++) {
|
||||
if(i % cols == 0) {
|
||||
USE_SERIAL.printf("\n[0x%08X] 0x%08X: ", (ptrdiff_t)src, i);
|
||||
}
|
||||
USE_SERIAL.printf("%02X ", *src);
|
||||
src++;
|
||||
}
|
||||
USE_SERIAL.printf("\n");
|
||||
}
|
||||
|
||||
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;
|
||||
case WStype_ERROR:
|
||||
case WStype_FRAGMENT_TEXT_START:
|
||||
case WStype_FRAGMENT_BIN_START:
|
||||
case WStype_FRAGMENT:
|
||||
case WStype_FRAGMENT_FIN:
|
||||
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);
|
||||
}
|
||||
|
||||
// server address, port and URL
|
||||
webSocket.begin("192.168.0.123", 81, "/");
|
||||
|
||||
// event handler
|
||||
webSocket.onEvent(webSocketEvent);
|
||||
|
||||
// use HTTP Basic Authorization this is optional remove if not needed
|
||||
webSocket.setAuthorization("user", "Password");
|
||||
|
||||
// try ever 5000 again if connection has failed
|
||||
webSocket.setReconnectInterval(5000);
|
||||
|
||||
}
|
||||
|
||||
void loop() {
|
||||
webSocket.loop();
|
||||
}
|
@ -1,26 +1,40 @@
|
||||
/*
|
||||
* WebSocketClient.ino
|
||||
* WebSocketClientSSL.ino
|
||||
*
|
||||
* Created on: 24.05.2015
|
||||
* Created on: 10.12.2015
|
||||
*
|
||||
* note SSL is only possible with the ESP8266
|
||||
*
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESP8266WiFiMulti.h>
|
||||
#include <WiFi.h>
|
||||
#include <WiFiMulti.h>
|
||||
#include <WiFiClientSecure.h>
|
||||
|
||||
#include <WebSocketsClient.h>
|
||||
|
||||
#include <Hash.h>
|
||||
|
||||
ESP8266WiFiMulti WiFiMulti;
|
||||
WiFiMulti WiFiMulti;
|
||||
WebSocketsClient webSocket;
|
||||
|
||||
|
||||
#define USE_SERIAL Serial1
|
||||
|
||||
void webSocketEvent(WStype_t type, uint8_t * payload, size_t lenght) {
|
||||
void hexdump(const void *mem, uint32_t len, uint8_t cols = 16) {
|
||||
const uint8_t* src = (const uint8_t*) mem;
|
||||
USE_SERIAL.printf("\n[HEXDUMP] Address: 0x%08X len: 0x%X (%d)", (ptrdiff_t)src, len, len);
|
||||
for(uint32_t i = 0; i < len; i++) {
|
||||
if(i % cols == 0) {
|
||||
USE_SERIAL.printf("\n[0x%08X] 0x%08X: ", (ptrdiff_t)src, i);
|
||||
}
|
||||
USE_SERIAL.printf("%02X ", *src);
|
||||
src++;
|
||||
}
|
||||
USE_SERIAL.printf("\n");
|
||||
}
|
||||
|
||||
void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) {
|
||||
|
||||
|
||||
switch(type) {
|
||||
@ -30,7 +44,7 @@ void webSocketEvent(WStype_t type, uint8_t * payload, size_t lenght) {
|
||||
case WStype_CONNECTED:
|
||||
{
|
||||
USE_SERIAL.printf("[WSc] Connected to url: %s\n", payload);
|
||||
|
||||
|
||||
// send message to server when Connected
|
||||
webSocket.sendTXT("Connected");
|
||||
}
|
||||
@ -42,12 +56,18 @@ void webSocketEvent(WStype_t type, uint8_t * payload, size_t lenght) {
|
||||
// webSocket.sendTXT("message here");
|
||||
break;
|
||||
case WStype_BIN:
|
||||
USE_SERIAL.printf("[WSc] get binary lenght: %u\n", lenght);
|
||||
hexdump(payload, lenght);
|
||||
USE_SERIAL.printf("[WSc] get binary length: %u\n", length);
|
||||
hexdump(payload, length);
|
||||
|
||||
// send data to server
|
||||
// webSocket.sendBIN(payload, lenght);
|
||||
// webSocket.sendBIN(payload, length);
|
||||
break;
|
||||
case WStype_ERROR:
|
||||
case WStype_FRAGMENT_TEXT_START:
|
||||
case WStype_FRAGMENT_BIN_START:
|
||||
case WStype_FRAGMENT:
|
||||
case WStype_FRAGMENT_FIN:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
@ -76,8 +96,7 @@ void setup() {
|
||||
delay(100);
|
||||
}
|
||||
|
||||
webSocket.begin("192.168.0.123", 81);
|
||||
//webSocket.setAuthorization("user", "Password"); // HTTP Basic Authorization
|
||||
webSocket.beginSSL("192.168.0.123", 81);
|
||||
webSocket.onEvent(webSocketEvent);
|
||||
|
||||
}
|
104
examples/esp32/WebSocketServer/WebSocketServer.ino
Normal file
104
examples/esp32/WebSocketServer/WebSocketServer.ino
Normal file
@ -0,0 +1,104 @@
|
||||
/*
|
||||
* WebSocketServer.ino
|
||||
*
|
||||
* Created on: 22.05.2015
|
||||
*
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include <WiFi.h>
|
||||
#include <WiFiMulti.h>
|
||||
#include <WiFiClientSecure.h>
|
||||
|
||||
#include <WebSocketsServer.h>
|
||||
|
||||
WiFiMulti WiFiMulti;
|
||||
WebSocketsServer webSocket = WebSocketsServer(81);
|
||||
|
||||
#define USE_SERIAL Serial1
|
||||
|
||||
void hexdump(const void *mem, uint32_t len, uint8_t cols = 16) {
|
||||
const uint8_t* src = (const uint8_t*) mem;
|
||||
USE_SERIAL.printf("\n[HEXDUMP] Address: 0x%08X len: 0x%X (%d)", (ptrdiff_t)src, len, len);
|
||||
for(uint32_t i = 0; i < len; i++) {
|
||||
if(i % cols == 0) {
|
||||
USE_SERIAL.printf("\n[0x%08X] 0x%08X: ", (ptrdiff_t)src, i);
|
||||
}
|
||||
USE_SERIAL.printf("%02X ", *src);
|
||||
src++;
|
||||
}
|
||||
USE_SERIAL.printf("\n");
|
||||
}
|
||||
|
||||
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;
|
||||
case WStype_ERROR:
|
||||
case WStype_FRAGMENT_TEXT_START:
|
||||
case WStype_FRAGMENT_BIN_START:
|
||||
case WStype_FRAGMENT:
|
||||
case WStype_FRAGMENT_FIN:
|
||||
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);
|
||||
}
|
||||
|
||||
webSocket.begin();
|
||||
webSocket.onEvent(webSocketEvent);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
webSocket.loop();
|
||||
}
|
106
examples/esp8266/WebSocketClient/WebSocketClient.ino
Normal file
106
examples/esp8266/WebSocketClient/WebSocketClient.ino
Normal file
@ -0,0 +1,106 @@
|
||||
/*
|
||||
* WebSocketClient.ino
|
||||
*
|
||||
* Created on: 24.05.2015
|
||||
*
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESP8266WiFiMulti.h>
|
||||
|
||||
#include <WebSocketsClient.h>
|
||||
|
||||
#include <Hash.h>
|
||||
|
||||
ESP8266WiFiMulti WiFiMulti;
|
||||
WebSocketsClient webSocket;
|
||||
|
||||
#define USE_SERIAL Serial1
|
||||
|
||||
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;
|
||||
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.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);
|
||||
}
|
||||
|
||||
// server address, port and URL
|
||||
webSocket.begin("192.168.0.123", 81, "/");
|
||||
|
||||
// event handler
|
||||
webSocket.onEvent(webSocketEvent);
|
||||
|
||||
// use HTTP Basic Authorization this is optional remove if not needed
|
||||
webSocket.setAuthorization("user", "Password");
|
||||
|
||||
// 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();
|
||||
}
|
@ -0,0 +1,125 @@
|
||||
/*
|
||||
* WebSocketClientSocketIO.ino
|
||||
*
|
||||
* Created on: 06.06.2016
|
||||
*
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESP8266WiFiMulti.h>
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
#include <WebSocketsClient.h>
|
||||
#include <SocketIOclient.h>
|
||||
|
||||
#include <Hash.h>
|
||||
|
||||
ESP8266WiFiMulti WiFiMulti;
|
||||
SocketIOclient socketIO;
|
||||
|
||||
#define USE_SERIAL Serial1
|
||||
|
||||
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);
|
||||
break;
|
||||
case sIOtype_EVENT:
|
||||
USE_SERIAL.printf("[IOc] get event: %s\n", payload);
|
||||
break;
|
||||
case sIOtype_ACK:
|
||||
USE_SERIAL.printf("[IOc] get ack: %u\n", length);
|
||||
hexdump(payload, length);
|
||||
break;
|
||||
case sIOtype_ERROR:
|
||||
USE_SERIAL.printf("[IOc] get error: %u\n", length);
|
||||
hexdump(payload, length);
|
||||
break;
|
||||
case sIOtype_BINARY_EVENT:
|
||||
USE_SERIAL.printf("[IOc] get binary: %u\n", length);
|
||||
hexdump(payload, length);
|
||||
break;
|
||||
case sIOtype_BINARY_ACK:
|
||||
USE_SERIAL.printf("[IOc] get binary ack: %u\n", length);
|
||||
hexdump(payload, length);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void setup() {
|
||||
// USE_SERIAL.begin(921600);
|
||||
USE_SERIAL.begin(115200);
|
||||
|
||||
//Serial.setDebugOutput(true);
|
||||
USE_SERIAL.setDebugOutput(true);
|
||||
|
||||
USE_SERIAL.println();
|
||||
USE_SERIAL.println();
|
||||
USE_SERIAL.println();
|
||||
|
||||
for(uint8_t t = 4; t > 0; t--) {
|
||||
USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t);
|
||||
USE_SERIAL.flush();
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
// disable AP
|
||||
if(WiFi.getMode() & WIFI_AP) {
|
||||
WiFi.softAPdisconnect(true);
|
||||
}
|
||||
|
||||
WiFiMulti.addAP("SSID", "passpasspass");
|
||||
|
||||
//WiFi.disconnect();
|
||||
while(WiFiMulti.run() != WL_CONNECTED) {
|
||||
delay(100);
|
||||
}
|
||||
|
||||
String ip = WiFi.localIP().toString();
|
||||
USE_SERIAL.printf("[SETUP] WiFi Connected %s\n", ip.c_str());
|
||||
|
||||
// server address, port and URL
|
||||
socketIO.begin("10.11.100.100", 8880);
|
||||
|
||||
// event handler
|
||||
socketIO.onEvent(socketIOEvent);
|
||||
}
|
||||
|
||||
unsigned long messageTimestamp = 0;
|
||||
void loop() {
|
||||
socketIO.loop();
|
||||
|
||||
uint64_t now = millis();
|
||||
|
||||
if(now - messageTimestamp > 2000) {
|
||||
messageTimestamp = now;
|
||||
|
||||
// creat JSON message for Socket.IO (event)
|
||||
DynamicJsonDocument doc(1024);
|
||||
JsonArray array = doc.to<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);
|
||||
}
|
||||
}
|
@ -0,0 +1,162 @@
|
||||
/*
|
||||
* WebSocketClientSocketIOack.ino
|
||||
*
|
||||
* Created on: 20.07.2019
|
||||
*
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESP8266WiFiMulti.h>
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
#include <WebSocketsClient.h>
|
||||
#include <SocketIOclient.h>
|
||||
|
||||
#include <Hash.h>
|
||||
|
||||
ESP8266WiFiMulti WiFiMulti;
|
||||
SocketIOclient socketIO;
|
||||
|
||||
#define USE_SERIAL Serial
|
||||
|
||||
|
||||
void socketIOEvent(socketIOmessageType_t type, uint8_t * payload, size_t length) {
|
||||
switch(type) {
|
||||
case sIOtype_DISCONNECT:
|
||||
USE_SERIAL.printf("[IOc] Disconnected!\n");
|
||||
break;
|
||||
case sIOtype_CONNECT:
|
||||
USE_SERIAL.printf("[IOc] Connected to url: %s\n", payload);
|
||||
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);
|
||||
hexdump(payload, length);
|
||||
break;
|
||||
case sIOtype_ERROR:
|
||||
USE_SERIAL.printf("[IOc] get error: %u\n", length);
|
||||
hexdump(payload, length);
|
||||
break;
|
||||
case sIOtype_BINARY_EVENT:
|
||||
USE_SERIAL.printf("[IOc] get binary: %u\n", length);
|
||||
hexdump(payload, length);
|
||||
break;
|
||||
case sIOtype_BINARY_ACK:
|
||||
USE_SERIAL.printf("[IOc] get binary ack: %u\n", length);
|
||||
hexdump(payload, length);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void setup() {
|
||||
//USE_SERIAL.begin(921600);
|
||||
USE_SERIAL.begin(115200);
|
||||
|
||||
//Serial.setDebugOutput(true);
|
||||
USE_SERIAL.setDebugOutput(true);
|
||||
|
||||
USE_SERIAL.println();
|
||||
USE_SERIAL.println();
|
||||
USE_SERIAL.println();
|
||||
|
||||
for(uint8_t t = 4; t > 0; t--) {
|
||||
USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t);
|
||||
USE_SERIAL.flush();
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
// disable AP
|
||||
if(WiFi.getMode() & WIFI_AP) {
|
||||
WiFi.softAPdisconnect(true);
|
||||
}
|
||||
|
||||
WiFiMulti.addAP("SSID", "passpasspass");
|
||||
|
||||
//WiFi.disconnect();
|
||||
while(WiFiMulti.run() != WL_CONNECTED) {
|
||||
delay(100);
|
||||
}
|
||||
|
||||
String ip = WiFi.localIP().toString();
|
||||
USE_SERIAL.printf("[SETUP] WiFi Connected %s\n", ip.c_str());
|
||||
|
||||
// server address, port and URL
|
||||
socketIO.begin("10.11.100.100", 8880);
|
||||
|
||||
// event handler
|
||||
socketIO.onEvent(socketIOEvent);
|
||||
}
|
||||
|
||||
unsigned long messageTimestamp = 0;
|
||||
void loop() {
|
||||
socketIO.loop();
|
||||
|
||||
uint64_t now = millis();
|
||||
|
||||
if(now - messageTimestamp > 2000) {
|
||||
messageTimestamp = now;
|
||||
|
||||
// creat JSON message for Socket.IO (event)
|
||||
DynamicJsonDocument doc(1024);
|
||||
JsonArray array = doc.to<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);
|
||||
}
|
||||
}
|
149
examples/esp8266/WebSocketClientStomp/WebSocketClientStomp.ino
Normal file
149
examples/esp8266/WebSocketClientStomp/WebSocketClientStomp.ino
Normal file
@ -0,0 +1,149 @@
|
||||
/*
|
||||
WebSocketClientStomp.ino
|
||||
|
||||
Example for connecting and maintining a connection with a STOMP websocket connection.
|
||||
In this example, we connect to a Spring application (see https://docs.spring.io/spring/docs/current/spring-framework-reference/html/websocket.html).
|
||||
|
||||
Created on: 25.09.2017
|
||||
Author: Martin Becker <mgbckr>, Contact: becker@informatik.uni-wuerzburg.de
|
||||
*/
|
||||
|
||||
// PRE
|
||||
|
||||
#define USE_SERIAL Serial
|
||||
|
||||
|
||||
// LIBRARIES
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <Hash.h>
|
||||
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <WebSocketsClient.h>
|
||||
|
||||
|
||||
// SETTINGS
|
||||
|
||||
const char* wlan_ssid = "yourssid";
|
||||
const char* wlan_password = "somepassword";
|
||||
|
||||
const char* ws_host = "the.host.net";
|
||||
const int ws_port = 80;
|
||||
|
||||
// URL for STOMP endpoint.
|
||||
// For the default config of Spring's STOMP support, the default URL is "/socketentry/websocket".
|
||||
const char* stompUrl = "/socketentry/websocket"; // don't forget the leading "/" !!!
|
||||
|
||||
|
||||
// VARIABLES
|
||||
|
||||
WebSocketsClient webSocket;
|
||||
|
||||
|
||||
// FUNCTIONS
|
||||
|
||||
/**
|
||||
* STOMP messages need to be NULL-terminated (i.e., \0 or \u0000).
|
||||
* However, when we send a String or a char[] array without specifying
|
||||
* a length, the size of the message payload is derived by strlen() internally,
|
||||
* thus dropping any NULL values appended to the "msg"-String.
|
||||
*
|
||||
* To solve this, we first convert the String to a NULL terminated char[] array
|
||||
* via "c_str" and set the length of the payload to include the NULL value.
|
||||
*/
|
||||
void sendMessage(String & msg) {
|
||||
webSocket.sendTXT(msg.c_str(), msg.length() + 1);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
String msg = "CONNECT\r\naccept-version:1.1,1.0\r\nheart-beat:10000,10000\r\n\r\n";
|
||||
sendMessage(msg);
|
||||
}
|
||||
break;
|
||||
case WStype_TEXT:
|
||||
{
|
||||
// #####################
|
||||
// handle STOMP protocol
|
||||
// #####################
|
||||
|
||||
String text = (char*) payload;
|
||||
USE_SERIAL.printf("[WSc] get text: %s\n", payload);
|
||||
|
||||
if (text.startsWith("CONNECTED")) {
|
||||
|
||||
// subscribe to some channels
|
||||
|
||||
String msg = "SUBSCRIBE\nid:sub-0\ndestination:/user/queue/messages\n\n";
|
||||
sendMessage(msg);
|
||||
delay(1000);
|
||||
|
||||
// and send a message
|
||||
|
||||
msg = "SEND\ndestination:/app/message\n\n{\"user\":\"esp\",\"message\":\"Hello!\"}";
|
||||
sendMessage(msg);
|
||||
delay(1000);
|
||||
|
||||
} else {
|
||||
|
||||
// do something with messages
|
||||
|
||||
}
|
||||
|
||||
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() {
|
||||
|
||||
// setup serial
|
||||
|
||||
// USE_SERIAL.begin(921600);
|
||||
USE_SERIAL.begin(115200);
|
||||
|
||||
// USE_SERIAL.setDebugOutput(true);
|
||||
|
||||
USE_SERIAL.println();
|
||||
|
||||
|
||||
// connect to WiFi
|
||||
|
||||
USE_SERIAL.print("Logging into WLAN: "); Serial.print(wlan_ssid); Serial.print(" ...");
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(wlan_ssid, wlan_password);
|
||||
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(500);
|
||||
USE_SERIAL.print(".");
|
||||
}
|
||||
USE_SERIAL.println(" success.");
|
||||
USE_SERIAL.print("IP: "); USE_SERIAL.println(WiFi.localIP());
|
||||
|
||||
|
||||
// connect to websocket
|
||||
webSocket.begin(ws_host, ws_port, stompUrl);
|
||||
webSocket.setExtraHeaders(); // remove "Origin: file://" header because it breaks the connection with Spring's default websocket config
|
||||
// webSocket.setExtraHeaders("foo: I am so funny\r\nbar: not"); // some headers, in case you feel funny
|
||||
webSocket.onEvent(webSocketEvent);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
webSocket.loop();
|
||||
}
|
@ -0,0 +1,150 @@
|
||||
/*
|
||||
WebSocketClientStompOverSockJs.ino
|
||||
|
||||
Example for connecting and maintining a connection with a SockJS+STOMP websocket connection.
|
||||
In this example, we connect to a Spring application (see https://docs.spring.io/spring/docs/current/spring-framework-reference/html/websocket.html).
|
||||
|
||||
Created on: 18.07.2017
|
||||
Author: Martin Becker <mgbckr>, Contact: becker@informatik.uni-wuerzburg.de
|
||||
*/
|
||||
|
||||
// PRE
|
||||
|
||||
#define USE_SERIAL Serial
|
||||
|
||||
|
||||
// LIBRARIES
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <Hash.h>
|
||||
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <WebSocketsClient.h>
|
||||
|
||||
|
||||
// SETTINGS
|
||||
|
||||
const char* wlan_ssid = "yourssid";
|
||||
const char* wlan_password = "somepassword";
|
||||
|
||||
const char* ws_host = "the.host.net";
|
||||
const int ws_port = 80;
|
||||
|
||||
// base URL for SockJS (websocket) connection
|
||||
// The complete URL will look something like this(cf. http://sockjs.github.io/sockjs-protocol/sockjs-protocol-0.3.3.html#section-36):
|
||||
// ws://<ws_host>:<ws_port>/<ws_baseurl>/<3digits>/<randomstring>/websocket
|
||||
// For the default config of Spring's SockJS/STOMP support, the default base URL is "/socketentry/".
|
||||
const char* ws_baseurl = "/socketentry/"; // don't forget leading and trailing "/" !!!
|
||||
|
||||
|
||||
// VARIABLES
|
||||
|
||||
WebSocketsClient webSocket;
|
||||
|
||||
|
||||
// FUNCTIONS
|
||||
|
||||
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);
|
||||
}
|
||||
break;
|
||||
case WStype_TEXT:
|
||||
{
|
||||
// #####################
|
||||
// handle SockJs+STOMP protocol
|
||||
// #####################
|
||||
|
||||
String text = (char*) payload;
|
||||
|
||||
USE_SERIAL.printf("[WSc] get text: %s\n", payload);
|
||||
|
||||
if (payload[0] == 'h') {
|
||||
|
||||
USE_SERIAL.println("Heartbeat!");
|
||||
|
||||
} else if (payload[0] == 'o') {
|
||||
|
||||
// on open connection
|
||||
char *msg = "[\"CONNECT\\naccept-version:1.1,1.0\\nheart-beat:10000,10000\\n\\n\\u0000\"]";
|
||||
webSocket.sendTXT(msg);
|
||||
|
||||
} else if (text.startsWith("a[\"CONNECTED")) {
|
||||
|
||||
// subscribe to some channels
|
||||
|
||||
char *msg = "[\"SUBSCRIBE\\nid:sub-0\\ndestination:/user/queue/messages\\n\\n\\u0000\"]";
|
||||
webSocket.sendTXT(msg);
|
||||
delay(1000);
|
||||
|
||||
// and send a message
|
||||
|
||||
msg = "[\"SEND\\ndestination:/app/message\\n\\n{\\\"user\\\":\\\"esp\\\",\\\"message\\\":\\\"Hello!\\\"}\\u0000\"]";
|
||||
webSocket.sendTXT(msg);
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
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() {
|
||||
|
||||
// setup serial
|
||||
|
||||
// USE_SERIAL.begin(921600);
|
||||
USE_SERIAL.begin(115200);
|
||||
|
||||
// USE_SERIAL.setDebugOutput(true);
|
||||
|
||||
USE_SERIAL.println();
|
||||
|
||||
|
||||
// connect to WiFi
|
||||
|
||||
USE_SERIAL.print("Logging into WLAN: "); Serial.print(wlan_ssid); Serial.print(" ...");
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(wlan_ssid, wlan_password);
|
||||
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(500);
|
||||
USE_SERIAL.print(".");
|
||||
}
|
||||
USE_SERIAL.println(" success.");
|
||||
USE_SERIAL.print("IP: "); USE_SERIAL.println(WiFi.localIP());
|
||||
|
||||
|
||||
// #####################
|
||||
// create socket url according to SockJS protocol (cf. http://sockjs.github.io/sockjs-protocol/sockjs-protocol-0.3.3.html#section-36)
|
||||
// #####################
|
||||
String socketUrl = ws_baseurl;
|
||||
socketUrl += random(0, 999);
|
||||
socketUrl += "/";
|
||||
socketUrl += random(0, 999999); // should be a random string, but this works (see )
|
||||
socketUrl += "/websocket";
|
||||
|
||||
// connect to websocket
|
||||
webSocket.begin(ws_host, ws_port, socketUrl);
|
||||
webSocket.setExtraHeaders(); // remove "Origin: file://" header because it breaks the connection with Spring's default websocket config
|
||||
// webSocket.setExtraHeaders("foo: I am so funny\r\nbar: not"); // some headers, in case you feel funny
|
||||
webSocket.onEvent(webSocketEvent);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
webSocket.loop();
|
||||
}
|
@ -18,7 +18,7 @@ WebSocketsServer webSocket = WebSocketsServer(81);
|
||||
|
||||
#define USE_SERIAL Serial1
|
||||
|
||||
void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t lenght) {
|
||||
void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
|
||||
|
||||
switch(type) {
|
||||
case WStype_DISCONNECTED:
|
||||
@ -43,11 +43,11 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t lenght
|
||||
// webSocket.broadcastTXT("message here");
|
||||
break;
|
||||
case WStype_BIN:
|
||||
USE_SERIAL.printf("[%u] get binary lenght: %u\n", num, lenght);
|
||||
hexdump(payload, lenght);
|
||||
USE_SERIAL.printf("[%u] get binary length: %u\n", num, length);
|
||||
hexdump(payload, length);
|
||||
|
||||
// send message to client
|
||||
// webSocket.sendBIN(num, payload, lenght);
|
||||
// webSocket.sendBIN(num, payload, length);
|
||||
break;
|
||||
}
|
||||
|
@ -0,0 +1,132 @@
|
||||
/*
|
||||
* WebSocketServerAllFunctionsDemo.ino
|
||||
*
|
||||
* Created on: 10.05.2018
|
||||
*
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESP8266WiFiMulti.h>
|
||||
#include <WebSocketsServer.h>
|
||||
#include <ESP8266WebServer.h>
|
||||
#include <ESP8266mDNS.h>
|
||||
#include <Hash.h>
|
||||
|
||||
#define LED_RED 15
|
||||
#define LED_GREEN 12
|
||||
#define LED_BLUE 13
|
||||
|
||||
#define USE_SERIAL Serial
|
||||
|
||||
ESP8266WiFiMulti WiFiMulti;
|
||||
|
||||
ESP8266WebServer server(80);
|
||||
WebSocketsServer webSocket = WebSocketsServer(81);
|
||||
|
||||
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);
|
||||
|
||||
if(payload[0] == '#') {
|
||||
// we get RGB data
|
||||
|
||||
// decode rgb data
|
||||
uint32_t rgb = (uint32_t) strtol((const char *) &payload[1], NULL, 16);
|
||||
|
||||
analogWrite(LED_RED, ((rgb >> 16) & 0xFF));
|
||||
analogWrite(LED_GREEN, ((rgb >> 8) & 0xFF));
|
||||
analogWrite(LED_BLUE, ((rgb >> 0) & 0xFF));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void setup() {
|
||||
//USE_SERIAL.begin(921600);
|
||||
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);
|
||||
}
|
||||
|
||||
pinMode(LED_RED, OUTPUT);
|
||||
pinMode(LED_GREEN, OUTPUT);
|
||||
pinMode(LED_BLUE, OUTPUT);
|
||||
|
||||
digitalWrite(LED_RED, 1);
|
||||
digitalWrite(LED_GREEN, 1);
|
||||
digitalWrite(LED_BLUE, 1);
|
||||
|
||||
WiFiMulti.addAP("SSID", "passpasspass");
|
||||
|
||||
while(WiFiMulti.run() != WL_CONNECTED) {
|
||||
delay(100);
|
||||
}
|
||||
|
||||
// start webSocket server
|
||||
webSocket.begin();
|
||||
webSocket.onEvent(webSocketEvent);
|
||||
|
||||
if(MDNS.begin("esp8266")) {
|
||||
USE_SERIAL.println("MDNS responder started");
|
||||
}
|
||||
|
||||
// handle index
|
||||
server.on("/", []() {
|
||||
// send index.html
|
||||
server.send(200, "text/html", "<html><head><script>var connection = new WebSocket('ws://'+location.hostname+':81/', ['arduino']);connection.onopen = function () { connection.send('Connect ' + new Date()); }; connection.onerror = function (error) { console.log('WebSocket Error ', error);};connection.onmessage = function (e) { console.log('Server: ', e.data);};function sendRGB() { var r = parseInt(document.getElementById('r').value).toString(16); var g = parseInt(document.getElementById('g').value).toString(16); var b = parseInt(document.getElementById('b').value).toString(16); if(r.length < 2) { r = '0' + r; } if(g.length < 2) { g = '0' + g; } if(b.length < 2) { b = '0' + b; } var rgb = '#'+r+g+b; console.log('RGB: ' + rgb); connection.send(rgb); }</script></head><body>LED Control:<br/><br/>R: <input id=\"r\" type=\"range\" min=\"0\" max=\"255\" step=\"1\" oninput=\"sendRGB();\" /><br/>G: <input id=\"g\" type=\"range\" min=\"0\" max=\"255\" step=\"1\" oninput=\"sendRGB();\" /><br/>B: <input id=\"b\" type=\"range\" min=\"0\" max=\"255\" step=\"1\" oninput=\"sendRGB();\" /><br/></body></html>");
|
||||
});
|
||||
|
||||
server.begin();
|
||||
|
||||
// Add service to MDNS
|
||||
MDNS.addService("http", "tcp", 80);
|
||||
MDNS.addService("ws", "tcp", 81);
|
||||
|
||||
digitalWrite(LED_RED, 0);
|
||||
digitalWrite(LED_GREEN, 0);
|
||||
digitalWrite(LED_BLUE, 0);
|
||||
|
||||
}
|
||||
|
||||
unsigned long last_10sec = 0;
|
||||
unsigned int counter = 0;
|
||||
|
||||
void loop() {
|
||||
unsigned long t = millis();
|
||||
webSocket.loop();
|
||||
server.handleClient();
|
||||
|
||||
if((t - last_10sec) > 10 * 1000) {
|
||||
counter++;
|
||||
bool ping = (counter % 2);
|
||||
int i = webSocket.connectedClients(ping);
|
||||
USE_SERIAL.printf("%d Connected websocket clients ping: %d\n", i, ping);
|
||||
last_10sec = millis();
|
||||
}
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* WebSocketServer.ino
|
||||
*
|
||||
* Created on: 22.05.2015
|
||||
*
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESP8266WiFiMulti.h>
|
||||
#include <WebSocketsServer.h>
|
||||
#include <Hash.h>
|
||||
|
||||
ESP8266WiFiMulti WiFiMulti;
|
||||
|
||||
WebSocketsServer webSocket = WebSocketsServer(81);
|
||||
|
||||
#define USE_SERIAL Serial
|
||||
|
||||
String fragmentBuffer = "";
|
||||
|
||||
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);
|
||||
break;
|
||||
case WStype_BIN:
|
||||
USE_SERIAL.printf("[%u] get binary length: %u\n", num, length);
|
||||
hexdump(payload, length);
|
||||
break;
|
||||
|
||||
// Fragmentation / continuation opcode handling
|
||||
// case WStype_FRAGMENT_BIN_START:
|
||||
case WStype_FRAGMENT_TEXT_START:
|
||||
fragmentBuffer = (char*)payload;
|
||||
USE_SERIAL.printf("[%u] get start start of Textfragment: %s\n", num, payload);
|
||||
break;
|
||||
case WStype_FRAGMENT:
|
||||
fragmentBuffer += (char*)payload;
|
||||
USE_SERIAL.printf("[%u] get Textfragment : %s\n", num, payload);
|
||||
break;
|
||||
case WStype_FRAGMENT_FIN:
|
||||
fragmentBuffer += (char*)payload;
|
||||
USE_SERIAL.printf("[%u] get end of Textfragment: %s\n", num, payload);
|
||||
USE_SERIAL.printf("[%u] full frame: %s\n", num, fragmentBuffer.c_str());
|
||||
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);
|
||||
}
|
||||
|
||||
webSocket.begin();
|
||||
webSocket.onEvent(webSocketEvent);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
webSocket.loop();
|
||||
}
|
||||
|
@ -23,10 +23,10 @@
|
||||
|
||||
ESP8266WiFiMulti WiFiMulti;
|
||||
|
||||
ESP8266WebServer server = ESP8266WebServer(80);
|
||||
ESP8266WebServer server(80);
|
||||
WebSocketsServer webSocket = WebSocketsServer(81);
|
||||
|
||||
void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t lenght) {
|
||||
void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
|
||||
|
||||
switch(type) {
|
||||
case WStype_DISCONNECTED:
|
||||
@ -100,7 +100,7 @@ void setup() {
|
||||
// handle index
|
||||
server.on("/", []() {
|
||||
// send index.html
|
||||
server.send(200, "text/html", "<html><head><script>var connection = new WebSocket('ws://'+location.hostname+':81/', ['arduino']);connection.onopen = function () { connection.send('Connect ' + new Date()); }; connection.onerror = function (error) { console.log('WebSocket Error ', error);};connection.onmessage = function (e) { console.log('Server: ', e.data);};function sendRGB() { var r = parseInt(document.getElementById('r').value).toString(16); var g = parseInt(document.getElementById('g').value).toString(16); var b = parseInt(document.getElementById('b').value).toString(16); if(r.length < 2) { r = '0' + r; } if(g.length < 2) { g = '0' + g; } if(b.length < 2) { b = '0' + b; } var rgb = '#'+r+g+b; console.log('RGB: ' + rgb); connection.send(rgb); }</script></head><body>LED Control:<br/><br/>R: <input id=\"r\" type=\"range\" min=\"0\" max=\"255\" step=\"1\" onchange=\"sendRGB();\" /><br/>G: <input id=\"g\" type=\"range\" min=\"0\" max=\"255\" step=\"1\" onchange=\"sendRGB();\" /><br/>B: <input id=\"b\" type=\"range\" min=\"0\" max=\"255\" step=\"1\" onchange=\"sendRGB();\" /><br/></body></html>");
|
||||
server.send(200, "text/html", "<html><head><script>var connection = new WebSocket('ws://'+location.hostname+':81/', ['arduino']);connection.onopen = function () { connection.send('Connect ' + new Date()); }; connection.onerror = function (error) { console.log('WebSocket Error ', error);};connection.onmessage = function (e) { console.log('Server: ', e.data);};function sendRGB() { var r = parseInt(document.getElementById('r').value).toString(16); var g = parseInt(document.getElementById('g').value).toString(16); var b = parseInt(document.getElementById('b').value).toString(16); if(r.length < 2) { r = '0' + r; } if(g.length < 2) { g = '0' + g; } if(b.length < 2) { b = '0' + b; } var rgb = '#'+r+g+b; console.log('RGB: ' + rgb); connection.send(rgb); }</script></head><body>LED Control:<br/><br/>R: <input id=\"r\" type=\"range\" min=\"0\" max=\"255\" step=\"1\" oninput=\"sendRGB();\" /><br/>G: <input id=\"g\" type=\"range\" min=\"0\" max=\"255\" step=\"1\" oninput=\"sendRGB();\" /><br/>B: <input id=\"b\" type=\"range\" min=\"0\" max=\"255\" step=\"1\" oninput=\"sendRGB();\" /><br/></body></html>");
|
||||
});
|
||||
|
||||
server.begin();
|
||||
@ -119,4 +119,3 @@ void loop() {
|
||||
webSocket.loop();
|
||||
server.handleClient();
|
||||
}
|
||||
|
46
examples/particle/ParticleWebSocketClient/application.cpp
Normal file
46
examples/particle/ParticleWebSocketClient/application.cpp
Normal file
@ -0,0 +1,46 @@
|
||||
/* To compile using make CLI, create a folder under \firmware\user\applications and copy application.cpp there.
|
||||
* Then, copy src files under particleWebSocket folder.
|
||||
*/
|
||||
|
||||
#include "application.h"
|
||||
#include "particleWebSocket/WebSocketsClient.h"
|
||||
|
||||
WebSocketsClient webSocket;
|
||||
|
||||
void webSocketEvent(WStype_t type, uint8_t* payload, size_t length)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case WStype_DISCONNECTED:
|
||||
Serial.printlnf("[WSc] Disconnected!");
|
||||
break;
|
||||
case WStype_CONNECTED:
|
||||
Serial.printlnf("[WSc] Connected to URL: %s", payload);
|
||||
webSocket.sendTXT("Connected\r\n");
|
||||
break;
|
||||
case WStype_TEXT:
|
||||
Serial.printlnf("[WSc] get text: %s", payload);
|
||||
break;
|
||||
case WStype_BIN:
|
||||
Serial.printlnf("[WSc] get binary length: %u", length);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(9600);
|
||||
|
||||
WiFi.setCredentials("[SSID]", "[PASSWORD]", WPA2, WLAN_CIPHER_AES_TKIP);
|
||||
WiFi.connect();
|
||||
|
||||
webSocket.begin("192.168.1.153", 85, "/ClientService/?variable=Test1212");
|
||||
webSocket.onEvent(webSocketEvent);
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
webSocket.sendTXT("Hello world!");
|
||||
delay(500);
|
||||
webSocket.loop();
|
||||
}
|
40
library.json
40
library.json
@ -1,19 +1,25 @@
|
||||
{
|
||||
"name": "WebSockets",
|
||||
"keywords": "wifi, http, web, server, client, websocket",
|
||||
"description": "WebSocket Server and Client for Arduino based on RFC6455",
|
||||
"repository":
|
||||
{
|
||||
"type": "git",
|
||||
"url": "https://github.com/Links2004/arduinoWebSockets.git"
|
||||
},
|
||||
"exclude": "tests",
|
||||
"frameworks": "arduino",
|
||||
"platforms": "*",
|
||||
"authors":
|
||||
{
|
||||
"name": "Markus Sattler",
|
||||
"url": "https://github.com/Links2004",
|
||||
"maintainer": true
|
||||
}
|
||||
"name": "WebSockets",
|
||||
"description": "WebSocket Server and Client for Arduino based on RFC6455",
|
||||
"keywords": "wifi, http, web, server, client, websocket",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Markus Sattler",
|
||||
"url": "https://github.com/Links2004",
|
||||
"maintainer": true
|
||||
}
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Links2004/arduinoWebSockets.git"
|
||||
},
|
||||
"version": "2.3.0",
|
||||
"license": "LGPL-2.1",
|
||||
"export": {
|
||||
"exclude": [
|
||||
"tests"
|
||||
]
|
||||
},
|
||||
"frameworks": "arduino",
|
||||
"platforms": "atmelavr, espressif8266, espressif32"
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
name=WebSockets
|
||||
version=2.0.4
|
||||
version=2.3.0
|
||||
author=Markus Sattler
|
||||
maintainer=Markus Sattler
|
||||
maintainer=Markus Sattler
|
||||
sentence=WebSockets for Arduino (Server + Client)
|
||||
paragraph=use 2.x.x for ESP and 1.3 for AVR
|
||||
category=Communication
|
||||
|
201
src/SocketIOclient.cpp
Normal file
201
src/SocketIOclient.cpp
Normal file
@ -0,0 +1,201 @@
|
||||
/*
|
||||
* SocketIOclient.cpp
|
||||
*
|
||||
* Created on: May 12, 2018
|
||||
* Author: links
|
||||
*/
|
||||
|
||||
#include "WebSockets.h"
|
||||
#include "WebSocketsClient.h"
|
||||
#include "SocketIOclient.h"
|
||||
|
||||
SocketIOclient::SocketIOclient() {
|
||||
}
|
||||
|
||||
SocketIOclient::~SocketIOclient() {
|
||||
}
|
||||
|
||||
void SocketIOclient::begin(const char * host, uint16_t port, const char * url, const char * protocol) {
|
||||
WebSocketsClient::beginSocketIO(host, port, url, protocol);
|
||||
WebSocketsClient::enableHeartbeat(60 * 1000, 90 * 1000, 5);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* set callback function
|
||||
* @param cbEvent SocketIOclientEvent
|
||||
*/
|
||||
void SocketIOclient::onEvent(SocketIOclientEvent cbEvent) {
|
||||
_cbEvent = cbEvent;
|
||||
}
|
||||
|
||||
bool SocketIOclient::isConnected(void) {
|
||||
return WebSocketsClient::isConnected();
|
||||
}
|
||||
|
||||
/**
|
||||
* send text data to client
|
||||
* @param num uint8_t client id
|
||||
* @param type socketIOmessageType_t
|
||||
* @param payload uint8_t *
|
||||
* @param length size_t
|
||||
* @param headerToPayload bool (see sendFrame for more details)
|
||||
* @return true if ok
|
||||
*/
|
||||
bool SocketIOclient::send(socketIOmessageType_t type, uint8_t * payload, size_t length, bool headerToPayload) {
|
||||
bool ret = false;
|
||||
if(length == 0) {
|
||||
length = strlen((const char *)payload);
|
||||
}
|
||||
if(clientIsConnected(&_client)) {
|
||||
if(!headerToPayload) {
|
||||
// webSocket Header
|
||||
ret = WebSocketsClient::sendFrameHeader(&_client, WSop_text, length + 2, true);
|
||||
// Engine.IO / Socket.IO Header
|
||||
if(ret) {
|
||||
uint8_t buf[3] = { eIOtype_MESSAGE, type, 0x00 };
|
||||
ret = WebSocketsClient::write(&_client, buf, 2);
|
||||
}
|
||||
if(ret && payload && length > 0) {
|
||||
ret = WebSocketsClient::write(&_client, payload, length);
|
||||
}
|
||||
return ret;
|
||||
} else {
|
||||
// TODO implement
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SocketIOclient::send(socketIOmessageType_t type, const uint8_t * payload, size_t length) {
|
||||
return send(type, (uint8_t *)payload, length);
|
||||
}
|
||||
|
||||
bool SocketIOclient::send(socketIOmessageType_t type, char * payload, size_t length, bool headerToPayload) {
|
||||
return send(type, (uint8_t *)payload, length, headerToPayload);
|
||||
}
|
||||
|
||||
bool SocketIOclient::send(socketIOmessageType_t type, const char * payload, size_t length) {
|
||||
return send(type, (uint8_t *)payload, length);
|
||||
}
|
||||
|
||||
bool SocketIOclient::send(socketIOmessageType_t type, String & payload) {
|
||||
return send(type, (uint8_t *)payload.c_str(), payload.length());
|
||||
}
|
||||
|
||||
/**
|
||||
* send text data to client
|
||||
* @param num uint8_t client id
|
||||
* @param payload uint8_t *
|
||||
* @param length size_t
|
||||
* @param headerToPayload bool (see sendFrame for more details)
|
||||
* @return true if ok
|
||||
*/
|
||||
bool SocketIOclient::sendEVENT(uint8_t * payload, size_t length, bool headerToPayload) {
|
||||
return send(sIOtype_EVENT, payload, length, headerToPayload);
|
||||
}
|
||||
|
||||
bool SocketIOclient::sendEVENT(const uint8_t * payload, size_t length) {
|
||||
return sendEVENT((uint8_t *)payload, length);
|
||||
}
|
||||
|
||||
bool SocketIOclient::sendEVENT(char * payload, size_t length, bool headerToPayload) {
|
||||
return sendEVENT((uint8_t *)payload, length, headerToPayload);
|
||||
}
|
||||
|
||||
bool SocketIOclient::sendEVENT(const char * payload, size_t length) {
|
||||
return sendEVENT((uint8_t *)payload, length);
|
||||
}
|
||||
|
||||
bool SocketIOclient::sendEVENT(String & payload) {
|
||||
return sendEVENT((uint8_t *)payload.c_str(), payload.length());
|
||||
}
|
||||
|
||||
void SocketIOclient::loop(void) {
|
||||
WebSocketsClient::loop();
|
||||
unsigned long t = millis();
|
||||
if((t - _lastConnectionFail) > EIO_HEARTBEAT_INTERVAL) {
|
||||
_lastConnectionFail = t;
|
||||
DEBUG_WEBSOCKETS("[wsIOc] send ping\n");
|
||||
WebSocketsClient::sendTXT(eIOtype_PING);
|
||||
}
|
||||
}
|
||||
|
||||
void SocketIOclient::handleCbEvent(WStype_t type, uint8_t * payload, size_t length) {
|
||||
switch(type) {
|
||||
case WStype_DISCONNECTED:
|
||||
runIOCbEvent(sIOtype_DISCONNECT, NULL, 0);
|
||||
DEBUG_WEBSOCKETS("[wsIOc] Disconnected!\n");
|
||||
break;
|
||||
case WStype_CONNECTED: {
|
||||
DEBUG_WEBSOCKETS("[wsIOc] Connected to url: %s\n", payload);
|
||||
// send message to server when Connected
|
||||
// Engine.io upgrade confirmation message (required)
|
||||
WebSocketsClient::sendTXT(eIOtype_UPGRADE);
|
||||
runIOCbEvent(sIOtype_CONNECT, payload, length);
|
||||
} break;
|
||||
case WStype_TEXT: {
|
||||
if(length < 1) {
|
||||
break;
|
||||
}
|
||||
|
||||
engineIOmessageType_t eType = (engineIOmessageType_t)payload[0];
|
||||
switch(eType) {
|
||||
case eIOtype_PING:
|
||||
payload[0] = eIOtype_PONG;
|
||||
DEBUG_WEBSOCKETS("[wsIOc] get ping send pong (%s)\n", payload);
|
||||
WebSocketsClient::sendTXT(payload, length, false);
|
||||
break;
|
||||
case eIOtype_PONG:
|
||||
DEBUG_WEBSOCKETS("[wsIOc] get pong\n");
|
||||
break;
|
||||
case eIOtype_MESSAGE: {
|
||||
if(length < 2) {
|
||||
break;
|
||||
}
|
||||
socketIOmessageType_t ioType = (socketIOmessageType_t)payload[1];
|
||||
uint8_t * data = &payload[2];
|
||||
size_t lData = length - 2;
|
||||
switch(ioType) {
|
||||
case sIOtype_EVENT:
|
||||
DEBUG_WEBSOCKETS("[wsIOc] get event (%d): %s\n", lData, data);
|
||||
break;
|
||||
case sIOtype_CONNECT:
|
||||
case sIOtype_DISCONNECT:
|
||||
case sIOtype_ACK:
|
||||
case sIOtype_ERROR:
|
||||
case sIOtype_BINARY_EVENT:
|
||||
case sIOtype_BINARY_ACK:
|
||||
default:
|
||||
DEBUG_WEBSOCKETS("[wsIOc] Socket.IO Message Type %c (%02X) is not implemented\n", ioType, ioType);
|
||||
DEBUG_WEBSOCKETS("[wsIOc] get text: %s\n", payload);
|
||||
break;
|
||||
}
|
||||
|
||||
runIOCbEvent(ioType, data, lData);
|
||||
} break;
|
||||
case eIOtype_OPEN:
|
||||
case eIOtype_CLOSE:
|
||||
case eIOtype_UPGRADE:
|
||||
case eIOtype_NOOP:
|
||||
default:
|
||||
DEBUG_WEBSOCKETS("[wsIOc] Engine.IO Message Type %c (%02X) is not implemented\n", eType, eType);
|
||||
DEBUG_WEBSOCKETS("[wsIOc] get text: %s\n", payload);
|
||||
break;
|
||||
}
|
||||
} break;
|
||||
case WStype_ERROR:
|
||||
case WStype_BIN:
|
||||
case WStype_FRAGMENT_TEXT_START:
|
||||
case WStype_FRAGMENT_BIN_START:
|
||||
case WStype_FRAGMENT:
|
||||
case WStype_FRAGMENT_FIN:
|
||||
case WStype_PING:
|
||||
case WStype_PONG:
|
||||
break;
|
||||
}
|
||||
}
|
86
src/SocketIOclient.h
Normal file
86
src/SocketIOclient.h
Normal file
@ -0,0 +1,86 @@
|
||||
/**
|
||||
* SocketIOclient.h
|
||||
*
|
||||
* Created on: May 12, 2018
|
||||
* Author: links
|
||||
*/
|
||||
|
||||
#ifndef SOCKETIOCLIENT_H_
|
||||
#define SOCKETIOCLIENT_H_
|
||||
|
||||
#include "WebSockets.h"
|
||||
|
||||
#define EIO_HEARTBEAT_INTERVAL 20000
|
||||
|
||||
#define EIO_MAX_HEADER_SIZE (WEBSOCKETS_MAX_HEADER_SIZE + 1)
|
||||
#define SIO_MAX_HEADER_SIZE (EIO_MAX_HEADER_SIZE + 1)
|
||||
|
||||
typedef enum {
|
||||
eIOtype_OPEN = '0', ///< Sent from the server when a new transport is opened (recheck)
|
||||
eIOtype_CLOSE = '1', ///< Request the close of this transport but does not shutdown the connection itself.
|
||||
eIOtype_PING = '2', ///< Sent by the client. Server should answer with a pong packet containing the same data
|
||||
eIOtype_PONG = '3', ///< Sent by the server to respond to ping packets.
|
||||
eIOtype_MESSAGE = '4', ///< actual message, client and server should call their callbacks with the data
|
||||
eIOtype_UPGRADE = '5', ///< Before engine.io switches a transport, it tests, if server and client can communicate over this transport. If this test succeed, the client sends an upgrade packets which requests the server to flush its cache on the old transport and switch to the new transport.
|
||||
eIOtype_NOOP = '6', ///< A noop packet. Used primarily to force a poll cycle when an incoming websocket connection is received.
|
||||
} engineIOmessageType_t;
|
||||
|
||||
typedef enum {
|
||||
sIOtype_CONNECT = '0',
|
||||
sIOtype_DISCONNECT = '1',
|
||||
sIOtype_EVENT = '2',
|
||||
sIOtype_ACK = '3',
|
||||
sIOtype_ERROR = '4',
|
||||
sIOtype_BINARY_EVENT = '5',
|
||||
sIOtype_BINARY_ACK = '6',
|
||||
} socketIOmessageType_t;
|
||||
|
||||
class SocketIOclient : protected WebSocketsClient {
|
||||
public:
|
||||
#ifdef __AVR__
|
||||
typedef void (*SocketIOclientEvent)(socketIOmessageType_t type, uint8_t * payload, size_t length);
|
||||
#else
|
||||
typedef std::function<void(socketIOmessageType_t type, uint8_t * payload, size_t length)> SocketIOclientEvent;
|
||||
#endif
|
||||
|
||||
SocketIOclient(void);
|
||||
virtual ~SocketIOclient(void);
|
||||
|
||||
void begin(const char * host, uint16_t port, const char * url = "/socket.io/?EIO=3", const char * protocol = "arduino");
|
||||
void begin(String host, uint16_t port, String url = "/socket.io/?EIO=3", String protocol = "arduino");
|
||||
|
||||
bool isConnected(void);
|
||||
|
||||
void onEvent(SocketIOclientEvent cbEvent);
|
||||
|
||||
bool sendEVENT(uint8_t * payload, size_t length = 0, bool headerToPayload = false);
|
||||
bool sendEVENT(const uint8_t * payload, size_t length = 0);
|
||||
bool sendEVENT(char * payload, size_t length = 0, bool headerToPayload = false);
|
||||
bool sendEVENT(const char * payload, size_t length = 0);
|
||||
bool sendEVENT(String & payload);
|
||||
|
||||
bool send(socketIOmessageType_t type, uint8_t * payload, size_t length = 0, bool headerToPayload = false);
|
||||
bool send(socketIOmessageType_t type, const uint8_t * payload, size_t length = 0);
|
||||
bool send(socketIOmessageType_t type, char * payload, size_t length = 0, bool headerToPayload = false);
|
||||
bool send(socketIOmessageType_t type, const char * payload, size_t length = 0);
|
||||
bool send(socketIOmessageType_t type, String & payload);
|
||||
|
||||
void loop(void);
|
||||
|
||||
protected:
|
||||
uint64_t _lastHeartbeat = 0;
|
||||
SocketIOclientEvent _cbEvent;
|
||||
virtual void runIOCbEvent(socketIOmessageType_t type, uint8_t * payload, size_t length) {
|
||||
if(_cbEvent) {
|
||||
_cbEvent(type, payload, length);
|
||||
}
|
||||
}
|
||||
|
||||
// Handeling events from websocket layer
|
||||
virtual void runCbEvent(WStype_t type, uint8_t * payload, size_t length) {
|
||||
handleCbEvent(type, payload, length);
|
||||
}
|
||||
void handleCbEvent(WStype_t type, uint8_t * payload, size_t length);
|
||||
};
|
||||
|
||||
#endif /* SOCKETIOCLIENT_H_ */
|
@ -38,6 +38,15 @@ extern "C" {
|
||||
|
||||
#ifdef ESP8266
|
||||
#include <Hash.h>
|
||||
#elif defined(ESP32)
|
||||
#include <esp_system.h>
|
||||
|
||||
#if ESP_IDF_VERSION_MAJOR >= 4
|
||||
#include <esp32/sha.h>
|
||||
#else
|
||||
#include <hwcrypto/sha.h>
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
extern "C" {
|
||||
@ -50,14 +59,14 @@ extern "C" {
|
||||
*
|
||||
* @param client WSclient_t * ptr to the client struct
|
||||
* @param code uint16_t see RFC
|
||||
* @param reason
|
||||
* @param reasonLen
|
||||
* @param reason ptr to the disconnect reason message
|
||||
* @param reasonLen length of the disconnect reason message
|
||||
*/
|
||||
void WebSockets::clientDisconnect(WSclient_t * client, uint16_t code, char * reason, size_t reasonLen) {
|
||||
DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] clientDisconnect code: %u\n", client->num, code);
|
||||
if(client->status == WSC_CONNECTED && code) {
|
||||
if(reason) {
|
||||
sendFrame(client, WSop_close, (uint8_t *) reason, reasonLen);
|
||||
sendFrame(client, WSop_close, (uint8_t *)reason, reasonLen);
|
||||
} else {
|
||||
uint8_t buffer[2];
|
||||
buffer[0] = ((code >> 8) & 0xFF);
|
||||
@ -70,43 +79,15 @@ void WebSockets::clientDisconnect(WSclient_t * client, uint16_t code, char * rea
|
||||
|
||||
/**
|
||||
*
|
||||
* @param client WSclient_t * ptr to the client struct
|
||||
* @param buf uint8_t * ptr to the buffer for writing
|
||||
* @param opcode WSopcode_t
|
||||
* @param payload uint8_t *
|
||||
* @param length size_t
|
||||
* @param length size_t length of the payload
|
||||
* @param mask bool add dummy mask to the frame (needed for web browser)
|
||||
* @param maskkey uint8_t[4] key used for payload
|
||||
* @param fin bool can be used to send data in more then one frame (set fin on the last frame)
|
||||
* @param headerToPayload bool set true if the payload has reserved 14 Byte at the beginning to dynamically add the Header (payload neet to be in RAM!)
|
||||
* @return true if ok
|
||||
*/
|
||||
bool WebSockets::sendFrame(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool mask, bool fin, bool headerToPayload) {
|
||||
|
||||
if(client->tcp && !client->tcp->connected()) {
|
||||
DEBUG_WEBSOCKETS("[WS][%d][sendFrame] not Connected!?\n", client->num);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(client->status != WSC_CONNECTED) {
|
||||
DEBUG_WEBSOCKETS("[WS][%d][sendFrame] not in WSC_CONNECTED state!?\n", client->num);
|
||||
return false;
|
||||
}
|
||||
|
||||
DEBUG_WEBSOCKETS("[WS][%d][sendFrame] ------- send massage frame -------\n", client->num);
|
||||
DEBUG_WEBSOCKETS("[WS][%d][sendFrame] fin: %u opCode: %u mask: %u length: %u headerToPayload: %u\n", client->num, fin, opcode, mask, length, headerToPayload);
|
||||
|
||||
if(opcode == WSop_text) {
|
||||
DEBUG_WEBSOCKETS("[WS][%d][sendFrame] text: %s\n", client->num, (payload + (headerToPayload ? 14 : 0)));
|
||||
}
|
||||
|
||||
uint8_t maskKey[4] = { 0x00, 0x00, 0x00, 0x00 };
|
||||
uint8_t buffer[WEBSOCKETS_MAX_HEADER_SIZE] = { 0 };
|
||||
|
||||
uint8_t WebSockets::createHeader(uint8_t * headerPtr, WSopcode_t opcode, size_t length, bool mask, uint8_t maskKey[4], bool fin) {
|
||||
uint8_t headerSize;
|
||||
uint8_t * headerPtr;
|
||||
uint8_t * payloadPtr = payload;
|
||||
bool useInternBuffer = false;
|
||||
bool ret = true;
|
||||
|
||||
// calculate header Size
|
||||
if(length < 126) {
|
||||
headerSize = 2;
|
||||
@ -120,29 +101,6 @@ bool WebSockets::sendFrame(WSclient_t * client, WSopcode_t opcode, uint8_t * pay
|
||||
headerSize += 4;
|
||||
}
|
||||
|
||||
#ifdef WEBSOCKETS_USE_BIG_MEM
|
||||
// only for ESP since AVR has less HEAP
|
||||
// try to send data in one TCP package (only if some free Heap is there)
|
||||
if(!headerToPayload && ((length > 0) && (length < 1400)) && (ESP.getFreeHeap() > 6000)) {
|
||||
DEBUG_WEBSOCKETS("[WS][%d][sendFrame] pack to one TCP package...\n", client->num);
|
||||
uint8_t * dataPtr = (uint8_t *) malloc(length + WEBSOCKETS_MAX_HEADER_SIZE);
|
||||
if(dataPtr) {
|
||||
memcpy((dataPtr + WEBSOCKETS_MAX_HEADER_SIZE), payload, length);
|
||||
headerToPayload = true;
|
||||
useInternBuffer = true;
|
||||
payloadPtr = dataPtr;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// set Header Pointer
|
||||
if(headerToPayload) {
|
||||
// calculate offset in payload
|
||||
headerPtr = (payloadPtr + (WEBSOCKETS_MAX_HEADER_SIZE - headerSize));
|
||||
} else {
|
||||
headerPtr = &buffer[0];
|
||||
}
|
||||
|
||||
// create header
|
||||
|
||||
// byte 0
|
||||
@ -150,7 +108,7 @@ bool WebSockets::sendFrame(WSclient_t * client, WSopcode_t opcode, uint8_t * pay
|
||||
if(fin) {
|
||||
*headerPtr |= bit(7); ///< set Fin
|
||||
}
|
||||
*headerPtr |= opcode; ///< set opcode
|
||||
*headerPtr |= opcode; ///< set opcode
|
||||
headerPtr++;
|
||||
|
||||
// byte 1
|
||||
@ -192,36 +150,133 @@ bool WebSockets::sendFrame(WSclient_t * client, WSopcode_t opcode, uint8_t * pay
|
||||
}
|
||||
|
||||
if(mask) {
|
||||
if(useInternBuffer) {
|
||||
// if we use a Intern Buffer we can modify the data
|
||||
// by this fact its possible the do the masking
|
||||
for(uint8_t x = 0; x < sizeof(maskKey); x++) {
|
||||
maskKey[x] = random(0xFF);
|
||||
*headerPtr = maskKey[x];
|
||||
headerPtr++;
|
||||
}
|
||||
*headerPtr = maskKey[0];
|
||||
headerPtr++;
|
||||
*headerPtr = maskKey[1];
|
||||
headerPtr++;
|
||||
*headerPtr = maskKey[2];
|
||||
headerPtr++;
|
||||
*headerPtr = maskKey[3];
|
||||
headerPtr++;
|
||||
}
|
||||
return headerSize;
|
||||
}
|
||||
|
||||
uint8_t * dataMaskPtr;
|
||||
/**
|
||||
*
|
||||
* @param client WSclient_t * ptr to the client struct
|
||||
* @param opcode WSopcode_t
|
||||
* @param length size_t length of the payload
|
||||
* @param fin bool can be used to send data in more then one frame (set fin on the last frame)
|
||||
* @return true if ok
|
||||
*/
|
||||
bool WebSockets::sendFrameHeader(WSclient_t * client, WSopcode_t opcode, size_t length, bool fin) {
|
||||
uint8_t maskKey[4] = { 0x00, 0x00, 0x00, 0x00 };
|
||||
uint8_t buffer[WEBSOCKETS_MAX_HEADER_SIZE] = { 0 };
|
||||
|
||||
if(headerToPayload) {
|
||||
dataMaskPtr = (payloadPtr + WEBSOCKETS_MAX_HEADER_SIZE);
|
||||
} else {
|
||||
dataMaskPtr = payloadPtr;
|
||||
}
|
||||
uint8_t headerSize = createHeader(&buffer[0], opcode, length, client->cIsClient, maskKey, fin);
|
||||
|
||||
for(size_t x = 0; x < length; x++) {
|
||||
dataMaskPtr[x] = (dataMaskPtr[x] ^ maskKey[x % 4]);
|
||||
}
|
||||
if(write(client, &buffer[0], headerSize) != headerSize) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param client WSclient_t * ptr to the client struct
|
||||
* @param opcode WSopcode_t
|
||||
* @param payload uint8_t * ptr to the payload
|
||||
* @param length size_t length of the payload
|
||||
* @param fin bool can be used to send data in more then one frame (set fin on the last frame)
|
||||
* @param headerToPayload bool set true if the payload has reserved 14 Byte at the beginning to dynamically add the Header (payload neet to be in RAM!)
|
||||
* @return true if ok
|
||||
*/
|
||||
bool WebSockets::sendFrame(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin, bool headerToPayload) {
|
||||
if(client->tcp && !client->tcp->connected()) {
|
||||
DEBUG_WEBSOCKETS("[WS][%d][sendFrame] not Connected!?\n", client->num);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(client->status != WSC_CONNECTED) {
|
||||
DEBUG_WEBSOCKETS("[WS][%d][sendFrame] not in WSC_CONNECTED state!?\n", client->num);
|
||||
return false;
|
||||
}
|
||||
|
||||
DEBUG_WEBSOCKETS("[WS][%d][sendFrame] ------- send message frame -------\n", client->num);
|
||||
DEBUG_WEBSOCKETS("[WS][%d][sendFrame] fin: %u opCode: %u mask: %u length: %u headerToPayload: %u\n", client->num, fin, opcode, client->cIsClient, length, headerToPayload);
|
||||
|
||||
if(opcode == WSop_text) {
|
||||
DEBUG_WEBSOCKETS("[WS][%d][sendFrame] text: %s\n", client->num, (payload + (headerToPayload ? 14 : 0)));
|
||||
}
|
||||
|
||||
uint8_t maskKey[4] = { 0x00, 0x00, 0x00, 0x00 };
|
||||
uint8_t buffer[WEBSOCKETS_MAX_HEADER_SIZE] = { 0 };
|
||||
|
||||
uint8_t headerSize;
|
||||
uint8_t * headerPtr;
|
||||
uint8_t * payloadPtr = payload;
|
||||
bool useInternBuffer = false;
|
||||
bool ret = true;
|
||||
|
||||
// calculate header Size
|
||||
if(length < 126) {
|
||||
headerSize = 2;
|
||||
} else if(length < 0xFFFF) {
|
||||
headerSize = 4;
|
||||
} else {
|
||||
headerSize = 10;
|
||||
}
|
||||
|
||||
if(client->cIsClient) {
|
||||
headerSize += 4;
|
||||
}
|
||||
|
||||
#ifdef WEBSOCKETS_USE_BIG_MEM
|
||||
// only for ESP since AVR has less HEAP
|
||||
// try to send data in one TCP package (only if some free Heap is there)
|
||||
if(!headerToPayload && ((length > 0) && (length < 1400)) && (GET_FREE_HEAP > 6000)) {
|
||||
DEBUG_WEBSOCKETS("[WS][%d][sendFrame] pack to one TCP package...\n", client->num);
|
||||
uint8_t * dataPtr = (uint8_t *)malloc(length + WEBSOCKETS_MAX_HEADER_SIZE);
|
||||
if(dataPtr) {
|
||||
memcpy((dataPtr + WEBSOCKETS_MAX_HEADER_SIZE), payload, length);
|
||||
headerToPayload = true;
|
||||
useInternBuffer = true;
|
||||
payloadPtr = dataPtr;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// set Header Pointer
|
||||
if(headerToPayload) {
|
||||
// calculate offset in payload
|
||||
headerPtr = (payloadPtr + (WEBSOCKETS_MAX_HEADER_SIZE - headerSize));
|
||||
} else {
|
||||
headerPtr = &buffer[0];
|
||||
}
|
||||
|
||||
if(client->cIsClient && useInternBuffer) {
|
||||
// if we use a Intern Buffer we can modify the data
|
||||
// by this fact its possible the do the masking
|
||||
for(uint8_t x = 0; x < sizeof(maskKey); x++) {
|
||||
maskKey[x] = random(0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
createHeader(headerPtr, opcode, length, client->cIsClient, maskKey, fin);
|
||||
|
||||
if(client->cIsClient && useInternBuffer) {
|
||||
uint8_t * dataMaskPtr;
|
||||
|
||||
if(headerToPayload) {
|
||||
dataMaskPtr = (payloadPtr + WEBSOCKETS_MAX_HEADER_SIZE);
|
||||
} else {
|
||||
*headerPtr = maskKey[0];
|
||||
headerPtr++;
|
||||
*headerPtr = maskKey[1];
|
||||
headerPtr++;
|
||||
*headerPtr = maskKey[2];
|
||||
headerPtr++;
|
||||
*headerPtr = maskKey[3];
|
||||
headerPtr++;
|
||||
dataMaskPtr = payloadPtr;
|
||||
}
|
||||
|
||||
for(size_t x = 0; x < length; x++) {
|
||||
dataMaskPtr[x] = (dataMaskPtr[x] ^ maskKey[x % 4]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -233,24 +288,24 @@ bool WebSockets::sendFrame(WSclient_t * client, WSopcode_t opcode, uint8_t * pay
|
||||
// header has be added to payload
|
||||
// payload is forced to reserved 14 Byte but we may not need all based on the length and mask settings
|
||||
// offset in payload is calculatetd 14 - headerSize
|
||||
if(client->tcp->write(&payloadPtr[(WEBSOCKETS_MAX_HEADER_SIZE - headerSize)], (length + headerSize)) != (length + headerSize)) {
|
||||
if(write(client, &payloadPtr[(WEBSOCKETS_MAX_HEADER_SIZE - headerSize)], (length + headerSize)) != (length + headerSize)) {
|
||||
ret = false;
|
||||
}
|
||||
} else {
|
||||
// send header
|
||||
if(client->tcp->write(&buffer[0], headerSize) != headerSize) {
|
||||
if(write(client, &buffer[0], headerSize) != headerSize) {
|
||||
ret = false;
|
||||
}
|
||||
|
||||
if(payloadPtr && length > 0) {
|
||||
// send payload
|
||||
if(client->tcp->write(&payloadPtr[0], length) != length) {
|
||||
if(write(client, &payloadPtr[0], length) != length) {
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_WEBSOCKETS("[WS][%d][sendFrame] sending Frame Done (%uus).\n", client->num, (micros() - start));
|
||||
DEBUG_WEBSOCKETS("[WS][%d][sendFrame] sending Frame Done (%luus).\n", client->num, (micros() - start));
|
||||
|
||||
#ifdef WEBSOCKETS_USE_BIG_MEM
|
||||
if(useInternBuffer && payloadPtr) {
|
||||
@ -266,10 +321,10 @@ bool WebSockets::sendFrame(WSclient_t * client, WSopcode_t opcode, uint8_t * pay
|
||||
* @param client WSclient_t * ptr to the client struct
|
||||
*/
|
||||
void WebSockets::headerDone(WSclient_t * client) {
|
||||
client->status = WSC_CONNECTED;
|
||||
client->status = WSC_CONNECTED;
|
||||
client->cWsRXsize = 0;
|
||||
DEBUG_WEBSOCKETS("[WS][%d][headerDone] Header Handling Done (%uus).\n", client->num);
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
|
||||
DEBUG_WEBSOCKETS("[WS][%d][headerDone] Header Handling Done.\n", client->num);
|
||||
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
|
||||
client->cHttpLine = "";
|
||||
handleWebsocket(client);
|
||||
#endif
|
||||
@ -296,7 +351,7 @@ bool WebSockets::handleWebsocketWaitFor(WSclient_t * client, size_t size) {
|
||||
}
|
||||
|
||||
if(size > WEBSOCKETS_MAX_HEADER_SIZE) {
|
||||
DEBUG_WEBSOCKETS("[WS][%d][handleWebsocketWaitFor] size: %d to big!\n", client->num, size);
|
||||
DEBUG_WEBSOCKETS("[WS][%d][handleWebsocketWaitFor] size: %d too big!\n", client->num, size);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -316,12 +371,12 @@ bool WebSockets::handleWebsocketWaitFor(WSclient_t * client, size_t size) {
|
||||
// timeout or error
|
||||
server->clientDisconnect(client, 1002);
|
||||
}
|
||||
}, this, size, std::placeholders::_1, std::placeholders::_2));
|
||||
},
|
||||
this, size, std::placeholders::_1, std::placeholders::_2));
|
||||
return false;
|
||||
}
|
||||
|
||||
void WebSockets::handleWebsocketCb(WSclient_t * client) {
|
||||
|
||||
if(!client->tcp || !client->tcp->connected()) {
|
||||
return;
|
||||
}
|
||||
@ -329,7 +384,7 @@ void WebSockets::handleWebsocketCb(WSclient_t * client) {
|
||||
uint8_t * buffer = client->cWsHeader;
|
||||
|
||||
WSMessageHeader_t * header = &client->cWsHeaderDecode;
|
||||
uint8_t * payload = NULL;
|
||||
uint8_t * payload = NULL;
|
||||
|
||||
uint8_t headerLen = 2;
|
||||
|
||||
@ -338,15 +393,15 @@ void WebSockets::handleWebsocketCb(WSclient_t * client) {
|
||||
}
|
||||
|
||||
// split first 2 bytes in the data
|
||||
header->fin = ((*buffer >> 7) & 0x01);
|
||||
header->rsv1 = ((*buffer >> 6) & 0x01);
|
||||
header->rsv2 = ((*buffer >> 5) & 0x01);
|
||||
header->rsv3 = ((*buffer >> 4) & 0x01);
|
||||
header->opCode = (WSopcode_t) (*buffer & 0x0F);
|
||||
header->fin = ((*buffer >> 7) & 0x01);
|
||||
header->rsv1 = ((*buffer >> 6) & 0x01);
|
||||
header->rsv2 = ((*buffer >> 5) & 0x01);
|
||||
header->rsv3 = ((*buffer >> 4) & 0x01);
|
||||
header->opCode = (WSopcode_t)(*buffer & 0x0F);
|
||||
buffer++;
|
||||
|
||||
header->mask = ((*buffer >> 7) & 0x01);
|
||||
header->payloadLen = (WSopcode_t) (*buffer & 0x7F);
|
||||
header->mask = ((*buffer >> 7) & 0x01);
|
||||
header->payloadLen = (WSopcode_t)(*buffer & 0x7F);
|
||||
buffer++;
|
||||
|
||||
if(header->payloadLen == 126) {
|
||||
@ -364,7 +419,7 @@ void WebSockets::handleWebsocketCb(WSclient_t * client) {
|
||||
}
|
||||
|
||||
if(buffer[0] != 0 || buffer[1] != 0 || buffer[2] != 0 || buffer[3] != 0) {
|
||||
// really to big!
|
||||
// really too big!
|
||||
header->payloadLen = 0xFFFFFFFF;
|
||||
} else {
|
||||
header->payloadLen = buffer[4] << 24 | buffer[5] << 16 | buffer[6] << 8 | buffer[7];
|
||||
@ -377,7 +432,7 @@ void WebSockets::handleWebsocketCb(WSclient_t * client) {
|
||||
DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] mask: %u payloadLen: %u\n", client->num, header->mask, header->payloadLen);
|
||||
|
||||
if(header->payloadLen > WEBSOCKETS_MAX_DATA_SIZE) {
|
||||
DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] payload to big! (%u)\n", client->num, header->payloadLen);
|
||||
DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] payload too big! (%u)\n", client->num, header->payloadLen);
|
||||
clientDisconnect(client, 1009);
|
||||
return;
|
||||
}
|
||||
@ -393,7 +448,7 @@ void WebSockets::handleWebsocketCb(WSclient_t * client) {
|
||||
|
||||
if(header->payloadLen > 0) {
|
||||
// if text data we need one more
|
||||
payload = (uint8_t *) malloc(header->payloadLen + 1);
|
||||
payload = (uint8_t *)malloc(header->payloadLen + 1);
|
||||
|
||||
if(!payload) {
|
||||
DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] to less memory to handle payload %d!\n", client->num, header->payloadLen);
|
||||
@ -407,7 +462,6 @@ void WebSockets::handleWebsocketCb(WSclient_t * client) {
|
||||
}
|
||||
|
||||
void WebSockets::handleWebsocketPayloadCb(WSclient_t * client, bool ok, uint8_t * payload) {
|
||||
|
||||
WSMessageHeader_t * header = &client->cWsHeaderDecode;
|
||||
if(ok) {
|
||||
if(header->payloadLen > 0) {
|
||||
@ -426,21 +480,27 @@ void WebSockets::handleWebsocketPayloadCb(WSclient_t * client, bool ok, uint8_t
|
||||
DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] text: %s\n", client->num, payload);
|
||||
// no break here!
|
||||
case WSop_binary:
|
||||
messageReceived(client, header->opCode, payload, header->payloadLen);
|
||||
case WSop_continuation:
|
||||
messageReceived(client, header->opCode, payload, header->payloadLen, header->fin);
|
||||
break;
|
||||
case WSop_ping:
|
||||
// send pong back
|
||||
DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] ping received (%s)\n", client->num, payload ? (const char *)payload : "");
|
||||
sendFrame(client, WSop_pong, payload, header->payloadLen);
|
||||
messageReceived(client, header->opCode, payload, header->payloadLen, header->fin);
|
||||
break;
|
||||
case WSop_pong:
|
||||
DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] get pong (%s)\n", client->num, payload);
|
||||
DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] get pong (%s)\n", client->num, payload ? (const char *)payload : "");
|
||||
client->pongReceived = true;
|
||||
messageReceived(client, header->opCode, payload, header->payloadLen, header->fin);
|
||||
break;
|
||||
case WSop_close: {
|
||||
#ifndef NODEBUG_WEBSOCKETS
|
||||
uint16_t reasonCode = 1000;
|
||||
if(header->payloadLen >= 2) {
|
||||
reasonCode = payload[0] << 8 | payload[1];
|
||||
}
|
||||
|
||||
#endif
|
||||
DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] get ask for close. Code: %d", client->num, reasonCode);
|
||||
if(header->payloadLen > 2) {
|
||||
DEBUG_WEBSOCKETS(" (%s)\n", (payload + 2));
|
||||
@ -448,12 +508,7 @@ void WebSockets::handleWebsocketPayloadCb(WSclient_t * client, bool ok, uint8_t
|
||||
DEBUG_WEBSOCKETS("\n");
|
||||
}
|
||||
clientDisconnect(client, 1000);
|
||||
}
|
||||
break;
|
||||
case WSop_continuation:
|
||||
// continuation is not supported
|
||||
clientDisconnect(client, 1003);
|
||||
break;
|
||||
} break;
|
||||
default:
|
||||
clientDisconnect(client, 1002);
|
||||
break;
|
||||
@ -465,7 +520,7 @@ void WebSockets::handleWebsocketPayloadCb(WSclient_t * client, bool ok, uint8_t
|
||||
|
||||
// reset input
|
||||
client->cWsRXsize = 0;
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
|
||||
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
|
||||
//register callback for next message
|
||||
handleWebsocketWaitFor(client, 2);
|
||||
#endif
|
||||
@ -486,11 +541,14 @@ String WebSockets::acceptKey(String & clientKey) {
|
||||
uint8_t sha1HashBin[20] = { 0 };
|
||||
#ifdef ESP8266
|
||||
sha1(clientKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11", &sha1HashBin[0]);
|
||||
#elif defined(ESP32)
|
||||
String data = clientKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
||||
esp_sha(SHA1, (unsigned char *)data.c_str(), data.length(), &sha1HashBin[0]);
|
||||
#else
|
||||
clientKey += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
||||
SHA1_CTX ctx;
|
||||
SHA1Init(&ctx);
|
||||
SHA1Update(&ctx, (const unsigned char*)clientKey.c_str(), clientKey.length());
|
||||
SHA1Update(&ctx, (const unsigned char *)clientKey.c_str(), clientKey.length());
|
||||
SHA1Final(&sha1HashBin[0], &ctx);
|
||||
#endif
|
||||
|
||||
@ -507,13 +565,13 @@ String WebSockets::acceptKey(String & clientKey) {
|
||||
* @return base64 encoded String
|
||||
*/
|
||||
String WebSockets::base64_encode(uint8_t * data, size_t length) {
|
||||
size_t size = ((length * 1.6f) + 1);
|
||||
char * buffer = (char *) malloc(size);
|
||||
size_t size = ((length * 1.6f) + 1);
|
||||
char * buffer = (char *)malloc(size);
|
||||
if(buffer) {
|
||||
base64_encodestate _state;
|
||||
base64_init_encodestate(&_state);
|
||||
int len = base64_encode_block((const char *) &data[0], length, &buffer[0], &_state);
|
||||
len = base64_encode_blockend((buffer + len), &_state);
|
||||
int len = base64_encode_block((const char *)&data[0], length, &buffer[0], &_state);
|
||||
len = base64_encode_blockend((buffer + len), &_state);
|
||||
|
||||
String base64 = String(buffer);
|
||||
free(buffer);
|
||||
@ -530,7 +588,7 @@ String WebSockets::base64_encode(uint8_t * data, size_t length) {
|
||||
* @return true if ok
|
||||
*/
|
||||
bool WebSockets::readCb(WSclient_t * client, uint8_t * out, size_t n, WSreadWaitCb cb) {
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
|
||||
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
|
||||
if(!client->tcp || !client->tcp->connected()) {
|
||||
return false;
|
||||
}
|
||||
@ -539,12 +597,13 @@ bool WebSockets::readCb(WSclient_t * client, uint8_t * out, size_t n, WSreadWait
|
||||
if(cb) {
|
||||
cb(client, ok);
|
||||
}
|
||||
}, client, std::placeholders::_1, cb));
|
||||
},
|
||||
client, std::placeholders::_1, cb));
|
||||
|
||||
#else
|
||||
unsigned long t = millis();
|
||||
size_t len;
|
||||
DEBUG_WEBSOCKETS("[readCb] n: %d t: %d\n", n, t);
|
||||
ssize_t len;
|
||||
DEBUG_WEBSOCKETS("[readCb] n: %zu t: %lu\n", n, t);
|
||||
while(n > 0) {
|
||||
if(client->tcp == NULL) {
|
||||
DEBUG_WEBSOCKETS("[readCb] tcp is null!\n");
|
||||
@ -563,7 +622,7 @@ bool WebSockets::readCb(WSclient_t * client, uint8_t * out, size_t n, WSreadWait
|
||||
}
|
||||
|
||||
if((millis() - t) > WEBSOCKETS_TCP_TIMEOUT) {
|
||||
DEBUG_WEBSOCKETS("[readCb] receive TIMEOUT! %d\n", (millis() - t));
|
||||
DEBUG_WEBSOCKETS("[readCb] receive TIMEOUT! %lu\n", (millis() - t));
|
||||
if(cb) {
|
||||
cb(client, false);
|
||||
}
|
||||
@ -571,14 +630,12 @@ bool WebSockets::readCb(WSclient_t * client, uint8_t * out, size_t n, WSreadWait
|
||||
}
|
||||
|
||||
if(!client->tcp->available()) {
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
||||
delay(0);
|
||||
#endif
|
||||
WEBSOCKETS_YIELD();
|
||||
continue;
|
||||
}
|
||||
|
||||
len = client->tcp->read((uint8_t*) out, n);
|
||||
if(len) {
|
||||
len = client->tcp->read((uint8_t *)out, n);
|
||||
if(len > 0) {
|
||||
t = millis();
|
||||
out += len;
|
||||
n -= len;
|
||||
@ -586,14 +643,110 @@ bool WebSockets::readCb(WSclient_t * client, uint8_t * out, size_t n, WSreadWait
|
||||
} else {
|
||||
//DEBUG_WEBSOCKETS("Receive %d left %d!\n", len, n);
|
||||
}
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
||||
delay(0);
|
||||
#endif
|
||||
WEBSOCKETS_YIELD();
|
||||
}
|
||||
if(cb) {
|
||||
cb(client, true);
|
||||
}
|
||||
WEBSOCKETS_YIELD();
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* write x byte to tcp or get timeout
|
||||
* @param client WSclient_t *
|
||||
* @param out uint8_t * data buffer
|
||||
* @param n size_t byte count
|
||||
* @return bytes send
|
||||
*/
|
||||
size_t WebSockets::write(WSclient_t * client, uint8_t * out, size_t n) {
|
||||
if(out == NULL)
|
||||
return 0;
|
||||
if(client == NULL)
|
||||
return 0;
|
||||
unsigned long t = millis();
|
||||
size_t len = 0;
|
||||
size_t total = 0;
|
||||
DEBUG_WEBSOCKETS("[write] n: %zu t: %lu\n", n, t);
|
||||
while(n > 0) {
|
||||
if(client->tcp == NULL) {
|
||||
DEBUG_WEBSOCKETS("[write] tcp is null!\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if(!client->tcp->connected()) {
|
||||
DEBUG_WEBSOCKETS("[write] not connected!\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if((millis() - t) > WEBSOCKETS_TCP_TIMEOUT) {
|
||||
DEBUG_WEBSOCKETS("[write] write TIMEOUT! %lu\n", (millis() - t));
|
||||
break;
|
||||
}
|
||||
|
||||
len = client->tcp->write((const uint8_t *)out, n);
|
||||
if(len) {
|
||||
t = millis();
|
||||
out += len;
|
||||
n -= len;
|
||||
total += len;
|
||||
//DEBUG_WEBSOCKETS("write %d left %d!\n", len, n);
|
||||
} else {
|
||||
//DEBUG_WEBSOCKETS("write %d failed left %d!\n", len, n);
|
||||
}
|
||||
WEBSOCKETS_YIELD();
|
||||
}
|
||||
WEBSOCKETS_YIELD();
|
||||
return total;
|
||||
}
|
||||
|
||||
size_t WebSockets::write(WSclient_t * client, const char * out) {
|
||||
if(client == NULL)
|
||||
return 0;
|
||||
if(out == NULL)
|
||||
return 0;
|
||||
return write(client, (uint8_t *)out, strlen(out));
|
||||
}
|
||||
|
||||
/**
|
||||
* enable ping/pong heartbeat process
|
||||
* @param client WSclient_t *
|
||||
* @param pingInterval uint32_t how often ping will be sent
|
||||
* @param pongTimeout uint32_t millis after which pong should timout if not received
|
||||
* @param disconnectTimeoutCount uint8_t how many timeouts before disconnect, 0=> do not disconnect
|
||||
*/
|
||||
void WebSockets::enableHeartbeat(WSclient_t * client, uint32_t pingInterval, uint32_t pongTimeout, uint8_t disconnectTimeoutCount) {
|
||||
if(client == NULL)
|
||||
return;
|
||||
client->pingInterval = pingInterval;
|
||||
client->pongTimeout = pongTimeout;
|
||||
client->disconnectTimeoutCount = disconnectTimeoutCount;
|
||||
client->pongReceived = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* handle ping/pong heartbeat timeout process
|
||||
* @param client WSclient_t *
|
||||
*/
|
||||
void WebSockets::handleHBTimeout(WSclient_t * client) {
|
||||
if(client->pingInterval) { // if heartbeat is enabled
|
||||
uint32_t pi = millis() - client->lastPing;
|
||||
|
||||
if(client->pongReceived) {
|
||||
client->pongTimeoutCount = 0;
|
||||
} else {
|
||||
if(pi > client->pongTimeout) { // pong not received in time
|
||||
client->pongTimeoutCount++;
|
||||
client->lastPing = millis() - client->pingInterval - 500; // force ping on the next run
|
||||
|
||||
DEBUG_WEBSOCKETS("[HBtimeout] pong TIMEOUT! lp=%d millis=%d pi=%d count=%d\n", client->lastPing, millis(), pi, client->pongTimeoutCount);
|
||||
|
||||
if(client->disconnectTimeoutCount && client->pongTimeoutCount >= client->disconnectTimeoutCount) {
|
||||
DEBUG_WEBSOCKETS("[HBtimeout] count=%d, DISCONNECTING\n", client->pongTimeoutCount);
|
||||
clientDisconnect(client);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
285
src/WebSockets.h
285
src/WebSockets.h
@ -25,62 +25,120 @@
|
||||
#ifndef WEBSOCKETS_H_
|
||||
#define WEBSOCKETS_H_
|
||||
|
||||
#ifdef STM32_DEVICE
|
||||
#include <application.h>
|
||||
#define bit(b) (1UL << (b)) // Taken directly from Arduino.h
|
||||
#else
|
||||
#include <Arduino.h>
|
||||
#include <IPAddress.h>
|
||||
#endif
|
||||
|
||||
#ifdef ARDUINO_ARCH_AVR
|
||||
#error Version 2.x.x currently does not support Arduino with AVR since there is no support for std namespace of c++.
|
||||
#error Use Version 1.x.x. (ATmega branch)
|
||||
#else
|
||||
#include <functional>
|
||||
#endif
|
||||
|
||||
#ifndef NODEBUG_WEBSOCKETS
|
||||
#ifdef DEBUG_ESP_PORT
|
||||
#define DEBUG_WEBSOCKETS(...) DEBUG_ESP_PORT.printf(__VA_ARGS__)
|
||||
#else
|
||||
//#define DEBUG_WEBSOCKETS(...) os_printf( __VA_ARGS__ )
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef DEBUG_WEBSOCKETS
|
||||
#define DEBUG_WEBSOCKETS(...)
|
||||
#ifndef NODEBUG_WEBSOCKETS
|
||||
#define NODEBUG_WEBSOCKETS
|
||||
#endif
|
||||
|
||||
#ifdef ESP8266
|
||||
#define WEBSOCKETS_MAX_DATA_SIZE (15*1024)
|
||||
#define WEBSOCKETS_USE_BIG_MEM
|
||||
#else
|
||||
//atmega328p has only 2KB ram!
|
||||
#define WEBSOCKETS_MAX_DATA_SIZE (1024)
|
||||
#endif
|
||||
|
||||
#define WEBSOCKETS_TCP_TIMEOUT (2000)
|
||||
#if defined(ESP8266) || defined(ESP32)
|
||||
|
||||
#define NETWORK_ESP8266_ASYNC (0)
|
||||
#define NETWORK_ESP8266 (1)
|
||||
#define NETWORK_W5100 (2)
|
||||
#define NETWORK_ENC28J60 (3)
|
||||
#define WEBSOCKETS_MAX_DATA_SIZE (15 * 1024)
|
||||
#define WEBSOCKETS_USE_BIG_MEM
|
||||
#define GET_FREE_HEAP ESP.getFreeHeap()
|
||||
// moves all Header strings to Flash (~300 Byte)
|
||||
//#define WEBSOCKETS_SAVE_RAM
|
||||
|
||||
#if defined(ESP8266)
|
||||
#define WEBSOCKETS_YIELD() delay(0)
|
||||
#elif defined(ESP32)
|
||||
#define WEBSOCKETS_YIELD() yield()
|
||||
#endif
|
||||
|
||||
#elif defined(STM32_DEVICE)
|
||||
|
||||
#define WEBSOCKETS_MAX_DATA_SIZE (15 * 1024)
|
||||
#define WEBSOCKETS_USE_BIG_MEM
|
||||
#define GET_FREE_HEAP System.freeMemory()
|
||||
#define WEBSOCKETS_YIELD()
|
||||
|
||||
#else
|
||||
|
||||
//atmega328p has only 2KB ram!
|
||||
#define WEBSOCKETS_MAX_DATA_SIZE (1024)
|
||||
// moves all Header strings to Flash
|
||||
#define WEBSOCKETS_SAVE_RAM
|
||||
#define WEBSOCKETS_YIELD()
|
||||
|
||||
#endif
|
||||
|
||||
#define WEBSOCKETS_TCP_TIMEOUT (5000)
|
||||
|
||||
#define NETWORK_ESP8266_ASYNC (0)
|
||||
#define NETWORK_ESP8266 (1)
|
||||
#define NETWORK_W5100 (2)
|
||||
#define NETWORK_ENC28J60 (3)
|
||||
#define NETWORK_ESP32 (4)
|
||||
#define NETWORK_ESP32_ETH (5)
|
||||
|
||||
// max size of the WS Message Header
|
||||
#define WEBSOCKETS_MAX_HEADER_SIZE (14)
|
||||
#define WEBSOCKETS_MAX_HEADER_SIZE (14)
|
||||
|
||||
#if !defined(WEBSOCKETS_NETWORK_TYPE)
|
||||
// select Network type based
|
||||
#if defined(ESP8266) || defined(ESP31B)
|
||||
#define WEBSOCKETS_NETWORK_TYPE NETWORK_ESP8266
|
||||
//#define WEBSOCKETS_NETWORK_TYPE NETWORK_ESP8266_ASYNC
|
||||
//#define WEBSOCKETS_NETWORK_TYPE NETWORK_W5100
|
||||
|
||||
#elif defined(ESP32)
|
||||
#define WEBSOCKETS_NETWORK_TYPE NETWORK_ESP32
|
||||
//#define WEBSOCKETS_NETWORK_TYPE NETWORK_ESP32_ETH
|
||||
#else
|
||||
#define WEBSOCKETS_NETWORK_TYPE NETWORK_W5100
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
|
||||
// Includes and defined based on Network Type
|
||||
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
|
||||
|
||||
// Note:
|
||||
// No SSL/WSS support for client in Async mode
|
||||
// TLS lib need a sync interface!
|
||||
|
||||
#if !defined(ESP8266) && !defined(ESP31B)
|
||||
#if defined(ESP8266)
|
||||
#include <ESP8266WiFi.h>
|
||||
#elif defined(ESP32)
|
||||
#include <WiFi.h>
|
||||
#include <WiFiClientSecure.h>
|
||||
#define SSL_AXTLS
|
||||
#elif defined(ESP31B)
|
||||
#include <ESP31BWiFi.h>
|
||||
#else
|
||||
#error "network type ESP8266 ASYNC only possible on the ESP mcu!"
|
||||
#endif
|
||||
|
||||
#ifdef ESP8266
|
||||
#include <ESP8266WiFi.h>
|
||||
#else
|
||||
#include <ESP31BWiFi.h>
|
||||
#endif
|
||||
#include <ESPAsyncTCP.h>
|
||||
#include <ESPAsyncTCPbuffer.h>
|
||||
#define WEBSOCKETS_NETWORK_CLASS AsyncTCPbuffer
|
||||
#define WEBSOCKETS_NETWORK_SERVER_CLASS AsyncServer
|
||||
|
||||
#elif (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
||||
#elif(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
||||
|
||||
#if !defined(ESP8266) && !defined(ESP31B)
|
||||
#error "network type ESP8266 only possible on the ESP mcu!"
|
||||
@ -88,29 +146,65 @@
|
||||
|
||||
#ifdef ESP8266
|
||||
#include <ESP8266WiFi.h>
|
||||
#if defined(wificlientbearssl_h) && !defined(USING_AXTLS) && !defined(wificlientsecure_h)
|
||||
#define SSL_BARESSL
|
||||
#else
|
||||
#define SSL_AXTLS
|
||||
#endif
|
||||
#else
|
||||
#include <ESP31BWiFi.h>
|
||||
#endif
|
||||
#define WEBSOCKETS_NETWORK_CLASS WiFiClient
|
||||
#define WEBSOCKETS_NETWORK_SSL_CLASS WiFiClientSecure
|
||||
#define WEBSOCKETS_NETWORK_SERVER_CLASS WiFiServer
|
||||
|
||||
#elif (WEBSOCKETS_NETWORK_TYPE == NETWORK_W5100)
|
||||
#elif(WEBSOCKETS_NETWORK_TYPE == NETWORK_W5100)
|
||||
|
||||
#ifdef STM32_DEVICE
|
||||
#define WEBSOCKETS_NETWORK_CLASS TCPClient
|
||||
#define WEBSOCKETS_NETWORK_SERVER_CLASS TCPServer
|
||||
#else
|
||||
#include <Ethernet.h>
|
||||
#include <SPI.h>
|
||||
#define WEBSOCKETS_NETWORK_CLASS EthernetClient
|
||||
#define WEBSOCKETS_NETWORK_SERVER_CLASS EthernetServer
|
||||
#endif
|
||||
|
||||
#elif (WEBSOCKETS_NETWORK_TYPE == NETWORK_ENC28J60)
|
||||
#elif(WEBSOCKETS_NETWORK_TYPE == NETWORK_ENC28J60)
|
||||
|
||||
#include <UIPEthernet.h>
|
||||
#define WEBSOCKETS_NETWORK_CLASS UIPClient
|
||||
#define WEBSOCKETS_NETWORK_SERVER_CLASS UIPServer
|
||||
|
||||
#elif(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
|
||||
|
||||
#include <WiFi.h>
|
||||
#include <WiFiClientSecure.h>
|
||||
#define SSL_AXTLS
|
||||
#define WEBSOCKETS_NETWORK_CLASS WiFiClient
|
||||
#define WEBSOCKETS_NETWORK_SSL_CLASS WiFiClientSecure
|
||||
#define WEBSOCKETS_NETWORK_SERVER_CLASS WiFiServer
|
||||
|
||||
#elif(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32_ETH)
|
||||
|
||||
#include <ETH.h>
|
||||
#define WEBSOCKETS_NETWORK_CLASS WiFiClient
|
||||
#define WEBSOCKETS_NETWORK_SERVER_CLASS WiFiServer
|
||||
|
||||
#else
|
||||
#error "no network type selected!"
|
||||
#endif
|
||||
|
||||
#ifdef WEBSOCKETS_NETWORK_SSL_CLASS
|
||||
#define HAS_SSL
|
||||
#endif
|
||||
|
||||
// moves all Header strings to Flash (~300 Byte)
|
||||
#ifdef WEBSOCKETS_SAVE_RAM
|
||||
#define WEBSOCKETS_STRING(var) F(var)
|
||||
#else
|
||||
#define WEBSOCKETS_STRING(var) var
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
WSC_NOT_CONNECTED,
|
||||
@ -123,107 +217,132 @@ typedef enum {
|
||||
WStype_DISCONNECTED,
|
||||
WStype_CONNECTED,
|
||||
WStype_TEXT,
|
||||
WStype_BIN
|
||||
WStype_BIN,
|
||||
WStype_FRAGMENT_TEXT_START,
|
||||
WStype_FRAGMENT_BIN_START,
|
||||
WStype_FRAGMENT,
|
||||
WStype_FRAGMENT_FIN,
|
||||
WStype_PING,
|
||||
WStype_PONG,
|
||||
} WStype_t;
|
||||
|
||||
typedef enum {
|
||||
WSop_continuation = 0x00, ///< %x0 denotes a continuation frame
|
||||
WSop_text = 0x01, ///< %x1 denotes a text frame
|
||||
WSop_binary = 0x02, ///< %x2 denotes a binary frame
|
||||
///< %x3-7 are reserved for further non-control frames
|
||||
WSop_close = 0x08, ///< %x8 denotes a connection close
|
||||
WSop_ping = 0x09, ///< %x9 denotes a ping
|
||||
WSop_pong = 0x0A ///< %xA denotes a pong
|
||||
///< %xB-F are reserved for further control frames
|
||||
WSop_continuation = 0x00, ///< %x0 denotes a continuation frame
|
||||
WSop_text = 0x01, ///< %x1 denotes a text frame
|
||||
WSop_binary = 0x02, ///< %x2 denotes a binary frame
|
||||
///< %x3-7 are reserved for further non-control frames
|
||||
WSop_close = 0x08, ///< %x8 denotes a connection close
|
||||
WSop_ping = 0x09, ///< %x9 denotes a ping
|
||||
WSop_pong = 0x0A ///< %xA denotes a pong
|
||||
///< %xB-F are reserved for further control frames
|
||||
} WSopcode_t;
|
||||
|
||||
typedef struct {
|
||||
bool fin;
|
||||
bool rsv1;
|
||||
bool rsv2;
|
||||
bool rsv3;
|
||||
|
||||
bool fin;
|
||||
bool rsv1;
|
||||
bool rsv2;
|
||||
bool rsv3;
|
||||
WSopcode_t opCode;
|
||||
bool mask;
|
||||
|
||||
WSopcode_t opCode;
|
||||
bool mask;
|
||||
size_t payloadLen;
|
||||
|
||||
size_t payloadLen;
|
||||
|
||||
uint8_t * maskKey;
|
||||
uint8_t * maskKey;
|
||||
} WSMessageHeader_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t num; ///< connection number
|
||||
uint8_t num; ///< connection number
|
||||
|
||||
WSclientsStatus_t status;
|
||||
WSclientsStatus_t status;
|
||||
|
||||
WEBSOCKETS_NETWORK_CLASS * tcp;
|
||||
WEBSOCKETS_NETWORK_CLASS * tcp;
|
||||
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
||||
bool isSSL; ///< run in ssl mode
|
||||
WiFiClientSecure * ssl;
|
||||
bool isSocketIO; ///< client for socket.io server
|
||||
|
||||
#if defined(HAS_SSL)
|
||||
bool isSSL; ///< 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; ///< http code
|
||||
|
||||
bool cIsUpgrade; ///< Connection == Upgrade
|
||||
bool cIsWebsocket; ///< Upgrade == websocket
|
||||
bool cIsClient = false; ///< will be used for masking
|
||||
bool cIsUpgrade; ///< Connection == Upgrade
|
||||
bool cIsWebsocket; ///< Upgrade == websocket
|
||||
|
||||
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; ///< client Sec-WebSocket-Version
|
||||
|
||||
uint8_t cWsRXsize; ///< State of the RX
|
||||
uint8_t cWsHeader[WEBSOCKETS_MAX_HEADER_SIZE]; ///< RX WS Message buffer
|
||||
WSMessageHeader_t cWsHeaderDecode;
|
||||
uint8_t cWsRXsize; ///< State of the RX
|
||||
uint8_t cWsHeader[WEBSOCKETS_MAX_HEADER_SIZE]; ///< RX WS Message buffer
|
||||
WSMessageHeader_t cWsHeaderDecode;
|
||||
|
||||
String base64Authorization; ///< Base64 encoded Auth request
|
||||
String plainAuthorization; ///< Base64 encoded Auth request
|
||||
String base64Authorization; ///< Base64 encoded Auth request
|
||||
String plainAuthorization; ///< Base64 encoded Auth request
|
||||
|
||||
bool cHttpHeadersValid; ///< non-websocket http header validity indicator
|
||||
size_t cMandatoryHeadersCount; ///< non-websocket mandatory http headers present count
|
||||
String extraHeaders;
|
||||
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
|
||||
String cHttpLine; ///< HTTP header lines
|
||||
bool cHttpHeadersValid; ///< 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
|
||||
|
||||
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
|
||||
String cHttpLine; ///< HTTP header lines
|
||||
#endif
|
||||
|
||||
} WSclient_t;
|
||||
|
||||
|
||||
|
||||
class WebSockets {
|
||||
protected:
|
||||
protected:
|
||||
#ifdef __AVR__
|
||||
typedef void (*WSreadWaitCb)(WSclient_t * client, bool ok);
|
||||
typedef void (*WSreadWaitCb)(WSclient_t * client, bool ok);
|
||||
#else
|
||||
typedef std::function<void(WSclient_t * client, bool ok)> WSreadWaitCb;
|
||||
typedef std::function<void(WSclient_t * client, bool ok)> WSreadWaitCb;
|
||||
#endif
|
||||
|
||||
virtual void clientDisconnect(WSclient_t * client);
|
||||
virtual bool clientIsConnected(WSclient_t * client);
|
||||
virtual void clientDisconnect(WSclient_t * client) = 0;
|
||||
virtual bool clientIsConnected(WSclient_t * client) = 0;
|
||||
|
||||
virtual void messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length);
|
||||
void clientDisconnect(WSclient_t * client, uint16_t code, char * reason = NULL, size_t reasonLen = 0);
|
||||
|
||||
void clientDisconnect(WSclient_t * client, uint16_t code, char * reason = NULL, size_t reasonLen = 0);
|
||||
bool sendFrame(WSclient_t * client, WSopcode_t opcode, uint8_t * payload = NULL, size_t length = 0, bool mask = false, bool fin = true, bool headerToPayload = false);
|
||||
virtual void messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin) = 0;
|
||||
|
||||
void headerDone(WSclient_t * client);
|
||||
uint8_t createHeader(uint8_t * buf, WSopcode_t opcode, size_t length, bool mask, uint8_t maskKey[4], bool fin);
|
||||
bool sendFrameHeader(WSclient_t * client, WSopcode_t opcode, size_t length = 0, bool fin = true);
|
||||
bool sendFrame(WSclient_t * client, WSopcode_t opcode, uint8_t * payload = NULL, size_t length = 0, bool fin = true, bool headerToPayload = false);
|
||||
|
||||
void handleWebsocket(WSclient_t * client);
|
||||
void headerDone(WSclient_t * client);
|
||||
|
||||
bool handleWebsocketWaitFor(WSclient_t * client, size_t size);
|
||||
void handleWebsocketCb(WSclient_t * client);
|
||||
void handleWebsocketPayloadCb(WSclient_t * client, bool ok, uint8_t * payload);
|
||||
void handleWebsocket(WSclient_t * client);
|
||||
|
||||
String acceptKey(String & clientKey);
|
||||
String base64_encode(uint8_t * data, size_t length);
|
||||
bool handleWebsocketWaitFor(WSclient_t * client, size_t size);
|
||||
void handleWebsocketCb(WSclient_t * client);
|
||||
void handleWebsocketPayloadCb(WSclient_t * client, bool ok, uint8_t * payload);
|
||||
|
||||
bool readCb(WSclient_t * client, uint8_t *out, size_t n, WSreadWaitCb cb);
|
||||
String acceptKey(String & clientKey);
|
||||
String base64_encode(uint8_t * data, size_t length);
|
||||
|
||||
bool readCb(WSclient_t * client, uint8_t * out, size_t n, WSreadWaitCb cb);
|
||||
virtual size_t write(WSclient_t * client, uint8_t * out, size_t n);
|
||||
size_t write(WSclient_t * client, const char * out);
|
||||
|
||||
void enableHeartbeat(WSclient_t * client, uint32_t pingInterval, uint32_t pongTimeout, uint8_t disconnectTimeoutCount);
|
||||
void handleHBTimeout(WSclient_t * client);
|
||||
};
|
||||
|
||||
#ifndef UNUSED
|
||||
#define UNUSED(var) (void)(var)
|
||||
#endif
|
||||
#endif /* WEBSOCKETS_H_ */
|
||||
|
@ -25,10 +25,14 @@
|
||||
#include "WebSockets.h"
|
||||
#include "WebSocketsClient.h"
|
||||
|
||||
|
||||
WebSocketsClient::WebSocketsClient() {
|
||||
_cbEvent = NULL;
|
||||
_client.num = 0;
|
||||
_cbEvent = NULL;
|
||||
_client.num = 0;
|
||||
_client.cIsClient = true;
|
||||
_client.extraHeaders = WEBSOCKETS_STRING("Origin: file://");
|
||||
_reconnectInterval = 500;
|
||||
_port = 0;
|
||||
_host = "";
|
||||
}
|
||||
|
||||
WebSocketsClient::~WebSocketsClient() {
|
||||
@ -38,31 +42,37 @@ WebSocketsClient::~WebSocketsClient() {
|
||||
/**
|
||||
* calles to init the Websockets server
|
||||
*/
|
||||
void WebSocketsClient::begin(const char *host, uint16_t port, const char * url, const char * protocol) {
|
||||
void WebSocketsClient::begin(const char * host, uint16_t port, const char * url, const char * protocol) {
|
||||
_host = host;
|
||||
_port = port;
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
||||
_fingerprint = "";
|
||||
#if defined(HAS_SSL)
|
||||
_fingerprint = SSL_FINGERPRINT_NULL;
|
||||
_CA_cert = NULL;
|
||||
#endif
|
||||
|
||||
_client.num = 0;
|
||||
_client.num = 0;
|
||||
_client.status = WSC_NOT_CONNECTED;
|
||||
_client.tcp = NULL;
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
||||
_client.tcp = NULL;
|
||||
#if defined(HAS_SSL)
|
||||
_client.isSSL = false;
|
||||
_client.ssl = NULL;
|
||||
_client.ssl = NULL;
|
||||
#endif
|
||||
_client.cUrl = url;
|
||||
_client.cCode = 0;
|
||||
_client.cIsUpgrade = false;
|
||||
_client.cIsWebsocket = true;
|
||||
_client.cKey = "";
|
||||
_client.cAccept = "";
|
||||
_client.cProtocol = protocol;
|
||||
_client.cExtensions = "";
|
||||
_client.cVersion = 0;
|
||||
_client.cUrl = url;
|
||||
_client.cCode = 0;
|
||||
_client.cIsUpgrade = false;
|
||||
_client.cIsWebsocket = true;
|
||||
_client.cKey = "";
|
||||
_client.cAccept = "";
|
||||
_client.cProtocol = protocol;
|
||||
_client.cExtensions = "";
|
||||
_client.cVersion = 0;
|
||||
_client.base64Authorization = "";
|
||||
_client.plainAuthorization = "";
|
||||
_client.plainAuthorization = "";
|
||||
_client.isSocketIO = false;
|
||||
|
||||
_client.lastPing = 0;
|
||||
_client.pongReceived = false;
|
||||
_client.pongTimeoutCount = 0;
|
||||
|
||||
#ifdef ESP8266
|
||||
randomSeed(RANDOM_REG32);
|
||||
@ -70,36 +80,114 @@ void WebSocketsClient::begin(const char *host, uint16_t port, const char * url,
|
||||
// todo find better seed
|
||||
randomSeed(millis());
|
||||
#endif
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
|
||||
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
|
||||
asyncConnect();
|
||||
#endif
|
||||
|
||||
_lastConnectionFail = 0;
|
||||
_lastHeaderSent = 0;
|
||||
}
|
||||
|
||||
void WebSocketsClient::begin(String host, uint16_t port, String url, String protocol) {
|
||||
begin(host.c_str(), port, url.c_str(), protocol.c_str());
|
||||
}
|
||||
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
||||
void WebSocketsClient::beginSSL(const char *host, uint16_t port, const char * url, const char * fingerprint, const char * protocol) {
|
||||
void WebSocketsClient::begin(IPAddress host, uint16_t port, const char * url, const char * protocol) {
|
||||
return begin(host.toString().c_str(), port, url, protocol);
|
||||
}
|
||||
|
||||
#if defined(HAS_SSL)
|
||||
#if defined(SSL_AXTLS)
|
||||
void WebSocketsClient::beginSSL(const char * host, uint16_t port, const char * url, const char * fingerprint, const char * protocol) {
|
||||
begin(host, port, url, protocol);
|
||||
_client.isSSL = true;
|
||||
_fingerprint = fingerprint;
|
||||
_fingerprint = fingerprint;
|
||||
_CA_cert = NULL;
|
||||
}
|
||||
|
||||
void WebSocketsClient::beginSSL(String host, uint16_t port, String url, String fingerprint, String protocol) {
|
||||
beginSSL(host.c_str(), port, url.c_str(), fingerprint.c_str(), protocol.c_str());
|
||||
}
|
||||
|
||||
void WebSocketsClient::beginSslWithCA(const char * host, uint16_t port, const char * url, const char * CA_cert, const char * protocol) {
|
||||
begin(host, port, url, protocol);
|
||||
_client.isSSL = true;
|
||||
_fingerprint = SSL_FINGERPRINT_NULL;
|
||||
_CA_cert = CA_cert;
|
||||
}
|
||||
#else
|
||||
void WebSocketsClient::beginSSL(const char * host, uint16_t port, const char * url, const uint8_t * fingerprint, const char * protocol) {
|
||||
begin(host, port, url, protocol);
|
||||
_client.isSSL = true;
|
||||
_fingerprint = fingerprint;
|
||||
_CA_cert = NULL;
|
||||
}
|
||||
void WebSocketsClient::beginSslWithCA(const char * host, uint16_t port, const char * url, const char * CA_cert, const char * protocol) {
|
||||
begin(host, port, url, protocol);
|
||||
_client.isSSL = true;
|
||||
_fingerprint = SSL_FINGERPRINT_NULL;
|
||||
_CA_cert = new BearSSL::X509List(CA_cert);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
#endif // SSL_AXTLS
|
||||
#endif // HAS_SSL
|
||||
|
||||
void WebSocketsClient::beginSocketIO(const char * host, uint16_t port, const char * url, const char * protocol) {
|
||||
begin(host, port, url, protocol);
|
||||
_client.isSocketIO = true;
|
||||
}
|
||||
|
||||
void WebSocketsClient::beginSocketIO(String host, uint16_t port, String url, String protocol) {
|
||||
beginSocketIO(host.c_str(), port, url.c_str(), protocol.c_str());
|
||||
}
|
||||
|
||||
#if defined(HAS_SSL)
|
||||
void WebSocketsClient::beginSocketIOSSL(const char * host, uint16_t port, const char * url, const char * protocol) {
|
||||
begin(host, port, url, protocol);
|
||||
_client.isSocketIO = true;
|
||||
_client.isSSL = true;
|
||||
_fingerprint = SSL_FINGERPRINT_NULL;
|
||||
}
|
||||
|
||||
void WebSocketsClient::beginSocketIOSSL(String host, uint16_t port, String url, String protocol) {
|
||||
beginSocketIOSSL(host.c_str(), port, url.c_str(), protocol.c_str());
|
||||
}
|
||||
|
||||
void WebSocketsClient::beginSocketIOSSLWithCA(const char * host, uint16_t port, const char * url, const char * CA_cert, const char * protocol) {
|
||||
begin(host, port, url, protocol);
|
||||
_client.isSocketIO = true;
|
||||
_client.isSSL = true;
|
||||
_fingerprint = SSL_FINGERPRINT_NULL;
|
||||
#if defined(SSL_AXTLS)
|
||||
_CA_cert = CA_cert;
|
||||
#else
|
||||
_CA_cert = new BearSSL::X509List(CA_cert);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
|
||||
#if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
|
||||
/**
|
||||
* called in arduino loop
|
||||
*/
|
||||
void WebSocketsClient::loop(void) {
|
||||
if(_port == 0) {
|
||||
return;
|
||||
}
|
||||
WEBSOCKETS_YIELD();
|
||||
if(!clientIsConnected(&_client)) {
|
||||
// do not flood the server
|
||||
if((millis() - _lastConnectionFail) < _reconnectInterval) {
|
||||
return;
|
||||
}
|
||||
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
||||
#if defined(HAS_SSL)
|
||||
if(_client.isSSL) {
|
||||
DEBUG_WEBSOCKETS("[WS-Client] connect wss...\n");
|
||||
if(_client.ssl) {
|
||||
@ -107,15 +195,33 @@ void WebSocketsClient::loop(void) {
|
||||
_client.ssl = NULL;
|
||||
_client.tcp = NULL;
|
||||
}
|
||||
_client.ssl = new WiFiClientSecure();
|
||||
_client.ssl = new WEBSOCKETS_NETWORK_SSL_CLASS();
|
||||
_client.tcp = _client.ssl;
|
||||
if(_CA_cert) {
|
||||
DEBUG_WEBSOCKETS("[WS-Client] setting CA certificate");
|
||||
#if defined(ESP32)
|
||||
_client.ssl->setCACert(_CA_cert);
|
||||
#elif defined(ESP8266) && defined(SSL_AXTLS)
|
||||
_client.ssl->setCACert((const uint8_t *)_CA_cert, strlen(_CA_cert) + 1);
|
||||
#elif defined(ESP8266) && defined(SSL_BARESSL)
|
||||
_client.ssl->setTrustAnchors(_CA_cert);
|
||||
#else
|
||||
#error setCACert not implemented
|
||||
#endif
|
||||
#if defined(SSL_BARESSL)
|
||||
} else if(_fingerprint) {
|
||||
_client.ssl->setFingerprint(_fingerprint);
|
||||
} else {
|
||||
_client.ssl->setInsecure();
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
DEBUG_WEBSOCKETS("[WS-Client] connect ws...\n");
|
||||
if(_client.tcp) {
|
||||
delete _client.tcp;
|
||||
_client.tcp = NULL;
|
||||
}
|
||||
_client.tcp = new WiFiClient();
|
||||
_client.tcp = new WEBSOCKETS_NETWORK_CLASS();
|
||||
}
|
||||
#else
|
||||
_client.tcp = new WEBSOCKETS_NETWORK_CLASS();
|
||||
@ -125,15 +231,25 @@ void WebSocketsClient::loop(void) {
|
||||
DEBUG_WEBSOCKETS("[WS-Client] creating Network class failed!");
|
||||
return;
|
||||
}
|
||||
|
||||
WEBSOCKETS_YIELD();
|
||||
#if defined(ESP32)
|
||||
if(_client.tcp->connect(_host.c_str(), _port, WEBSOCKETS_TCP_TIMEOUT)) {
|
||||
#else
|
||||
if(_client.tcp->connect(_host.c_str(), _port)) {
|
||||
#endif
|
||||
connectedCb();
|
||||
_lastConnectionFail = 0;
|
||||
} else {
|
||||
connectFailedCb();
|
||||
delay(10); //some little delay to not flood the server
|
||||
_lastConnectionFail = millis();
|
||||
}
|
||||
} else {
|
||||
handleClientData();
|
||||
WEBSOCKETS_YIELD();
|
||||
if(_client.status == WSC_CONNECTED) {
|
||||
handleHBPing();
|
||||
handleHBTimeout(&_client);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -156,28 +272,34 @@ void WebSocketsClient::onEvent(WebSocketClientEvent cbEvent) {
|
||||
*/
|
||||
bool WebSocketsClient::sendTXT(uint8_t * payload, size_t length, bool headerToPayload) {
|
||||
if(length == 0) {
|
||||
length = strlen((const char *) payload);
|
||||
length = strlen((const char *)payload);
|
||||
}
|
||||
if(clientIsConnected(&_client)) {
|
||||
return sendFrame(&_client, WSop_text, payload, length, true, true, headerToPayload);
|
||||
return sendFrame(&_client, WSop_text, payload, length, true, headerToPayload);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WebSocketsClient::sendTXT(const uint8_t * payload, size_t length) {
|
||||
return sendTXT((uint8_t *) payload, length);
|
||||
return sendTXT((uint8_t *)payload, length);
|
||||
}
|
||||
|
||||
bool WebSocketsClient::sendTXT(char * payload, size_t length, bool headerToPayload) {
|
||||
return sendTXT((uint8_t *) payload, length, headerToPayload);
|
||||
return sendTXT((uint8_t *)payload, length, headerToPayload);
|
||||
}
|
||||
|
||||
bool WebSocketsClient::sendTXT(const char * payload, size_t length) {
|
||||
return sendTXT((uint8_t *) payload, length);
|
||||
return sendTXT((uint8_t *)payload, length);
|
||||
}
|
||||
|
||||
bool WebSocketsClient::sendTXT(String & payload) {
|
||||
return sendTXT((uint8_t *) payload.c_str(), payload.length());
|
||||
return sendTXT((uint8_t *)payload.c_str(), payload.length());
|
||||
}
|
||||
|
||||
bool WebSocketsClient::sendTXT(char payload) {
|
||||
uint8_t buf[WEBSOCKETS_MAX_HEADER_SIZE + 2] = { 0x00 };
|
||||
buf[WEBSOCKETS_MAX_HEADER_SIZE] = payload;
|
||||
return sendTXT(buf, 1, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -190,13 +312,33 @@ bool WebSocketsClient::sendTXT(String & payload) {
|
||||
*/
|
||||
bool WebSocketsClient::sendBIN(uint8_t * payload, size_t length, bool headerToPayload) {
|
||||
if(clientIsConnected(&_client)) {
|
||||
return sendFrame(&_client, WSop_binary, payload, length, true, true, headerToPayload);
|
||||
return sendFrame(&_client, WSop_binary, payload, length, true, headerToPayload);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WebSocketsClient::sendBIN(const uint8_t * payload, size_t length) {
|
||||
return sendBIN((uint8_t *) payload, length);
|
||||
return sendBIN((uint8_t *)payload, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* sends a WS ping to Server
|
||||
* @param payload uint8_t *
|
||||
* @param length size_t
|
||||
* @return true if ping is send out
|
||||
*/
|
||||
bool WebSocketsClient::sendPing(uint8_t * payload, size_t length) {
|
||||
if(clientIsConnected(&_client)) {
|
||||
bool sent = sendFrame(&_client, WSop_ping, payload, length);
|
||||
if(sent)
|
||||
_client.lastPing = millis();
|
||||
return sent;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WebSocketsClient::sendPing(String & payload) {
|
||||
return sendPing((uint8_t *)payload.c_str(), payload.length());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -234,6 +376,28 @@ void WebSocketsClient::setAuthorization(const char * auth) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* set extra headers for the http request;
|
||||
* separate headers by "\r\n"
|
||||
* @param extraHeaders const char * extraHeaders
|
||||
*/
|
||||
void WebSocketsClient::setExtraHeaders(const char * extraHeaders) {
|
||||
_client.extraHeaders = extraHeaders;
|
||||
}
|
||||
|
||||
/**
|
||||
* set the reconnect Interval
|
||||
* how long to wait after a connection initiate failed
|
||||
* @param time in ms
|
||||
*/
|
||||
void WebSocketsClient::setReconnectInterval(unsigned long time) {
|
||||
_reconnectInterval = time;
|
||||
}
|
||||
|
||||
bool WebSocketsClient::isConnected(void) {
|
||||
return (_client.status == WSC_CONNECTED);
|
||||
}
|
||||
|
||||
//#################################################################################
|
||||
//#################################################################################
|
||||
//#################################################################################
|
||||
@ -243,22 +407,35 @@ void WebSocketsClient::setAuthorization(const char * auth) {
|
||||
* @param client WSclient_t * ptr to the client struct
|
||||
* @param opcode WSopcode_t
|
||||
* @param payload uint8_t *
|
||||
* @param lenght size_t
|
||||
* @param length size_t
|
||||
*/
|
||||
void WebSocketsClient::messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t lenght) {
|
||||
void WebSocketsClient::messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin) {
|
||||
WStype_t type = WStype_ERROR;
|
||||
|
||||
UNUSED(client);
|
||||
|
||||
switch(opcode) {
|
||||
case WSop_text:
|
||||
type = WStype_TEXT;
|
||||
type = fin ? WStype_TEXT : WStype_FRAGMENT_TEXT_START;
|
||||
break;
|
||||
case WSop_binary:
|
||||
type = WStype_BIN;
|
||||
type = fin ? WStype_BIN : WStype_FRAGMENT_BIN_START;
|
||||
break;
|
||||
case WSop_continuation:
|
||||
type = fin ? WStype_FRAGMENT_FIN : WStype_FRAGMENT;
|
||||
break;
|
||||
case WSop_ping:
|
||||
type = WStype_PING;
|
||||
break;
|
||||
case WSop_pong:
|
||||
type = WStype_PONG;
|
||||
break;
|
||||
case WSop_close:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
runCbEvent(type, payload, lenght);
|
||||
|
||||
runCbEvent(type, payload, length);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -266,10 +443,9 @@ void WebSocketsClient::messageReceived(WSclient_t * client, WSopcode_t opcode, u
|
||||
* @param client WSclient_t * ptr to the client struct
|
||||
*/
|
||||
void WebSocketsClient::clientDisconnect(WSclient_t * client) {
|
||||
|
||||
bool event = false;
|
||||
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
||||
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
|
||||
if(client->isSSL && client->ssl) {
|
||||
if(client->ssl->connected()) {
|
||||
client->ssl->flush();
|
||||
@ -284,13 +460,13 @@ void WebSocketsClient::clientDisconnect(WSclient_t * client) {
|
||||
|
||||
if(client->tcp) {
|
||||
if(client->tcp->connected()) {
|
||||
#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
|
||||
#if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
|
||||
client->tcp->flush();
|
||||
#endif
|
||||
client->tcp->stop();
|
||||
}
|
||||
event = true;
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
|
||||
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
|
||||
client->status = WSC_NOT_CONNECTED;
|
||||
#else
|
||||
delete client->tcp;
|
||||
@ -298,13 +474,13 @@ void WebSocketsClient::clientDisconnect(WSclient_t * client) {
|
||||
client->tcp = NULL;
|
||||
}
|
||||
|
||||
client->cCode = 0;
|
||||
client->cKey = "";
|
||||
client->cAccept = "";
|
||||
client->cProtocol = "";
|
||||
client->cVersion = 0;
|
||||
client->cIsUpgrade = false;
|
||||
client->cCode = 0;
|
||||
client->cKey = "";
|
||||
client->cAccept = "";
|
||||
client->cVersion = 0;
|
||||
client->cIsUpgrade = false;
|
||||
client->cIsWebsocket = false;
|
||||
client->cSessionId = "";
|
||||
|
||||
client->status = WSC_NOT_CONNECTED;
|
||||
|
||||
@ -320,7 +496,6 @@ void WebSocketsClient::clientDisconnect(WSclient_t * client) {
|
||||
* @return true = conneted
|
||||
*/
|
||||
bool WebSocketsClient::clientIsConnected(WSclient_t * client) {
|
||||
|
||||
if(!client->tcp) {
|
||||
return false;
|
||||
}
|
||||
@ -345,20 +520,24 @@ bool WebSocketsClient::clientIsConnected(WSclient_t * client) {
|
||||
|
||||
return false;
|
||||
}
|
||||
#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
|
||||
#if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
|
||||
/**
|
||||
* Handel incomming data from Client
|
||||
*/
|
||||
void WebSocketsClient::handleClientData(void) {
|
||||
if(_client.status == WSC_HEADER && _lastHeaderSent + WEBSOCKETS_TCP_TIMEOUT < millis()) {
|
||||
DEBUG_WEBSOCKETS("[WS-Client][handleClientData] header response timeout.. disconnecting!\n");
|
||||
clientDisconnect(&_client);
|
||||
WEBSOCKETS_YIELD();
|
||||
return;
|
||||
}
|
||||
int len = _client.tcp->available();
|
||||
if(len > 0) {
|
||||
switch(_client.status) {
|
||||
case WSC_HEADER:
|
||||
{
|
||||
case WSC_HEADER: {
|
||||
String headerLine = _client.tcp->readStringUntil('\n');
|
||||
handleHeader(&_client, &headerLine);
|
||||
}
|
||||
break;
|
||||
} break;
|
||||
case WSC_CONNECTED:
|
||||
WebSockets::handleWebsocket(&_client);
|
||||
break;
|
||||
@ -367,9 +546,7 @@ void WebSocketsClient::handleClientData(void) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
||||
delay(0);
|
||||
#endif
|
||||
WEBSOCKETS_YIELD();
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -378,6 +555,7 @@ void WebSocketsClient::handleClientData(void) {
|
||||
* @param client WSclient_t * ptr to the client struct
|
||||
*/
|
||||
void WebSocketsClient::sendHeader(WSclient_t * client) {
|
||||
static const char * NEW_LINE = "\r\n";
|
||||
|
||||
DEBUG_WEBSOCKETS("[WS-Client][sendHeader] sending header...\n");
|
||||
|
||||
@ -393,41 +571,75 @@ void WebSocketsClient::sendHeader(WSclient_t * client) {
|
||||
unsigned long start = micros();
|
||||
#endif
|
||||
|
||||
String handshake = "GET " + client->cUrl + " HTTP/1.1\r\n"
|
||||
"Host: " + _host + ":" + _port + "\r\n"
|
||||
"Connection: Upgrade\r\n"
|
||||
"Upgrade: websocket\r\n"
|
||||
"Origin: file://\r\n"
|
||||
"User-Agent: arduino-WebSocket-Client\r\n"
|
||||
"Sec-WebSocket-Version: 13\r\n"
|
||||
"Sec-WebSocket-Key: " + client->cKey + "\r\n";
|
||||
String handshake;
|
||||
bool ws_header = true;
|
||||
String url = client->cUrl;
|
||||
|
||||
if(client->cProtocol.length() > 0) {
|
||||
handshake += "Sec-WebSocket-Protocol: " + client->cProtocol + "\r\n";
|
||||
if(client->isSocketIO) {
|
||||
if(client->cSessionId.length() == 0) {
|
||||
url += WEBSOCKETS_STRING("&transport=polling");
|
||||
ws_header = false;
|
||||
} else {
|
||||
url += WEBSOCKETS_STRING("&transport=websocket&sid=");
|
||||
url += client->cSessionId;
|
||||
}
|
||||
}
|
||||
|
||||
if(client->cExtensions.length() > 0) {
|
||||
handshake += "Sec-WebSocket-Extensions: " + client->cExtensions + "\r\n";
|
||||
handshake = WEBSOCKETS_STRING("GET ");
|
||||
handshake += url + WEBSOCKETS_STRING(
|
||||
" HTTP/1.1\r\n"
|
||||
"Host: ");
|
||||
handshake += _host + ":" + _port + NEW_LINE;
|
||||
|
||||
if(ws_header) {
|
||||
handshake += WEBSOCKETS_STRING(
|
||||
"Connection: Upgrade\r\n"
|
||||
"Upgrade: websocket\r\n"
|
||||
"Sec-WebSocket-Version: 13\r\n"
|
||||
"Sec-WebSocket-Key: ");
|
||||
handshake += client->cKey + NEW_LINE;
|
||||
|
||||
if(client->cProtocol.length() > 0) {
|
||||
handshake += WEBSOCKETS_STRING("Sec-WebSocket-Protocol: ");
|
||||
handshake += client->cProtocol + NEW_LINE;
|
||||
}
|
||||
|
||||
if(client->cExtensions.length() > 0) {
|
||||
handshake += WEBSOCKETS_STRING("Sec-WebSocket-Extensions: ");
|
||||
handshake += client->cExtensions + NEW_LINE;
|
||||
}
|
||||
} else {
|
||||
handshake += WEBSOCKETS_STRING("Connection: keep-alive\r\n");
|
||||
}
|
||||
|
||||
// add extra headers; by default this includes "Origin: file://"
|
||||
if(client->extraHeaders) {
|
||||
handshake += client->extraHeaders + NEW_LINE;
|
||||
}
|
||||
|
||||
handshake += WEBSOCKETS_STRING("User-Agent: arduino-WebSocket-Client\r\n");
|
||||
|
||||
if(client->base64Authorization.length() > 0) {
|
||||
handshake += "Authorization: Basic " + client->base64Authorization + "\r\n";
|
||||
handshake += WEBSOCKETS_STRING("Authorization: Basic ");
|
||||
handshake += client->base64Authorization + NEW_LINE;
|
||||
}
|
||||
|
||||
if(client->plainAuthorization.length() > 0) {
|
||||
handshake += "Authorization: " + client->plainAuthorization + "\r\n";
|
||||
handshake += WEBSOCKETS_STRING("Authorization: ");
|
||||
handshake += client->plainAuthorization + NEW_LINE;
|
||||
}
|
||||
|
||||
handshake += "\r\n";
|
||||
handshake += NEW_LINE;
|
||||
|
||||
client->tcp->write(handshake.c_str(), handshake.length());
|
||||
DEBUG_WEBSOCKETS("[WS-Client][sendHeader] handshake %s", (uint8_t *)handshake.c_str());
|
||||
write(client, (uint8_t *)handshake.c_str(), handshake.length());
|
||||
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
|
||||
client->tcp->readStringUntil('\n', &(client->cHttpLine), std::bind(&WebSocketsClient::handleHeader, this, client, &(client->cHttpLine)));
|
||||
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
|
||||
client->tcp->readStringUntil('\n', &(client->cHttpLine), std::bind(&WebSocketsClient::handleHeader, this, client, &(client->cHttpLine)));
|
||||
#endif
|
||||
|
||||
DEBUG_WEBSOCKETS("[WS-Client][sendHeader] sending header... Done (%uus).\n", (micros() - start));
|
||||
|
||||
DEBUG_WEBSOCKETS("[WS-Client][sendHeader] sending header... Done (%luus).\n", (micros() - start));
|
||||
_lastHeaderSent = millis();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -435,46 +647,55 @@ void WebSocketsClient::sendHeader(WSclient_t * client) {
|
||||
* @param client WSclient_t * ptr to the client struct
|
||||
*/
|
||||
void WebSocketsClient::handleHeader(WSclient_t * client, String * headerLine) {
|
||||
|
||||
headerLine->trim(); // remove \r
|
||||
headerLine->trim(); // remove \r
|
||||
|
||||
if(headerLine->length() > 0) {
|
||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] RX: %s\n", headerLine->c_str());
|
||||
|
||||
if(headerLine->startsWith("HTTP/1.")) {
|
||||
if(headerLine->startsWith(WEBSOCKETS_STRING("HTTP/1."))) {
|
||||
// "HTTP/1.1 101 Switching Protocols"
|
||||
client->cCode = headerLine->substring(9, headerLine->indexOf(' ', 9)).toInt();
|
||||
} else if(headerLine->indexOf(':')) {
|
||||
String headerName = headerLine->substring(0, headerLine->indexOf(':'));
|
||||
String headerValue = headerLine->substring(headerLine->indexOf(':') + 2);
|
||||
} else if(headerLine->indexOf(':') >= 0) {
|
||||
String headerName = headerLine->substring(0, headerLine->indexOf(':'));
|
||||
String headerValue = headerLine->substring(headerLine->indexOf(':') + 1);
|
||||
|
||||
if(headerName.equalsIgnoreCase("Connection")) {
|
||||
if(headerValue.equalsIgnoreCase("upgrade")) {
|
||||
// remove space in the beginning (RFC2616)
|
||||
if(headerValue[0] == ' ') {
|
||||
headerValue.remove(0, 1);
|
||||
}
|
||||
|
||||
if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Connection"))) {
|
||||
if(headerValue.equalsIgnoreCase(WEBSOCKETS_STRING("upgrade"))) {
|
||||
client->cIsUpgrade = true;
|
||||
}
|
||||
} else if(headerName.equalsIgnoreCase("Upgrade")) {
|
||||
if(headerValue.equalsIgnoreCase("websocket")) {
|
||||
} else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Upgrade"))) {
|
||||
if(headerValue.equalsIgnoreCase(WEBSOCKETS_STRING("websocket"))) {
|
||||
client->cIsWebsocket = true;
|
||||
}
|
||||
} else if(headerName.equalsIgnoreCase("Sec-WebSocket-Accept")) {
|
||||
} else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Accept"))) {
|
||||
client->cAccept = headerValue;
|
||||
client->cAccept.trim(); // see rfc6455
|
||||
} else if(headerName.equalsIgnoreCase("Sec-WebSocket-Protocol")) {
|
||||
client->cAccept.trim(); // see rfc6455
|
||||
} else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Protocol"))) {
|
||||
client->cProtocol = headerValue;
|
||||
} else if(headerName.equalsIgnoreCase("Sec-WebSocket-Extensions")) {
|
||||
} else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Extensions"))) {
|
||||
client->cExtensions = headerValue;
|
||||
} else if(headerName.equalsIgnoreCase("Sec-WebSocket-Version")) {
|
||||
} 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) {
|
||||
client->cSessionId = headerValue.substring(headerValue.indexOf('=') + 1, headerValue.indexOf(";"));
|
||||
} else {
|
||||
client->cSessionId = headerValue.substring(headerValue.indexOf('=') + 1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Header error (%s)\n", headerLine->c_str());
|
||||
}
|
||||
|
||||
(*headerLine) = "";
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
|
||||
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
|
||||
client->tcp->readStringUntil('\n', &(client->cHttpLine), std::bind(&WebSocketsClient::handleHeader, this, client, &(client->cHttpLine)));
|
||||
#endif
|
||||
|
||||
} else {
|
||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Header read fin.\n");
|
||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Client settings:\n");
|
||||
@ -490,26 +711,31 @@ void WebSocketsClient::handleHeader(WSclient_t * client, String * headerLine) {
|
||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cProtocol: %s\n", client->cProtocol.c_str());
|
||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cExtensions: %s\n", client->cExtensions.c_str());
|
||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cVersion: %d\n", client->cVersion);
|
||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cSessionId: %s\n", client->cSessionId.c_str());
|
||||
|
||||
bool ok = (client->cIsUpgrade && client->cIsWebsocket);
|
||||
|
||||
if(ok) {
|
||||
switch(client->cCode) {
|
||||
case 101: ///< Switching Protocols
|
||||
case 101: ///< Switching Protocols
|
||||
|
||||
break;
|
||||
case 403: ///< Forbidden
|
||||
case 200:
|
||||
if(client->isSocketIO) {
|
||||
break;
|
||||
}
|
||||
case 403: ///< Forbidden
|
||||
// todo handle login
|
||||
default: ///< Server dont unterstand requrst
|
||||
default: ///< Server dont unterstand requrst
|
||||
ok = false;
|
||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] serverCode is not 101 (%d)\n", client->cCode);
|
||||
clientDisconnect(client);
|
||||
_lastConnectionFail = millis();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(ok) {
|
||||
|
||||
if(client->cAccept.length() == 0) {
|
||||
ok = false;
|
||||
} else {
|
||||
@ -523,71 +749,89 @@ void WebSocketsClient::handleHeader(WSclient_t * client, String * headerLine) {
|
||||
}
|
||||
|
||||
if(ok) {
|
||||
|
||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Websocket connection init done.\n");
|
||||
headerDone(client);
|
||||
|
||||
|
||||
runCbEvent(WStype_CONNECTED, (uint8_t *) client->cUrl.c_str(), client->cUrl.length());
|
||||
|
||||
runCbEvent(WStype_CONNECTED, (uint8_t *)client->cUrl.c_str(), client->cUrl.length());
|
||||
#if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
|
||||
} 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();
|
||||
}
|
||||
}
|
||||
sendHeader(client);
|
||||
#endif
|
||||
} else {
|
||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] no Websocket connection close.\n");
|
||||
client->tcp->write("This is a webSocket client!");
|
||||
_lastConnectionFail = millis();
|
||||
if(clientIsConnected(client)) {
|
||||
write(client, "This is a webSocket client!");
|
||||
}
|
||||
clientDisconnect(client);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WebSocketsClient::connectedCb() {
|
||||
|
||||
DEBUG_WEBSOCKETS("[WS-Client] connected to %s:%u.\n", _host.c_str(), _port);
|
||||
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
|
||||
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
|
||||
_client.tcp->onDisconnect(std::bind([](WebSocketsClient * c, AsyncTCPbuffer * obj, WSclient_t * client) -> bool {
|
||||
DEBUG_WEBSOCKETS("[WS-Server][%d] Disconnect client\n", client->num);
|
||||
client->status = WSC_NOT_CONNECTED;
|
||||
client->tcp = NULL;
|
||||
client->tcp = NULL;
|
||||
|
||||
// reconnect
|
||||
c->asyncConnect();
|
||||
|
||||
return true;
|
||||
}, this, std::placeholders::_1, &_client));
|
||||
},
|
||||
this, std::placeholders::_1, &_client));
|
||||
#endif
|
||||
|
||||
_client.status = WSC_HEADER;
|
||||
|
||||
#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
|
||||
#if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
|
||||
// set Timeout for readBytesUntil and readStringUntil
|
||||
_client.tcp->setTimeout(WEBSOCKETS_TCP_TIMEOUT);
|
||||
#endif
|
||||
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
||||
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
|
||||
_client.tcp->setNoDelay(true);
|
||||
#endif
|
||||
|
||||
#if defined(HAS_SSL)
|
||||
#if defined(SSL_AXTLS) || defined(ESP32)
|
||||
if(_client.isSSL && _fingerprint.length()) {
|
||||
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 && _fingerprint) {
|
||||
#endif
|
||||
} else if(_client.isSSL && !_CA_cert) {
|
||||
#if defined(SSL_BARESSL)
|
||||
_client.ssl->setInsecure();
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
// send Header to Server
|
||||
sendHeader(&_client);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void WebSocketsClient::connectFailedCb() {
|
||||
DEBUG_WEBSOCKETS("[WS-Client] connection to %s:%u Faild\n", _host.c_str(), _port);
|
||||
DEBUG_WEBSOCKETS("[WS-Client] connection to %s:%u Failed\n", _host.c_str(), _port);
|
||||
}
|
||||
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
|
||||
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
|
||||
|
||||
void WebSocketsClient::asyncConnect() {
|
||||
|
||||
DEBUG_WEBSOCKETS("[WS-Client] asyncConnect...\n");
|
||||
|
||||
AsyncClient * tcpclient = new AsyncClient();
|
||||
@ -597,12 +841,12 @@ void WebSocketsClient::asyncConnect() {
|
||||
return;
|
||||
}
|
||||
|
||||
tcpclient->onDisconnect([](void *obj, AsyncClient* c) {
|
||||
tcpclient->onDisconnect([](void * obj, AsyncClient * c) {
|
||||
c->free();
|
||||
delete c;
|
||||
});
|
||||
|
||||
tcpclient->onConnect(std::bind([](WebSocketsClient * ws , AsyncClient * tcp) {
|
||||
tcpclient->onConnect(std::bind([](WebSocketsClient * ws, AsyncClient * tcp) {
|
||||
ws->_client.tcp = new AsyncTCPbuffer(tcp);
|
||||
if(!ws->_client.tcp) {
|
||||
DEBUG_WEBSOCKETS("[WS-Client] creating Network class failed!\n");
|
||||
@ -610,20 +854,54 @@ void WebSocketsClient::asyncConnect() {
|
||||
return;
|
||||
}
|
||||
ws->connectedCb();
|
||||
}, this, std::placeholders::_2));
|
||||
},
|
||||
this, std::placeholders::_2));
|
||||
|
||||
tcpclient->onError(std::bind([](WebSocketsClient * ws , AsyncClient * tcp) {
|
||||
tcpclient->onError(std::bind([](WebSocketsClient * ws, AsyncClient * tcp) {
|
||||
ws->connectFailedCb();
|
||||
|
||||
// reconnect
|
||||
ws->asyncConnect();
|
||||
}, this, std::placeholders::_2));
|
||||
},
|
||||
this, std::placeholders::_2));
|
||||
|
||||
if(!tcpclient->connect(_host.c_str(), _port)) {
|
||||
connectFailedCb();
|
||||
delete tcpclient;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* send heartbeat ping to server in set intervals
|
||||
*/
|
||||
void WebSocketsClient::handleHBPing() {
|
||||
if(_client.pingInterval == 0)
|
||||
return;
|
||||
uint32_t pi = millis() - _client.lastPing;
|
||||
if(pi > _client.pingInterval) {
|
||||
DEBUG_WEBSOCKETS("[WS-Client] sending HB ping\n");
|
||||
if(sendPing()) {
|
||||
_client.lastPing = millis();
|
||||
_client.pongReceived = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* enable ping/pong heartbeat process
|
||||
* @param pingInterval uint32_t how often ping will be sent
|
||||
* @param pongTimeout uint32_t millis after which pong should timout if not received
|
||||
* @param disconnectTimeoutCount uint8_t how many timeouts before disconnect, 0=> do not disconnect
|
||||
*/
|
||||
void WebSocketsClient::enableHeartbeat(uint32_t pingInterval, uint32_t pongTimeout, uint8_t disconnectTimeoutCount) {
|
||||
WebSockets::enableHeartbeat(&_client, pingInterval, pongTimeout, disconnectTimeoutCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* disable ping/pong heartbeat process
|
||||
*/
|
||||
void WebSocketsClient::disableHeartbeat() {
|
||||
_client.pingInterval = 0;
|
||||
}
|
||||
|
@ -25,94 +25,135 @@
|
||||
#ifndef WEBSOCKETSCLIENT_H_
|
||||
#define WEBSOCKETSCLIENT_H_
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "WebSockets.h"
|
||||
|
||||
class WebSocketsClient: private WebSockets {
|
||||
public:
|
||||
class WebSocketsClient : protected WebSockets {
|
||||
public:
|
||||
#ifdef __AVR__
|
||||
typedef void (*WebSocketClientEvent)(WStype_t type, uint8_t * payload, size_t length);
|
||||
typedef void (*WebSocketClientEvent)(WStype_t type, uint8_t * payload, size_t length);
|
||||
#else
|
||||
typedef std::function<void (WStype_t type, uint8_t * payload, size_t length)> WebSocketClientEvent;
|
||||
typedef std::function<void(WStype_t type, uint8_t * payload, size_t length)> WebSocketClientEvent;
|
||||
#endif
|
||||
|
||||
WebSocketsClient(void);
|
||||
virtual ~WebSocketsClient(void);
|
||||
|
||||
WebSocketsClient(void);
|
||||
~WebSocketsClient(void);
|
||||
void begin(const char * host, uint16_t port, const char * url = "/", const char * protocol = "arduino");
|
||||
void begin(String host, uint16_t port, String url = "/", String protocol = "arduino");
|
||||
void begin(IPAddress host, uint16_t port, const char * url = "/", const char * protocol = "arduino");
|
||||
|
||||
void begin(const char *host, uint16_t port, const char * url = "/", const char * protocol = "arduino");
|
||||
void begin(String host, uint16_t port, String url = "/", String protocol = "arduino");
|
||||
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
||||
void beginSSL(const char *host, uint16_t port, const char * url = "/", const char * = "", const char * protocol = "arduino");
|
||||
void beginSSL(String host, uint16_t port, String url = "/", String fingerprint = "", String protocol = "arduino");
|
||||
#endif
|
||||
|
||||
#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
|
||||
void loop(void);
|
||||
#if defined(HAS_SSL)
|
||||
#ifdef SSL_AXTLS
|
||||
void beginSSL(const char * host, uint16_t port, const char * url = "/", const char * fingerprint = "", const char * protocol = "arduino");
|
||||
void beginSSL(String host, uint16_t port, String url = "/", String fingerprint = "", String protocol = "arduino");
|
||||
#else
|
||||
// Async interface not need a loop call
|
||||
void loop(void) __attribute__ ((deprecated)) {}
|
||||
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");
|
||||
#endif
|
||||
void beginSslWithCA(const char * host, uint16_t port, const char * url = "/", const char * CA_cert = NULL, const char * protocol = "arduino");
|
||||
#endif
|
||||
|
||||
void onEvent(WebSocketClientEvent cbEvent);
|
||||
void beginSocketIO(const char * host, uint16_t port, const char * url = "/socket.io/?EIO=3", const char * protocol = "arduino");
|
||||
void beginSocketIO(String host, uint16_t port, String url = "/socket.io/?EIO=3", String protocol = "arduino");
|
||||
|
||||
bool sendTXT(uint8_t * payload, size_t length = 0, bool headerToPayload = false);
|
||||
bool sendTXT(const uint8_t * payload, size_t length = 0);
|
||||
bool sendTXT(char * payload, size_t length = 0, bool headerToPayload = false);
|
||||
bool sendTXT(const char * payload, size_t length = 0);
|
||||
bool sendTXT(String & payload);
|
||||
|
||||
bool sendBIN(uint8_t * payload, size_t length, bool headerToPayload = false);
|
||||
bool sendBIN(const uint8_t * payload, size_t length);
|
||||
|
||||
void disconnect(void);
|
||||
|
||||
void setAuthorization(const char * user, const char * password);
|
||||
void setAuthorization(const char * auth);
|
||||
|
||||
protected:
|
||||
String _host;
|
||||
uint16_t _port;
|
||||
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
||||
String _fingerprint;
|
||||
#endif
|
||||
WSclient_t _client;
|
||||
|
||||
WebSocketClientEvent _cbEvent;
|
||||
|
||||
void messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length);
|
||||
|
||||
void clientDisconnect(WSclient_t * client);
|
||||
bool clientIsConnected(WSclient_t * client);
|
||||
|
||||
#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
|
||||
void handleClientData(void);
|
||||
#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");
|
||||
#endif
|
||||
|
||||
void sendHeader(WSclient_t * client);
|
||||
void handleHeader(WSclient_t * client, String * headerLine);
|
||||
|
||||
void connectedCb();
|
||||
void connectFailedCb();
|
||||
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
|
||||
void asyncConnect();
|
||||
#if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
|
||||
void loop(void);
|
||||
#else
|
||||
// Async interface not need a loop call
|
||||
void loop(void) __attribute__((deprecated)) {}
|
||||
#endif
|
||||
|
||||
/**
|
||||
void onEvent(WebSocketClientEvent cbEvent);
|
||||
|
||||
bool sendTXT(uint8_t * payload, size_t length = 0, bool headerToPayload = false);
|
||||
bool sendTXT(const uint8_t * payload, size_t length = 0);
|
||||
bool sendTXT(char * payload, size_t length = 0, bool headerToPayload = false);
|
||||
bool sendTXT(const char * payload, size_t length = 0);
|
||||
bool sendTXT(String & payload);
|
||||
bool sendTXT(char payload);
|
||||
|
||||
bool sendBIN(uint8_t * payload, size_t length, bool headerToPayload = false);
|
||||
bool sendBIN(const uint8_t * payload, size_t length);
|
||||
|
||||
bool sendPing(uint8_t * payload = NULL, size_t length = 0);
|
||||
bool sendPing(String & payload);
|
||||
|
||||
void disconnect(void);
|
||||
|
||||
void setAuthorization(const char * user, const char * password);
|
||||
void setAuthorization(const char * auth);
|
||||
|
||||
void setExtraHeaders(const char * extraHeaders = NULL);
|
||||
|
||||
void setReconnectInterval(unsigned long time);
|
||||
|
||||
void enableHeartbeat(uint32_t pingInterval, uint32_t pongTimeout, uint8_t disconnectTimeoutCount);
|
||||
void disableHeartbeat();
|
||||
|
||||
bool isConnected(void);
|
||||
|
||||
protected:
|
||||
String _host;
|
||||
uint16_t _port;
|
||||
|
||||
#if defined(HAS_SSL)
|
||||
#ifdef SSL_AXTLS
|
||||
String _fingerprint;
|
||||
const char * _CA_cert;
|
||||
#define SSL_FINGERPRINT_NULL ""
|
||||
#else
|
||||
const uint8_t * _fingerprint;
|
||||
BearSSL::X509List * _CA_cert;
|
||||
#define SSL_FINGERPRINT_NULL NULL
|
||||
#endif
|
||||
|
||||
#endif
|
||||
WSclient_t _client;
|
||||
|
||||
WebSocketClientEvent _cbEvent;
|
||||
|
||||
unsigned long _lastConnectionFail;
|
||||
unsigned long _reconnectInterval;
|
||||
unsigned long _lastHeaderSent;
|
||||
|
||||
void messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin);
|
||||
|
||||
void clientDisconnect(WSclient_t * client);
|
||||
bool clientIsConnected(WSclient_t * client);
|
||||
|
||||
#if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
|
||||
void handleClientData(void);
|
||||
#endif
|
||||
|
||||
void sendHeader(WSclient_t * client);
|
||||
void handleHeader(WSclient_t * client, String * headerLine);
|
||||
|
||||
void connectedCb();
|
||||
void connectFailedCb();
|
||||
|
||||
void handleHBPing(); // send ping in specified intervals
|
||||
|
||||
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
|
||||
void asyncConnect();
|
||||
#endif
|
||||
|
||||
/**
|
||||
* called for sending a Event to the app
|
||||
* @param type WStype_t
|
||||
* @param payload uint8_t *
|
||||
* @param length size_t
|
||||
*/
|
||||
virtual void runCbEvent(WStype_t type, uint8_t * payload, size_t length) {
|
||||
if(_cbEvent) {
|
||||
_cbEvent(type, payload, length);
|
||||
}
|
||||
virtual void runCbEvent(WStype_t type, uint8_t * payload, size_t length) {
|
||||
if(_cbEvent) {
|
||||
_cbEvent(type, payload, length);
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* WEBSOCKETSCLIENT_H_ */
|
||||
|
@ -26,37 +26,37 @@
|
||||
#include "WebSocketsServer.h"
|
||||
|
||||
WebSocketsServer::WebSocketsServer(uint16_t port, String origin, String protocol) {
|
||||
_port = port;
|
||||
_origin = origin;
|
||||
_protocol = protocol;
|
||||
_port = port;
|
||||
_origin = origin;
|
||||
_protocol = protocol;
|
||||
_runnning = false;
|
||||
_pingInterval = 0;
|
||||
_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);
|
||||
#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;
|
||||
_mandatoryHttpHeaders = NULL;
|
||||
_mandatoryHttpHeaderCount = 0;
|
||||
}
|
||||
|
||||
memset(&_clients[0], 0x00, (sizeof(WSclient_t) * WEBSOCKETS_SERVER_CLIENT_MAX));
|
||||
}
|
||||
|
||||
WebSocketsServer::~WebSocketsServer() {
|
||||
// disconnect all clients
|
||||
disconnect();
|
||||
close();
|
||||
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
||||
_server->close();
|
||||
#else
|
||||
// TODO how to close server?
|
||||
#endif
|
||||
|
||||
if (_mandatoryHttpHeaders)
|
||||
if(_mandatoryHttpHeaders)
|
||||
delete[] _mandatoryHttpHeaders;
|
||||
|
||||
_mandatoryHttpHeaderCount = 0;
|
||||
@ -72,49 +72,74 @@ void WebSocketsServer::begin(void) {
|
||||
for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
|
||||
client = &_clients[i];
|
||||
|
||||
client->num = i;
|
||||
client->num = i;
|
||||
client->status = WSC_NOT_CONNECTED;
|
||||
client->tcp = NULL;
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
||||
client->tcp = NULL;
|
||||
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
|
||||
client->isSSL = false;
|
||||
client->ssl = NULL;
|
||||
client->ssl = NULL;
|
||||
#endif
|
||||
client->cUrl = "";
|
||||
client->cCode = 0;
|
||||
client->cKey = "";
|
||||
client->cProtocol = "";
|
||||
client->cVersion = 0;
|
||||
client->cIsUpgrade = false;
|
||||
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)
|
||||
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
|
||||
client->cHttpLine = "";
|
||||
#endif
|
||||
|
||||
client->pingInterval = _pingInterval;
|
||||
client->pongTimeout = _pongTimeout;
|
||||
client->disconnectTimeoutCount = _disconnectTimeoutCount;
|
||||
}
|
||||
|
||||
#ifdef ESP8266
|
||||
randomSeed(RANDOM_REG32);
|
||||
#elif defined(ESP32)
|
||||
#define DR_REG_RNG_BASE 0x3ff75144
|
||||
randomSeed(READ_PERI_REG(DR_REG_RNG_BASE));
|
||||
#else
|
||||
// TODO find better seed
|
||||
randomSeed(millis());
|
||||
#endif
|
||||
|
||||
_runnning = true;
|
||||
_server->begin();
|
||||
|
||||
DEBUG_WEBSOCKETS("[WS-Server] Server Started.\n");
|
||||
}
|
||||
|
||||
#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
|
||||
void WebSocketsServer::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) {
|
||||
handleNewClients();
|
||||
handleClientData();
|
||||
if(_runnning) {
|
||||
WEBSOCKETS_YIELD();
|
||||
handleNewClients();
|
||||
WEBSOCKETS_YIELD();
|
||||
handleClientData();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -133,21 +158,20 @@ void WebSocketsServer::onEvent(WebSocketServerEvent cbEvent) {
|
||||
* @param mandatoryHttpHeaderCount size_t ///< the number of items in the mandatoryHttpHeaders array
|
||||
*/
|
||||
void WebSocketsServer::onValidateHttpHeader(
|
||||
WebSocketServerHttpHeaderValFunc validationFunc,
|
||||
const char* mandatoryHttpHeaders[],
|
||||
size_t mandatoryHttpHeaderCount)
|
||||
{
|
||||
_httpHeaderValidationFunc = validationFunc;
|
||||
WebSocketServerHttpHeaderValFunc validationFunc,
|
||||
const char * mandatoryHttpHeaders[],
|
||||
size_t mandatoryHttpHeaderCount) {
|
||||
_httpHeaderValidationFunc = validationFunc;
|
||||
|
||||
if (_mandatoryHttpHeaders)
|
||||
delete[] _mandatoryHttpHeaders;
|
||||
if(_mandatoryHttpHeaders)
|
||||
delete[] _mandatoryHttpHeaders;
|
||||
|
||||
_mandatoryHttpHeaderCount = mandatoryHttpHeaderCount;
|
||||
_mandatoryHttpHeaders = new String[_mandatoryHttpHeaderCount];
|
||||
_mandatoryHttpHeaderCount = mandatoryHttpHeaderCount;
|
||||
_mandatoryHttpHeaders = new String[_mandatoryHttpHeaderCount];
|
||||
|
||||
for (size_t i = 0; i < _mandatoryHttpHeaderCount; i++) {
|
||||
_mandatoryHttpHeaders[i] = mandatoryHttpHeaders[i];
|
||||
}
|
||||
for(size_t i = 0; i < _mandatoryHttpHeaderCount; i++) {
|
||||
_mandatoryHttpHeaders[i] = mandatoryHttpHeaders[i];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -163,29 +187,29 @@ bool WebSocketsServer::sendTXT(uint8_t num, uint8_t * payload, size_t length, bo
|
||||
return false;
|
||||
}
|
||||
if(length == 0) {
|
||||
length = strlen((const char *) payload);
|
||||
length = strlen((const char *)payload);
|
||||
}
|
||||
WSclient_t * client = &_clients[num];
|
||||
if(clientIsConnected(client)) {
|
||||
return sendFrame(client, WSop_text, payload, length, false, true, headerToPayload);
|
||||
return sendFrame(client, WSop_text, payload, length, true, headerToPayload);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WebSocketsServer::sendTXT(uint8_t num, const uint8_t * payload, size_t length) {
|
||||
return sendTXT(num, (uint8_t *) payload, length);
|
||||
return sendTXT(num, (uint8_t *)payload, length);
|
||||
}
|
||||
|
||||
bool WebSocketsServer::sendTXT(uint8_t num, char * payload, size_t length, bool headerToPayload) {
|
||||
return sendTXT(num, (uint8_t *) payload, length, headerToPayload);
|
||||
return sendTXT(num, (uint8_t *)payload, length, headerToPayload);
|
||||
}
|
||||
|
||||
bool WebSocketsServer::sendTXT(uint8_t num, const char * payload, size_t length) {
|
||||
return sendTXT(num, (uint8_t *) payload, length);
|
||||
return sendTXT(num, (uint8_t *)payload, length);
|
||||
}
|
||||
|
||||
bool WebSocketsServer::sendTXT(uint8_t num, String & payload) {
|
||||
return sendTXT(num, (uint8_t *) payload.c_str(), payload.length());
|
||||
return sendTXT(num, (uint8_t *)payload.c_str(), payload.length());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -199,37 +223,35 @@ bool WebSocketsServer::broadcastTXT(uint8_t * payload, size_t length, bool heade
|
||||
WSclient_t * client;
|
||||
bool ret = true;
|
||||
if(length == 0) {
|
||||
length = strlen((const char *) payload);
|
||||
length = strlen((const char *)payload);
|
||||
}
|
||||
|
||||
for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
|
||||
client = &_clients[i];
|
||||
if(clientIsConnected(client)) {
|
||||
if(!sendFrame(client, WSop_text, payload, length, false, true, headerToPayload)) {
|
||||
if(!sendFrame(client, WSop_text, payload, length, true, headerToPayload)) {
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
||||
delay(0);
|
||||
#endif
|
||||
WEBSOCKETS_YIELD();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool WebSocketsServer::broadcastTXT(const uint8_t * payload, size_t length) {
|
||||
return broadcastTXT((uint8_t *) payload, length);
|
||||
return broadcastTXT((uint8_t *)payload, length);
|
||||
}
|
||||
|
||||
bool WebSocketsServer::broadcastTXT(char * payload, size_t length, bool headerToPayload) {
|
||||
return broadcastTXT((uint8_t *) payload, length, headerToPayload);
|
||||
return broadcastTXT((uint8_t *)payload, length, headerToPayload);
|
||||
}
|
||||
|
||||
bool WebSocketsServer::broadcastTXT(const char * payload, size_t length) {
|
||||
return broadcastTXT((uint8_t *) payload, length);
|
||||
return broadcastTXT((uint8_t *)payload, length);
|
||||
}
|
||||
|
||||
bool WebSocketsServer::broadcastTXT(String & payload) {
|
||||
return broadcastTXT((uint8_t *) payload.c_str(), payload.length());
|
||||
return broadcastTXT((uint8_t *)payload.c_str(), payload.length());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -246,13 +268,13 @@ bool WebSocketsServer::sendBIN(uint8_t num, uint8_t * payload, size_t length, bo
|
||||
}
|
||||
WSclient_t * client = &_clients[num];
|
||||
if(clientIsConnected(client)) {
|
||||
return sendFrame(client, WSop_binary, payload, length, false, true, headerToPayload);
|
||||
return sendFrame(client, WSop_binary, payload, length, true, headerToPayload);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WebSocketsServer::sendBIN(uint8_t num, const uint8_t * payload, size_t length) {
|
||||
return sendBIN(num, (uint8_t *) payload, length);
|
||||
return sendBIN(num, (uint8_t *)payload, length);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -268,19 +290,64 @@ bool WebSocketsServer::broadcastBIN(uint8_t * payload, size_t length, bool heade
|
||||
for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
|
||||
client = &_clients[i];
|
||||
if(clientIsConnected(client)) {
|
||||
if(!sendFrame(client, WSop_binary, payload, length, false, true, headerToPayload)) {
|
||||
if(!sendFrame(client, WSop_binary, payload, length, true, headerToPayload)) {
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
||||
delay(0);
|
||||
#endif
|
||||
WEBSOCKETS_YIELD();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool WebSocketsServer::broadcastBIN(const uint8_t * payload, size_t length) {
|
||||
return broadcastBIN((uint8_t *) payload, length);
|
||||
return broadcastBIN((uint8_t *)payload, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* sends a WS ping to Client
|
||||
* @param num uint8_t client id
|
||||
* @param payload uint8_t *
|
||||
* @param length size_t
|
||||
* @return true if ping is send out
|
||||
*/
|
||||
bool WebSocketsServer::sendPing(uint8_t num, uint8_t * payload, size_t length) {
|
||||
if(num >= WEBSOCKETS_SERVER_CLIENT_MAX) {
|
||||
return false;
|
||||
}
|
||||
WSclient_t * client = &_clients[num];
|
||||
if(clientIsConnected(client)) {
|
||||
return sendFrame(client, WSop_ping, payload, length);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WebSocketsServer::sendPing(uint8_t num, String & payload) {
|
||||
return sendPing(num, (uint8_t *)payload.c_str(), payload.length());
|
||||
}
|
||||
|
||||
/**
|
||||
* sends a WS ping to all Client
|
||||
* @param payload uint8_t *
|
||||
* @param length size_t
|
||||
* @return true if ping is send out
|
||||
*/
|
||||
bool WebSocketsServer::broadcastPing(uint8_t * payload, size_t length) {
|
||||
WSclient_t * client;
|
||||
bool ret = true;
|
||||
for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
|
||||
client = &_clients[i];
|
||||
if(clientIsConnected(client)) {
|
||||
if(!sendFrame(client, WSop_ping, payload, length)) {
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
WEBSOCKETS_YIELD();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool WebSocketsServer::broadcastPing(String & payload) {
|
||||
return broadcastPing((uint8_t *)payload.c_str(), payload.length());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -310,7 +377,6 @@ void WebSocketsServer::disconnect(uint8_t num) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* set the Authorization for the http request
|
||||
* @param user const char *
|
||||
@ -335,7 +401,37 @@ void WebSocketsServer::setAuthorization(const char * auth) {
|
||||
}
|
||||
}
|
||||
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
|
||||
/**
|
||||
* count the connected clients (optional ping them)
|
||||
* @param ping bool ping the connected clients
|
||||
*/
|
||||
int WebSocketsServer::connectedClients(bool ping) {
|
||||
WSclient_t * client;
|
||||
int count = 0;
|
||||
for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
|
||||
client = &_clients[i];
|
||||
if(client->status == WSC_CONNECTED) {
|
||||
if(ping != true || sendPing(i)) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* see if one client is connected
|
||||
* @param num uint8_t client id
|
||||
*/
|
||||
bool WebSocketsServer::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
|
||||
@ -369,42 +465,48 @@ bool WebSocketsServer::newClient(WEBSOCKETS_NETWORK_CLASS * TCPclient) {
|
||||
|
||||
// state is not connected or tcp connection is lost
|
||||
if(!clientIsConnected(client)) {
|
||||
|
||||
client->tcp = TCPclient;
|
||||
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
||||
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
|
||||
client->isSSL = false;
|
||||
client->tcp->setNoDelay(true);
|
||||
#endif
|
||||
#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
|
||||
#if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
|
||||
// set Timeout for readBytesUntil and readStringUntil
|
||||
client->tcp->setTimeout(WEBSOCKETS_TCP_TIMEOUT);
|
||||
#endif
|
||||
client->status = WSC_HEADER;
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
|
||||
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
|
||||
#ifndef NODEBUG_WEBSOCKETS
|
||||
IPAddress ip = client->tcp->remoteIP();
|
||||
#endif
|
||||
DEBUG_WEBSOCKETS("[WS-Server][%d] new client from %d.%d.%d.%d\n", client->num, ip[0], ip[1], ip[2], ip[3]);
|
||||
#else
|
||||
DEBUG_WEBSOCKETS("[WS-Server][%d] new client\n", client->num);
|
||||
#endif
|
||||
|
||||
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
|
||||
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
|
||||
client->tcp->onDisconnect(std::bind([](WebSocketsServer * server, AsyncTCPbuffer * obj, WSclient_t * client) -> bool {
|
||||
DEBUG_WEBSOCKETS("[WS-Server][%d] Disconnect client\n", client->num);
|
||||
|
||||
AsyncTCPbuffer ** sl = &server->_clients[client->num].tcp;
|
||||
if(*sl == obj) {
|
||||
client->status = WSC_NOT_CONNECTED;
|
||||
*sl = NULL;
|
||||
*sl = NULL;
|
||||
}
|
||||
return true;
|
||||
}, this, std::placeholders::_1, client));
|
||||
|
||||
},
|
||||
this, std::placeholders::_1, client));
|
||||
|
||||
client->tcp->readStringUntil('\n', &(client->cHttpLine), std::bind(&WebSocketsServer::handleHeader, this, client, &(client->cHttpLine)));
|
||||
#endif
|
||||
|
||||
client->pingInterval = _pingInterval;
|
||||
client->pongTimeout = _pongTimeout;
|
||||
client->disconnectTimeoutCount = _disconnectTimeoutCount;
|
||||
client->lastPing = millis();
|
||||
client->pongReceived = false;
|
||||
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
@ -417,22 +519,33 @@ bool WebSocketsServer::newClient(WEBSOCKETS_NETWORK_CLASS * TCPclient) {
|
||||
* @param client WSclient_t * ptr to the client struct
|
||||
* @param opcode WSopcode_t
|
||||
* @param payload uint8_t *
|
||||
* @param lenght size_t
|
||||
* @param length size_t
|
||||
*/
|
||||
void WebSocketsServer::messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t lenght) {
|
||||
void WebSocketsServer::messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin) {
|
||||
WStype_t type = WStype_ERROR;
|
||||
|
||||
switch(opcode) {
|
||||
case WSop_text:
|
||||
type = WStype_TEXT;
|
||||
type = fin ? WStype_TEXT : WStype_FRAGMENT_TEXT_START;
|
||||
break;
|
||||
case WSop_binary:
|
||||
type = WStype_BIN;
|
||||
type = fin ? WStype_BIN : WStype_FRAGMENT_BIN_START;
|
||||
break;
|
||||
case WSop_continuation:
|
||||
type = fin ? WStype_FRAGMENT_FIN : WStype_FRAGMENT;
|
||||
break;
|
||||
case WSop_ping:
|
||||
type = WStype_PING;
|
||||
break;
|
||||
case WSop_pong:
|
||||
type = WStype_PONG;
|
||||
break;
|
||||
case WSop_close:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
runCbEvent(client->num, type, payload, lenght);
|
||||
|
||||
runCbEvent(client->num, type, payload, length);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -440,9 +553,7 @@ void WebSocketsServer::messageReceived(WSclient_t * client, WSopcode_t opcode, u
|
||||
* @param client WSclient_t * ptr to the client struct
|
||||
*/
|
||||
void WebSocketsServer::clientDisconnect(WSclient_t * client) {
|
||||
|
||||
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
||||
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
|
||||
if(client->isSSL && client->ssl) {
|
||||
if(client->ssl->connected()) {
|
||||
client->ssl->flush();
|
||||
@ -456,12 +567,12 @@ void WebSocketsServer::clientDisconnect(WSclient_t * client) {
|
||||
|
||||
if(client->tcp) {
|
||||
if(client->tcp->connected()) {
|
||||
#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
|
||||
#if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
|
||||
client->tcp->flush();
|
||||
#endif
|
||||
client->tcp->stop();
|
||||
}
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
|
||||
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
|
||||
client->status = WSC_NOT_CONNECTED;
|
||||
#else
|
||||
delete client->tcp;
|
||||
@ -469,16 +580,16 @@ void WebSocketsServer::clientDisconnect(WSclient_t * client) {
|
||||
client->tcp = NULL;
|
||||
}
|
||||
|
||||
client->cUrl = "";
|
||||
client->cKey = "";
|
||||
client->cProtocol = "";
|
||||
client->cVersion = 0;
|
||||
client->cIsUpgrade = false;
|
||||
client->cUrl = "";
|
||||
client->cKey = "";
|
||||
client->cProtocol = "";
|
||||
client->cVersion = 0;
|
||||
client->cIsUpgrade = false;
|
||||
client->cIsWebsocket = false;
|
||||
|
||||
client->cWsRXsize = 0;
|
||||
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
|
||||
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
|
||||
client->cHttpLine = "";
|
||||
#endif
|
||||
|
||||
@ -487,7 +598,6 @@ void WebSocketsServer::clientDisconnect(WSclient_t * client) {
|
||||
DEBUG_WEBSOCKETS("[WS-Server][%d] client disconnected.\n", client->num);
|
||||
|
||||
runCbEvent(client->num, WStype_DISCONNECTED, NULL, 0);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -496,7 +606,6 @@ void WebSocketsServer::clientDisconnect(WSclient_t * client) {
|
||||
* @return true = connected
|
||||
*/
|
||||
bool WebSocketsServer::clientIsConnected(WSclient_t * client) {
|
||||
|
||||
if(!client->tcp) {
|
||||
return false;
|
||||
}
|
||||
@ -522,22 +631,21 @@ bool WebSocketsServer::clientIsConnected(WSclient_t * client) {
|
||||
|
||||
return false;
|
||||
}
|
||||
#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
|
||||
#if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
|
||||
/**
|
||||
* Handle incoming Connection Request
|
||||
*/
|
||||
void WebSocketsServer::handleNewClients(void) {
|
||||
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
||||
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
|
||||
while(_server->hasClient()) {
|
||||
#endif
|
||||
bool ok = false;
|
||||
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
||||
#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());
|
||||
WEBSOCKETS_NETWORK_CLASS * tcpClient = new WEBSOCKETS_NETWORK_CLASS(_server->available());
|
||||
#endif
|
||||
|
||||
if(!tcpClient) {
|
||||
@ -549,28 +657,27 @@ void WebSocketsServer::handleNewClients(void) {
|
||||
|
||||
if(!ok) {
|
||||
// no free space to handle client
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
||||
#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");
|
||||
DEBUG_WEBSOCKETS("[WS-Server] no free space new client\n");
|
||||
#endif
|
||||
tcpClient->stop();
|
||||
}
|
||||
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
||||
delay(0);
|
||||
WEBSOCKETS_YIELD();
|
||||
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handel incomming data from Client
|
||||
*/
|
||||
void WebSocketsServer::handleClientData(void) {
|
||||
|
||||
WSclient_t * client;
|
||||
for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
|
||||
client = &_clients[i];
|
||||
@ -579,12 +686,10 @@ void WebSocketsServer::handleClientData(void) {
|
||||
if(len > 0) {
|
||||
//DEBUG_WEBSOCKETS("[WS-Server][%d][handleClientData] len: %d\n", client->num, len);
|
||||
switch(client->status) {
|
||||
case WSC_HEADER:
|
||||
{
|
||||
case WSC_HEADER: {
|
||||
String headerLine = client->tcp->readStringUntil('\n');
|
||||
handleHeader(client, &headerLine);
|
||||
}
|
||||
break;
|
||||
} break;
|
||||
case WSC_CONNECTED:
|
||||
WebSockets::handleWebsocket(client);
|
||||
break;
|
||||
@ -593,10 +698,11 @@ void WebSocketsServer::handleClientData(void) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
handleHBPing(client);
|
||||
handleHBTimeout(client);
|
||||
}
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
||||
delay(0);
|
||||
#endif
|
||||
WEBSOCKETS_YIELD();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -606,11 +712,11 @@ void WebSocketsServer::handleClientData(void) {
|
||||
* @param headerName String ///< the name of the header being checked
|
||||
*/
|
||||
bool WebSocketsServer::hasMandatoryHeader(String headerName) {
|
||||
for (size_t i = 0; i < _mandatoryHttpHeaderCount; i++) {
|
||||
if (_mandatoryHttpHeaders[i].equalsIgnoreCase(headerName))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
for(size_t i = 0; i < _mandatoryHttpHeaderCount; i++) {
|
||||
if(_mandatoryHttpHeaders[i].equalsIgnoreCase(headerName))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -619,51 +725,56 @@ bool WebSocketsServer::hasMandatoryHeader(String headerName) {
|
||||
* @param headerLine String ///< the header being read / processed
|
||||
*/
|
||||
void WebSocketsServer::handleHeader(WSclient_t * client, String * headerLine) {
|
||||
static const char * NEW_LINE = "\r\n";
|
||||
|
||||
headerLine->trim(); // remove \r
|
||||
headerLine->trim(); // remove \r
|
||||
|
||||
if(headerLine->length() > 0) {
|
||||
DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] RX: %s\n", client->num, headerLine->c_str());
|
||||
|
||||
// websocket requests always start with GET see rfc6455
|
||||
if(headerLine->startsWith("GET ")) {
|
||||
|
||||
// cut URL out
|
||||
client->cUrl = headerLine->substring(4, headerLine->indexOf(' ', 4));
|
||||
|
||||
//reset non-websocket http header validation state for this client
|
||||
client->cHttpHeadersValid = true;
|
||||
client->cMandatoryHeadersCount = 0;
|
||||
client->cHttpHeadersValid = true;
|
||||
client->cMandatoryHeadersCount = 0;
|
||||
|
||||
} else if(headerLine->indexOf(':')) {
|
||||
String headerName = headerLine->substring(0, headerLine->indexOf(':'));
|
||||
String headerValue = headerLine->substring(headerLine->indexOf(':') + 2);
|
||||
} else if(headerLine->indexOf(':') >= 0) {
|
||||
String headerName = headerLine->substring(0, headerLine->indexOf(':'));
|
||||
String headerValue = headerLine->substring(headerLine->indexOf(':') + 1);
|
||||
|
||||
if(headerName.equalsIgnoreCase("Connection")) {
|
||||
// remove space in the beginning (RFC2616)
|
||||
if(headerValue[0] == ' ') {
|
||||
headerValue.remove(0, 1);
|
||||
}
|
||||
|
||||
if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Connection"))) {
|
||||
headerValue.toLowerCase();
|
||||
if(headerValue.indexOf("upgrade") >= 0) {
|
||||
if(headerValue.indexOf(WEBSOCKETS_STRING("upgrade")) >= 0) {
|
||||
client->cIsUpgrade = true;
|
||||
}
|
||||
} else if(headerName.equalsIgnoreCase("Upgrade")) {
|
||||
if(headerValue.equalsIgnoreCase("websocket")) {
|
||||
} else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Upgrade"))) {
|
||||
if(headerValue.equalsIgnoreCase(WEBSOCKETS_STRING("websocket"))) {
|
||||
client->cIsWebsocket = true;
|
||||
}
|
||||
} else if(headerName.equalsIgnoreCase("Sec-WebSocket-Version")) {
|
||||
} else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Version"))) {
|
||||
client->cVersion = headerValue.toInt();
|
||||
} else if(headerName.equalsIgnoreCase("Sec-WebSocket-Key")) {
|
||||
} else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Key"))) {
|
||||
client->cKey = headerValue;
|
||||
client->cKey.trim(); // see rfc6455
|
||||
} else if(headerName.equalsIgnoreCase("Sec-WebSocket-Protocol")) {
|
||||
client->cKey.trim(); // see rfc6455
|
||||
} else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Protocol"))) {
|
||||
client->cProtocol = headerValue;
|
||||
} else if(headerName.equalsIgnoreCase("Sec-WebSocket-Extensions")) {
|
||||
} else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Extensions"))) {
|
||||
client->cExtensions = headerValue;
|
||||
} else if(headerName.equalsIgnoreCase("Authorization")) {
|
||||
} else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Authorization"))) {
|
||||
client->base64Authorization = headerValue;
|
||||
} else {
|
||||
client->cHttpHeadersValid &= execHttpHeaderValidation(headerName, headerValue);
|
||||
if (_mandatoryHttpHeaderCount > 0 && hasMandatoryHeader(headerName)) {
|
||||
client->cMandatoryHeadersCount++;
|
||||
}
|
||||
client->cHttpHeadersValid &= execHttpHeaderValidation(headerName, headerValue);
|
||||
if(_mandatoryHttpHeaderCount > 0 && hasMandatoryHeader(headerName)) {
|
||||
client->cMandatoryHeadersCount++;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
@ -671,11 +782,10 @@ void WebSocketsServer::handleHeader(WSclient_t * client, String * headerLine) {
|
||||
}
|
||||
|
||||
(*headerLine) = "";
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
|
||||
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
|
||||
client->tcp->readStringUntil('\n', &(client->cHttpLine), std::bind(&WebSocketsServer::handleHeader, this, client, &(client->cHttpLine)));
|
||||
#endif
|
||||
} else {
|
||||
|
||||
DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] Header read fin.\n", client->num);
|
||||
DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cURL: %s\n", client->num, client->cUrl.c_str());
|
||||
DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cIsUpgrade: %d\n", client->num, client->cIsUpgrade);
|
||||
@ -701,29 +811,24 @@ void WebSocketsServer::handleHeader(WSclient_t * client, String * headerLine) {
|
||||
ok = false;
|
||||
}
|
||||
if(!client->cHttpHeadersValid) {
|
||||
ok = false;
|
||||
ok = false;
|
||||
}
|
||||
if (client->cMandatoryHeadersCount != _mandatoryHttpHeaderCount) {
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
|
||||
if(_base64Authorization.length() > 0) {
|
||||
if(client->base64Authorization.length() > 0) {
|
||||
String auth = "Basic ";
|
||||
auth += _base64Authorization;
|
||||
if(auth != client->base64Authorization) {
|
||||
DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] HTTP Authorization failed!\n", client->num);
|
||||
handleAuthorizationFailed(client);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if(client->cMandatoryHeadersCount != _mandatoryHttpHeaderCount) {
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
|
||||
if(ok) {
|
||||
if(_base64Authorization.length() > 0) {
|
||||
String auth = WEBSOCKETS_STRING("Basic ");
|
||||
auth += _base64Authorization;
|
||||
if(auth != client->base64Authorization) {
|
||||
DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] HTTP Authorization failed!\n", client->num);
|
||||
handleAuthorizationFailed(client);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(ok) {
|
||||
DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] Websocket connection incoming.\n", client->num);
|
||||
|
||||
// generate Sec-WebSocket-Accept key
|
||||
@ -733,39 +838,38 @@ void WebSocketsServer::handleHeader(WSclient_t * client, String * headerLine) {
|
||||
|
||||
client->status = WSC_CONNECTED;
|
||||
|
||||
client->tcp->write("HTTP/1.1 101 Switching Protocols\r\n"
|
||||
"Server: arduino-WebSocketsServer\r\n"
|
||||
"Upgrade: websocket\r\n"
|
||||
"Connection: Upgrade\r\n"
|
||||
"Sec-WebSocket-Version: 13\r\n"
|
||||
"Sec-WebSocket-Accept: ");
|
||||
client->tcp->write(sKey.c_str(), sKey.length());
|
||||
String handshake = WEBSOCKETS_STRING(
|
||||
"HTTP/1.1 101 Switching Protocols\r\n"
|
||||
"Server: arduino-WebSocketsServer\r\n"
|
||||
"Upgrade: websocket\r\n"
|
||||
"Connection: Upgrade\r\n"
|
||||
"Sec-WebSocket-Version: 13\r\n"
|
||||
"Sec-WebSocket-Accept: ");
|
||||
handshake += sKey + NEW_LINE;
|
||||
|
||||
if(_origin.length() > 0) {
|
||||
String origin = "\r\nAccess-Control-Allow-Origin: ";
|
||||
origin += _origin;
|
||||
origin += "\r\n";
|
||||
client->tcp->write(origin.c_str(), origin.length());
|
||||
handshake += WEBSOCKETS_STRING("Access-Control-Allow-Origin: ");
|
||||
handshake += _origin + NEW_LINE;
|
||||
}
|
||||
|
||||
if(client->cProtocol.length() > 0) {
|
||||
String protocol = "\r\nSec-WebSocket-Protocol: ";
|
||||
protocol += _protocol;
|
||||
protocol += "\r\n";
|
||||
client->tcp->write(protocol.c_str(), protocol.length());
|
||||
} else {
|
||||
client->tcp->write("\r\n");
|
||||
handshake += WEBSOCKETS_STRING("Sec-WebSocket-Protocol: ");
|
||||
handshake += _protocol + NEW_LINE;
|
||||
}
|
||||
|
||||
// header end
|
||||
client->tcp->write("\r\n");
|
||||
handshake += NEW_LINE;
|
||||
|
||||
DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] handshake %s", client->num, (uint8_t *)handshake.c_str());
|
||||
|
||||
write(client, (uint8_t *)handshake.c_str(), handshake.length());
|
||||
|
||||
headerDone(client);
|
||||
|
||||
// send ping
|
||||
WebSockets::sendFrame(client, WSop_ping);
|
||||
|
||||
runCbEvent(client->num, WStype_CONNECTED, (uint8_t *) client->cUrl.c_str(), client->cUrl.length());
|
||||
runCbEvent(client->num, WStype_CONNECTED, (uint8_t *)client->cUrl.c_str(), client->cUrl.length());
|
||||
|
||||
} else {
|
||||
handleNonWebsocketConnection(client);
|
||||
@ -773,5 +877,49 @@ void WebSocketsServer::handleHeader(WSclient_t * client, String * headerLine) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* send heartbeat ping to server in set intervals
|
||||
*/
|
||||
void WebSocketsServer::handleHBPing(WSclient_t * client) {
|
||||
if(client->pingInterval == 0)
|
||||
return;
|
||||
uint32_t pi = millis() - client->lastPing;
|
||||
if(pi > client->pingInterval) {
|
||||
DEBUG_WEBSOCKETS("[WS-Server][%d] sending HB ping\n", client->num);
|
||||
if(sendPing(client->num)) {
|
||||
client->lastPing = millis();
|
||||
client->pongReceived = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* enable ping/pong heartbeat process
|
||||
* @param pingInterval uint32_t how often ping will be sent
|
||||
* @param pongTimeout uint32_t millis after which pong should timout if not received
|
||||
* @param disconnectTimeoutCount uint8_t how many timeouts before disconnect, 0=> do not disconnect
|
||||
*/
|
||||
void WebSocketsServer::enableHeartbeat(uint32_t pingInterval, uint32_t pongTimeout, uint8_t disconnectTimeoutCount) {
|
||||
_pingInterval = pingInterval;
|
||||
_pongTimeout = pongTimeout;
|
||||
_disconnectTimeoutCount = disconnectTimeoutCount;
|
||||
|
||||
WSclient_t * client;
|
||||
for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
|
||||
client = &_clients[i];
|
||||
WebSockets::enableHeartbeat(client, pingInterval, pongTimeout, disconnectTimeoutCount);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* disable ping/pong heartbeat process
|
||||
*/
|
||||
void WebSocketsServer::disableHeartbeat() {
|
||||
_pingInterval = 0;
|
||||
|
||||
WSclient_t * client;
|
||||
for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
|
||||
client = &_clients[i];
|
||||
client->pingInterval = 0;
|
||||
}
|
||||
}
|
@ -25,152 +25,171 @@
|
||||
#ifndef WEBSOCKETSSERVER_H_
|
||||
#define WEBSOCKETSSERVER_H_
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "WebSockets.h"
|
||||
|
||||
#define WEBSOCKETS_SERVER_CLIENT_MAX (5)
|
||||
|
||||
|
||||
|
||||
|
||||
class WebSocketsServer: private WebSockets {
|
||||
public:
|
||||
#ifndef WEBSOCKETS_SERVER_CLIENT_MAX
|
||||
#define WEBSOCKETS_SERVER_CLIENT_MAX (5)
|
||||
#endif
|
||||
|
||||
class WebSocketsServer : protected WebSockets {
|
||||
public:
|
||||
#ifdef __AVR__
|
||||
typedef void (*WebSocketServerEvent)(uint8_t num, WStype_t type, uint8_t * payload, size_t length);
|
||||
typedef bool (*WebSocketServerHttpHeaderValFunc)(String headerName, String headerValue);
|
||||
typedef void (*WebSocketServerEvent)(uint8_t num, WStype_t type, uint8_t * payload, size_t length);
|
||||
typedef bool (*WebSocketServerHttpHeaderValFunc)(String headerName, String headerValue);
|
||||
#else
|
||||
typedef std::function<void (uint8_t num, WStype_t type, uint8_t * payload, size_t length)> WebSocketServerEvent;
|
||||
typedef std::function<bool (String headerName, String headerValue)> WebSocketServerHttpHeaderValFunc;
|
||||
typedef std::function<void(uint8_t num, WStype_t type, uint8_t * payload, size_t length)> WebSocketServerEvent;
|
||||
typedef std::function<bool(String headerName, String headerValue)> WebSocketServerHttpHeaderValFunc;
|
||||
#endif
|
||||
|
||||
WebSocketsServer(uint16_t port, String origin = "", String protocol = "arduino");
|
||||
~WebSocketsServer(void);
|
||||
WebSocketsServer(uint16_t port, String origin = "", String protocol = "arduino");
|
||||
virtual ~WebSocketsServer(void);
|
||||
|
||||
void begin(void);
|
||||
void begin(void);
|
||||
void close(void);
|
||||
|
||||
#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
|
||||
void loop(void);
|
||||
#if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
|
||||
void loop(void);
|
||||
#else
|
||||
// Async interface not need a loop call
|
||||
void loop(void) __attribute__ ((deprecated)) {}
|
||||
// Async interface not need a loop call
|
||||
void loop(void) __attribute__((deprecated)) {}
|
||||
#endif
|
||||
|
||||
void onEvent(WebSocketServerEvent cbEvent);
|
||||
void onValidateHttpHeader(
|
||||
WebSocketServerHttpHeaderValFunc validationFunc,
|
||||
const char* mandatoryHttpHeaders[],
|
||||
size_t mandatoryHttpHeaderCount);
|
||||
void onEvent(WebSocketServerEvent cbEvent);
|
||||
void onValidateHttpHeader(
|
||||
WebSocketServerHttpHeaderValFunc validationFunc,
|
||||
const char * mandatoryHttpHeaders[],
|
||||
size_t mandatoryHttpHeaderCount);
|
||||
|
||||
bool sendTXT(uint8_t num, uint8_t * payload, size_t length = 0, bool headerToPayload = false);
|
||||
bool sendTXT(uint8_t num, const uint8_t * payload, size_t length = 0);
|
||||
bool sendTXT(uint8_t num, char * payload, size_t length = 0, bool headerToPayload = false);
|
||||
bool sendTXT(uint8_t num, const char * payload, size_t length = 0);
|
||||
bool sendTXT(uint8_t num, String & payload);
|
||||
|
||||
bool sendTXT(uint8_t num, uint8_t * payload, size_t length = 0, bool headerToPayload = false);
|
||||
bool sendTXT(uint8_t num, const uint8_t * payload, size_t length = 0);
|
||||
bool sendTXT(uint8_t num, char * payload, size_t length = 0, bool headerToPayload = false);
|
||||
bool sendTXT(uint8_t num, const char * payload, size_t length = 0);
|
||||
bool sendTXT(uint8_t num, String & payload);
|
||||
bool broadcastTXT(uint8_t * payload, size_t length = 0, bool headerToPayload = false);
|
||||
bool broadcastTXT(const uint8_t * payload, size_t length = 0);
|
||||
bool broadcastTXT(char * payload, size_t length = 0, bool headerToPayload = false);
|
||||
bool broadcastTXT(const char * payload, size_t length = 0);
|
||||
bool broadcastTXT(String & payload);
|
||||
|
||||
bool broadcastTXT(uint8_t * payload, size_t length = 0, bool headerToPayload = false);
|
||||
bool broadcastTXT(const uint8_t * payload, size_t length = 0);
|
||||
bool broadcastTXT(char * payload, size_t length = 0, bool headerToPayload = false);
|
||||
bool broadcastTXT(const char * payload, size_t length = 0);
|
||||
bool broadcastTXT(String & payload);
|
||||
bool sendBIN(uint8_t num, uint8_t * payload, size_t length, bool headerToPayload = false);
|
||||
bool sendBIN(uint8_t num, const uint8_t * payload, size_t length);
|
||||
|
||||
bool sendBIN(uint8_t num, uint8_t * payload, size_t length, bool headerToPayload = false);
|
||||
bool sendBIN(uint8_t num, const uint8_t * payload, size_t length);
|
||||
bool broadcastBIN(uint8_t * payload, size_t length, bool headerToPayload = false);
|
||||
bool broadcastBIN(const uint8_t * payload, size_t length);
|
||||
|
||||
bool broadcastBIN(uint8_t * payload, size_t length, bool headerToPayload = false);
|
||||
bool broadcastBIN(const uint8_t * payload, size_t length);
|
||||
bool sendPing(uint8_t num, uint8_t * payload = NULL, size_t length = 0);
|
||||
bool sendPing(uint8_t num, String & payload);
|
||||
|
||||
void disconnect(void);
|
||||
void disconnect(uint8_t num);
|
||||
bool broadcastPing(uint8_t * payload = NULL, size_t length = 0);
|
||||
bool broadcastPing(String & payload);
|
||||
|
||||
void setAuthorization(const char * user, const char * password);
|
||||
void setAuthorization(const char * auth);
|
||||
void disconnect(void);
|
||||
void disconnect(uint8_t num);
|
||||
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
|
||||
IPAddress remoteIP(uint8_t num);
|
||||
void setAuthorization(const char * user, const char * password);
|
||||
void setAuthorization(const char * auth);
|
||||
|
||||
int connectedClients(bool ping = false);
|
||||
|
||||
bool clientIsConnected(uint8_t num);
|
||||
|
||||
void enableHeartbeat(uint32_t pingInterval, uint32_t pongTimeout, uint8_t disconnectTimeoutCount);
|
||||
void disableHeartbeat();
|
||||
|
||||
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
|
||||
IPAddress remoteIP(uint8_t num);
|
||||
#endif
|
||||
|
||||
protected:
|
||||
uint16_t _port;
|
||||
String _origin;
|
||||
String _protocol;
|
||||
String _base64Authorization; ///< Base64 encoded Auth request
|
||||
String * _mandatoryHttpHeaders;
|
||||
size_t _mandatoryHttpHeaderCount;
|
||||
protected:
|
||||
uint16_t _port;
|
||||
String _origin;
|
||||
String _protocol;
|
||||
String _base64Authorization; ///< Base64 encoded Auth request
|
||||
String * _mandatoryHttpHeaders;
|
||||
size_t _mandatoryHttpHeaderCount;
|
||||
|
||||
WEBSOCKETS_NETWORK_SERVER_CLASS * _server;
|
||||
WEBSOCKETS_NETWORK_SERVER_CLASS * _server;
|
||||
|
||||
WSclient_t _clients[WEBSOCKETS_SERVER_CLIENT_MAX];
|
||||
WSclient_t _clients[WEBSOCKETS_SERVER_CLIENT_MAX];
|
||||
|
||||
WebSocketServerEvent _cbEvent;
|
||||
WebSocketServerHttpHeaderValFunc _httpHeaderValidationFunc;
|
||||
WebSocketServerEvent _cbEvent;
|
||||
WebSocketServerHttpHeaderValFunc _httpHeaderValidationFunc;
|
||||
|
||||
bool newClient(WEBSOCKETS_NETWORK_CLASS * TCPclient);
|
||||
bool _runnning;
|
||||
|
||||
void messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length);
|
||||
uint32_t _pingInterval;
|
||||
uint32_t _pongTimeout;
|
||||
uint8_t _disconnectTimeoutCount;
|
||||
|
||||
void clientDisconnect(WSclient_t * client);
|
||||
bool clientIsConnected(WSclient_t * client);
|
||||
bool newClient(WEBSOCKETS_NETWORK_CLASS * TCPclient);
|
||||
|
||||
#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
|
||||
void handleNewClients(void);
|
||||
void handleClientData(void);
|
||||
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
|
||||
|
||||
void handleHeader(WSclient_t * client, String * headerLine);
|
||||
void handleHeader(WSclient_t * client, String * headerLine);
|
||||
|
||||
/**
|
||||
void handleHBPing(WSclient_t * client); // send ping in specified intervals
|
||||
|
||||
/**
|
||||
* called if a non Websocket connection is coming in.
|
||||
* Note: can be override
|
||||
* @param client WSclient_t * ptr to the client struct
|
||||
*/
|
||||
virtual void handleNonWebsocketConnection(WSclient_t * client) {
|
||||
DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] no Websocket connection close.\n", client->num);
|
||||
client->tcp->write("HTTP/1.1 400 Bad Request\r\n"
|
||||
"Server: arduino-WebSocket-Server\r\n"
|
||||
"Content-Type: text/plain\r\n"
|
||||
"Content-Length: 32\r\n"
|
||||
"Connection: close\r\n"
|
||||
"Sec-WebSocket-Version: 13\r\n"
|
||||
"\r\n"
|
||||
"This is a Websocket server only!");
|
||||
clientDisconnect(client);
|
||||
}
|
||||
virtual void handleNonWebsocketConnection(WSclient_t * client) {
|
||||
DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] no Websocket connection close.\n", client->num);
|
||||
client->tcp->write(
|
||||
"HTTP/1.1 400 Bad Request\r\n"
|
||||
"Server: arduino-WebSocket-Server\r\n"
|
||||
"Content-Type: text/plain\r\n"
|
||||
"Content-Length: 32\r\n"
|
||||
"Connection: close\r\n"
|
||||
"Sec-WebSocket-Version: 13\r\n"
|
||||
"\r\n"
|
||||
"This is a Websocket server only!");
|
||||
clientDisconnect(client);
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* called if a non Authorization connection is coming in.
|
||||
* Note: can be override
|
||||
* @param client WSclient_t * ptr to the client struct
|
||||
*/
|
||||
virtual void handleAuthorizationFailed(WSclient_t *client) {
|
||||
virtual void handleAuthorizationFailed(WSclient_t * client) {
|
||||
client->tcp->write(
|
||||
"HTTP/1.1 401 Unauthorized\r\n"
|
||||
"Server: arduino-WebSocket-Server\r\n"
|
||||
"Content-Type: text/plain\r\n"
|
||||
"Content-Length: 45\r\n"
|
||||
"Connection: close\r\n"
|
||||
"Sec-WebSocket-Version: 13\r\n"
|
||||
"WWW-Authenticate: Basic realm=\"WebSocket Server\""
|
||||
"\r\n"
|
||||
"This Websocket server requires Authorization!");
|
||||
clientDisconnect(client);
|
||||
}
|
||||
|
||||
client->tcp->write("HTTP/1.1 401 Unauthorized\r\n"
|
||||
"Server: arduino-WebSocket-Server\r\n"
|
||||
"Content-Type: text/plain\r\n"
|
||||
"Content-Length: 45\r\n"
|
||||
"Connection: close\r\n"
|
||||
"Sec-WebSocket-Version: 13\r\n"
|
||||
"WWW-Authenticate: Basic realm=\"WebSocket Server\""
|
||||
"\r\n"
|
||||
"This Websocket server requires Authorization!");
|
||||
clientDisconnect(client);
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* called for sending a Event to the app
|
||||
* @param num uint8_t
|
||||
* @param type WStype_t
|
||||
* @param payload uint8_t *
|
||||
* @param length size_t
|
||||
*/
|
||||
virtual void runCbEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
|
||||
if(_cbEvent) {
|
||||
_cbEvent(num, type, payload, length);
|
||||
}
|
||||
virtual void runCbEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
|
||||
if(_cbEvent) {
|
||||
_cbEvent(num, type, payload, length);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* Called at client socket connect handshake negotiation time for each http header that is not
|
||||
* a websocket specific http header (not Connection, Upgrade, Sec-WebSocket-*)
|
||||
* If the custom httpHeaderValidationFunc returns false for any headerName / headerValue passed, the
|
||||
@ -178,24 +197,21 @@ protected:
|
||||
* This mechanism can be used to enable custom authentication schemes e.g. test the value
|
||||
* of a session cookie to determine if a user is logged on / authenticated
|
||||
*/
|
||||
virtual bool execHttpHeaderValidation(String headerName, String headerValue) {
|
||||
if(_httpHeaderValidationFunc) {
|
||||
//return the value of the custom http header validation function
|
||||
return _httpHeaderValidationFunc(headerName, headerValue);
|
||||
}
|
||||
//no custom http header validation so just assume all is good
|
||||
return true;
|
||||
virtual bool execHttpHeaderValidation(String headerName, String headerValue) {
|
||||
if(_httpHeaderValidationFunc) {
|
||||
//return the value of the custom http header validation function
|
||||
return _httpHeaderValidationFunc(headerName, headerValue);
|
||||
}
|
||||
//no custom http header validation so just assume all is good
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
/*
|
||||
private:
|
||||
/*
|
||||
* returns an indicator whether the given named header exists in the configured _mandatoryHttpHeaders collection
|
||||
* @param headerName String ///< the name of the header being checked
|
||||
*/
|
||||
bool hasMandatoryHeader(String headerName);
|
||||
|
||||
bool hasMandatoryHeader(String headerName);
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif /* WEBSOCKETSSERVER_H_ */
|
||||
|
@ -9,6 +9,10 @@ For details, see http://sourceforge.net/projects/libb64
|
||||
#include <core_esp8266_features.h>
|
||||
#endif
|
||||
|
||||
#if defined(ESP32)
|
||||
#define CORE_HAS_LIBB64
|
||||
#endif
|
||||
|
||||
#ifndef CORE_HAS_LIBB64
|
||||
#include "cdecode_inc.h"
|
||||
|
||||
@ -32,9 +36,9 @@ int base64_decode_block(const char* code_in, const int length_in, char* plaintex
|
||||
const char* codechar = code_in;
|
||||
char* plainchar = plaintext_out;
|
||||
char fragment;
|
||||
|
||||
|
||||
*plainchar = state_in->plainchar;
|
||||
|
||||
|
||||
switch (state_in->step)
|
||||
{
|
||||
while (1)
|
||||
|
@ -9,6 +9,10 @@ For details, see http://sourceforge.net/projects/libb64
|
||||
#include <core_esp8266_features.h>
|
||||
#endif
|
||||
|
||||
#if defined(ESP32)
|
||||
#define CORE_HAS_LIBB64
|
||||
#endif
|
||||
|
||||
#ifndef CORE_HAS_LIBB64
|
||||
#include "cencode_inc.h"
|
||||
|
||||
@ -35,9 +39,9 @@ int base64_encode_block(const char* plaintext_in, int length_in, char* code_out,
|
||||
char* codechar = code_out;
|
||||
char result;
|
||||
char fragment;
|
||||
|
||||
|
||||
result = state_in->result;
|
||||
|
||||
|
||||
switch (state_in->step)
|
||||
{
|
||||
while (1)
|
||||
@ -76,7 +80,7 @@ int base64_encode_block(const char* plaintext_in, int length_in, char* code_out,
|
||||
*codechar++ = base64_encode_value(result);
|
||||
result = (fragment & 0x03f) >> 0;
|
||||
*codechar++ = base64_encode_value(result);
|
||||
|
||||
|
||||
++(state_in->stepcount);
|
||||
if (state_in->stepcount == CHARS_PER_LINE/4)
|
||||
{
|
||||
@ -92,7 +96,7 @@ int base64_encode_block(const char* plaintext_in, int length_in, char* code_out,
|
||||
int base64_encode_blockend(char* code_out, base64_encodestate* state_in)
|
||||
{
|
||||
char* codechar = code_out;
|
||||
|
||||
|
||||
switch (state_in->step)
|
||||
{
|
||||
case step_B:
|
||||
@ -108,7 +112,7 @@ int base64_encode_blockend(char* code_out, base64_encodestate* state_in)
|
||||
break;
|
||||
}
|
||||
*codechar++ = 0x00;
|
||||
|
||||
|
||||
return codechar - code_out;
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,7 @@ A million repetitions of "a"
|
||||
/* #define LITTLE_ENDIAN * This should be #define'd already, if true. */
|
||||
/* #define SHA1HANDSOFF * Copies data before messing with it. */
|
||||
|
||||
#ifndef ESP8266
|
||||
#if !defined(ESP8266) && !defined(ESP32)
|
||||
|
||||
#define SHA1HANDSOFF
|
||||
|
||||
|
@ -5,7 +5,7 @@ By Steve Reid <steve@edmweb.com>
|
||||
100% Public Domain
|
||||
*/
|
||||
|
||||
#ifndef ESP8266
|
||||
#if !defined(ESP8266) && !defined(ESP32)
|
||||
|
||||
typedef struct {
|
||||
uint32_t state[5];
|
||||
@ -18,4 +18,4 @@ void SHA1Init(SHA1_CTX* context);
|
||||
void SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len);
|
||||
void SHA1Final(unsigned char digest[20], SHA1_CTX* context);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
@ -7,7 +7,7 @@ var server = http.createServer(function(request, response) {
|
||||
response.writeHead(404);
|
||||
response.end();
|
||||
});
|
||||
server.listen(81, function() {
|
||||
server.listen(8011, function() {
|
||||
console.log((new Date()) + ' Server is listening on port 8011');
|
||||
});
|
||||
|
||||
@ -54,4 +54,4 @@ wsServer.on('request', function(request) {
|
||||
});
|
||||
|
||||
connection.sendUTF("Hallo Client!");
|
||||
});
|
||||
});
|
||||
|
53
travis/common.sh
Normal file
53
travis/common.sh
Normal file
@ -0,0 +1,53 @@
|
||||
#!/bin/bash
|
||||
|
||||
function build_sketches()
|
||||
{
|
||||
local arduino=$1
|
||||
local srcpath=$2
|
||||
local platform=$3
|
||||
local sketches=$(find $srcpath -name *.ino)
|
||||
for sketch in $sketches; do
|
||||
local sketchdir=$(dirname $sketch)
|
||||
if [[ -f "$sketchdir/.$platform.skip" ]]; then
|
||||
echo -e "\n\n ------------ Skipping $sketch ------------ \n\n";
|
||||
continue
|
||||
fi
|
||||
echo -e "\n\n ------------ Building $sketch ------------ \n\n";
|
||||
$arduino --verify $sketch;
|
||||
local result=$?
|
||||
if [ $result -ne 0 ]; then
|
||||
echo "Build failed ($sketch) build verbose..."
|
||||
$arduino --verify --verbose --preserve-temp-files $sketch
|
||||
result=$?
|
||||
fi
|
||||
if [ $result -ne 0 ]; then
|
||||
echo "Build failed ($1) $sketch"
|
||||
return $result
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
function get_core()
|
||||
{
|
||||
echo Setup core for $1
|
||||
|
||||
cd $HOME/arduino_ide/hardware
|
||||
|
||||
if [ "$1" = "esp8266" ] ; then
|
||||
mkdir esp8266com
|
||||
cd esp8266com
|
||||
git clone https://github.com/esp8266/Arduino.git esp8266
|
||||
cd esp8266/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
|
||||
python get.py
|
||||
fi
|
||||
|
||||
}
|
Reference in New Issue
Block a user