Compare commits

...

214 Commits

Author SHA1 Message Date
f6e730c2b4 fix _fingerprint is set checks for ESP32
see #633 and #632
2021-03-06 09:24:07 +01:00
7c3b1b7408 install libgtk-3-0 2021-03-06 08:59:21 +01:00
738e43fda4 install libgtk-3-0 2021-03-06 08:56:08 +01:00
f55bf8d4ed bump version to 2.3.5 2021-02-09 18:26:21 +01:00
a484da47ed Merge branch 'eio_4_ping_handling' 2021-01-31 20:32:25 +01:00
4355199120 bump version 2.3.4 2021-01-31 20:31:52 +01:00
0a4fcd44c2 EIO=4 ping handling #611 2021-01-31 20:27:56 +01:00
3a2b757155 EIO=4 ping handling #611 2021-01-31 10:28:15 +01:00
900d81e534 make WSclient_t * newClient(WEBSOCKETS_NETWORK_CLASS * TCPclient); public 2021-01-19 16:59:03 +01:00
0ecef8c552 Fixing WebSocket Close see #610 2021-01-19 16:58:37 +01:00
410489f7c5 bump version to 2.3.3 2021-01-07 10:27:42 +01:00
ec22d67c12 version.py can now create WebSocketsVersion.h 2021-01-07 10:24:23 +01:00
39e6a8e709 print version in DEBUG mode 2021-01-07 10:14:30 +01:00
784b7f9cb8 no python3.9 on github actions 2021-01-07 10:05:30 +01:00
fd83d6ad45 add WebSocketsVersion.h and some build checks 2021-01-07 09:59:49 +01:00
0e729cd896 cleanup clone_library 2021-01-06 09:39:19 +01:00
2f21590e55 no need for a full clone 2021-01-06 09:18:27 +01:00
c98baafda7 jobs steps array 2021-01-05 21:54:58 +01:00
983b9801fb Merge branch 'master' of github.com:Links2004/arduinoWebSockets 2021-01-05 21:50:56 +01:00
e70262dab9 add done job 2021-01-05 21:50:40 +01:00
4a05eab627 Update README.md
use github actions as Build Status
2021-01-05 21:30:28 +01:00
822618f606 code style fix 2021-01-05 21:18:30 +01:00
1b4f186fa6 use github actions for build 2021-01-05 21:17:09 +01:00
c68e015322 github actions test3 2021-01-05 18:32:11 +01:00
dc30a2b7bf github actions test2 2021-01-05 18:28:59 +01:00
6bee53a8bd github actions test 2021-01-05 18:11:48 +01:00
ebb87cdc8a + constructors for scalars 2020-11-26 19:33:18 +01:00
e7ab913693 fix clearing _Client[] 2020-11-26 19:33:18 +01:00
52547ec47c fix clients init logic 2020-11-26 19:33:18 +01:00
74411bf729 use native contructor and destructor to initialize WSclient_t 2020-11-26 19:33:18 +01:00
f0cc36dede bump version to 2.3.2 2020-11-21 18:13:22 +01:00
074a674833 bionic is broken for Arduino IDE 1.6.13 so we keep xenial 2020-11-21 16:25:18 +01:00
8239e1625e update travis config 2020-11-21 16:21:04 +01:00
c5900db636 add handling for Socket.IO V3 connection 2020-11-21 16:19:59 +01:00
9470961d85 socket.io improve Cookie handling 2020-11-21 16:19:08 +01:00
04919f848f only try to send socket.io messages in WSC_CONNECTED state
handle sIOtype_CONNECT messages
send 2probe on websocket connect (#574)
2020-11-21 16:17:43 +01:00
25318111a1 add namespace join to socket.io examples 2020-11-21 16:16:59 +01:00
08caf3bfc6 code style 2020-11-21 14:45:53 +01:00
fb26433e75 add error messages if Webserver Hook Functions are not supported by platform 2020-11-21 14:33:25 +01:00
4f52a0f38e cleanup server client init 2020-11-21 14:07:32 +01:00
f20fbbfcd9 code style 2020-11-21 14:07:14 +01:00
28cd929e7e code style 2020-11-21 14:06:30 +01:00
826c6b423a esp8266: share port 80 with regular webserver (#575) 2020-11-21 13:51:16 +01:00
7fea0b8bdf Fix for Newline in Headers when extraHeaders is Empty [577] (#578)
Closes https://github.com/Links2004/arduinoWebSockets/issues/577
2020-11-14 18:17:10 +01:00
4db14451fb clang-format 2020-10-26 17:04:19 +01:00
80bf087cd0 Add SSL client certificate support (#572)
* Add client certificate support

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

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

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

make some other yields conditional to avoid some waste when it would
double-yield.
2020-09-16 11:02:02 -07:00
05ec18e49b Merge pull request #565 from tobiges/master
header response timeout added
2020-09-14 10:25:59 +02:00
e185668a97 header response timeout added 2020-09-12 17:20:03 +02:00
7e34a8b246 Merge pull request #563 from simap/accessClients
add api to check connectedness of clients by num
2020-09-08 21:08:32 +02:00
4acc7eff8a add api to check connectedness of clients by num 2020-09-08 09:54:33 -07:00
b3c5348e9b update arduino IDE version in travis test 2020-07-25 09:07:02 +02:00
19c39d5ea0 typo 2020-07-11 18:38:39 +02:00
ebdc8def4a Merge branch 'master' of github.com:Links2004/arduinoWebSockets 2020-07-11 18:17:05 +02:00
44fb41e3b7 WebSocketClientSocketIOack.ino:150:25: ARDUINOJSON_USE_LONG_LONG fix 2020-07-11 18:16:34 +02:00
cadbd6458f Merge pull request #551 from Links2004/ESP32v4
fix #525 (detect ESP32 version)
2020-07-11 18:13:13 +02:00
f244741caa WebSocketClientSocketIO.ino:113:25: ArduinoJson, you must set ARDUINOJSON_USE_LONG_LONG error 2020-07-11 18:07:16 +02:00
3f1cedb21c fix #541 allow loop() to be called before begin() 2020-07-11 17:58:36 +02:00
ba6275e7fe fix #525 (detect ESP32 vesion) 2020-07-11 17:55:18 +02:00
a71a480676 fix #539 (NODEBUG_WEBSOCKETS warning)
fix warning when using NODEBUG_WEBSOCKETS as compiler option.
2020-05-14 18:04:46 +02:00
1e271b5e12 Merge remote-tracking branch 'origin/master' 2020-05-03 13:07:13 +02:00
0c3b15c5e7 fix #454 2020-05-03 13:05:55 +02:00
7a4160814c fix #454 2020-05-03 13:04:53 +02:00
784088d9cc bump version to 2.2.1 2020-05-03 10:03:53 +02:00
9048ef9ee4 code style 2020-05-03 09:28:55 +02:00
80b0867786 update travis for IDE 1.8.12 2020-05-03 09:27:49 +02:00
b9e1336826 add new define for yield WEBSOCKETS_YIELD 2020-05-03 09:12:03 +02:00
ac9b2ccdf6 Merge pull request #521 from amrbekhit/patch-1
Initialize _reconnectInterval in constructor.
2020-04-01 19:09:53 +02:00
12684582aa Initialize _reconnectInterval in constructor.
This allows setReconnectInterval() to be called before begin() and stops begin() from overriding _reconnectInterval every time it is called.
2020-03-21 22:14:25 +03:00
420cc55960 Merge pull request #501 from amrbekhit/websockets_tcp_timeout_increase
Increased WEBSOCKETS_TCP_TIMEOUT to 5000ms.
2020-03-14 14:51:25 +01:00
64296cced7 Merge pull request #512 from maurus95/master
Implementing automatic WS server-side heartbeat
2020-02-15 22:27:40 +01:00
c3f01e43fc Merge pull request #514 from simap/master
read can return -1, check for this to avoid corrupting protocol
2020-02-15 22:18:48 +01:00
04e4be03fe Merge pull request #515 from simap/esp32yield
add a yield here for esp32 to avoid a busy loop
2020-02-15 22:18:08 +01:00
ebd7a528ac add a yield here for esp32 to avoid a busy loop
if data isn't available yet, but is expected, yielding here is nicer
than burning the cpu in a loop, just like in esp8266 case.
2020-02-15 06:43:46 -08:00
516911900b read can return -1, check for this to avoid corrupting protocol
fixes #470
2020-02-15 06:37:07 -08:00
674a6e98c9 Implemented HeartBeat in WebSocketsServer 2020-02-10 19:45:55 +01:00
4ee0ba5630 Increased WEBSOCKETS_TCP_TIMEOUT to 5000ms.
See https://github.com/Links2004/arduinoWebSockets/issues/500.
2019-12-24 17:03:32 +03:00
370f217a30 add example for ssl websocket with CA certificate 2019-10-27 21:40:53 +01:00
c038f100d6 Merge pull request #458 from Nufflee/patch-1
Syntax highlighting in README.md
2019-07-21 08:53:00 +02:00
0444a78441 [SocketIO] add example for use of ACK / callback handling 2019-07-20 14:24:55 +02:00
8cc20b6e4b Merge branch 'master' of github.com:Links2004/arduinoWebSockets 2019-07-20 13:47:55 +02:00
40152be197 code style 2019-07-20 13:47:40 +02:00
e2669c1c5e [SocketIO] allow to send any message 2019-07-20 13:46:59 +02:00
771831c57e Syntax highlighting in README.md 2019-07-15 23:13:38 +02:00
06a7bb177b Merge pull request #452 from luc-github/master
Fix warnings in platformIO
2019-07-04 16:12:21 +02:00
Luc
bd158c9c5c Fix warnings in platformIO
Fix : warning: enumeration value 'WStype_ERROR' not handled in switch [-Wswitch]
Fix : warning: variable 'ip' set but not used [-Wunused-but-set-variable]
2019-07-04 12:58:37 +02:00
9a803e1fb3 bump version to 2.2.0 2019-06-29 19:04:10 +02:00
c64f2b81e5 Merge pull request #449 from Links2004/socketio
Socket.IO 2.0.x Support
2019-06-29 18:56:31 +02:00
4ef8d733dc add missing include 2019-06-29 18:35:21 +02:00
7816bb3fa4 adding example for new socket.IO support 2019-06-29 18:31:14 +02:00
adf2b07f3b fix #447 2019-06-23 12:14:44 +02:00
d3fde2ba34 Merge pull request #442 from Links2004/gpn19
Gpn19
2019-06-20 10:41:29 +02:00
c164c47f08 Merge branch 'gpn19' into socketio 2019-06-20 10:08:36 +02:00
6a76efa791 fix #445 #446 set TCP timeout for ESP32 on connect 2019-06-20 10:06:21 +02:00
5d26a65df4 only send payload when we have a ptr and length 2019-06-20 10:02:07 +02:00
007b5ba9a8 ignore not needed WS events for SocketIO 2019-06-10 15:13:26 +02:00
dbca2ab420 SocketIO message RX is working 2019-06-10 14:57:49 +02:00
dfa35fa9ae clang-format 2019-06-10 13:31:51 +02:00
794163cec9 Merge branch 'gpn18' into socketio 2019-06-10 13:30:54 +02:00
294436840c bump version to 2.1.4 2019-06-10 13:15:11 +02:00
b1b21d188f update gitignore to Ignore IDE files from Eclipse and VScode 2019-06-10 13:02:25 +02:00
3063ad27ae clang-format 2019-06-10 13:00:01 +02:00
e8df841b7f add support for ESP32 ETH.h see #443 2019-06-10 09:29:58 +02:00
0aa07421a6 add events for ping / pong rx #382 2019-05-30 20:15:03 +02:00
df3ef524b6 fix #428 by setting Insecure if we do not have a CA or fingerprint 2019-05-30 18:24:28 +02:00
8ec27b0468 fix indexOf #430 2019-05-30 16:52:20 +02:00
c361895a4b add setCACert based on #434 2019-05-30 16:32:30 +02:00
31bade1530 test with newer arduino IDE (1.8.9) 2019-05-30 14:33:44 +02:00
4223a9e41f fix Travis 2019-05-30 14:24:57 +02:00
72731beb10 Merge pull request #383 from sovcik/master
Implementing automatic WS client-side heartbeat
2019-01-16 16:42:59 +01:00
1b6b42b877 processing client data before potential disconnect 2018-11-03 13:16:12 +01:00
d6dd7be987 disconnecting after pong not received 2018-11-03 13:15:14 +01:00
d325bd338e Merge branch 'master' into gpn18 2018-10-26 20:33:29 +02:00
d693437908 version bump 2018-10-24 08:19:02 +02:00
9b6ce7563a added example for heartbeat 2018-10-24 08:17:37 +02:00
68800e2e7a implementing heartbeat 2018-10-23 18:01:33 +02:00
eaef4f0801 Merge pull request #369 from sanketplus/master
make masking RFC complaint and fix #208
2018-09-23 18:10:30 +02:00
4db22fe5e4 make masking RFC complaint and fix #208 2018-09-23 16:49:10 +05:30
50e2e366cc fix travis debug test build 2018-09-23 12:07:42 +02:00
e8249f76e6 travis config second fix 2018-09-23 12:02:06 +02:00
a70d644f5d adapt travis config to match esp8266 changes 2018-09-23 11:56:33 +02:00
a3079f5645 Merge commit '38a881a86a35c2cbf755f9c8da33178b70bd4f82' 2018-08-08 18:13:29 +02:00
2626b26315 Merge commit '32dd01b91bbae7f10b0b77ac200e69ea064ed44f' 2018-08-08 18:13:14 +02:00
38a881a86a bump version to 2.1.2 2018-08-08 18:12:02 +02:00
32dd01b91b bump version to 2.1.1 2018-08-08 18:11:32 +02:00
db8167f9c7 Merge pull request #355 from Links2004/ESP32_serial
ESP32 fixes
2018-08-07 18:31:43 +02:00
388683d4cd fix compiler warnings for ESP32 - #319 2018-08-07 18:10:48 +02:00
38c401b8af adapt to ESP32 repo changes
4e5cbdaa7f
2018-08-07 18:06:32 +02:00
f7a7ab6ab4 wrap reasonCode in define
see #343
2018-07-04 16:29:28 +02:00
bdee8b9744 call end for ESP8266_ASYNC TCP Server 2018-06-21 09:30:11 +02:00
50903cd410 basic event RX working 2018-05-12 17:10:17 +02:00
f95c014342 basic event sending works 2018-05-12 16:53:34 +02:00
8967356af3 improve ram usage on one char send 2018-05-12 15:27:08 +02:00
d2043bf8ef start work for socket.IO
basic Engine.IO implementation
2018-05-12 15:13:50 +02:00
7ddcdc2bd3 add info for AVR usage with version 2.x.x 2018-05-12 12:40:56 +02:00
bde97179bf fix unused parameter warnings
fix switch warinings

part of #319
2018-05-12 11:39:59 +02:00
9ce044e550 code style 2018-05-12 11:34:12 +02:00
a796079b0c fix code style 2018-05-12 11:27:03 +02:00
e8c0d775fb fix #146 HTTP Header handler 2018-05-12 11:22:16 +02:00
5636b02b46 add WebSocketServerAllFunctionsDemo (WIP) 2018-05-10 20:47:05 +02:00
4b33575af1 add connectedClients see #242 2018-05-10 20:43:08 +02:00
486a612693 allow usage of IPAddress #230 2018-05-10 20:12:48 +02:00
ea8e81c6ce add close #322 2018-05-10 18:56:31 +02:00
8a187b523b Merge pull request #323 from chemicstry/printf_fix
Fix format specifiers
2018-05-10 18:48:43 +02:00
ac8d806085 Fix format specifiers 2018-05-10 01:16:39 +03:00
02e0128802 Update index.js
fix port
2018-04-17 19:42:47 +02:00
01e1fdfb50 Merge pull request #304 from CarlosRDomin/master
Allow custom WEBSOCKETS_SERVER_CLIENT_MAX definition
2018-04-03 19:21:22 +02:00
d02932ef21 Allow custom WEBSOCKETS_SERVER_CLIENT_MAX definition 2018-04-02 22:17:34 -07:00
5bf8b2407e Merge pull request #296 from getbyte/master
fix undefined virtual methods
2018-03-18 07:34:57 +01:00
50d9a8d6e5 fix undefined virtual methods 2018-03-18 01:25:49 +03:00
7443f9b1dc Merge pull request #292 from ivankravets/patch-2
Update supported platforms, allow PIO to detect examples automatically
2018-03-07 18:49:05 +01:00
4b04f6fa53 Update supported platforms, allow PIO to detect examples automatically 2018-03-07 10:33:33 +02:00
a67a8dd0e0 Merge pull request #265 from Links2004/esp32
ESP32 support
improved test
sorted examples
2018-02-07 18:32:49 +01:00
4759a66a62 Arduino 1.8.5 is very peaky with the board parameters 2018-02-07 18:15:50 +01:00
48e690a42a bump version to 2.1.0
travis arduino 1.8.5
2018-02-07 18:11:08 +01:00
b059d0711c sort examples by platform
rework the travis tests
create ESP32 examples
2018-02-07 17:28:17 +01:00
a09b8c48ee ide version 2018-02-06 21:58:26 +01:00
94eb4b12e5 IDE version 2018-02-06 21:52:13 +01:00
2df62558f1 more verbose verify step 2018-02-06 21:48:49 +01:00
86074c90ae update IDE test 2018-02-06 21:41:47 +01:00
d73e3ecc9c skip some test for ESP32 2018-02-06 21:37:24 +01:00
f62aa6478d more ESP32 stuff 2018-02-06 21:36:44 +01:00
d340c89b09 ... 2018-02-06 21:26:06 +01:00
05f4cfd7bf fix includes for WebSocketClient.ino 2018-02-06 21:17:14 +01:00
46cff38287 fix ESP32 sha1 problem 2018-02-06 21:12:39 +01:00
0b35e32b2a examples for ESP32 2018-02-06 21:10:29 +01:00
255f40eaa7 travis.yml 2018-02-06 20:46:17 +01:00
5e1f9b1c26 CORE_HAS_LIBB64 for ESP32 2018-02-06 20:44:10 +01:00
30b6deedd2 use travis matrix 2018-02-06 20:41:36 +01:00
0e6e1c76ee Merge pull request #281 from baggior/esp32 2018-02-06 20:24:40 +01:00
887683fabb Merge branch 'master' into esp32 2018-02-06 20:24:03 +01:00
0b10299549 travis builds for ESP32 2018-02-06 20:22:29 +01:00
1d300084e0 fix 2018-02-06 09:57:04 +01:00
d9e1e97cf6 Merge branch 'master' into esp32 2018-01-23 12:40:10 +01:00
642750e232 Merge pull request #245 from PeterEmbedded/patch-1
Added some missing comments to WebSockets.cpp
2017-10-16 19:04:40 +02:00
a0c714bf10 Update WebSockets.cpp
Added some missing comments
2017-10-16 13:01:00 +02:00
c5461d5779 memset zero _clients struct for server
fixes #244
2017-10-06 18:30:05 +02:00
b87bce4466 add virtual to destructor #243 2017-10-04 19:17:21 +02:00
9608c2d210 Merge pull request #240 from mgbckr/master
Add example for plain STOMP connection
2017-09-25 21:31:24 +02:00
4baf4896cc Add missing Hash.h library 2017-09-25 17:20:07 +02:00
4fd2e9d993 Add example for plain STOMP connection 2017-09-25 15:04:56 +02:00
fa580a568f Merge pull request #223 from robokoding/master
Add ESP32 support from @robokoding
2017-09-20 18:53:55 +02:00
a93a845780 add: ESP32 support 2017-09-03 10:06:00 +02:00
83a1539e1e fix: ESP32 new client connection 2017-09-03 09:55:15 +02:00
1bd4638621 bump version to 2.0.10 2017-08-21 21:44:30 +02:00
f40a390ab1 workaround windows gcc Template issue (#228, #229) 2017-08-21 21:39:14 +02:00
b6c27f74cd bump version to 2.0.9 2017-08-19 22:05:33 +02:00
c64a082270 Merge branch 'write_big_data' 2017-08-19 21:36:10 +02:00
522a67bc1b add setReconnectInterval for the Client 2017-08-19 21:16:07 +02:00
29df9c30f7 bump version to 2.0.8 2017-08-18 17:16:51 +02:00
4c1c5378cb esp32 no hasClient function 2017-07-30 01:40:36 +02:00
dbabc1025b added esp32 support 2017-07-30 00:58:15 +02:00
1d40160d99 added esp32 support 2017-07-30 00:54:17 +02:00
a388676b40 added remoteIP for esp32 2017-07-30 00:22:23 +02:00
8c19d7ba48 Update WebSockets.cpp
added esp32 hwcrypto for sha1
2017-07-30 00:21:13 +02:00
4fc80871a6 Update WebSockets.h 2017-07-30 00:18:37 +02:00
c911776860 add virtual to write
fix some naming
2017-04-10 17:49:42 +02:00
adb52b11e9 handle cases when not all data can be written to TCP stack #187 2017-04-09 17:58:23 +02:00
47 changed files with 3778 additions and 1031 deletions

63
.clang-format Normal file
View 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
...

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

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

8
.gitignore vendored
View File

@ -27,3 +27,11 @@
*.out *.out
*.app *.app
/tests/webSocketServer/node_modules /tests/webSocketServer/node_modules
# IDE
.vscode
.cproject
.project
.settings
*.swp

View File

@ -1,37 +1,45 @@
sudo: false sudo: false
dist:
- xenial
addons:
apt:
packages:
- xvfb
language: bash language: bash
os: os:
- linux - linux
env:
matrix:
- CPU="esp8266" BOARD="esp8266com:esp8266:generic:xtal=80" IDE_VERSION=1.6.13
- CPU="esp8266" BOARD="esp8266com:esp8266:generic:xtal=80,dbg=Serial1" IDE_VERSION=1.6.13
- CPU="esp8266" BOARD="esp8266com:esp8266:generic:xtal=80,eesz=1M,FlashMode=qio,FlashFreq=80" IDE_VERSION=1.8.13
- CPU="esp32" BOARD="espressif:esp32:esp32:FlashFreq=80" IDE_VERSION=1.8.5
- CPU="esp32" BOARD="espressif:esp32:esp32:FlashFreq=80" IDE_VERSION=1.8.9
- CPU="esp32" BOARD="espressif:esp32:esp32:FlashFreq=80" IDE_VERSION=1.8.13
script: 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 - /sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_1.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :1 -ac -screen 0 1280x1024x16
- sleep 3
- export DISPLAY=:1.0 - export DISPLAY=:1.0
- wget http://downloads.arduino.cc/arduino-1.6.5-linux64.tar.xz - sleep 3
- tar xf arduino-1.6.5-linux64.tar.xz - wget http://downloads.arduino.cc/arduino-$IDE_VERSION-linux64.tar.xz
- mv arduino-1.6.5 $HOME/arduino_ide - tar xf arduino-$IDE_VERSION-linux64.tar.xz
- mv arduino-$IDE_VERSION $HOME/arduino_ide
- export PATH="$HOME/arduino_ide:$PATH" - export PATH="$HOME/arduino_ide:$PATH"
- which arduino - which arduino
- mkdir -p $HOME/Arduino/libraries - 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 - cp -r $TRAVIS_BUILD_DIR $HOME/Arduino/libraries/arduinoWebSockets
- cd $HOME/arduino_ide/hardware
- mkdir esp8266com
- cd esp8266com
- git clone https://github.com/esp8266/Arduino.git esp8266
- cd esp8266/tools
- python get.py
- source $TRAVIS_BUILD_DIR/travis/common.sh - source $TRAVIS_BUILD_DIR/travis/common.sh
- arduino --board esp8266com:esp8266:generic --save-prefs - get_core $CPU
- cd $TRAVIS_BUILD_DIR
- arduino --board $BOARD --save-prefs
- arduino --get-pref sketchbook.path - arduino --get-pref sketchbook.path
- build_sketches arduino $HOME/Arduino/libraries/arduinoWebSockets esp8266 - arduino --pref update.check=false
- build_sketches arduino $HOME/Arduino/libraries/arduinoWebSockets/examples/$CPU $CPU
notifications: notifications:
email: email:
on_success: change on_success: change
on_failure: change on_failure: change
webhooks:
urls:
- https://webhooks.gitter.im/e/1aa78fbe15080b0c2e37
on_success: change # options: [always|never|change] default: always
on_failure: always # options: [always|never|change] default: always
on_start: false # default: false

View File

@ -1,4 +1,4 @@
WebSocket Server and Client for Arduino [![Build Status](https://travis-ci.org/Links2004/arduinoWebSockets.svg?branch=master)](https://travis-ci.org/Links2004/arduinoWebSockets) WebSocket Server and Client for Arduino [![Build Status](https://github.com/Links2004/arduinoWebSockets/workflows/CI/badge.svg?branch=master)](https://github.com/Links2004/arduinoWebSockets/actions?query=workflow%3ACI+branch%3Amaster)
=========================================== ===========================================
a WebSocket Server and Client for Arduino based on RFC6455. a WebSocket Server and Client for Arduino based on RFC6455.
@ -23,7 +23,8 @@ a WebSocket Server and Client for Arduino based on RFC6455.
- wss / SSL is not possible. - wss / SSL is not possible.
##### Supported Hardware ##### ##### 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 - ESP31B
- Particle with STM32 ARM Cortex M3 - Particle with STM32 ARM Cortex M3
- ATmega328 with Ethernet Shield (ATmega branch) - ATmega328 with Ethernet Shield (ATmega branch)
@ -33,7 +34,9 @@ a WebSocket Server and Client for Arduino based on RFC6455.
###### Note: ###### ###### 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++. Arduino for AVR not supports std namespace of c++.
@ -56,32 +59,34 @@ The mode can be activated in the ```WebSockets.h``` (see WEBSOCKETS_NETWORK_TYPE
### High Level Client API ### ### High Level Client API ###
- `begin` : Initiate connection sequence to the websocket host. - `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(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(String host, uint16_t port, String url = "/", String protocol = "arduino");
``` ```
- `onEvent`: Callback to handle for websocket events - `onEvent`: Callback to handle for websocket events
``` ```c++
void onEvent(WebSocketClientEvent cbEvent); void onEvent(WebSocketClientEvent cbEvent);
``` ```
- `WebSocketClientEvent`: Handler for websocket events - `WebSocketClientEvent`: Handler for websocket events
``` ```c++
void (*WebSocketClientEvent)(WStype_t type, uint8_t * payload, size_t length) void (*WebSocketClientEvent)(WStype_t type, uint8_t * payload, size_t length)
``` ```
Where `WStype_t type` is defined as: Where `WStype_t type` is defined as:
``` ```c++
typedef enum { typedef enum {
WStype_ERROR, WStype_ERROR,
WStype_DISCONNECTED, WStype_DISCONNECTED,
WStype_CONNECTED, WStype_CONNECTED,
WStype_TEXT, WStype_TEXT,
WStype_BIN, WStype_BIN,
WStype_FRAGMENT_TEXT_START, WStype_FRAGMENT_TEXT_START,
WStype_FRAGMENT_BIN_START, WStype_FRAGMENT_BIN_START,
WStype_FRAGMENT, WStype_FRAGMENT,
WStype_FRAGMENT_FIN, WStype_FRAGMENT_FIN,
WStype_PING,
WStype_PONG,
} WStype_t; } WStype_t;
``` ```

View File

@ -1,113 +0,0 @@
/*
* WebSocketClientSocketIO.ino
*
* Created on: 06.06.2016
*
*/
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <WebSocketsClient.h>
#include <Hash.h>
ESP8266WiFiMulti WiFiMulti;
WebSocketsClient webSocket;
#define USE_SERIAL Serial1
#define MESSAGE_INTERVAL 30000
#define HEARTBEAT_INTERVAL 25000
uint64_t messageTimestamp = 0;
uint64_t heartbeatTimestamp = 0;
bool isConnected = false;
void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) {
switch(type) {
case WStype_DISCONNECTED:
USE_SERIAL.printf("[WSc] Disconnected!\n");
isConnected = false;
break;
case WStype_CONNECTED:
{
USE_SERIAL.printf("[WSc] Connected to url: %s\n", payload);
isConnected = true;
// send message to server when Connected
// socket.io upgrade confirmation message (required)
webSocket.sendTXT("5");
}
break;
case WStype_TEXT:
USE_SERIAL.printf("[WSc] get text: %s\n", payload);
// send message to server
// webSocket.sendTXT("message here");
break;
case WStype_BIN:
USE_SERIAL.printf("[WSc] get binary length: %u\n", length);
hexdump(payload, length);
// send data to server
// webSocket.sendBIN(payload, length);
break;
}
}
void setup() {
// USE_SERIAL.begin(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);
}
webSocket.beginSocketIO("192.168.0.123", 81);
//webSocket.setAuthorization("user", "Password"); // HTTP Basic Authorization
webSocket.onEvent(webSocketEvent);
}
void loop() {
webSocket.loop();
if(isConnected) {
uint64_t now = millis();
if(now - messageTimestamp > MESSAGE_INTERVAL) {
messageTimestamp = now;
// example socket.io message with type "messageType" and JSON payload
webSocket.sendTXT("42[\"messageType\",{\"greeting\":\"hello\"}]");
}
if((now - heartbeatTimestamp) > HEARTBEAT_INTERVAL) {
heartbeatTimestamp = now;
// socket.io heartbeat message
webSocket.sendTXT("2");
}
}
}

View 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();
}

View File

@ -1,25 +1,39 @@
/* /*
* 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 <Arduino.h>
#include <ESP8266WiFi.h> #include <WiFi.h>
#include <ESP8266WiFiMulti.h> #include <WiFiMulti.h>
#include <WiFiClientSecure.h>
#include <WebSocketsClient.h> #include <WebSocketsClient.h>
#include <Hash.h>
ESP8266WiFiMulti WiFiMulti; WiFiMulti WiFiMulti;
WebSocketsClient webSocket; WebSocketsClient webSocket;
#define USE_SERIAL Serial1 #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) { void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) {
@ -30,7 +44,7 @@ void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) {
case WStype_CONNECTED: case WStype_CONNECTED:
{ {
USE_SERIAL.printf("[WSc] Connected to url: %s\n", payload); USE_SERIAL.printf("[WSc] Connected to url: %s\n", payload);
// send message to server when Connected // send message to server when Connected
webSocket.sendTXT("Connected"); webSocket.sendTXT("Connected");
} }
@ -48,6 +62,12 @@ void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) {
// send data to server // send data to server
// webSocket.sendBIN(payload, length); // webSocket.sendBIN(payload, length);
break; 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); delay(100);
} }
webSocket.begin("192.168.0.123", 81); webSocket.beginSSL("192.168.0.123", 81);
//webSocket.setAuthorization("user", "Password"); // HTTP Basic Authorization
webSocket.onEvent(webSocketEvent); webSocket.onEvent(webSocketEvent);
} }

View 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();
}

View 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();
}

View File

@ -0,0 +1,103 @@
/*
* WebSocketClientSSLWithCA.ino
*
* Created on: 27.10.2019
*
* note SSL is only possible with the ESP8266
*
*/
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <WebSocketsClient.h>
ESP8266WiFiMulti WiFiMulti;
WebSocketsClient webSocket;
#define USE_SERIAL Serial1
// Can be obtained with:
// openssl s_client -showcerts -connect echo.websocket.org:443 </dev/null
const char ENDPOINT_CA_CERT[] PROGMEM = R"EOF(
-----BEGIN CERTIFICATE-----
MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0NlowSjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT
GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF
q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0
Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWAa6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj
/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG
CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNvbTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k
c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAwVAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC
ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAzMDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu
Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsFAAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo
uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu
X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlGPfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6
KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==
-----END CERTIFICATE-----
)EOF";
void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) {
switch(type) {
case WStype_DISCONNECTED:
USE_SERIAL.printf("[WSc] Disconnected!\n");
break;
case WStype_CONNECTED:
{
USE_SERIAL.printf("[WSc] Connected to url: %s\n", payload);
// send message to server when Connected
webSocket.sendTXT("Connected");
}
break;
case WStype_TEXT:
USE_SERIAL.printf("[WSc] get text: %s\n", payload);
// send message to server
// webSocket.sendTXT("message here");
break;
case WStype_BIN:
USE_SERIAL.printf("[WSc] get binary length: %u\n", length);
hexdump(payload, length);
// send data to server
// webSocket.sendBIN(payload, length);
break;
}
}
void setup() {
USE_SERIAL.begin(115200);
USE_SERIAL.setDebugOutput(true);
USE_SERIAL.println();
USE_SERIAL.println();
USE_SERIAL.println();
for(uint8_t t = 4; t > 0; t--) {
USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t);
USE_SERIAL.flush();
delay(1000);
}
WiFiMulti.addAP("SSID", "passpasspass");
while(WiFiMulti.run() != WL_CONNECTED) {
delay(100);
}
//When using BearSSL, client certificate and private key can be set:
//webSocket.setSSLClientCertKey(clientCert, clientPrivateKey);
//clientCert and clientPrivateKey can be of types (const char *, const char *) , or of types (BearSSL::X509List, BearSSL::PrivateKey)
webSocket.beginSslWithCA("echo.websocket.org", 443, "/", ENDPOINT_CA_CERT);
webSocket.onEvent(webSocketEvent);
}
void loop() {
webSocket.loop();
}

View File

@ -0,0 +1,128 @@
/*
* 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);
// join default namespace (no auto join in Socket.IO V3)
socketIO.send(sIOtype_CONNECT, "/");
break;
case sIOtype_EVENT:
USE_SERIAL.printf("[IOc] get event: %s\n", payload);
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);
}
}

View File

@ -0,0 +1,165 @@
/*
* 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);
// join default namespace (no auto join in Socket.IO V3)
socketIO.send(sIOtype_CONNECT, "/");
break;
case sIOtype_EVENT:
{
char * sptr = NULL;
int id = strtol((char *)payload, &sptr, 10);
USE_SERIAL.printf("[IOc] get event: %s id: %d\n", payload, id);
if(id) {
payload = (uint8_t *)sptr;
}
DynamicJsonDocument doc(1024);
DeserializationError error = deserializeJson(doc, payload, length);
if(error) {
USE_SERIAL.print(F("deserializeJson() failed: "));
USE_SERIAL.println(error.c_str());
return;
}
String eventName = doc[0];
USE_SERIAL.printf("[IOc] event name: %s\n", eventName.c_str());
// Message Includes a ID for a ACK (callback)
if(id) {
// creat JSON message for Socket.IO (ack)
DynamicJsonDocument docOut(1024);
JsonArray array = docOut.to<JsonArray>();
// add payload (parameters) for the ack (callback function)
JsonObject param1 = array.createNestedObject();
param1["now"] = millis();
// JSON to String (serializion)
String output;
output += id;
serializeJson(docOut, output);
// Send event
socketIO.send(sIOtype_ACK, output);
}
}
break;
case sIOtype_ACK:
USE_SERIAL.printf("[IOc] get ack: %u\n", length);
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);
}
}

View 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();
}

View File

@ -1,8 +1,8 @@
/* /*
WebSocketClientSockJsAndStomp.ino WebSocketClientStompOverSockJs.ino
Example for connecting and maintining a connection with a SockJS+STOMP websocket connection. 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). 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 Created on: 18.07.2017
Author: Martin Becker <mgbckr>, Contact: becker@informatik.uni-wuerzburg.de Author: Martin Becker <mgbckr>, Contact: becker@informatik.uni-wuerzburg.de
@ -33,7 +33,7 @@ const int ws_port = 80;
// base URL for SockJS (websocket) connection // 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): // 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 // 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/". // 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 "/" !!! const char* ws_baseurl = "/socketentry/"; // don't forget leading and trailing "/" !!!
@ -58,7 +58,7 @@ void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) {
case WStype_TEXT: case WStype_TEXT:
{ {
// ##################### // #####################
// handle STOMP protocol // handle SockJs+STOMP protocol
// ##################### // #####################
String text = (char*) payload; String text = (char*) payload;

View File

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

View File

@ -0,0 +1,103 @@
/*
* WebSocketServerHooked.ino
*
* Created on: 22.05.2015
* Hooked on: 28.10.2020
*
*/
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <WebSockets4WebServer.h>
#include <Hash.h>
#include <ESP8266mDNS.h>
ESP8266WiFiMulti WiFiMulti;
ESP8266WebServer server(80);
WebSockets4WebServer webSocket;
#define USE_SERIAL Serial
void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
switch(type) {
case WStype_DISCONNECTED:
USE_SERIAL.printf("[%u] Disconnected!\n", num);
break;
case WStype_CONNECTED:
{
IPAddress ip = webSocket.remoteIP(num);
USE_SERIAL.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload);
// send message to client
webSocket.sendTXT(num, "Connected");
}
break;
case WStype_TEXT:
USE_SERIAL.printf("[%u] get Text: %s\n", num, payload);
// send message to client
// webSocket.sendTXT(num, "message here");
// send data to all connected clients
// webSocket.broadcastTXT("message here");
break;
case WStype_BIN:
USE_SERIAL.printf("[%u] get binary length: %u\n", num, length);
hexdump(payload, length);
// send message to client
// webSocket.sendBIN(num, payload, length);
break;
}
}
void setup() {
// USE_SERIAL.begin(921600);
USE_SERIAL.begin(115200);
//Serial.setDebugOutput(true);
USE_SERIAL.setDebugOutput(true);
USE_SERIAL.println();
USE_SERIAL.println();
USE_SERIAL.println();
for(uint8_t t = 4; t > 0; t--) {
USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t);
USE_SERIAL.flush();
delay(1000);
}
WiFiMulti.addAP("SSID", "passpasspass");
while(WiFiMulti.run() != WL_CONNECTED) {
delay(100);
}
server.on("/", []() {
server.send(200, "text/plain", "I am a regular webserver on port 80!\r\n");
server.send(200, "text/plain", "I am also a websocket server on '/ws' on the same port 80\r\n");
});
server.addHook(webSocket.hookForWebserver("/ws", webSocketEvent));
server.begin();
Serial.println("HTTP server started on port 80");
Serial.println("WebSocket server started on the same port");
Serial.printf("my network address is either 'arduinoWebsockets.local' (mDNS) or '%s'\n", WiFi.localIP().toString().c_str());
if (!MDNS.begin("arduinoWebsockets")) {
Serial.println("Error setting up MDNS responder!");
}
}
void loop() {
server.handleClient();
webSocket.loop();
MDNS.update();
}

View File

@ -0,0 +1,20 @@
#!/bin/sh
# linux script to compile&run arduinoWebSockets in a mock environment
if [ -z "$ESP8266ARDUINO" ]; then
echo "please set ESP8266ARDUINO env-var to where esp8266/arduino sits"
exit 1
fi
set -e
where=$(pwd)
cd $ESP8266ARDUINO/tests/host/
make -j FORCE32=0 \
ULIBDIRS=../../libraries/Hash/:~/dev/proj/arduino/libraries/arduinoWebSockets \
${where}/WebSocketServerHooked
valgrind ./bin/WebSocketServerHooked/WebSocketServerHooked -b "$@"

View File

@ -0,0 +1,45 @@
#!/usr/bin/env python3
# python websocket client to test with
# emulator: server is at ws://127.0.0.1:9080/ws
# esp8266: server is at ws:///ws
# (uncomment the right line below)
#uri = "ws://127.0.0.1:9080/ws"
uri = "ws://arduinoWebsockets.local/ws"
import websocket
try:
import thread
except ImportError:
import _thread as thread
import time
def on_message(ws, message):
print("message");
print(message)
def on_error(ws, error):
print("error")
print(error)
def on_close(ws):
print("### closed ###")
def on_open(ws):
print("opened")
def run(*args):
for i in range(3):
time.sleep(1)
ws.send("Hello %d" % i)
time.sleep(1)
ws.close()
print("thread terminating...")
thread.start_new_thread(run, ())
if __name__ == "__main__":
websocket.enableTrace(True)
ws = websocket.WebSocketApp(uri, on_message = on_message, on_error = on_error, on_close = on_close)
ws.on_open = on_open
ws.run_forever()

View File

@ -23,7 +23,7 @@
ESP8266WiFiMulti WiFiMulti; ESP8266WiFiMulti WiFiMulti;
ESP8266WebServer server = ESP8266WebServer(80); ESP8266WebServer server(80);
WebSocketsServer webSocket = WebSocketsServer(81); WebSocketsServer webSocket = WebSocketsServer(81);
void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) { void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
@ -119,4 +119,3 @@ void loop() {
webSocket.loop(); webSocket.loop();
server.handleClient(); server.handleClient();
} }

View File

@ -1,28 +1,25 @@
{ {
"name": "WebSockets",
"description": "WebSocket Server and Client for Arduino based on RFC6455",
"keywords": "wifi, http, web, server, client, websocket",
"authors": [ "authors": [
{ {
"maintainer": true,
"name": "Markus Sattler", "name": "Markus Sattler",
"url": "https://github.com/Links2004", "url": "https://github.com/Links2004"
"maintainer": true
} }
], ],
"repository": { "description": "WebSocket Server and Client for Arduino based on RFC6455",
"type": "git",
"url": "https://github.com/Links2004/arduinoWebSockets.git"
},
"version": "2.0.7",
"license": "LGPL-2.1",
"export": { "export": {
"exclude": [ "exclude": [
"tests" "tests"
] ]
}, },
"frameworks": "arduino", "frameworks": "arduino",
"platforms": "*", "keywords": "wifi, http, web, server, client, websocket",
"examples": [ "license": "LGPL-2.1",
"examples/*/*.ino" "name": "WebSockets",
] "platforms": "atmelavr, espressif8266, espressif32",
} "repository": {
"type": "git",
"url": "https://github.com/Links2004/arduinoWebSockets.git"
},
"version": "2.3.5"
}

View File

@ -1,7 +1,7 @@
name=WebSockets name=WebSockets
version=2.0.7 version=2.3.5
author=Markus Sattler author=Markus Sattler
maintainer=Markus Sattler maintainer=Markus Sattler
sentence=WebSockets for Arduino (Server + Client) sentence=WebSockets for Arduino (Server + Client)
paragraph=use 2.x.x for ESP and 1.3 for AVR paragraph=use 2.x.x for ESP and 1.3 for AVR
category=Communication category=Communication

252
src/SocketIOclient.cpp Normal file
View File

@ -0,0 +1,252 @@
/*
* 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);
initClient();
}
void SocketIOclient::begin(String host, uint16_t port, String url, String protocol) {
WebSocketsClient::beginSocketIO(host, port, url, protocol);
WebSocketsClient::enableHeartbeat(60 * 1000, 90 * 1000, 5);
initClient();
}
#if defined(HAS_SSL)
void SocketIOclient::beginSSL(const char * host, uint16_t port, const char * url, const char * protocol) {
WebSocketsClient::beginSocketIOSSL(host, port, url, protocol);
WebSocketsClient::enableHeartbeat(60 * 1000, 90 * 1000, 5);
initClient();
}
void SocketIOclient::beginSSL(String host, uint16_t port, String url, String protocol) {
WebSocketsClient::beginSocketIOSSL(host, port, url, protocol);
WebSocketsClient::enableHeartbeat(60 * 1000, 90 * 1000, 5);
initClient();
}
#if defined(SSL_BARESSL)
void SocketIOclient::beginSSLWithCA(const char * host, uint16_t port, const char * url, const char * CA_cert, const char * protocol) {
WebSocketsClient::beginSocketIOSSLWithCA(host, port, url, CA_cert, protocol);
WebSocketsClient::enableHeartbeat(60 * 1000, 90 * 1000, 5);
initClient();
}
void SocketIOclient::beginSSLWithCA(const char * host, uint16_t port, const char * url, BearSSL::X509List * CA_cert, const char * protocol) {
WebSocketsClient::beginSocketIOSSLWithCA(host, port, url, CA_cert, protocol);
WebSocketsClient::enableHeartbeat(60 * 1000, 90 * 1000, 5);
initClient();
}
void SocketIOclient::setSSLClientCertKey(const char * clientCert, const char * clientPrivateKey) {
WebSocketsClient::setSSLClientCertKey(clientCert, clientPrivateKey);
}
void SocketIOclient::setSSLClientCertKey(BearSSL::X509List * clientCert, BearSSL::PrivateKey * clientPrivateKey) {
WebSocketsClient::setSSLClientCertKey(clientCert, clientPrivateKey);
}
#endif
#endif
void SocketIOclient::configureEIOping(bool disableHeartbeat) {
_disableHeartbeat = disableHeartbeat;
}
void SocketIOclient::initClient(void) {
if(_client.cUrl.indexOf("EIO=4") != -1) {
DEBUG_WEBSOCKETS("[wsIOc] found EIO=4 disable EIO ping on client\n");
configureEIOping(true);
}
}
/**
* 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) && _client.status == WSC_CONNECTED) {
if(!headerToPayload) {
// webSocket Header
ret = WebSocketsClient::sendFrameHeader(&_client, WSop_text, length + 2, true);
// Engine.IO / Socket.IO Header
if(ret) {
uint8_t buf[3] = { eIOtype_MESSAGE, 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(!_disableHeartbeat && (t - _lastHeartbeat) > EIO_HEARTBEAT_INTERVAL) {
_lastHeartbeat = 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("2probe");
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:
DEBUG_WEBSOCKETS("[wsIOc] connected (%d): %s\n", lData, data);
return;
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;
}
}

101
src/SocketIOclient.h Normal file
View File

@ -0,0 +1,101 @@
/**
* 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");
#ifdef HAS_SSL
void beginSSL(const char * host, uint16_t port, const char * url = "/socket.io/?EIO=3", const char * protocol = "arduino");
void beginSSL(String host, uint16_t port, String url = "/socket.io/?EIO=3", String protocol = "arduino");
#ifndef SSL_AXTLS
void beginSSLWithCA(const char * host, uint16_t port, const char * url = "/socket.io/?EIO=3", const char * CA_cert = NULL, const char * protocol = "arduino");
void beginSSLWithCA(const char * host, uint16_t port, const char * url = "/socket.io/?EIO=3", BearSSL::X509List * CA_cert = NULL, const char * protocol = "arduino");
void setSSLClientCertKey(const char * clientCert = NULL, const char * clientPrivateKey = NULL);
void setSSLClientCertKey(BearSSL::X509List * clientCert = NULL, BearSSL::PrivateKey * clientPrivateKey = NULL);
#endif
#endif
bool isConnected(void);
void onEvent(SocketIOclientEvent cbEvent);
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);
void configureEIOping(bool disableHeartbeat = false);
protected:
bool _disableHeartbeat = false;
uint64_t _lastHeartbeat = 0;
SocketIOclientEvent _cbEvent;
virtual void runIOCbEvent(socketIOmessageType_t type, uint8_t * payload, size_t length) {
if(_cbEvent) {
_cbEvent(type, payload, length);
}
}
void initClient(void);
// 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_ */

View File

@ -38,6 +38,15 @@ extern "C" {
#ifdef ESP8266 #ifdef ESP8266
#include <Hash.h> #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 #else
extern "C" { extern "C" {
@ -50,14 +59,14 @@ extern "C" {
* *
* @param client WSclient_t * ptr to the client struct * @param client WSclient_t * ptr to the client struct
* @param code uint16_t see RFC * @param code uint16_t see RFC
* @param reason * @param reason ptr to the disconnect reason message
* @param reasonLen * @param reasonLen length of the disconnect reason message
*/ */
void WebSockets::clientDisconnect(WSclient_t * client, uint16_t code, char * reason, size_t reasonLen) { 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); DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] clientDisconnect code: %u\n", client->num, code);
if(client->status == WSC_CONNECTED && code) { if(client->status == WSC_CONNECTED && code) {
if(reason) { if(reason) {
sendFrame(client, WSop_close, (uint8_t *) reason, reasonLen); sendFrame(client, WSop_close, (uint8_t *)reason, reasonLen);
} else { } else {
uint8_t buffer[2]; uint8_t buffer[2];
buffer[0] = ((code >> 8) & 0xFF); 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 opcode WSopcode_t
* @param payload uint8_t * * @param length size_t length of the payload
* @param length size_t
* @param mask bool add dummy mask to the frame (needed for web browser) * @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 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) { uint8_t WebSockets::createHeader(uint8_t * headerPtr, WSopcode_t opcode, size_t length, bool mask, uint8_t maskKey[4], bool fin) {
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 headerSize; uint8_t headerSize;
uint8_t * headerPtr;
uint8_t * payloadPtr = payload;
bool useInternBuffer = false;
bool ret = true;
// calculate header Size // calculate header Size
if(length < 126) { if(length < 126) {
headerSize = 2; headerSize = 2;
@ -120,29 +101,6 @@ bool WebSockets::sendFrame(WSclient_t * client, WSopcode_t opcode, uint8_t * pay
headerSize += 4; 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];
}
// create header // create header
// byte 0 // byte 0
@ -150,7 +108,7 @@ bool WebSockets::sendFrame(WSclient_t * client, WSopcode_t opcode, uint8_t * pay
if(fin) { if(fin) {
*headerPtr |= bit(7); ///< set Fin *headerPtr |= bit(7); ///< set Fin
} }
*headerPtr |= opcode; ///< set opcode *headerPtr |= opcode; ///< set opcode
headerPtr++; headerPtr++;
// byte 1 // byte 1
@ -192,36 +150,133 @@ bool WebSockets::sendFrame(WSclient_t * client, WSopcode_t opcode, uint8_t * pay
} }
if(mask) { if(mask) {
if(useInternBuffer) { *headerPtr = maskKey[0];
// if we use a Intern Buffer we can modify the data headerPtr++;
// by this fact its possible the do the masking *headerPtr = maskKey[1];
for(uint8_t x = 0; x < sizeof(maskKey); x++) { headerPtr++;
maskKey[x] = random(0xFF); *headerPtr = maskKey[2];
*headerPtr = maskKey[x]; headerPtr++;
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) { uint8_t headerSize = createHeader(&buffer[0], opcode, length, client->cIsClient, maskKey, fin);
dataMaskPtr = (payloadPtr + WEBSOCKETS_MAX_HEADER_SIZE);
} else {
dataMaskPtr = payloadPtr;
}
for(size_t x = 0; x < length; x++) { if(write(client, &buffer[0], headerSize) != headerSize) {
dataMaskPtr[x] = (dataMaskPtr[x] ^ maskKey[x % 4]); 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 { } else {
*headerPtr = maskKey[0]; dataMaskPtr = payloadPtr;
headerPtr++; }
*headerPtr = maskKey[1];
headerPtr++; for(size_t x = 0; x < length; x++) {
*headerPtr = maskKey[2]; dataMaskPtr[x] = (dataMaskPtr[x] ^ maskKey[x % 4]);
headerPtr++;
*headerPtr = maskKey[3];
headerPtr++;
} }
} }
@ -233,24 +288,24 @@ bool WebSockets::sendFrame(WSclient_t * client, WSopcode_t opcode, uint8_t * pay
// header has be added to payload // 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 // 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 // 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; ret = false;
} }
} else { } else {
// send header // send header
if(client->tcp->write(&buffer[0], headerSize) != headerSize) { if(write(client, &buffer[0], headerSize) != headerSize) {
ret = false; ret = false;
} }
if(payloadPtr && length > 0) { if(payloadPtr && length > 0) {
// send payload // send payload
if(client->tcp->write(&payloadPtr[0], length) != length) { if(write(client, &payloadPtr[0], length) != length) {
ret = false; 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 #ifdef WEBSOCKETS_USE_BIG_MEM
if(useInternBuffer && payloadPtr) { 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 * @param client WSclient_t * ptr to the client struct
*/ */
void WebSockets::headerDone(WSclient_t * client) { void WebSockets::headerDone(WSclient_t * client) {
client->status = WSC_CONNECTED; client->status = WSC_CONNECTED;
client->cWsRXsize = 0; client->cWsRXsize = 0;
DEBUG_WEBSOCKETS("[WS][%d][headerDone] Header Handling Done (%uus).\n", client->num); DEBUG_WEBSOCKETS("[WS][%d][headerDone] Header Handling Done.\n", client->num);
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) #if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
client->cHttpLine = ""; client->cHttpLine = "";
handleWebsocket(client); handleWebsocket(client);
#endif #endif
@ -316,12 +371,12 @@ bool WebSockets::handleWebsocketWaitFor(WSclient_t * client, size_t size) {
// timeout or error // timeout or error
server->clientDisconnect(client, 1002); server->clientDisconnect(client, 1002);
} }
}, this, size, std::placeholders::_1, std::placeholders::_2)); },
this, size, std::placeholders::_1, std::placeholders::_2));
return false; return false;
} }
void WebSockets::handleWebsocketCb(WSclient_t * client) { void WebSockets::handleWebsocketCb(WSclient_t * client) {
if(!client->tcp || !client->tcp->connected()) { if(!client->tcp || !client->tcp->connected()) {
return; return;
} }
@ -329,7 +384,7 @@ void WebSockets::handleWebsocketCb(WSclient_t * client) {
uint8_t * buffer = client->cWsHeader; uint8_t * buffer = client->cWsHeader;
WSMessageHeader_t * header = &client->cWsHeaderDecode; WSMessageHeader_t * header = &client->cWsHeaderDecode;
uint8_t * payload = NULL; uint8_t * payload = NULL;
uint8_t headerLen = 2; uint8_t headerLen = 2;
@ -338,15 +393,15 @@ void WebSockets::handleWebsocketCb(WSclient_t * client) {
} }
// split first 2 bytes in the data // split first 2 bytes in the data
header->fin = ((*buffer >> 7) & 0x01); header->fin = ((*buffer >> 7) & 0x01);
header->rsv1 = ((*buffer >> 6) & 0x01); header->rsv1 = ((*buffer >> 6) & 0x01);
header->rsv2 = ((*buffer >> 5) & 0x01); header->rsv2 = ((*buffer >> 5) & 0x01);
header->rsv3 = ((*buffer >> 4) & 0x01); header->rsv3 = ((*buffer >> 4) & 0x01);
header->opCode = (WSopcode_t) (*buffer & 0x0F); header->opCode = (WSopcode_t)(*buffer & 0x0F);
buffer++; buffer++;
header->mask = ((*buffer >> 7) & 0x01); header->mask = ((*buffer >> 7) & 0x01);
header->payloadLen = (WSopcode_t) (*buffer & 0x7F); header->payloadLen = (WSopcode_t)(*buffer & 0x7F);
buffer++; buffer++;
if(header->payloadLen == 126) { if(header->payloadLen == 126) {
@ -393,7 +448,7 @@ void WebSockets::handleWebsocketCb(WSclient_t * client) {
if(header->payloadLen > 0) { if(header->payloadLen > 0) {
// if text data we need one more // if text data we need one more
payload = (uint8_t *) malloc(header->payloadLen + 1); payload = (uint8_t *)malloc(header->payloadLen + 1);
if(!payload) { if(!payload) {
DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] to less memory to handle payload %d!\n", client->num, header->payloadLen); 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) { void WebSockets::handleWebsocketPayloadCb(WSclient_t * client, bool ok, uint8_t * payload) {
WSMessageHeader_t * header = &client->cWsHeaderDecode; WSMessageHeader_t * header = &client->cWsHeaderDecode;
if(ok) { if(ok) {
if(header->payloadLen > 0) { if(header->payloadLen > 0) {
@ -431,27 +485,32 @@ void WebSockets::handleWebsocketPayloadCb(WSclient_t * client, bool ok, uint8_t
break; break;
case WSop_ping: case WSop_ping:
// send pong back // send pong back
sendFrame(client, WSop_pong, payload, header->payloadLen, true); 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; break;
case WSop_pong: case WSop_pong:
DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] get pong (%s)\n", client->num, payload ? (const char*)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; break;
case WSop_close: { case WSop_close: {
#ifndef NODEBUG_WEBSOCKETS
uint16_t reasonCode = 1000; uint16_t reasonCode = 1000;
if(header->payloadLen >= 2) { if(header->payloadLen >= 2) {
reasonCode = payload[0] << 8 | payload[1]; reasonCode = payload[0] << 8 | payload[1];
} }
#endif
DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] get ask for close. Code: %d", client->num, reasonCode); DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] get ask for close. Code: %d\n", client->num, reasonCode);
if(header->payloadLen > 2) { if(header->payloadLen > 2) {
DEBUG_WEBSOCKETS(" (%s)\n", (payload + 2)); DEBUG_WEBSOCKETS(" (%s)\n", (payload + 2));
} else { } else {
DEBUG_WEBSOCKETS("\n"); DEBUG_WEBSOCKETS("\n");
} }
clientDisconnect(client, 1000); clientDisconnect(client, 1000);
} } break;
break;
default: default:
DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] got unknown opcode: %d\n", client->num, header->opCode);
clientDisconnect(client, 1002); clientDisconnect(client, 1002);
break; break;
} }
@ -462,7 +521,7 @@ void WebSockets::handleWebsocketPayloadCb(WSclient_t * client, bool ok, uint8_t
// reset input // reset input
client->cWsRXsize = 0; client->cWsRXsize = 0;
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) #if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
//register callback for next message //register callback for next message
handleWebsocketWaitFor(client, 2); handleWebsocketWaitFor(client, 2);
#endif #endif
@ -483,11 +542,14 @@ String WebSockets::acceptKey(String & clientKey) {
uint8_t sha1HashBin[20] = { 0 }; uint8_t sha1HashBin[20] = { 0 };
#ifdef ESP8266 #ifdef ESP8266
sha1(clientKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11", &sha1HashBin[0]); 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 #else
clientKey += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; clientKey += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
SHA1_CTX ctx; SHA1_CTX ctx;
SHA1Init(&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); SHA1Final(&sha1HashBin[0], &ctx);
#endif #endif
@ -504,13 +566,13 @@ String WebSockets::acceptKey(String & clientKey) {
* @return base64 encoded String * @return base64 encoded String
*/ */
String WebSockets::base64_encode(uint8_t * data, size_t length) { String WebSockets::base64_encode(uint8_t * data, size_t length) {
size_t size = ((length * 1.6f) + 1); size_t size = ((length * 1.6f) + 1);
char * buffer = (char *) malloc(size); char * buffer = (char *)malloc(size);
if(buffer) { if(buffer) {
base64_encodestate _state; base64_encodestate _state;
base64_init_encodestate(&_state); base64_init_encodestate(&_state);
int len = base64_encode_block((const char *) &data[0], length, &buffer[0], &_state); int len = base64_encode_block((const char *)&data[0], length, &buffer[0], &_state);
len = base64_encode_blockend((buffer + len), &_state); len = base64_encode_blockend((buffer + len), &_state);
String base64 = String(buffer); String base64 = String(buffer);
free(buffer); free(buffer);
@ -527,7 +589,7 @@ String WebSockets::base64_encode(uint8_t * data, size_t length) {
* @return true if ok * @return true if ok
*/ */
bool WebSockets::readCb(WSclient_t * client, uint8_t * out, size_t n, WSreadWaitCb cb) { 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()) { if(!client->tcp || !client->tcp->connected()) {
return false; return false;
} }
@ -536,12 +598,13 @@ bool WebSockets::readCb(WSclient_t * client, uint8_t * out, size_t n, WSreadWait
if(cb) { if(cb) {
cb(client, ok); cb(client, ok);
} }
}, client, std::placeholders::_1, cb)); },
client, std::placeholders::_1, cb));
#else #else
unsigned long t = millis(); unsigned long t = millis();
size_t len; ssize_t len;
DEBUG_WEBSOCKETS("[readCb] n: %d t: %d\n", n, t); DEBUG_WEBSOCKETS("[readCb] n: %zu t: %lu\n", n, t);
while(n > 0) { while(n > 0) {
if(client->tcp == NULL) { if(client->tcp == NULL) {
DEBUG_WEBSOCKETS("[readCb] tcp is null!\n"); DEBUG_WEBSOCKETS("[readCb] tcp is null!\n");
@ -560,7 +623,7 @@ bool WebSockets::readCb(WSclient_t * client, uint8_t * out, size_t n, WSreadWait
} }
if((millis() - t) > WEBSOCKETS_TCP_TIMEOUT) { 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) { if(cb) {
cb(client, false); cb(client, false);
} }
@ -568,14 +631,12 @@ bool WebSockets::readCb(WSclient_t * client, uint8_t * out, size_t n, WSreadWait
} }
if(!client->tcp->available()) { if(!client->tcp->available()) {
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) WEBSOCKETS_YIELD_MORE();
delay(0);
#endif
continue; continue;
} }
len = client->tcp->read((uint8_t*) out, n); len = client->tcp->read((uint8_t *)out, n);
if(len) { if(len > 0) {
t = millis(); t = millis();
out += len; out += len;
n -= len; n -= len;
@ -583,13 +644,114 @@ bool WebSockets::readCb(WSclient_t * client, uint8_t * out, size_t n, WSreadWait
} else { } else {
//DEBUG_WEBSOCKETS("Receive %d left %d!\n", len, n); //DEBUG_WEBSOCKETS("Receive %d left %d!\n", len, n);
} }
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) if(n > 0) {
delay(0); WEBSOCKETS_YIELD();
#endif }
} }
if(cb) { if(cb) {
cb(client, true); cb(client, true);
} }
WEBSOCKETS_YIELD();
#endif #endif
return true; 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("WS write %d failed left %d!\n", len, n);
}
if(n > 0) {
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);
}
}
}
}
}

View File

@ -27,85 +27,126 @@
#ifdef STM32_DEVICE #ifdef STM32_DEVICE
#include <application.h> #include <application.h>
#define bit(b) (1UL << (b)) // Taken directly from Arduino.h #define bit(b) (1UL << (b)) // Taken directly from Arduino.h
#else #else
#include <Arduino.h> #include <Arduino.h>
#include <IPAddress.h>
#endif #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> #include <functional>
#endif
#include "WebSocketsVersion.h"
#ifndef NODEBUG_WEBSOCKETS
#ifdef DEBUG_ESP_PORT #ifdef DEBUG_ESP_PORT
#define DEBUG_WEBSOCKETS(...) DEBUG_ESP_PORT.printf( __VA_ARGS__ ) #define DEBUG_WEBSOCKETS(...) \
{ \
DEBUG_ESP_PORT.printf(__VA_ARGS__); \
DEBUG_ESP_PORT.flush(); \
}
#else #else
//#define DEBUG_WEBSOCKETS(...) os_printf( __VA_ARGS__ ) //#define DEBUG_WEBSOCKETS(...) os_printf( __VA_ARGS__ )
#endif #endif
#endif
#ifndef DEBUG_WEBSOCKETS #ifndef DEBUG_WEBSOCKETS
#define DEBUG_WEBSOCKETS(...) #define DEBUG_WEBSOCKETS(...)
#ifndef NODEBUG_WEBSOCKETS
#define NODEBUG_WEBSOCKETS #define NODEBUG_WEBSOCKETS
#endif #endif
#endif
#ifdef ESP8266 #if defined(ESP8266) || defined(ESP32)
#define WEBSOCKETS_MAX_DATA_SIZE (15*1024)
#define WEBSOCKETS_MAX_DATA_SIZE (15 * 1024)
#define WEBSOCKETS_USE_BIG_MEM #define WEBSOCKETS_USE_BIG_MEM
#define GET_FREE_HEAP ESP.getFreeHeap() #define GET_FREE_HEAP ESP.getFreeHeap()
// moves all Header strings to Flash (~300 Byte) // moves all Header strings to Flash (~300 Byte)
//#define WEBSOCKETS_SAVE_RAM //#define WEBSOCKETS_SAVE_RAM
#else
#ifdef STM32_DEVICE #if defined(ESP8266)
#define WEBSOCKETS_MAX_DATA_SIZE (15*1024) #define WEBSOCKETS_YIELD() delay(0)
#define WEBSOCKETS_YIELD_MORE() delay(1)
#elif defined(ESP32)
#define WEBSOCKETS_YIELD() yield()
#define WEBSOCKETS_YIELD_MORE() delay(1)
#endif
#elif defined(STM32_DEVICE)
#define WEBSOCKETS_MAX_DATA_SIZE (15 * 1024)
#define WEBSOCKETS_USE_BIG_MEM #define WEBSOCKETS_USE_BIG_MEM
#define GET_FREE_HEAP System.freeMemory() #define GET_FREE_HEAP System.freeMemory()
#define WEBSOCKETS_YIELD()
#define WEBSOCKETS_YIELD_MORE()
#else #else
//atmega328p has only 2KB ram! //atmega328p has only 2KB ram!
#define WEBSOCKETS_MAX_DATA_SIZE (1024) #define WEBSOCKETS_MAX_DATA_SIZE (1024)
// moves all Header strings to Flash // moves all Header strings to Flash
#define WEBSOCKETS_SAVE_RAM #define WEBSOCKETS_SAVE_RAM
#endif #define WEBSOCKETS_YIELD()
#define WEBSOCKETS_YIELD_MORE()
#endif #endif
#define WEBSOCKETS_TCP_TIMEOUT (2000) #define WEBSOCKETS_TCP_TIMEOUT (5000)
#define NETWORK_ESP8266_ASYNC (0) #define NETWORK_ESP8266_ASYNC (0)
#define NETWORK_ESP8266 (1) #define NETWORK_ESP8266 (1)
#define NETWORK_W5100 (2) #define NETWORK_W5100 (2)
#define NETWORK_ENC28J60 (3) #define NETWORK_ENC28J60 (3)
#define NETWORK_ESP32 (4)
#define NETWORK_ESP32_ETH (5)
// max size of the WS Message Header // max size of the WS Message Header
#define WEBSOCKETS_MAX_HEADER_SIZE (14) #define WEBSOCKETS_MAX_HEADER_SIZE (14)
#if !defined(WEBSOCKETS_NETWORK_TYPE) #if !defined(WEBSOCKETS_NETWORK_TYPE)
// select Network type based // select Network type based
#if defined(ESP8266) || defined(ESP31B) #if defined(ESP8266) || defined(ESP31B)
#define WEBSOCKETS_NETWORK_TYPE NETWORK_ESP8266 #define WEBSOCKETS_NETWORK_TYPE NETWORK_ESP8266
//#define WEBSOCKETS_NETWORK_TYPE NETWORK_ESP8266_ASYNC //#define WEBSOCKETS_NETWORK_TYPE NETWORK_ESP8266_ASYNC
//#define WEBSOCKETS_NETWORK_TYPE NETWORK_W5100 //#define WEBSOCKETS_NETWORK_TYPE NETWORK_W5100
#elif defined(ESP32)
#define WEBSOCKETS_NETWORK_TYPE NETWORK_ESP32
//#define WEBSOCKETS_NETWORK_TYPE NETWORK_ESP32_ETH
#else #else
#define WEBSOCKETS_NETWORK_TYPE NETWORK_W5100 #define WEBSOCKETS_NETWORK_TYPE NETWORK_W5100
#endif #endif
#endif #endif
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) // Includes and defined based on Network Type
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
// Note: // Note:
// No SSL/WSS support for client in Async mode // No SSL/WSS support for client in Async mode
// TLS lib need a sync interface! // 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!" #error "network type ESP8266 ASYNC only possible on the ESP mcu!"
#endif #endif
#ifdef ESP8266
#include <ESP8266WiFi.h>
#else
#include <ESP31BWiFi.h>
#endif
#include <ESPAsyncTCP.h> #include <ESPAsyncTCP.h>
#include <ESPAsyncTCPbuffer.h> #include <ESPAsyncTCPbuffer.h>
#define WEBSOCKETS_NETWORK_CLASS AsyncTCPbuffer #define WEBSOCKETS_NETWORK_CLASS AsyncTCPbuffer
#define WEBSOCKETS_NETWORK_SERVER_CLASS AsyncServer #define WEBSOCKETS_NETWORK_SERVER_CLASS AsyncServer
#elif (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) #elif(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
#if !defined(ESP8266) && !defined(ESP31B) #if !defined(ESP8266) && !defined(ESP31B)
#error "network type ESP8266 only possible on the ESP mcu!" #error "network type ESP8266 only possible on the ESP mcu!"
@ -113,13 +154,19 @@
#ifdef ESP8266 #ifdef ESP8266
#include <ESP8266WiFi.h> #include <ESP8266WiFi.h>
#if defined(wificlientbearssl_h) && !defined(USING_AXTLS) && !defined(wificlientsecure_h)
#define SSL_BARESSL
#else
#define SSL_AXTLS
#endif
#else #else
#include <ESP31BWiFi.h> #include <ESP31BWiFi.h>
#endif #endif
#define WEBSOCKETS_NETWORK_CLASS WiFiClient #define WEBSOCKETS_NETWORK_CLASS WiFiClient
#define WEBSOCKETS_NETWORK_SSL_CLASS WiFiClientSecure
#define WEBSOCKETS_NETWORK_SERVER_CLASS WiFiServer #define WEBSOCKETS_NETWORK_SERVER_CLASS WiFiServer
#elif (WEBSOCKETS_NETWORK_TYPE == NETWORK_W5100) #elif(WEBSOCKETS_NETWORK_TYPE == NETWORK_W5100)
#ifdef STM32_DEVICE #ifdef STM32_DEVICE
#define WEBSOCKETS_NETWORK_CLASS TCPClient #define WEBSOCKETS_NETWORK_CLASS TCPClient
@ -131,26 +178,46 @@
#define WEBSOCKETS_NETWORK_SERVER_CLASS EthernetServer #define WEBSOCKETS_NETWORK_SERVER_CLASS EthernetServer
#endif #endif
#elif (WEBSOCKETS_NETWORK_TYPE == NETWORK_ENC28J60) #elif(WEBSOCKETS_NETWORK_TYPE == NETWORK_ENC28J60)
#include <UIPEthernet.h> #include <UIPEthernet.h>
#define WEBSOCKETS_NETWORK_CLASS UIPClient #define WEBSOCKETS_NETWORK_CLASS UIPClient
#define WEBSOCKETS_NETWORK_SERVER_CLASS UIPServer #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 #else
#error "no network type selected!" #error "no network type selected!"
#endif #endif
#ifdef WEBSOCKETS_NETWORK_SSL_CLASS
#define HAS_SSL
#endif
// moves all Header strings to Flash (~300 Byte) // moves all Header strings to Flash (~300 Byte)
#ifdef WEBSOCKETS_SAVE_RAM #ifdef WEBSOCKETS_SAVE_RAM
#define WEBSOCKETS_STRING(var) F(var) #define WEBSOCKETS_STRING(var) F(var)
#else #else
#define WEBSOCKETS_STRING(var) var #define WEBSOCKETS_STRING(var) var
#endif #endif
typedef enum { typedef enum {
WSC_NOT_CONNECTED, WSC_NOT_CONNECTED,
WSC_HEADER, WSC_HEADER,
WSC_BODY,
WSC_CONNECTED WSC_CONNECTED
} WSclientsStatus_t; } WSclientsStatus_t;
@ -160,115 +227,141 @@ typedef enum {
WStype_CONNECTED, WStype_CONNECTED,
WStype_TEXT, WStype_TEXT,
WStype_BIN, WStype_BIN,
WStype_FRAGMENT_TEXT_START, WStype_FRAGMENT_TEXT_START,
WStype_FRAGMENT_BIN_START, WStype_FRAGMENT_BIN_START,
WStype_FRAGMENT, WStype_FRAGMENT,
WStype_FRAGMENT_FIN, WStype_FRAGMENT_FIN,
WStype_PING,
WStype_PONG,
} WStype_t; } WStype_t;
typedef enum { typedef enum {
WSop_continuation = 0x00, ///< %x0 denotes a continuation frame WSop_continuation = 0x00, ///< %x0 denotes a continuation frame
WSop_text = 0x01, ///< %x1 denotes a text frame WSop_text = 0x01, ///< %x1 denotes a text frame
WSop_binary = 0x02, ///< %x2 denotes a binary frame WSop_binary = 0x02, ///< %x2 denotes a binary frame
///< %x3-7 are reserved for further non-control frames ///< %x3-7 are reserved for further non-control frames
WSop_close = 0x08, ///< %x8 denotes a connection close WSop_close = 0x08, ///< %x8 denotes a connection close
WSop_ping = 0x09, ///< %x9 denotes a ping WSop_ping = 0x09, ///< %x9 denotes a ping
WSop_pong = 0x0A ///< %xA denotes a pong WSop_pong = 0x0A ///< %xA denotes a pong
///< %xB-F are reserved for further control frames ///< %xB-F are reserved for further control frames
} WSopcode_t; } WSopcode_t;
typedef struct { typedef struct {
bool fin;
bool rsv1;
bool rsv2;
bool rsv3;
bool fin; WSopcode_t opCode;
bool rsv1; bool mask;
bool rsv2;
bool rsv3;
WSopcode_t opCode; size_t payloadLen;
bool mask;
size_t payloadLen; uint8_t * maskKey;
uint8_t * maskKey;
} WSMessageHeader_t; } WSMessageHeader_t;
typedef struct { typedef struct {
uint8_t num; ///< connection number void init(uint8_t num,
uint32_t pingInterval,
uint32_t pongTimeout,
uint8_t disconnectTimeoutCount) {
this->num = num;
this->pingInterval = pingInterval;
this->pongTimeout = pongTimeout;
this->disconnectTimeoutCount = disconnectTimeoutCount;
}
WSclientsStatus_t status; uint8_t num = 0; ///< connection number
WEBSOCKETS_NETWORK_CLASS * tcp; WSclientsStatus_t status = WSC_NOT_CONNECTED;
bool isSocketIO; ///< client for socket.io server WEBSOCKETS_NETWORK_CLASS * tcp = nullptr;
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) bool isSocketIO = false; ///< client for socket.io server
bool isSSL; ///< run in ssl mode
WiFiClientSecure * ssl; #if defined(HAS_SSL)
bool isSSL = false; ///< run in ssl mode
WEBSOCKETS_NETWORK_SSL_CLASS * ssl;
#endif #endif
String cUrl; ///< http url String cUrl; ///< http url
uint16_t cCode; ///< http code uint16_t cCode = 0; ///< http code
bool cIsUpgrade; ///< Connection == Upgrade bool cIsClient = false; ///< will be used for masking
bool cIsWebsocket; ///< Upgrade == websocket bool cIsUpgrade = false; ///< Connection == Upgrade
bool cIsWebsocket = false; ///< Upgrade == websocket
String cSessionId; ///< client Set-Cookie (session id) String cSessionId; ///< client Set-Cookie (session id)
String cKey; ///< client Sec-WebSocket-Key String cKey; ///< client Sec-WebSocket-Key
String cAccept; ///< client Sec-WebSocket-Accept String cAccept; ///< client Sec-WebSocket-Accept
String cProtocol; ///< client Sec-WebSocket-Protocol String cProtocol; ///< client Sec-WebSocket-Protocol
String cExtensions; ///< client Sec-WebSocket-Extensions String cExtensions; ///< client Sec-WebSocket-Extensions
uint16_t cVersion; ///< client Sec-WebSocket-Version uint16_t cVersion = 0; ///< client Sec-WebSocket-Version
uint8_t cWsRXsize; ///< State of the RX uint8_t cWsRXsize = 0; ///< State of the RX
uint8_t cWsHeader[WEBSOCKETS_MAX_HEADER_SIZE]; ///< RX WS Message buffer uint8_t cWsHeader[WEBSOCKETS_MAX_HEADER_SIZE]; ///< RX WS Message buffer
WSMessageHeader_t cWsHeaderDecode; WSMessageHeader_t cWsHeaderDecode;
String base64Authorization; ///< Base64 encoded Auth request String base64Authorization; ///< Base64 encoded Auth request
String plainAuthorization; ///< Base64 encoded Auth request String plainAuthorization; ///< Base64 encoded Auth request
String extraHeaders; String extraHeaders;
bool cHttpHeadersValid; ///< non-websocket http header validity indicator bool cHttpHeadersValid = false; ///< non-websocket http header validity indicator
size_t cMandatoryHeadersCount; ///< non-websocket mandatory http headers present count size_t cMandatoryHeadersCount; ///< non-websocket mandatory http headers present count
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) bool pongReceived = false;
String cHttpLine; ///< HTTP header lines uint32_t pingInterval = 0; // how often ping will be sent, 0 means "heartbeat is not active"
uint32_t lastPing = 0; // millis when last pong has been received
uint32_t pongTimeout = 0; // interval in millis after which pong is considered to timeout
uint8_t disconnectTimeoutCount = 0; // after how many subsequent pong timeouts discconnect will happen, 0 means "do not disconnect"
uint8_t pongTimeoutCount = 0; // current pong timeout count
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
String cHttpLine; ///< HTTP header lines
#endif #endif
} WSclient_t; } WSclient_t;
class WebSockets { class WebSockets {
protected: protected:
#ifdef __AVR__ #ifdef __AVR__
typedef void (*WSreadWaitCb)(WSclient_t * client, bool ok); typedef void (*WSreadWaitCb)(WSclient_t * client, bool ok);
#else #else
typedef std::function<void(WSclient_t * client, bool ok)> WSreadWaitCb; typedef std::function<void(WSclient_t * client, bool ok)> WSreadWaitCb;
#endif #endif
virtual void clientDisconnect(WSclient_t * client); virtual void clientDisconnect(WSclient_t * client) = 0;
virtual bool clientIsConnected(WSclient_t * client); virtual bool clientIsConnected(WSclient_t * client) = 0;
virtual void messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin); 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); virtual void messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin) = 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);
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 handleWebsocket(WSclient_t * client);
void handleWebsocketCb(WSclient_t * client);
void handleWebsocketPayloadCb(WSclient_t * client, bool ok, uint8_t * payload);
String acceptKey(String & clientKey); bool handleWebsocketWaitFor(WSclient_t * client, size_t size);
String base64_encode(uint8_t * data, size_t length); 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_ */ #endif /* WEBSOCKETS_H_ */

View File

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

View File

@ -25,11 +25,14 @@
#include "WebSockets.h" #include "WebSockets.h"
#include "WebSocketsClient.h" #include "WebSocketsClient.h"
WebSocketsClient::WebSocketsClient() { WebSocketsClient::WebSocketsClient() {
_cbEvent = NULL; _cbEvent = NULL;
_client.num = 0; _client.num = 0;
_client.cIsClient = true;
_client.extraHeaders = WEBSOCKETS_STRING("Origin: file://"); _client.extraHeaders = WEBSOCKETS_STRING("Origin: file://");
_reconnectInterval = 500;
_port = 0;
_host = "";
} }
WebSocketsClient::~WebSocketsClient() { WebSocketsClient::~WebSocketsClient() {
@ -39,32 +42,37 @@ WebSocketsClient::~WebSocketsClient() {
/** /**
* calles to init the Websockets server * 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; _host = host;
_port = port; _port = port;
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) #if defined(HAS_SSL)
_fingerprint = ""; _fingerprint = SSL_FINGERPRINT_NULL;
_CA_cert = NULL;
#endif #endif
_client.num = 0; _client.num = 0;
_client.status = WSC_NOT_CONNECTED; _client.status = WSC_NOT_CONNECTED;
_client.tcp = NULL; _client.tcp = NULL;
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) #if defined(HAS_SSL)
_client.isSSL = false; _client.isSSL = false;
_client.ssl = NULL; _client.ssl = NULL;
#endif #endif
_client.cUrl = url; _client.cUrl = url;
_client.cCode = 0; _client.cCode = 0;
_client.cIsUpgrade = false; _client.cIsUpgrade = false;
_client.cIsWebsocket = true; _client.cIsWebsocket = true;
_client.cKey = ""; _client.cKey = "";
_client.cAccept = ""; _client.cAccept = "";
_client.cProtocol = protocol; _client.cProtocol = protocol;
_client.cExtensions = ""; _client.cExtensions = "";
_client.cVersion = 0; _client.cVersion = 0;
_client.base64Authorization = ""; _client.base64Authorization = "";
_client.plainAuthorization = ""; _client.plainAuthorization = "";
_client.isSocketIO = false; _client.isSocketIO = false;
_client.lastPing = 0;
_client.pongReceived = false;
_client.pongTimeoutCount = 0;
#ifdef ESP8266 #ifdef ESP8266
randomSeed(RANDOM_REG32); randomSeed(RANDOM_REG32);
@ -72,28 +80,75 @@ void WebSocketsClient::begin(const char *host, uint16_t port, const char * url,
// todo find better seed // todo find better seed
randomSeed(millis()); randomSeed(millis());
#endif #endif
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) #if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
asyncConnect(); asyncConnect();
#endif #endif
_lastConnectionFail = 0;
_lastHeaderSent = 0;
DEBUG_WEBSOCKETS("[WS-Client] Websocket Version: " WEBSOCKETS_VERSION "\n");
} }
void WebSocketsClient::begin(String host, uint16_t port, String url, String protocol) { void WebSocketsClient::begin(String host, uint16_t port, String url, String protocol) {
begin(host.c_str(), port, url.c_str(), protocol.c_str()); begin(host.c_str(), port, url.c_str(), protocol.c_str());
} }
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) void WebSocketsClient::begin(IPAddress host, uint16_t port, const char * url, const char * protocol) {
void WebSocketsClient::beginSSL(const char *host, uint16_t port, const char * url, const char * fingerprint, 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); begin(host, port, url, protocol);
_client.isSSL = true; _client.isSSL = true;
_fingerprint = fingerprint; _fingerprint = fingerprint;
_CA_cert = NULL;
} }
void WebSocketsClient::beginSSL(String host, uint16_t port, String url, String fingerprint, String protocol) { 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()); beginSSL(host.c_str(), port, url.c_str(), fingerprint.c_str(), protocol.c_str());
} }
#endif
void WebSocketsClient::beginSocketIO(const char *host, uint16_t port, const char * url, const char * protocol) { 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, BearSSL::X509List * CA_cert, const char * protocol) {
begin(host, port, url, protocol);
_client.isSSL = true;
_fingerprint = SSL_FINGERPRINT_NULL;
_CA_cert = CA_cert;
}
void WebSocketsClient::beginSslWithCA(const char * host, uint16_t port, const char * url, const char * CA_cert, const char * protocol) {
beginSslWithCA(host, port, url, new BearSSL::X509List(CA_cert), protocol);
}
void WebSocketsClient::setSSLClientCertKey(BearSSL::X509List * clientCert, BearSSL::PrivateKey * clientPrivateKey) {
_client_cert = clientCert;
_client_key = clientPrivateKey;
}
void WebSocketsClient::setSSLClientCertKey(const char * clientCert, const char * clientPrivateKey) {
setSSLClientCertKey(new BearSSL::X509List(clientCert), new BearSSL::PrivateKey(clientPrivateKey));
}
#endif // SSL_AXTLS
#endif // HAS_SSL
void WebSocketsClient::beginSocketIO(const char * host, uint16_t port, const char * url, const char * protocol) {
begin(host, port, url, protocol); begin(host, port, url, protocol);
_client.isSocketIO = true; _client.isSocketIO = true;
} }
@ -102,27 +157,58 @@ void WebSocketsClient::beginSocketIO(String host, uint16_t port, String url, Str
beginSocketIO(host.c_str(), port, url.c_str(), protocol.c_str()); beginSocketIO(host.c_str(), port, url.c_str(), protocol.c_str());
} }
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) #if defined(HAS_SSL)
void WebSocketsClient::beginSocketIOSSL(const char *host, uint16_t port, const char * url, const char * protocol) { void WebSocketsClient::beginSocketIOSSL(const char * host, uint16_t port, const char * url, const char * protocol) {
begin(host, port, url, protocol); begin(host, port, url, protocol);
_client.isSocketIO = true; _client.isSocketIO = true;
_client.isSSL = true; _client.isSSL = true;
_fingerprint = ""; _fingerprint = SSL_FINGERPRINT_NULL;
} }
void WebSocketsClient::beginSocketIOSSL(String host, uint16_t port, String url, String protocol) { void WebSocketsClient::beginSocketIOSSL(String host, uint16_t port, String url, String protocol) {
beginSocketIOSSL(host.c_str(), port, url.c_str(), protocol.c_str()); beginSocketIOSSL(host.c_str(), port, url.c_str(), protocol.c_str());
} }
#if defined(SSL_BARESSL)
void WebSocketsClient::beginSocketIOSSLWithCA(const char * host, uint16_t port, const char * url, BearSSL::X509List * CA_cert, const char * protocol) {
begin(host, port, url, protocol);
_client.isSocketIO = true;
_client.isSSL = true;
_fingerprint = SSL_FINGERPRINT_NULL;
_CA_cert = CA_cert;
}
#endif #endif
#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) 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_BARESSL)
_CA_cert = new BearSSL::X509List(CA_cert);
#else
_CA_cert = CA_cert;
#endif
}
#endif
#if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
/** /**
* called in arduino loop * called in arduino loop
*/ */
void WebSocketsClient::loop(void) { void WebSocketsClient::loop(void) {
if(_port == 0) {
return;
}
WEBSOCKETS_YIELD();
if(!clientIsConnected(&_client)) { 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) { if(_client.isSSL) {
DEBUG_WEBSOCKETS("[WS-Client] connect wss...\n"); DEBUG_WEBSOCKETS("[WS-Client] connect wss...\n");
if(_client.ssl) { if(_client.ssl) {
@ -130,15 +216,40 @@ void WebSocketsClient::loop(void) {
_client.ssl = NULL; _client.ssl = NULL;
_client.tcp = NULL; _client.tcp = NULL;
} }
_client.ssl = new WiFiClientSecure(); _client.ssl = new WEBSOCKETS_NETWORK_SSL_CLASS();
_client.tcp = _client.ssl; _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(ESP32)
} else if(!SSL_FINGERPRINT_IS_SET) {
_client.ssl->setInsecure();
#elif defined(SSL_BARESSL)
} else if(SSL_FINGERPRINT_IS_SET) {
_client.ssl->setFingerprint(_fingerprint);
} else {
_client.ssl->setInsecure();
}
if(_client_cert && _client_key) {
_client.ssl->setClientRSACert(_client_cert, _client_key);
DEBUG_WEBSOCKETS("[WS-Client] setting client certificate and key");
#endif
}
} else { } else {
DEBUG_WEBSOCKETS("[WS-Client] connect ws...\n"); DEBUG_WEBSOCKETS("[WS-Client] connect ws...\n");
if(_client.tcp) { if(_client.tcp) {
delete _client.tcp; delete _client.tcp;
_client.tcp = NULL; _client.tcp = NULL;
} }
_client.tcp = new WiFiClient(); _client.tcp = new WEBSOCKETS_NETWORK_CLASS();
} }
#else #else
_client.tcp = new WEBSOCKETS_NETWORK_CLASS(); _client.tcp = new WEBSOCKETS_NETWORK_CLASS();
@ -148,15 +259,25 @@ void WebSocketsClient::loop(void) {
DEBUG_WEBSOCKETS("[WS-Client] creating Network class failed!"); DEBUG_WEBSOCKETS("[WS-Client] creating Network class failed!");
return; 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)) { if(_client.tcp->connect(_host.c_str(), _port)) {
#endif
connectedCb(); connectedCb();
_lastConnectionFail = 0;
} else { } else {
connectFailedCb(); connectFailedCb();
delay(10); //some little delay to not flood the server _lastConnectionFail = millis();
} }
} else { } else {
handleClientData(); handleClientData();
WEBSOCKETS_YIELD();
if(_client.status == WSC_CONNECTED) {
handleHBPing();
handleHBTimeout(&_client);
}
} }
} }
#endif #endif
@ -179,28 +300,34 @@ void WebSocketsClient::onEvent(WebSocketClientEvent cbEvent) {
*/ */
bool WebSocketsClient::sendTXT(uint8_t * payload, size_t length, bool headerToPayload) { bool WebSocketsClient::sendTXT(uint8_t * payload, size_t length, bool headerToPayload) {
if(length == 0) { if(length == 0) {
length = strlen((const char *) payload); length = strlen((const char *)payload);
} }
if(clientIsConnected(&_client)) { if(clientIsConnected(&_client)) {
return sendFrame(&_client, WSop_text, payload, length, true, true, headerToPayload); return sendFrame(&_client, WSop_text, payload, length, true, headerToPayload);
} }
return false; return false;
} }
bool WebSocketsClient::sendTXT(const uint8_t * payload, size_t length) { 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) { 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) { 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) { 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);
} }
/** /**
@ -213,13 +340,13 @@ bool WebSocketsClient::sendTXT(String & payload) {
*/ */
bool WebSocketsClient::sendBIN(uint8_t * payload, size_t length, bool headerToPayload) { bool WebSocketsClient::sendBIN(uint8_t * payload, size_t length, bool headerToPayload) {
if(clientIsConnected(&_client)) { if(clientIsConnected(&_client)) {
return sendFrame(&_client, WSop_binary, payload, length, true, true, headerToPayload); return sendFrame(&_client, WSop_binary, payload, length, true, headerToPayload);
} }
return false; return false;
} }
bool WebSocketsClient::sendBIN(const uint8_t * payload, size_t length) { bool WebSocketsClient::sendBIN(const uint8_t * payload, size_t length) {
return sendBIN((uint8_t *) payload, length); return sendBIN((uint8_t *)payload, length);
} }
/** /**
@ -230,16 +357,18 @@ bool WebSocketsClient::sendBIN(const uint8_t * payload, size_t length) {
*/ */
bool WebSocketsClient::sendPing(uint8_t * payload, size_t length) { bool WebSocketsClient::sendPing(uint8_t * payload, size_t length) {
if(clientIsConnected(&_client)) { if(clientIsConnected(&_client)) {
return sendFrame(&_client, WSop_ping, payload, length, true); bool sent = sendFrame(&_client, WSop_ping, payload, length);
if(sent)
_client.lastPing = millis();
return sent;
} }
return false; return false;
} }
bool WebSocketsClient::sendPing(String & payload) { bool WebSocketsClient::sendPing(String & payload) {
return sendPing((uint8_t *) payload.c_str(), payload.length()); return sendPing((uint8_t *)payload.c_str(), payload.length());
} }
/** /**
* disconnect one client * disconnect one client
* @param num uint8_t client id * @param num uint8_t client id
@ -284,6 +413,19 @@ void WebSocketsClient::setExtraHeaders(const char * extraHeaders) {
_client.extraHeaders = 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);
}
//################################################################################# //#################################################################################
//################################################################################# //#################################################################################
//################################################################################# //#################################################################################
@ -298,6 +440,8 @@ void WebSocketsClient::setExtraHeaders(const char * extraHeaders) {
void WebSocketsClient::messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin) { void WebSocketsClient::messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin) {
WStype_t type = WStype_ERROR; WStype_t type = WStype_ERROR;
UNUSED(client);
switch(opcode) { switch(opcode) {
case WSop_text: case WSop_text:
type = fin ? WStype_TEXT : WStype_FRAGMENT_TEXT_START; type = fin ? WStype_TEXT : WStype_FRAGMENT_TEXT_START;
@ -305,13 +449,21 @@ void WebSocketsClient::messageReceived(WSclient_t * client, WSopcode_t opcode, u
case WSop_binary: case WSop_binary:
type = fin ? WStype_BIN : WStype_FRAGMENT_BIN_START; type = fin ? WStype_BIN : WStype_FRAGMENT_BIN_START;
break; break;
case WSop_continuation: case WSop_continuation:
type = fin ? WStype_FRAGMENT_FIN : WStype_FRAGMENT; type = fin ? WStype_FRAGMENT_FIN : WStype_FRAGMENT;
break; break;
case WSop_ping:
type = WStype_PING;
break;
case WSop_pong:
type = WStype_PONG;
break;
case WSop_close:
default:
break;
} }
runCbEvent(type, payload, length); runCbEvent(type, payload, length);
} }
/** /**
@ -319,10 +471,9 @@ void WebSocketsClient::messageReceived(WSclient_t * client, WSopcode_t opcode, u
* @param client WSclient_t * ptr to the client struct * @param client WSclient_t * ptr to the client struct
*/ */
void WebSocketsClient::clientDisconnect(WSclient_t * client) { void WebSocketsClient::clientDisconnect(WSclient_t * client) {
bool event = false; 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->isSSL && client->ssl) {
if(client->ssl->connected()) { if(client->ssl->connected()) {
client->ssl->flush(); client->ssl->flush();
@ -337,13 +488,13 @@ void WebSocketsClient::clientDisconnect(WSclient_t * client) {
if(client->tcp) { if(client->tcp) {
if(client->tcp->connected()) { if(client->tcp->connected()) {
#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) #if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
client->tcp->flush(); client->tcp->flush();
#endif #endif
client->tcp->stop(); client->tcp->stop();
} }
event = true; event = true;
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) #if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
client->status = WSC_NOT_CONNECTED; client->status = WSC_NOT_CONNECTED;
#else #else
delete client->tcp; delete client->tcp;
@ -351,16 +502,16 @@ void WebSocketsClient::clientDisconnect(WSclient_t * client) {
client->tcp = NULL; client->tcp = NULL;
} }
client->cCode = 0; client->cCode = 0;
client->cKey = ""; client->cKey = "";
client->cAccept = ""; client->cAccept = "";
client->cProtocol = ""; client->cVersion = 0;
client->cVersion = 0; client->cIsUpgrade = false;
client->cIsUpgrade = false;
client->cIsWebsocket = false; client->cIsWebsocket = false;
client->cSessionId = ""; client->cSessionId = "";
client->status = WSC_NOT_CONNECTED; client->status = WSC_NOT_CONNECTED;
_lastConnectionFail = millis();
DEBUG_WEBSOCKETS("[WS-Client] client disconnected.\n"); DEBUG_WEBSOCKETS("[WS-Client] client disconnected.\n");
if(event) { if(event) {
@ -374,7 +525,6 @@ void WebSocketsClient::clientDisconnect(WSclient_t * client) {
* @return true = conneted * @return true = conneted
*/ */
bool WebSocketsClient::clientIsConnected(WSclient_t * client) { bool WebSocketsClient::clientIsConnected(WSclient_t * client) {
if(!client->tcp) { if(!client->tcp) {
return false; return false;
} }
@ -399,20 +549,31 @@ bool WebSocketsClient::clientIsConnected(WSclient_t * client) {
return false; return false;
} }
#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) #if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
/** /**
* Handel incomming data from Client * Handel incomming data from Client
*/ */
void WebSocketsClient::handleClientData(void) { void WebSocketsClient::handleClientData(void) {
if((_client.status == WSC_HEADER || _client.status == WSC_BODY) && _lastHeaderSent + WEBSOCKETS_TCP_TIMEOUT < millis()) {
DEBUG_WEBSOCKETS("[WS-Client][handleClientData] header response timeout.. disconnecting!\n");
clientDisconnect(&_client);
WEBSOCKETS_YIELD();
return;
}
int len = _client.tcp->available(); int len = _client.tcp->available();
if(len > 0) { if(len > 0) {
switch(_client.status) { switch(_client.status) {
case WSC_HEADER: case WSC_HEADER: {
{
String headerLine = _client.tcp->readStringUntil('\n'); String headerLine = _client.tcp->readStringUntil('\n');
handleHeader(&_client, &headerLine); handleHeader(&_client, &headerLine);
} } break;
break; case WSC_BODY: {
char buf[256] = { 0 };
_client.tcp->readBytes(&buf[0], std::min((size_t)len, sizeof(buf)));
String bodyLine = buf;
handleHeader(&_client, &bodyLine);
} break;
case WSC_CONNECTED: case WSC_CONNECTED:
WebSockets::handleWebsocket(&_client); WebSockets::handleWebsocket(&_client);
break; break;
@ -421,20 +582,16 @@ void WebSocketsClient::handleClientData(void) {
break; break;
} }
} }
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) WEBSOCKETS_YIELD();
delay(0);
#endif
} }
#endif #endif
/** /**
* send the WebSocket header to Server * send the WebSocket header to Server
* @param client WSclient_t * ptr to the client struct * @param client WSclient_t * ptr to the client struct
*/ */
void WebSocketsClient::sendHeader(WSclient_t * client) { void WebSocketsClient::sendHeader(WSclient_t * client) {
static const char * NEW_LINE = "\r\n";
static const char * NEW_LINE = "\r\n";
DEBUG_WEBSOCKETS("[WS-Client][sendHeader] sending header...\n"); DEBUG_WEBSOCKETS("[WS-Client][sendHeader] sending header...\n");
@ -452,71 +609,73 @@ void WebSocketsClient::sendHeader(WSclient_t * client) {
String handshake; String handshake;
bool ws_header = true; bool ws_header = true;
String url = client->cUrl; String url = client->cUrl;
if(client->isSocketIO) { if(client->isSocketIO) {
if(client->cSessionId.length() == 0) { if(client->cSessionId.length() == 0) {
url += WEBSOCKETS_STRING("&transport=polling"); url += WEBSOCKETS_STRING("&transport=polling");
ws_header = false; ws_header = false;
} else { } else {
url += WEBSOCKETS_STRING("&transport=websocket&sid="); url += WEBSOCKETS_STRING("&transport=websocket&sid=");
url += client->cSessionId; url += client->cSessionId;
} }
} }
handshake = WEBSOCKETS_STRING("GET "); handshake = WEBSOCKETS_STRING("GET ");
handshake += url + WEBSOCKETS_STRING(" HTTP/1.1\r\n" handshake += url + WEBSOCKETS_STRING(
"Host: "); " HTTP/1.1\r\n"
handshake += _host + ":" + _port + NEW_LINE; "Host: ");
handshake += _host + ":" + _port + NEW_LINE;
if(ws_header) { if(ws_header) {
handshake += WEBSOCKETS_STRING("Connection: Upgrade\r\n" handshake += WEBSOCKETS_STRING(
"Upgrade: websocket\r\n" "Connection: Upgrade\r\n"
"Sec-WebSocket-Version: 13\r\n" "Upgrade: websocket\r\n"
"Sec-WebSocket-Key: "); "Sec-WebSocket-Version: 13\r\n"
handshake += client->cKey + NEW_LINE; "Sec-WebSocket-Key: ");
handshake += client->cKey + NEW_LINE;
if(client->cProtocol.length() > 0) { if(client->cProtocol.length() > 0) {
handshake += WEBSOCKETS_STRING("Sec-WebSocket-Protocol: "); handshake += WEBSOCKETS_STRING("Sec-WebSocket-Protocol: ");
handshake +=client->cProtocol + NEW_LINE; handshake += client->cProtocol + NEW_LINE;
} }
if(client->cExtensions.length() > 0) { if(client->cExtensions.length() > 0) {
handshake += WEBSOCKETS_STRING("Sec-WebSocket-Extensions: "); handshake += WEBSOCKETS_STRING("Sec-WebSocket-Extensions: ");
handshake +=client->cExtensions + NEW_LINE; handshake += client->cExtensions + NEW_LINE;
} }
} else { } else {
handshake += WEBSOCKETS_STRING("Connection: keep-alive\r\n"); handshake += WEBSOCKETS_STRING("Connection: keep-alive\r\n");
} }
// add extra headers; by default this includes "Origin: file://" // add extra headers; by default this includes "Origin: file://"
if (client->extraHeaders) { if(client->extraHeaders.length() > 0) {
handshake += client->extraHeaders + NEW_LINE; handshake += client->extraHeaders + NEW_LINE;
} }
handshake += WEBSOCKETS_STRING("User-Agent: arduino-WebSocket-Client\r\n"); handshake += WEBSOCKETS_STRING("User-Agent: arduino-WebSocket-Client\r\n");
if(client->base64Authorization.length() > 0) { if(client->base64Authorization.length() > 0) {
handshake += WEBSOCKETS_STRING("Authorization: Basic "); handshake += WEBSOCKETS_STRING("Authorization: Basic ");
handshake += client->base64Authorization + NEW_LINE; handshake += client->base64Authorization + NEW_LINE;
} }
if(client->plainAuthorization.length() > 0) { if(client->plainAuthorization.length() > 0) {
handshake += WEBSOCKETS_STRING("Authorization: "); handshake += WEBSOCKETS_STRING("Authorization: ");
handshake += client->plainAuthorization + NEW_LINE; handshake += client->plainAuthorization + NEW_LINE;
} }
handshake += NEW_LINE; handshake += NEW_LINE;
DEBUG_WEBSOCKETS("[WS-Client][sendHeader] handshake %s", (uint8_t*)handshake.c_str()); DEBUG_WEBSOCKETS("[WS-Client][sendHeader] handshake %s", (uint8_t *)handshake.c_str());
client->tcp->write((uint8_t*)handshake.c_str(), handshake.length()); write(client, (uint8_t *)handshake.c_str(), handshake.length());
#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))); client->tcp->readStringUntil('\n', &(client->cHttpLine), std::bind(&WebSocketsClient::handleHeader, this, client, &(client->cHttpLine)));
#endif #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();
} }
/** /**
@ -524,18 +683,38 @@ void WebSocketsClient::sendHeader(WSclient_t * client) {
* @param client WSclient_t * ptr to the client struct * @param client WSclient_t * ptr to the client struct
*/ */
void WebSocketsClient::handleHeader(WSclient_t * client, String * headerLine) { void WebSocketsClient::handleHeader(WSclient_t * client, String * headerLine) {
headerLine->trim(); // remove \r
headerLine->trim(); // remove \r // this code handels the http body for Socket.IO V3 requests
if(headerLine->length() > 0 && client->isSocketIO && client->status == WSC_BODY && client->cSessionId.length() == 0) {
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] socket.io json: %s\n", headerLine->c_str());
String sid_begin = WEBSOCKETS_STRING("\"sid\":\"");
if(headerLine->indexOf(sid_begin) > -1) {
int start = headerLine->indexOf(sid_begin) + sid_begin.length();
int end = headerLine->indexOf('"', start);
client->cSessionId = headerLine->substring(start, end);
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cSessionId: %s\n", client->cSessionId.c_str());
// Trigger websocket connection code path
*headerLine = "";
}
}
// headle HTTP header
if(headerLine->length() > 0) { if(headerLine->length() > 0) {
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] RX: %s\n", headerLine->c_str()); DEBUG_WEBSOCKETS("[WS-Client][handleHeader] RX: %s\n", headerLine->c_str());
if(headerLine->startsWith(WEBSOCKETS_STRING("HTTP/1."))) { if(headerLine->startsWith(WEBSOCKETS_STRING("HTTP/1."))) {
// "HTTP/1.1 101 Switching Protocols" // "HTTP/1.1 101 Switching Protocols"
client->cCode = headerLine->substring(9, headerLine->indexOf(' ', 9)).toInt(); client->cCode = headerLine->substring(9, headerLine->indexOf(' ', 9)).toInt();
} else if(headerLine->indexOf(':')) { } else if(headerLine->indexOf(':') >= 0) {
String headerName = headerLine->substring(0, headerLine->indexOf(':')); String headerName = headerLine->substring(0, headerLine->indexOf(':'));
String headerValue = headerLine->substring(headerLine->indexOf(':') + 2); String headerValue = headerLine->substring(headerLine->indexOf(':') + 1);
// remove space in the beginning (RFC2616)
if(headerValue[0] == ' ') {
headerValue.remove(0, 1);
}
if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Connection"))) { if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Connection"))) {
if(headerValue.equalsIgnoreCase(WEBSOCKETS_STRING("upgrade"))) { if(headerValue.equalsIgnoreCase(WEBSOCKETS_STRING("upgrade"))) {
@ -547,7 +726,7 @@ void WebSocketsClient::handleHeader(WSclient_t * client, String * headerLine) {
} }
} else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Accept"))) { } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Accept"))) {
client->cAccept = headerValue; client->cAccept = headerValue;
client->cAccept.trim(); // see rfc6455 client->cAccept.trim(); // see rfc6455
} else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Protocol"))) { } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Protocol"))) {
client->cProtocol = headerValue; client->cProtocol = headerValue;
} else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Extensions"))) { } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Extensions"))) {
@ -555,10 +734,10 @@ void WebSocketsClient::handleHeader(WSclient_t * client, String * headerLine) {
} else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Version"))) { } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Version"))) {
client->cVersion = headerValue.toInt(); client->cVersion = headerValue.toInt();
} else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Set-Cookie"))) { } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Set-Cookie"))) {
if (headerValue.indexOf(WEBSOCKETS_STRING("HttpOnly")) > -1) { if(headerValue.indexOf(';') > -1) {
client->cSessionId = headerValue.substring(headerValue.indexOf('=') + 1, headerValue.indexOf(";")); client->cSessionId = headerValue.substring(headerValue.indexOf('=') + 1, headerValue.indexOf(";"));
} else { } else {
client->cSessionId = headerValue.substring(headerValue.indexOf('=') + 1); client->cSessionId = headerValue.substring(headerValue.indexOf('=') + 1);
} }
} }
} else { } else {
@ -566,10 +745,9 @@ void WebSocketsClient::handleHeader(WSclient_t * client, String * headerLine) {
} }
(*headerLine) = ""; (*headerLine) = "";
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) #if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
client->tcp->readStringUntil('\n', &(client->cHttpLine), std::bind(&WebSocketsClient::handleHeader, this, client, &(client->cHttpLine))); client->tcp->readStringUntil('\n', &(client->cHttpLine), std::bind(&WebSocketsClient::handleHeader, this, client, &(client->cHttpLine)));
#endif #endif
} else { } else {
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Header read fin.\n"); DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Header read fin.\n");
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Client settings:\n"); DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Client settings:\n");
@ -587,29 +765,37 @@ void WebSocketsClient::handleHeader(WSclient_t * client, String * headerLine) {
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cVersion: %d\n", client->cVersion); DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cVersion: %d\n", client->cVersion);
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cSessionId: %s\n", client->cSessionId.c_str()); DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cSessionId: %s\n", client->cSessionId.c_str());
if(client->isSocketIO && client->cSessionId.length() == 0 && clientIsConnected(client)) {
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] still missing cSessionId try socket.io V3\n");
client->status = WSC_BODY;
return;
} else {
client->status = WSC_HEADER;
}
bool ok = (client->cIsUpgrade && client->cIsWebsocket); bool ok = (client->cIsUpgrade && client->cIsWebsocket);
if(ok) { if(ok) {
switch(client->cCode) { switch(client->cCode) {
case 101: ///< Switching Protocols case 101: ///< Switching Protocols
break; break;
case 200: case 200:
if(client->isSocketIO) { if(client->isSocketIO) {
break; break;
} }
case 403: ///< Forbidden case 403: ///< Forbidden
// todo handle login // todo handle login
default: ///< Server dont unterstand requrst default: ///< Server dont unterstand requrst
ok = false; ok = false;
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] serverCode is not 101 (%d)\n", client->cCode); DEBUG_WEBSOCKETS("[WS-Client][handleHeader] serverCode is not 101 (%d)\n", client->cCode);
clientDisconnect(client); clientDisconnect(client);
_lastConnectionFail = millis();
break; break;
} }
} }
if(ok) { if(ok) {
if(client->cAccept.length() == 0) { if(client->cAccept.length() == 0) {
ok = false; ok = false;
} else { } else {
@ -623,75 +809,92 @@ void WebSocketsClient::handleHeader(WSclient_t * client, String * headerLine) {
} }
if(ok) { if(ok) {
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Websocket connection init done.\n"); DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Websocket connection init done.\n");
headerDone(client); 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(client->isSocketIO) {
} else if(clientIsConnected(client) && client->isSocketIO && client->cSessionId.length() > 0) { if(client->cSessionId.length() > 0) {
sendHeader(client); DEBUG_WEBSOCKETS("[WS-Client][handleHeader] found cSessionId\n");
} else { if(clientIsConnected(client) && _client.tcp->available()) {
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] no Websocket connection close.\n"); // read not needed data
if(clientIsConnected(client)) { DEBUG_WEBSOCKETS("[WS-Client][handleHeader] still data in buffer (%d), clean up.\n", _client.tcp->available());
client->tcp->write("This is a webSocket client!"); while(_client.tcp->available() > 0) {
} _client.tcp->read();
clientDisconnect(client); }
} }
} sendHeader(client);
}
#endif
} else {
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] no Websocket connection close.\n");
_lastConnectionFail = millis();
if(clientIsConnected(client)) {
write(client, "This is a webSocket client!");
}
clientDisconnect(client);
}
}
} }
void WebSocketsClient::connectedCb() { void WebSocketsClient::connectedCb() {
DEBUG_WEBSOCKETS("[WS-Client] connected to %s:%u.\n", _host.c_str(), _port); 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 { _client.tcp->onDisconnect(std::bind([](WebSocketsClient * c, AsyncTCPbuffer * obj, WSclient_t * client) -> bool {
DEBUG_WEBSOCKETS("[WS-Server][%d] Disconnect client\n", client->num); DEBUG_WEBSOCKETS("[WS-Server][%d] Disconnect client\n", client->num);
client->status = WSC_NOT_CONNECTED; client->status = WSC_NOT_CONNECTED;
client->tcp = NULL; client->tcp = NULL;
// reconnect // reconnect
c->asyncConnect(); c->asyncConnect();
return true; return true;
}, this, std::placeholders::_1, &_client)); },
this, std::placeholders::_1, &_client));
#endif #endif
_client.status = WSC_HEADER; _client.status = WSC_HEADER;
#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) #if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
// set Timeout for readBytesUntil and readStringUntil // set Timeout for readBytesUntil and readStringUntil
_client.tcp->setTimeout(WEBSOCKETS_TCP_TIMEOUT); _client.tcp->setTimeout(WEBSOCKETS_TCP_TIMEOUT);
#endif #endif
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) #if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
_client.tcp->setNoDelay(true); _client.tcp->setNoDelay(true);
#endif
if(_client.isSSL && _fingerprint.length()) { #if defined(HAS_SSL)
#if defined(SSL_AXTLS) || defined(ESP32)
if(_client.isSSL && SSL_FINGERPRINT_IS_SET) {
if(!_client.ssl->verify(_fingerprint.c_str(), _host.c_str())) { if(!_client.ssl->verify(_fingerprint.c_str(), _host.c_str())) {
DEBUG_WEBSOCKETS("[WS-Client] certificate mismatch\n"); DEBUG_WEBSOCKETS("[WS-Client] certificate mismatch\n");
WebSockets::clientDisconnect(&_client, 1000); WebSockets::clientDisconnect(&_client, 1000);
return; return;
} }
#else
if(_client.isSSL && SSL_FINGERPRINT_IS_SET) {
#endif
} else if(_client.isSSL && !_CA_cert) {
#if defined(SSL_BARESSL)
_client.ssl->setInsecure();
#endif
} }
#endif #endif
// send Header to Server // send Header to Server
sendHeader(&_client); sendHeader(&_client);
} }
void WebSocketsClient::connectFailedCb() { 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() { void WebSocketsClient::asyncConnect() {
DEBUG_WEBSOCKETS("[WS-Client] asyncConnect...\n"); DEBUG_WEBSOCKETS("[WS-Client] asyncConnect...\n");
AsyncClient * tcpclient = new AsyncClient(); AsyncClient * tcpclient = new AsyncClient();
@ -701,12 +904,12 @@ void WebSocketsClient::asyncConnect() {
return; return;
} }
tcpclient->onDisconnect([](void *obj, AsyncClient* c) { tcpclient->onDisconnect([](void * obj, AsyncClient * c) {
c->free(); c->free();
delete c; delete c;
}); });
tcpclient->onConnect(std::bind([](WebSocketsClient * ws , AsyncClient * tcp) { tcpclient->onConnect(std::bind([](WebSocketsClient * ws, AsyncClient * tcp) {
ws->_client.tcp = new AsyncTCPbuffer(tcp); ws->_client.tcp = new AsyncTCPbuffer(tcp);
if(!ws->_client.tcp) { if(!ws->_client.tcp) {
DEBUG_WEBSOCKETS("[WS-Client] creating Network class failed!\n"); DEBUG_WEBSOCKETS("[WS-Client] creating Network class failed!\n");
@ -714,20 +917,57 @@ void WebSocketsClient::asyncConnect() {
return; return;
} }
ws->connectedCb(); 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(); ws->connectFailedCb();
// reconnect // reconnect
ws->asyncConnect(); ws->asyncConnect();
}, this, std::placeholders::_2)); },
this, std::placeholders::_2));
if(!tcpclient->connect(_host.c_str(), _port)) { if(!tcpclient->connect(_host.c_str(), _port)) {
connectFailedCb(); connectFailedCb();
delete tcpclient; delete tcpclient;
} }
} }
#endif #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;
} else {
DEBUG_WEBSOCKETS("[WS-Client] sending HB ping failed\n");
WebSockets::clientDisconnect(&_client, 1000);
}
}
}
/**
* 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;
}

View File

@ -27,104 +27,143 @@
#include "WebSockets.h" #include "WebSockets.h"
class WebSocketsClient: private WebSockets { class WebSocketsClient : protected WebSockets {
public: public:
#ifdef __AVR__ #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 #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 #endif
WebSocketsClient(void);
virtual ~WebSocketsClient(void);
WebSocketsClient(void); void begin(const char * host, uint16_t port, const char * url = "/", const char * protocol = "arduino");
~WebSocketsClient(void); 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"); #if defined(HAS_SSL)
void begin(String host, uint16_t port, String url = "/", String protocol = "arduino"); #ifdef SSL_AXTLS
void beginSSL(const char * host, uint16_t port, const char * url = "/", const char * fingerprint = "", const char * protocol = "arduino");
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) void beginSSL(String host, uint16_t port, String url = "/", String fingerprint = "", String protocol = "arduino");
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
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");
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
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");
#endif
#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
void loop(void);
#else #else
// Async interface not need a loop call void beginSSL(const char * host, uint16_t port, const char * url = "/", const uint8_t * fingerprint = NULL, const char * protocol = "arduino");
void loop(void) __attribute__ ((deprecated)) {} void beginSslWithCA(const char * host, uint16_t port, const char * url = "/", BearSSL::X509List * CA_cert = NULL, const char * protocol = "arduino");
void setSSLClientCertKey(BearSSL::X509List * clientCert = NULL, BearSSL::PrivateKey * clientPrivateKey = NULL);
void setSSLClientCertKey(const char * clientCert = NULL, const char * clientPrivateKey = NULL);
#endif
void beginSslWithCA(const char * host, uint16_t port, const char * url = "/", const char * CA_cert = NULL, const char * protocol = "arduino");
#endif #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); #if defined(HAS_SSL)
bool sendTXT(const uint8_t * payload, size_t length = 0); void beginSocketIOSSL(const char * host, uint16_t port, const char * url = "/socket.io/?EIO=3", const char * protocol = "arduino");
bool sendTXT(char * payload, size_t length = 0, bool headerToPayload = false); void beginSocketIOSSL(String host, uint16_t port, String url = "/socket.io/?EIO=3", String protocol = "arduino");
bool sendTXT(const char * payload, size_t length = 0);
bool sendTXT(String & payload);
bool sendBIN(uint8_t * payload, size_t length, bool headerToPayload = false); void beginSocketIOSSLWithCA(const char * host, uint16_t port, const char * url = "/socket.io/?EIO=3", const char * CA_cert = NULL, const char * protocol = "arduino");
bool sendBIN(const uint8_t * payload, size_t length); #if defined(SSL_BARESSL)
void beginSocketIOSSLWithCA(const char * host, uint16_t port, const char * url = "/socket.io/?EIO=3", BearSSL::X509List * CA_cert = NULL, const char * protocol = "arduino");
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);
protected:
String _host;
uint16_t _port;
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
String _fingerprint;
#endif #endif
WSclient_t _client;
WebSocketClientEvent _cbEvent;
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 #endif
void sendHeader(WSclient_t * client); #if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
void handleHeader(WSclient_t * client, String * headerLine); void loop(void);
#else
void connectedCb(); // Async interface not need a loop call
void connectFailedCb(); void loop(void) __attribute__((deprecated)) {}
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
void asyncConnect();
#endif #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_IS_SET (_fingerprint.length())
#define SSL_FINGERPRINT_NULL ""
#else
const uint8_t * _fingerprint;
BearSSL::X509List * _CA_cert;
BearSSL::X509List * _client_cert;
BearSSL::PrivateKey * _client_key;
#define SSL_FINGERPRINT_IS_SET (_fingerprint != NULL)
#define SSL_FINGERPRINT_NULL NULL
#endif
#endif
WSclient_t _client;
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 * called for sending a Event to the app
* @param type WStype_t * @param type WStype_t
* @param payload uint8_t * * @param payload uint8_t *
* @param length size_t * @param length size_t
*/ */
virtual void runCbEvent(WStype_t type, uint8_t * payload, size_t length) { virtual void runCbEvent(WStype_t type, uint8_t * payload, size_t length) {
if(_cbEvent) { if(_cbEvent) {
_cbEvent(type, payload, length); _cbEvent(type, payload, length);
}
} }
}
}; };
#endif /* WEBSOCKETSCLIENT_H_ */ #endif /* WEBSOCKETSCLIENT_H_ */

File diff suppressed because it is too large Load Diff

View File

@ -27,154 +27,162 @@
#include "WebSockets.h" #include "WebSockets.h"
#define WEBSOCKETS_SERVER_CLIENT_MAX (5) #ifndef WEBSOCKETS_SERVER_CLIENT_MAX
#define WEBSOCKETS_SERVER_CLIENT_MAX (5)
#endif
class WebSocketsServerCore : protected WebSockets {
public:
WebSocketsServerCore(const String & origin = "", const String & protocol = "arduino");
virtual ~WebSocketsServerCore(void);
void begin(void);
void close(void);
class WebSocketsServer: protected WebSockets {
public:
#ifdef __AVR__ #ifdef __AVR__
typedef void (*WebSocketServerEvent)(uint8_t num, WStype_t type, uint8_t * payload, size_t length); typedef void (*WebSocketServerEvent)(uint8_t num, WStype_t type, uint8_t * payload, size_t length);
typedef bool (*WebSocketServerHttpHeaderValFunc)(String headerName, String headerValue); typedef bool (*WebSocketServerHttpHeaderValFunc)(String headerName, String headerValue);
#else #else
typedef std::function<void (uint8_t num, WStype_t type, uint8_t * payload, size_t length)> WebSocketServerEvent; 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<bool(String headerName, String headerValue)> WebSocketServerHttpHeaderValFunc;
#endif #endif
WebSocketsServer(uint16_t port, String origin = "", String protocol = "arduino"); void onEvent(WebSocketServerEvent cbEvent);
~WebSocketsServer(void); void onValidateHttpHeader(
WebSocketServerHttpHeaderValFunc validationFunc,
const char * mandatoryHttpHeaders[],
size_t mandatoryHttpHeaderCount);
void begin(void); 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);
#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) bool broadcastTXT(uint8_t * payload, size_t length = 0, bool headerToPayload = false);
void loop(void); bool broadcastTXT(const uint8_t * payload, size_t length = 0);
#else bool broadcastTXT(char * payload, size_t length = 0, bool headerToPayload = false);
// Async interface not need a loop call bool broadcastTXT(const char * payload, size_t length = 0);
void loop(void) __attribute__ ((deprecated)) {} 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 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);
bool broadcastPing(uint8_t * payload = NULL, size_t length = 0);
bool broadcastPing(String & payload);
void disconnect(void);
void disconnect(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 #endif
void onEvent(WebSocketServerEvent cbEvent); #if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
void onValidateHttpHeader( void loop(void); // handle client data only
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 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 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);
bool broadcastPing(uint8_t * payload = NULL, size_t length = 0);
bool broadcastPing(String & payload);
void disconnect(void);
void disconnect(uint8_t num);
void setAuthorization(const char * user, const char * password);
void setAuthorization(const char * auth);
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
IPAddress remoteIP(uint8_t num);
#endif #endif
protected: WSclient_t * newClient(WEBSOCKETS_NETWORK_CLASS * TCPclient);
uint16_t _port;
String _origin;
String _protocol;
String _base64Authorization; ///< Base64 encoded Auth request
String * _mandatoryHttpHeaders;
size_t _mandatoryHttpHeaderCount;
WEBSOCKETS_NETWORK_SERVER_CLASS * _server; protected:
String _origin;
String _protocol;
String _base64Authorization; ///< Base64 encoded Auth request
String * _mandatoryHttpHeaders;
size_t _mandatoryHttpHeaderCount;
WSclient_t _clients[WEBSOCKETS_SERVER_CLIENT_MAX]; WSclient_t _clients[WEBSOCKETS_SERVER_CLIENT_MAX];
WebSocketServerEvent _cbEvent; WebSocketServerEvent _cbEvent;
WebSocketServerHttpHeaderValFunc _httpHeaderValidationFunc; WebSocketServerHttpHeaderValFunc _httpHeaderValidationFunc;
bool newClient(WEBSOCKETS_NETWORK_CLASS * TCPclient); bool _runnning;
void messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin); uint32_t _pingInterval;
uint32_t _pongTimeout;
uint8_t _disconnectTimeoutCount;
void clientDisconnect(WSclient_t * client); void messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin);
bool clientIsConnected(WSclient_t * client);
#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) void clientDisconnect(WSclient_t * client);
void handleNewClients(void); bool clientIsConnected(WSclient_t * client);
void handleClientData(void);
#if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
void handleClientData(void);
#endif #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. * called if a non Websocket connection is coming in.
* Note: can be override * Note: can be override
* @param client WSclient_t * ptr to the client struct * @param client WSclient_t * ptr to the client struct
*/ */
virtual void handleNonWebsocketConnection(WSclient_t * client) { virtual void handleNonWebsocketConnection(WSclient_t * client) {
DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] no Websocket connection close.\n", client->num); DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] no Websocket connection close.\n", client->num);
client->tcp->write("HTTP/1.1 400 Bad Request\r\n" client->tcp->write(
"Server: arduino-WebSocket-Server\r\n" "HTTP/1.1 400 Bad Request\r\n"
"Content-Type: text/plain\r\n" "Server: arduino-WebSocket-Server\r\n"
"Content-Length: 32\r\n" "Content-Type: text/plain\r\n"
"Connection: close\r\n" "Content-Length: 32\r\n"
"Sec-WebSocket-Version: 13\r\n" "Connection: close\r\n"
"\r\n" "Sec-WebSocket-Version: 13\r\n"
"This is a Websocket server only!"); "\r\n"
clientDisconnect(client); "This is a Websocket server only!");
} clientDisconnect(client);
}
/** /**
* called if a non Authorization connection is coming in. * called if a non Authorization connection is coming in.
* Note: can be override * Note: can be override
* @param client WSclient_t * ptr to the client struct * @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" client->tcp->write(
"Server: arduino-WebSocket-Server\r\n" "HTTP/1.1 401 Unauthorized\r\n"
"Content-Type: text/plain\r\n" "Server: arduino-WebSocket-Server\r\n"
"Content-Length: 45\r\n" "Content-Type: text/plain\r\n"
"Connection: close\r\n" "Content-Length: 45\r\n"
"Sec-WebSocket-Version: 13\r\n" "Connection: close\r\n"
"WWW-Authenticate: Basic realm=\"WebSocket Server\"" "Sec-WebSocket-Version: 13\r\n"
"\r\n" "WWW-Authenticate: Basic realm=\"WebSocket Server\""
"This Websocket server requires Authorization!"); "\r\n"
clientDisconnect(client); "This Websocket server requires Authorization!");
} clientDisconnect(client);
}
/** /**
* called for sending a Event to the app * called for sending a Event to the app
* @param num uint8_t * @param num uint8_t
* @param type WStype_t * @param type WStype_t
* @param payload uint8_t * * @param payload uint8_t *
* @param length size_t * @param length size_t
*/ */
virtual void runCbEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) { virtual void runCbEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
if(_cbEvent) { if(_cbEvent) {
_cbEvent(num, type, payload, length); _cbEvent(num, type, payload, length);
}
} }
}
/* /*
* Called at client socket connect handshake negotiation time for each http header that is not * 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-*) * a websocket specific http header (not Connection, Upgrade, Sec-WebSocket-*)
* If the custom httpHeaderValidationFunc returns false for any headerName / headerValue passed, the * If the custom httpHeaderValidationFunc returns false for any headerName / headerValue passed, the
@ -182,24 +190,54 @@ protected:
* This mechanism can be used to enable custom authentication schemes e.g. test the value * 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 * of a session cookie to determine if a user is logged on / authenticated
*/ */
virtual bool execHttpHeaderValidation(String headerName, String headerValue) { virtual bool execHttpHeaderValidation(String headerName, String headerValue) {
if(_httpHeaderValidationFunc) { if(_httpHeaderValidationFunc) {
//return the value of the custom http header validation function //return the value of the custom http header validation function
return _httpHeaderValidationFunc(headerName, headerValue); return _httpHeaderValidationFunc(headerName, headerValue);
}
//no custom http header validation so just assume all is good
return true;
} }
//no custom http header validation so just assume all is good
return true;
}
private: #if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
/* WSclient_t * handleNewClient(WEBSOCKETS_NETWORK_CLASS * tcpClient);
#endif
/**
* drop native tcp connection (client->tcp)
*/
void dropNativeClient(WSclient_t * client);
private:
/*
* returns an indicator whether the given named header exists in the configured _mandatoryHttpHeaders collection * returns an indicator whether the given named header exists in the configured _mandatoryHttpHeaders collection
* @param headerName String ///< the name of the header being checked * @param headerName String ///< the name of the header being checked
*/ */
bool hasMandatoryHeader(String headerName); bool hasMandatoryHeader(String headerName);
}; };
class WebSocketsServer : public WebSocketsServerCore {
public:
WebSocketsServer(uint16_t port, const String & origin = "", const String & protocol = "arduino");
virtual ~WebSocketsServer(void);
void begin(void);
void close(void);
#if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
void loop(void); // handle incoming client and client data
#else
// Async interface not need a loop call
void loop(void) __attribute__((deprecated)) {}
#endif
protected:
#if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
void handleNewClients(void);
#endif
uint16_t _port;
WEBSOCKETS_NETWORK_SERVER_CLASS * _server;
};
#endif /* WEBSOCKETSSERVER_H_ */ #endif /* WEBSOCKETSSERVER_H_ */

36
src/WebSocketsVersion.h Normal file
View File

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

View File

@ -9,6 +9,10 @@ For details, see http://sourceforge.net/projects/libb64
#include <core_esp8266_features.h> #include <core_esp8266_features.h>
#endif #endif
#if defined(ESP32)
#define CORE_HAS_LIBB64
#endif
#ifndef CORE_HAS_LIBB64 #ifndef CORE_HAS_LIBB64
#include "cdecode_inc.h" #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; const char* codechar = code_in;
char* plainchar = plaintext_out; char* plainchar = plaintext_out;
char fragment; char fragment;
*plainchar = state_in->plainchar; *plainchar = state_in->plainchar;
switch (state_in->step) switch (state_in->step)
{ {
while (1) while (1)

View File

@ -9,6 +9,10 @@ For details, see http://sourceforge.net/projects/libb64
#include <core_esp8266_features.h> #include <core_esp8266_features.h>
#endif #endif
#if defined(ESP32)
#define CORE_HAS_LIBB64
#endif
#ifndef CORE_HAS_LIBB64 #ifndef CORE_HAS_LIBB64
#include "cencode_inc.h" #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* codechar = code_out;
char result; char result;
char fragment; char fragment;
result = state_in->result; result = state_in->result;
switch (state_in->step) switch (state_in->step)
{ {
while (1) 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); *codechar++ = base64_encode_value(result);
result = (fragment & 0x03f) >> 0; result = (fragment & 0x03f) >> 0;
*codechar++ = base64_encode_value(result); *codechar++ = base64_encode_value(result);
++(state_in->stepcount); ++(state_in->stepcount);
if (state_in->stepcount == CHARS_PER_LINE/4) 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) int base64_encode_blockend(char* code_out, base64_encodestate* state_in)
{ {
char* codechar = code_out; char* codechar = code_out;
switch (state_in->step) switch (state_in->step)
{ {
case step_B: case step_B:
@ -108,7 +112,7 @@ int base64_encode_blockend(char* code_out, base64_encodestate* state_in)
break; break;
} }
*codechar++ = 0x00; *codechar++ = 0x00;
return codechar - code_out; return codechar - code_out;
} }

View File

@ -18,7 +18,7 @@ A million repetitions of "a"
/* #define LITTLE_ENDIAN * This should be #define'd already, if true. */ /* #define LITTLE_ENDIAN * This should be #define'd already, if true. */
/* #define SHA1HANDSOFF * Copies data before messing with it. */ /* #define SHA1HANDSOFF * Copies data before messing with it. */
#ifndef ESP8266 #if !defined(ESP8266) && !defined(ESP32)
#define SHA1HANDSOFF #define SHA1HANDSOFF

View File

@ -5,7 +5,7 @@ By Steve Reid <steve@edmweb.com>
100% Public Domain 100% Public Domain
*/ */
#ifndef ESP8266 #if !defined(ESP8266) && !defined(ESP32)
typedef struct { typedef struct {
uint32_t state[5]; 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 SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len);
void SHA1Final(unsigned char digest[20], SHA1_CTX* context); void SHA1Final(unsigned char digest[20], SHA1_CTX* context);
#endif #endif

View File

@ -7,7 +7,7 @@ var server = http.createServer(function(request, response) {
response.writeHead(404); response.writeHead(404);
response.end(); response.end();
}); });
server.listen(81, function() { server.listen(8011, function() {
console.log((new Date()) + ' Server is listening on port 8011'); console.log((new Date()) + ' Server is listening on port 8011');
}); });
@ -54,4 +54,4 @@ wsServer.on('request', function(request) {
}); });
connection.sendUTF("Hallo Client!"); connection.sendUTF("Hallo Client!");
}); });

View File

@ -16,9 +16,116 @@ function build_sketches()
$arduino --verify $sketch; $arduino --verify $sketch;
local result=$? local result=$?
if [ $result -ne 0 ]; then if [ $result -ne 0 ]; then
echo "Build failed ($1)" 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 return $result
fi fi
done done
} }
function build_sketch()
{
local arduino=$1
local sketch=$2
$arduino --verify $sketch;
local result=$?
if [ $result -ne 0 ]; then
echo "Build failed ($sketch) build verbose..."
$arduino --verify --verbose --preserve-temp-files $sketch
result=$?
fi
if [ $result -ne 0 ]; then
echo "Build failed ($1) $sketch"
return $result
fi
}
function get_sketches_json()
{
local arduino=$1
local srcpath=$2
local platform=$3
local sketches=($(find $srcpath -name *.ino))
echo -en "["
for sketch in "${sketches[@]}" ; do
local sketchdir=$(dirname $sketch)
if [[ -f "$sketchdir/.$platform.skip" ]]; then
continue
fi
echo -en "\"$sketch\""
if [[ $sketch != ${sketches[-1]} ]] ; then
echo -en ","
fi
done
echo -en "]"
}
function get_sketches_json_matrix()
{
local arduino=$1
local srcpath=$2
local platform=$3
local ideversion=$4
local board=$5
local sketches=($(find $srcpath -name *.ino))
for sketch in "${sketches[@]}" ; do
local sketchdir=$(dirname $sketch)
local sketchname=$(basename $sketch)
if [[ -f "$sketchdir/.$platform.skip" ]]; then
continue
fi
echo -en "{\"name\":\"$sketchname\",\"board\":\"$board\",\"ideversion\":\"$ideversion\",\"cpu\":\"$platform\",\"sketch\":\"$sketch\"}"
if [[ $sketch != ${sketches[-1]} ]] ; then
echo -en ","
fi
done
}
function get_core()
{
echo Setup core for $1
cd $HOME/arduino_ide/hardware
if [ "$1" = "esp8266" ] ; then
mkdir esp8266com
cd esp8266com
git clone --depth 1 https://github.com/esp8266/Arduino.git esp8266
cd esp8266/
rm -rf .git
cd tools
python get.py
fi
if [ "$1" = "esp32" ] ; then
mkdir espressif
cd espressif
git clone --depth 1 https://github.com/espressif/arduino-esp32.git esp32
cd esp32/
rm -rf .git
cd tools
python get.py
fi
}
function clone_library() {
local url=$1
echo clone $(basename $url)
mkdir -p $HOME/Arduino/libraries
cd $HOME/Arduino/libraries
git clone --depth 1 $url
rm -rf */.git
rm -rf */.github
rm -rf */examples
}
function hash_library_names() {
cd $HOME/Arduino/libraries
ls | sha1sum -z | cut -c1-5
}

132
travis/version.py Executable file
View File

@ -0,0 +1,132 @@
#!/usr/bin/python3
import json
import configparser
import argparse
import re
import os
import datetime
travis_dir = os.path.dirname(os.path.abspath(__file__))
base_dir = os.path.abspath(travis_dir + "/../")
def write_header_file(version):
hvs = version.split('.')
intversion = int(hvs[0]) * 1000000 + int(hvs[1]) * 1000 + int(hvs[2])
now = datetime.datetime.now()
text = f'''/**
* @file WebSocketsVersion.h
* @date {now.strftime("%d.%m.%Y")}
* @author Markus Sattler
*
* Copyright (c) 2015 Markus Sattler. All rights reserved.
* This file is part of the WebSockets for Arduino.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifndef WEBSOCKETSVERSION_H_
#define WEBSOCKETSVERSION_H_
#define WEBSOCKETS_VERSION "{version}"
#define WEBSOCKETS_VERSION_MAJOR {hvs[0]}
#define WEBSOCKETS_VERSION_MINOR {hvs[1]}
#define WEBSOCKETS_VERSION_PATCH {hvs[2]}
#define WEBSOCKETS_VERSION_INT {intversion}
#endif /* WEBSOCKETSVERSION_H_ */
'''
with open(f'{base_dir}/src/WebSocketsVersion.h', 'w') as f:
f.write(text)
def get_library_properties_version():
library_properties = {}
with open(f'{base_dir}/library.properties', 'r') as f:
library_properties = configparser.ConfigParser()
library_properties.read_string('[root]\n' + f.read())
return library_properties['root']['version']
def get_library_json_version():
library_json = {}
with open(f'{base_dir}/library.json', 'r') as f:
library_json = json.load(f)
return library_json['version']
def get_header_versions():
data = {}
define = re.compile('^#define WEBSOCKETS_VERSION_?(.*) "?([0-9\.]*)"?$')
with open(f'{base_dir}/src/WebSocketsVersion.h', 'r') as f:
for line in f:
m = define.match(line)
if m:
name = m[1]
if name == "":
name = "VERSION"
data[name] = m[2]
return data
parser = argparse.ArgumentParser(description='Checks and update Version files')
parser.add_argument(
'--update', action='store_true', default=False)
parser.add_argument(
'--check', action='store_true', default=True)
args = parser.parse_args()
if args.update:
library_properties_version = get_library_properties_version()
with open(f'{base_dir}/library.json', 'r') as f:
library_json = json.load(f)
library_json['version'] = library_properties_version
with open(f'{base_dir}/library.json', 'w') as f:
json.dump(library_json, f, indent=4, sort_keys=True)
write_header_file(library_properties_version)
library_json_version = get_library_json_version()
library_properties_version = get_library_properties_version()
header_version = get_header_versions()
print("WebSocketsVersion.h", header_version)
print(f"library.json: {library_json_version}")
print(f"library.properties: {library_properties_version}")
if args.check:
if library_json_version != library_properties_version or header_version['VERSION'] != library_properties_version:
raise Exception('versions did not match!')
hvs = header_version['VERSION'].split('.')
if header_version['MAJOR'] != hvs[0]:
raise Exception('header MAJOR version wrong!')
if header_version['MINOR'] != hvs[1]:
raise Exception('header MINOR version wrong!')
if header_version['PATCH'] != hvs[2]:
raise Exception('header PATCH version wrong!')
intversion = int(hvs[0]) * 1000000 + int(hvs[1]) * 1000 + int(hvs[2])
if int(header_version['INT']) != intversion:
raise Exception('header INT version wrong!')