Compare commits

...

69 Commits
2.0.4 ... 2.0.9

Author SHA1 Message Date
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
0ca020bbd2 add build status 2017-07-20 18:47:36 +02:00
cc3ce02c67 move files to get travis working 2017-07-20 18:40:38 +02:00
96c266101e add needed includes 2017-07-20 18:32:25 +02:00
64c8908b05 add script for travis 2017-07-20 18:26:04 +02:00
87c6c80c67 setup travis 2017-07-20 18:18:35 +02:00
adb255bda0 Merge pull request #216 from mgbckr/master
Extra headers (including removal of "Origin") and SockJS+STOMP example
2017-07-20 18:09:37 +02:00
d0ab6c4fd1 Remove redundant method header and fix indent 2017-07-20 08:21:29 +02:00
fcb623ce91 Simplified example 2017-07-19 15:50:25 +02:00
669f0b489e Fix empty header issue 2017-07-19 11:12:00 +02:00
32cb052f23 Update SockJS+STOMP example 2017-07-19 10:19:32 +02:00
42ab3168c5 Switch to setting extra headers via function 2017-07-19 09:59:34 +02:00
86d2e2400a Add SockJS+Stomp example 2017-07-18 13:33:13 +02:00
ad07f3c665 Allow to disable "Origin" header
The origin header often makes trouble with current WebSocket implementations. Thus, we introduce the WEBSOCKET_HEADERS_NO_ORIGIN macro which disables this header it if defined.
2017-07-18 12:37:04 +02:00
a4533a028c fix NULL ptr when server abort the connection (#207) 2017-07-08 08:38:05 +02:00
ae3dd103a1 Merge pull request #199 from jesben/master
Added method for beginSocketIOSSL
2017-06-18 17:55:55 +02:00
40f84c19f4 Adding beginSocketIOSSL 2017-06-18 12:51:34 +02:00
f68d9d8030 Adding method for beginSocketIOSSL 2017-06-18 12:50:07 +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
81e567b248 send 401 header too if auth string is empty #116 2017-03-18 10:35:23 +01:00
1afe317c7d improve LED example
onchange to oninput #178
2017-03-13 17:25:10 +01:00
177ec8239d Merge branch 'master' of github.com:Links2004/arduinoWebSockets 2017-03-09 18:44:18 +01:00
e675c7590e add missing include for ESP with W5100 #177 2017-03-09 18:43:23 +01:00
615926ae1e Merge pull request #176 from quantumlicht/master
Fix typo accross the project
2017-03-07 17:57:53 +01:00
acd0a603eb Merge branch 'master' of https://github.com/quantumlicht/arduinoWebSockets 2017-03-06 15:09:22 -05:00
26140be6c9 fix #150 typo lenght -> length 2017-03-06 15:09:03 -05:00
3b1dabbe1e fix typo lenght -> length 2017-03-06 15:07:20 -05:00
55bc7db7ad move API docs 2017-03-06 19:16:26 +01:00
1a6f46c67d Merge pull request #175 from quantumlicht/master
Add documentation for top level API in README
2017-03-06 18:22:13 +01:00
210f2e8fa1 Add documentation for top level API in README 2017-03-06 11:03:43 -05:00
34a2d282e4 allow to moves all Header strings to Flash (~300 Byte) #152 2017-02-22 15:30:58 +01:00
e93a323e56 add support for Fragmentation / continuation opcode Receive 2017-02-22 14:29:26 +01:00
6da0bc97e8 bump version to 2.0.7 2017-02-08 19:06:06 +01:00
ae2ba7effe bump version to 2.0.7 2017-02-08 19:05:27 +01:00
dc426c9a61 Merge pull request #170 from CAOU123/master
Make library compatible with Particle devices
2017-02-08 18:58:39 +01:00
60e3d1080e Merge branch 'master' into master 2017-02-07 16:05:20 -05:00
26fc61f7a2 Merge pull request #169 from nguyenhunga5/master
Fix socket.io issue #167
2017-02-03 17:38:24 +01:00
604a781122 Merge pull request #168 from tzapu/patch-1
Update README.md
2017-02-03 17:35:02 +01:00
f52d718cf2 Update README.md 2017-02-03 13:53:46 +02:00
6757b8b74c Fix socket.io issue
Fix socket.io issue reference from https://github.com/Links2004/arduinoWebSockets/issues/167#issuecomment-276724057
2017-02-03 13:30:20 +07:00
bef2541ede fix #162
missing isSocketIO init
2017-01-21 12:31:12 +01:00
dac71c4c23 moving host header
try to fix #159
2017-01-08 09:55:28 +01:00
60903a2fa5 mask pong
#34
2017-01-06 10:48:30 +01:00
0aaf50f87f mask ping for client
fix #34
2017-01-06 10:44:25 +01:00
2add886219 Merge pull request #147 from odelot/patch-1
Update WebSocketsClient.cpp
2016-11-24 19:21:14 +01:00
ab3b5bae46 Update WebSocketsClient.cpp
fix typo that was breaking the build
2016-11-24 02:25:01 -02:00
45bb7dbe23 Update WebSocketsClient.cpp
fix #140 Socket.io client doesn't reconnect to server
2016-11-23 17:39:48 +01:00
92032289ec Merge pull request #143 from quantumlicht/master
Remove duplicate handshake headers
2016-11-17 17:48:38 +01:00
5cd68c5304 - Remove duplicate handshake headers
- Add debug log for displaying handshake headers
Host, Origin, and User-Agent were duplicated in the case the client was
not socketIO or if it was and has a sessionId
2016-11-16 18:35:14 -05:00
ddaeea0e4b Merge pull request #137 from nerochiaro/master
Change inheritance of WebSocketServer
2016-11-09 18:19:11 +01:00
a097f0defd protected inheritance is enough 2016-11-08 22:52:00 +01:00
689e3ef921 Change inheritance of WebSocketServer
to allow classes inheriting from it to call sendFrame with custom
arguments.
2016-11-08 01:48:01 +01:00
75133dfa6d version bumb 2016-10-22 19:48:16 +02:00
d2719573d4 add function to send WS ping
sendPing();

#130
2016-10-22 19:47:44 +02:00
0d9aa043f0 fix WebSocketClientSocketIO example 2016-10-22 20:36:36 +02:00
7810d0d0b3 Make library compatible with Particle devices 2016-10-20 15:46:44 -04:00
1defe6d8e1 Merge pull request #126 from Roman3349/master
Add information about library to @PlatformIO Library Registry manifest file
2016-09-28 18:28:28 +02:00
d036dcd8e2 Add information about examples, license and version to @PlatformIO Library Registry manifest file
Signed-off-by: Roman3349 <ondracek.roman@centrum.cz>
2016-09-27 11:25:56 +02:00
529a86cc26 Merge pull request #90 from kenkus-futurice/master
Add socket.io client
2016-09-11 17:13:57 +02:00
958ab08a6b Merge pull request #111 from anisimovsergey/master
WEBSOCKETS_NETWORK_TYPE exteral definition
2016-07-15 21:44:03 +02:00
7361e2b1b6 WEBSOCKETS_NETWORK_TYPE exteral definition
Making possible to define WEBSOCKETS_NETWORK_TYPE as a compile parameter.
2016-07-15 20:29:13 +01:00
1961ddc159 bump git version 2016-07-11 17:30:08 +02:00
bf3cfa6237 Fix reference to INTERVAL 2016-06-06 17:50:31 +03:00
f8a5acc9b7 Add socket.io client 2016-06-06 15:21:13 +03:00
21 changed files with 998 additions and 192 deletions

37
.travis.yml Normal file
View File

@ -0,0 +1,37 @@
sudo: false
language: bash
os:
- linux
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
- sleep 3
- export DISPLAY=:1.0
- wget http://downloads.arduino.cc/arduino-1.6.5-linux64.tar.xz
- tar xf arduino-1.6.5-linux64.tar.xz
- mv arduino-1.6.5 $HOME/arduino_ide
- export PATH="$HOME/arduino_ide:$PATH"
- which arduino
- mkdir -p $HOME/Arduino/libraries
- 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
- arduino --board esp8266com:esp8266:generic --save-prefs
- arduino --get-pref sketchbook.path
- build_sketches arduino $HOME/Arduino/libraries/arduinoWebSockets esp8266
notifications:
email:
on_success: change
on_failure: change
webhooks:
urls:
- https://webhooks.gitter.im/e/1aa78fbe15080b0c2e37
on_success: change # options: [always|never|change] default: always
on_failure: always # options: [always|never|change] default: always
on_start: false # default: false

View File

@ -1,4 +1,4 @@
WebSocket Server and Client for Arduino WebSocket Server and Client for Arduino [![Build Status](https://travis-ci.org/Links2004/arduinoWebSockets.svg?branch=master)](https://travis-ci.org/Links2004/arduinoWebSockets)
=========================================== ===========================================
a WebSocket Server and Client for Arduino based on RFC6455. a WebSocket Server and Client for Arduino based on RFC6455.
@ -10,14 +10,13 @@ a WebSocket Server and Client for Arduino based on RFC6455.
- connection close - connection close
- ping - ping
- pong - pong
##### Not supported features of RFC6455 #####
- continuation frame - continuation frame
##### Limitations ##### ##### Limitations #####
- max input length is limited to the ram size and the ```WEBSOCKETS_MAX_DATA_SIZE``` define - max input length is limited to the ram size and the ```WEBSOCKETS_MAX_DATA_SIZE``` define
- max output length has no limit (the hardware is the limit) - max output length has no limit (the hardware is the limit)
- Client send big frames with mask 0x00000000 (on AVR all frames) - Client send big frames with mask 0x00000000 (on AVR all frames)
- continuation frame reassembly need to be handled in the application code
##### Limitations for Async ##### ##### Limitations for Async #####
- Functions called from within the context of the websocket event might not honor `yield()` and/or `delay()`. See [this issue](https://github.com/Links2004/arduinoWebSockets/issues/58#issuecomment-192376395) for more info and a potential workaround. - Functions called from within the context of the websocket event might not honor `yield()` and/or `delay()`. See [this issue](https://github.com/Links2004/arduinoWebSockets/issues/58#issuecomment-192376395) for more info and a potential workaround.
@ -26,6 +25,7 @@ a WebSocket Server and Client for Arduino based on RFC6455.
##### Supported Hardware ##### ##### Supported Hardware #####
- ESP8266 [Arduino for ESP8266](https://github.com/Links2004/Arduino) - ESP8266 [Arduino for ESP8266](https://github.com/Links2004/Arduino)
- ESP31B - ESP31B
- Particle with STM32 ARM Cortex M3
- ATmega328 with Ethernet Shield (ATmega branch) - ATmega328 with Ethernet Shield (ATmega branch)
- ATmega328 with enc28j60 (ATmega branch) - ATmega328 with enc28j60 (ATmega branch)
- ATmega2560 with Ethernet Shield (ATmega branch) - ATmega2560 with Ethernet Shield (ATmega branch)
@ -48,10 +48,43 @@ a WebSocket Server and Client for Arduino based on RFC6455.
This libary can run in Async TCP mode on the ESP. This libary can run in Async TCP mode on the ESP.
The mode can be aktivated in the ```WebSockets.h``` (see WEBSOCKETS_NETWORK_TYPE define). The mode can be activated in the ```WebSockets.h``` (see WEBSOCKETS_NETWORK_TYPE define).
[ESPAsyncTCP](https://github.com/me-no-dev/ESPAsyncTCP) libary is required. [ESPAsyncTCP](https://github.com/me-no-dev/ESPAsyncTCP) libary is required.
### High Level Client API ###
- `begin` : Initiate connection sequence to the websocket host.
```
void begin(const char *host, uint16_t port, const char * url = "/", const char * protocol = "arduino");
void begin(String host, uint16_t port, String url = "/", String protocol = "arduino");
```
- `onEvent`: Callback to handle for websocket events
```
void onEvent(WebSocketClientEvent cbEvent);
```
- `WebSocketClientEvent`: Handler for websocket events
```
void (*WebSocketClientEvent)(WStype_t type, uint8_t * payload, size_t length)
```
Where `WStype_t type` is defined as:
```
typedef enum {
WStype_ERROR,
WStype_DISCONNECTED,
WStype_CONNECTED,
WStype_TEXT,
WStype_BIN,
WStype_FRAGMENT_TEXT_START,
WStype_FRAGMENT_BIN_START,
WStype_FRAGMENT,
WStype_FRAGMENT_FIN,
} WStype_t;
```
### Issues ### ### Issues ###
Submit issues to: https://github.com/Links2004/arduinoWebSockets/issues Submit issues to: https://github.com/Links2004/arduinoWebSockets/issues

View File

@ -0,0 +1,46 @@
/* To compile using make CLI, create a folder under \firmware\user\applications and copy application.cpp there.
* Then, copy src files under particleWebSocket folder.
*/
#include "application.h"
#include "particleWebSocket/WebSocketsClient.h"
WebSocketsClient webSocket;
void webSocketEvent(WStype_t type, uint8_t* payload, size_t length)
{
switch (type)
{
case WStype_DISCONNECTED:
Serial.printlnf("[WSc] Disconnected!");
break;
case WStype_CONNECTED:
Serial.printlnf("[WSc] Connected to URL: %s", payload);
webSocket.sendTXT("Connected\r\n");
break;
case WStype_TEXT:
Serial.printlnf("[WSc] get text: %s", payload);
break;
case WStype_BIN:
Serial.printlnf("[WSc] get binary length: %u", length);
break;
}
}
void setup()
{
Serial.begin(9600);
WiFi.setCredentials("[SSID]", "[PASSWORD]", WPA2, WLAN_CIPHER_AES_TKIP);
WiFi.connect();
webSocket.begin("192.168.1.153", 85, "/ClientService/?variable=Test1212");
webSocket.onEvent(webSocketEvent);
}
void loop()
{
webSocket.sendTXT("Hello world!");
delay(500);
webSocket.loop();
}

View File

@ -17,18 +17,15 @@
ESP8266WiFiMulti WiFiMulti; ESP8266WiFiMulti WiFiMulti;
WebSocketsClient webSocket; WebSocketsClient webSocket;
#define USE_SERIAL Serial1 #define USE_SERIAL Serial1
void webSocketEvent(WStype_t type, uint8_t * payload, size_t lenght) { void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) {
switch(type) { switch(type) {
case WStype_DISCONNECTED: case WStype_DISCONNECTED:
USE_SERIAL.printf("[WSc] Disconnected!\n"); USE_SERIAL.printf("[WSc] Disconnected!\n");
break; break;
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
@ -42,11 +39,11 @@ void webSocketEvent(WStype_t type, uint8_t * payload, size_t lenght) {
// webSocket.sendTXT("message here"); // webSocket.sendTXT("message here");
break; break;
case WStype_BIN: case WStype_BIN:
USE_SERIAL.printf("[WSc] get binary lenght: %u\n", lenght); USE_SERIAL.printf("[WSc] get binary length: %u\n", length);
hexdump(payload, lenght); hexdump(payload, length);
// send data to server // send data to server
// webSocket.sendBIN(payload, lenght); // webSocket.sendBIN(payload, length);
break; break;
} }
@ -76,10 +73,18 @@ void setup() {
delay(100); delay(100);
} }
webSocket.begin("192.168.0.123", 81); // server address, port and URL
//webSocket.setAuthorization("user", "Password"); // HTTP Basic Authorization webSocket.begin("192.168.0.123", 81, "/");
// event handler
webSocket.onEvent(webSocketEvent); 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() { void loop() {

View File

@ -0,0 +1,150 @@
/*
WebSocketClientSockJsAndStomp.ino
Example for connecting and maintining a connection with a SockJS+STOMP websocket connection.
In this example we connect to a Spring application (see https://docs.spring.io/spring/docs/current/spring-framework-reference/html/websocket.html).
Created on: 18.07.2017
Author: Martin Becker <mgbckr>, Contact: becker@informatik.uni-wuerzburg.de
*/
// PRE
#define USE_SERIAL Serial
// LIBRARIES
#include <Arduino.h>
#include <Hash.h>
#include <ESP8266WiFi.h>
#include <WebSocketsClient.h>
// SETTINGS
const char* wlan_ssid = "yourssid";
const char* wlan_password = "somepassword";
const char* ws_host = "the.host.net";
const int ws_port = 80;
// base URL for SockJS (websocket) connection
// The complete URL will look something like this(cf. http://sockjs.github.io/sockjs-protocol/sockjs-protocol-0.3.3.html#section-36):
// ws://<ws_host>:<ws_port>/<ws_baseurl>/<3digits>/<randomstring>/websocket
// For the default config of Spring's SockJS/STOMP support the default base URL is "/socketentry/".
const char* ws_baseurl = "/socketentry/"; // don't forget leading and trailing "/" !!!
// VARIABLES
WebSocketsClient webSocket;
// FUNCTIONS
void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) {
switch (type) {
case WStype_DISCONNECTED:
USE_SERIAL.printf("[WSc] Disconnected!\n");
break;
case WStype_CONNECTED:
{
USE_SERIAL.printf("[WSc] Connected to url: %s\n", payload);
}
break;
case WStype_TEXT:
{
// #####################
// handle STOMP protocol
// #####################
String text = (char*) payload;
USE_SERIAL.printf("[WSc] get text: %s\n", payload);
if (payload[0] == 'h') {
USE_SERIAL.println("Heartbeat!");
} else if (payload[0] == 'o') {
// on open connection
char *msg = "[\"CONNECT\\naccept-version:1.1,1.0\\nheart-beat:10000,10000\\n\\n\\u0000\"]";
webSocket.sendTXT(msg);
} else if (text.startsWith("a[\"CONNECTED")) {
// subscribe to some channels
char *msg = "[\"SUBSCRIBE\\nid:sub-0\\ndestination:/user/queue/messages\\n\\n\\u0000\"]";
webSocket.sendTXT(msg);
delay(1000);
// and send a message
msg = "[\"SEND\\ndestination:/app/message\\n\\n{\\\"user\\\":\\\"esp\\\",\\\"message\\\":\\\"Hello!\\\"}\\u0000\"]";
webSocket.sendTXT(msg);
delay(1000);
}
break;
}
case WStype_BIN:
USE_SERIAL.printf("[WSc] get binary length: %u\n", length);
hexdump(payload, length);
// send data to server
// webSocket.sendBIN(payload, length);
break;
}
}
void setup() {
// setup serial
// USE_SERIAL.begin(921600);
USE_SERIAL.begin(115200);
// USE_SERIAL.setDebugOutput(true);
USE_SERIAL.println();
// connect to WiFi
USE_SERIAL.print("Logging into WLAN: "); Serial.print(wlan_ssid); Serial.print(" ...");
WiFi.mode(WIFI_STA);
WiFi.begin(wlan_ssid, wlan_password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
USE_SERIAL.print(".");
}
USE_SERIAL.println(" success.");
USE_SERIAL.print("IP: "); USE_SERIAL.println(WiFi.localIP());
// #####################
// create socket url according to SockJS protocol (cf. http://sockjs.github.io/sockjs-protocol/sockjs-protocol-0.3.3.html#section-36)
// #####################
String socketUrl = ws_baseurl;
socketUrl += random(0, 999);
socketUrl += "/";
socketUrl += random(0, 999999); // should be a random string, but this works (see )
socketUrl += "/websocket";
// connect to websocket
webSocket.begin(ws_host, ws_port, socketUrl);
webSocket.setExtraHeaders(); // remove "Origin: file://" header because it breaks the connection with Spring's default websocket config
// webSocket.setExtraHeaders("foo: I am so funny\r\nbar: not"); // some headers, in case you feel funny
webSocket.onEvent(webSocketEvent);
}
void loop() {
webSocket.loop();
}

View File

@ -0,0 +1,113 @@
/*
* 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

@ -18,7 +18,7 @@ WebSocketsServer webSocket = WebSocketsServer(81);
#define USE_SERIAL Serial1 #define USE_SERIAL Serial1
void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t lenght) { void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
switch(type) { switch(type) {
case WStype_DISCONNECTED: case WStype_DISCONNECTED:
@ -43,11 +43,11 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t lenght
// webSocket.broadcastTXT("message here"); // webSocket.broadcastTXT("message here");
break; break;
case WStype_BIN: case WStype_BIN:
USE_SERIAL.printf("[%u] get binary lenght: %u\n", num, lenght); USE_SERIAL.printf("[%u] get binary length: %u\n", num, length);
hexdump(payload, lenght); hexdump(payload, length);
// send message to client // send message to client
// webSocket.sendBIN(num, payload, lenght); // webSocket.sendBIN(num, payload, length);
break; break;
} }

View File

@ -0,0 +1,94 @@
/*
* WebSocketServer.ino
*
* Created on: 22.05.2015
*
*/
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <WebSocketsServer.h>
#include <Hash.h>
ESP8266WiFiMulti WiFiMulti;
WebSocketsServer webSocket = WebSocketsServer(81);
#define USE_SERIAL Serial
String fragmentBuffer = "";
void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
switch(type) {
case WStype_DISCONNECTED:
USE_SERIAL.printf("[%u] Disconnected!\n", num);
break;
case WStype_CONNECTED: {
IPAddress ip = webSocket.remoteIP(num);
USE_SERIAL.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload);
// send message to client
webSocket.sendTXT(num, "Connected");
}
break;
case WStype_TEXT:
USE_SERIAL.printf("[%u] get Text: %s\n", num, payload);
break;
case WStype_BIN:
USE_SERIAL.printf("[%u] get binary length: %u\n", num, length);
hexdump(payload, length);
break;
// Fragmentation / continuation opcode handling
// case WStype_FRAGMENT_BIN_START:
case WStype_FRAGMENT_TEXT_START:
fragmentBuffer = (char*)payload;
USE_SERIAL.printf("[%u] get start start of Textfragment: %s\n", num, payload);
break;
case WStype_FRAGMENT:
fragmentBuffer += (char*)payload;
USE_SERIAL.printf("[%u] get Textfragment : %s\n", num, payload);
break;
case WStype_FRAGMENT_FIN:
fragmentBuffer += (char*)payload;
USE_SERIAL.printf("[%u] get end of Textfragment: %s\n", num, payload);
USE_SERIAL.printf("[%u] full frame: %s\n", num, fragmentBuffer.c_str());
break;
}
}
void setup() {
// USE_SERIAL.begin(921600);
USE_SERIAL.begin(115200);
//Serial.setDebugOutput(true);
USE_SERIAL.setDebugOutput(true);
USE_SERIAL.println();
USE_SERIAL.println();
USE_SERIAL.println();
for(uint8_t t = 4; t > 0; t--) {
USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t);
USE_SERIAL.flush();
delay(1000);
}
WiFiMulti.addAP("SSID", "passpasspass");
while(WiFiMulti.run() != WL_CONNECTED) {
delay(100);
}
webSocket.begin();
webSocket.onEvent(webSocketEvent);
}
void loop() {
webSocket.loop();
}

View File

@ -26,7 +26,7 @@ ESP8266WiFiMulti WiFiMulti;
ESP8266WebServer server = ESP8266WebServer(80); ESP8266WebServer server = ESP8266WebServer(80);
WebSocketsServer webSocket = WebSocketsServer(81); WebSocketsServer webSocket = WebSocketsServer(81);
void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t lenght) { void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
switch(type) { switch(type) {
case WStype_DISCONNECTED: case WStype_DISCONNECTED:
@ -100,7 +100,7 @@ void setup() {
// handle index // handle index
server.on("/", []() { server.on("/", []() {
// send index.html // send index.html
server.send(200, "text/html", "<html><head><script>var connection = new WebSocket('ws://'+location.hostname+':81/', ['arduino']);connection.onopen = function () { connection.send('Connect ' + new Date()); }; connection.onerror = function (error) { console.log('WebSocket Error ', error);};connection.onmessage = function (e) { console.log('Server: ', e.data);};function sendRGB() { var r = parseInt(document.getElementById('r').value).toString(16); var g = parseInt(document.getElementById('g').value).toString(16); var b = parseInt(document.getElementById('b').value).toString(16); if(r.length < 2) { r = '0' + r; } if(g.length < 2) { g = '0' + g; } if(b.length < 2) { b = '0' + b; } var rgb = '#'+r+g+b; console.log('RGB: ' + rgb); connection.send(rgb); }</script></head><body>LED Control:<br/><br/>R: <input id=\"r\" type=\"range\" min=\"0\" max=\"255\" step=\"1\" onchange=\"sendRGB();\" /><br/>G: <input id=\"g\" type=\"range\" min=\"0\" max=\"255\" step=\"1\" onchange=\"sendRGB();\" /><br/>B: <input id=\"b\" type=\"range\" min=\"0\" max=\"255\" step=\"1\" onchange=\"sendRGB();\" /><br/></body></html>"); server.send(200, "text/html", "<html><head><script>var connection = new WebSocket('ws://'+location.hostname+':81/', ['arduino']);connection.onopen = function () { connection.send('Connect ' + new Date()); }; connection.onerror = function (error) { console.log('WebSocket Error ', error);};connection.onmessage = function (e) { console.log('Server: ', e.data);};function sendRGB() { var r = parseInt(document.getElementById('r').value).toString(16); var g = parseInt(document.getElementById('g').value).toString(16); var b = parseInt(document.getElementById('b').value).toString(16); if(r.length < 2) { r = '0' + r; } if(g.length < 2) { g = '0' + g; } if(b.length < 2) { b = '0' + b; } var rgb = '#'+r+g+b; console.log('RGB: ' + rgb); connection.send(rgb); }</script></head><body>LED Control:<br/><br/>R: <input id=\"r\" type=\"range\" min=\"0\" max=\"255\" step=\"1\" oninput=\"sendRGB();\" /><br/>G: <input id=\"g\" type=\"range\" min=\"0\" max=\"255\" step=\"1\" oninput=\"sendRGB();\" /><br/>B: <input id=\"b\" type=\"range\" min=\"0\" max=\"255\" step=\"1\" oninput=\"sendRGB();\" /><br/></body></html>");
}); });
server.begin(); server.begin();

View File

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

View File

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

View File

@ -123,7 +123,7 @@ bool WebSockets::sendFrame(WSclient_t * client, WSopcode_t opcode, uint8_t * pay
#ifdef WEBSOCKETS_USE_BIG_MEM #ifdef WEBSOCKETS_USE_BIG_MEM
// only for ESP since AVR has less HEAP // only for ESP since AVR has less HEAP
// try to send data in one TCP package (only if some free Heap is there) // try to send data in one TCP package (only if some free Heap is there)
if(!headerToPayload && ((length > 0) && (length < 1400)) && (ESP.getFreeHeap() > 6000)) { if(!headerToPayload && ((length > 0) && (length < 1400)) && (GET_FREE_HEAP > 6000)) {
DEBUG_WEBSOCKETS("[WS][%d][sendFrame] pack to one TCP package...\n", client->num); DEBUG_WEBSOCKETS("[WS][%d][sendFrame] pack to one TCP package...\n", client->num);
uint8_t * dataPtr = (uint8_t *) malloc(length + WEBSOCKETS_MAX_HEADER_SIZE); uint8_t * dataPtr = (uint8_t *) malloc(length + WEBSOCKETS_MAX_HEADER_SIZE);
if(dataPtr) { if(dataPtr) {
@ -233,18 +233,18 @@ 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;
} }
} }
@ -296,7 +296,7 @@ bool WebSockets::handleWebsocketWaitFor(WSclient_t * client, size_t size) {
} }
if(size > WEBSOCKETS_MAX_HEADER_SIZE) { if(size > WEBSOCKETS_MAX_HEADER_SIZE) {
DEBUG_WEBSOCKETS("[WS][%d][handleWebsocketWaitFor] size: %d to big!\n", client->num, size); DEBUG_WEBSOCKETS("[WS][%d][handleWebsocketWaitFor] size: %d too big!\n", client->num, size);
return false; return false;
} }
@ -364,7 +364,7 @@ void WebSockets::handleWebsocketCb(WSclient_t * client) {
} }
if(buffer[0] != 0 || buffer[1] != 0 || buffer[2] != 0 || buffer[3] != 0) { if(buffer[0] != 0 || buffer[1] != 0 || buffer[2] != 0 || buffer[3] != 0) {
// really to big! // really too big!
header->payloadLen = 0xFFFFFFFF; header->payloadLen = 0xFFFFFFFF;
} else { } else {
header->payloadLen = buffer[4] << 24 | buffer[5] << 16 | buffer[6] << 8 | buffer[7]; header->payloadLen = buffer[4] << 24 | buffer[5] << 16 | buffer[6] << 8 | buffer[7];
@ -377,7 +377,7 @@ void WebSockets::handleWebsocketCb(WSclient_t * client) {
DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] mask: %u payloadLen: %u\n", client->num, header->mask, header->payloadLen); DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] mask: %u payloadLen: %u\n", client->num, header->mask, header->payloadLen);
if(header->payloadLen > WEBSOCKETS_MAX_DATA_SIZE) { if(header->payloadLen > WEBSOCKETS_MAX_DATA_SIZE) {
DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] payload to big! (%u)\n", client->num, header->payloadLen); DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] payload too big! (%u)\n", client->num, header->payloadLen);
clientDisconnect(client, 1009); clientDisconnect(client, 1009);
return; return;
} }
@ -426,14 +426,15 @@ void WebSockets::handleWebsocketPayloadCb(WSclient_t * client, bool ok, uint8_t
DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] text: %s\n", client->num, payload); DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] text: %s\n", client->num, payload);
// no break here! // no break here!
case WSop_binary: case WSop_binary:
messageReceived(client, header->opCode, payload, header->payloadLen); case WSop_continuation:
messageReceived(client, header->opCode, payload, header->payloadLen, header->fin);
break; break;
case WSop_ping: case WSop_ping:
// send pong back // send pong back
sendFrame(client, WSop_pong, payload, header->payloadLen); sendFrame(client, WSop_pong, payload, header->payloadLen, true);
break; break;
case WSop_pong: case WSop_pong:
DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] get pong (%s)\n", client->num, payload); DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] get pong (%s)\n", client->num, payload ? (const char*)payload : "");
break; break;
case WSop_close: { case WSop_close: {
uint16_t reasonCode = 1000; uint16_t reasonCode = 1000;
@ -450,10 +451,6 @@ void WebSockets::handleWebsocketPayloadCb(WSclient_t * client, bool ok, uint8_t
clientDisconnect(client, 1000); clientDisconnect(client, 1000);
} }
break; break;
case WSop_continuation:
// continuation is not supported
clientDisconnect(client, 1003);
break;
default: default:
clientDisconnect(client, 1002); clientDisconnect(client, 1002);
break; break;
@ -597,3 +594,55 @@ bool WebSockets::readCb(WSclient_t * client, uint8_t * out, size_t n, WSreadWait
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: %d t: %d\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! %d\n", (millis() - t));
break;
}
len = client->tcp->write((const uint8_t*)out, n);
if(len) {
t = millis();
out += len;
n -= len;
total += len;
//DEBUG_WEBSOCKETS("write %d left %d!\n", len, n);
} else {
//DEBUG_WEBSOCKETS("write %d failed left %d!\n", len, n);
}
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
delay(0);
#endif
}
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));
}

View File

@ -25,9 +25,23 @@
#ifndef WEBSOCKETS_H_ #ifndef WEBSOCKETS_H_
#define WEBSOCKETS_H_ #define WEBSOCKETS_H_
#ifdef STM32_DEVICE
#include <application.h>
#define bit(b) (1UL << (b)) // Taken directly from Arduino.h
#else
#include <Arduino.h> #include <Arduino.h>
#endif
#include <functional>
#ifndef NODEBUG_WEBSOCKETS
#ifdef DEBUG_ESP_PORT
#define DEBUG_WEBSOCKETS(...) DEBUG_ESP_PORT.printf( __VA_ARGS__ )
#else
//#define DEBUG_WEBSOCKETS(...) os_printf( __VA_ARGS__ ) //#define DEBUG_WEBSOCKETS(...) os_printf( __VA_ARGS__ )
#endif
#endif
#ifndef DEBUG_WEBSOCKETS #ifndef DEBUG_WEBSOCKETS
#define DEBUG_WEBSOCKETS(...) #define DEBUG_WEBSOCKETS(...)
@ -37,9 +51,20 @@
#ifdef ESP8266 #ifdef ESP8266
#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()
// moves all Header strings to Flash (~300 Byte)
//#define WEBSOCKETS_SAVE_RAM
#else
#ifdef STM32_DEVICE
#define WEBSOCKETS_MAX_DATA_SIZE (15*1024)
#define WEBSOCKETS_USE_BIG_MEM
#define GET_FREE_HEAP System.freeMemory()
#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
#define WEBSOCKETS_SAVE_RAM
#endif
#endif #endif
#define WEBSOCKETS_TCP_TIMEOUT (2000) #define WEBSOCKETS_TCP_TIMEOUT (2000)
@ -52,13 +77,16 @@
// 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)
// 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
#else #else
#define WEBSOCKETS_NETWORK_TYPE NETWORK_W5100 #define WEBSOCKETS_NETWORK_TYPE NETWORK_W5100
#endif #endif
#endif
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) #if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
@ -96,10 +124,15 @@
#elif (WEBSOCKETS_NETWORK_TYPE == NETWORK_W5100) #elif (WEBSOCKETS_NETWORK_TYPE == NETWORK_W5100)
#ifdef STM32_DEVICE
#define WEBSOCKETS_NETWORK_CLASS TCPClient
#define WEBSOCKETS_NETWORK_SERVER_CLASS TCPServer
#else
#include <Ethernet.h> #include <Ethernet.h>
#include <SPI.h> #include <SPI.h>
#define WEBSOCKETS_NETWORK_CLASS EthernetClient #define WEBSOCKETS_NETWORK_CLASS EthernetClient
#define WEBSOCKETS_NETWORK_SERVER_CLASS EthernetServer #define WEBSOCKETS_NETWORK_SERVER_CLASS EthernetServer
#endif
#elif (WEBSOCKETS_NETWORK_TYPE == NETWORK_ENC28J60) #elif (WEBSOCKETS_NETWORK_TYPE == NETWORK_ENC28J60)
@ -111,6 +144,12 @@
#error "no network type selected!" #error "no network type selected!"
#endif #endif
// moves all Header strings to Flash (~300 Byte)
#ifdef WEBSOCKETS_SAVE_RAM
#define WEBSOCKETS_STRING(var) F(var)
#else
#define WEBSOCKETS_STRING(var) var
#endif
typedef enum { typedef enum {
WSC_NOT_CONNECTED, WSC_NOT_CONNECTED,
@ -123,7 +162,11 @@ typedef enum {
WStype_DISCONNECTED, WStype_DISCONNECTED,
WStype_CONNECTED, WStype_CONNECTED,
WStype_TEXT, WStype_TEXT,
WStype_BIN WStype_BIN,
WStype_FRAGMENT_TEXT_START,
WStype_FRAGMENT_BIN_START,
WStype_FRAGMENT,
WStype_FRAGMENT_FIN,
} WStype_t; } WStype_t;
typedef enum { typedef enum {
@ -159,6 +202,8 @@ typedef struct {
WEBSOCKETS_NETWORK_CLASS * tcp; WEBSOCKETS_NETWORK_CLASS * tcp;
bool isSocketIO; ///< client for socket.io server
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) #if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
bool isSSL; ///< run in ssl mode bool isSSL; ///< run in ssl mode
WiFiClientSecure * ssl; WiFiClientSecure * ssl;
@ -170,6 +215,7 @@ typedef struct {
bool cIsUpgrade; ///< Connection == Upgrade bool cIsUpgrade; ///< Connection == Upgrade
bool cIsWebsocket; ///< Upgrade == websocket bool cIsWebsocket; ///< Upgrade == websocket
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
@ -183,6 +229,8 @@ typedef struct {
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;
bool cHttpHeadersValid; ///< non-websocket http header validity indicator bool cHttpHeadersValid; ///< 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
@ -205,7 +253,7 @@ class WebSockets {
virtual void clientDisconnect(WSclient_t * client); virtual void clientDisconnect(WSclient_t * client);
virtual bool clientIsConnected(WSclient_t * client); virtual bool clientIsConnected(WSclient_t * client);
virtual void messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length); 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);
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); 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);
@ -222,6 +270,8 @@ class WebSockets {
String base64_encode(uint8_t * data, size_t length); String base64_encode(uint8_t * data, size_t length);
bool readCb(WSclient_t * client, uint8_t *out, size_t n, WSreadWaitCb cb); 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);
}; };

View File

@ -29,6 +29,7 @@
WebSocketsClient::WebSocketsClient() { WebSocketsClient::WebSocketsClient() {
_cbEvent = NULL; _cbEvent = NULL;
_client.num = 0; _client.num = 0;
_client.extraHeaders = WEBSOCKETS_STRING("Origin: file://");
} }
WebSocketsClient::~WebSocketsClient() { WebSocketsClient::~WebSocketsClient() {
@ -63,6 +64,7 @@ void WebSocketsClient::begin(const char *host, uint16_t port, const char * url,
_client.cVersion = 0; _client.cVersion = 0;
_client.base64Authorization = ""; _client.base64Authorization = "";
_client.plainAuthorization = ""; _client.plainAuthorization = "";
_client.isSocketIO = false;
#ifdef ESP8266 #ifdef ESP8266
randomSeed(RANDOM_REG32); randomSeed(RANDOM_REG32);
@ -73,6 +75,9 @@ void WebSocketsClient::begin(const char *host, uint16_t port, const char * url,
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) #if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
asyncConnect(); asyncConnect();
#endif #endif
_lastConnectionFail = 0;
_reconnectInterval = 500;
} }
void WebSocketsClient::begin(String host, uint16_t port, String url, String protocol) { void WebSocketsClient::begin(String host, uint16_t port, String url, String protocol) {
@ -91,6 +96,27 @@ void WebSocketsClient::beginSSL(String host, uint16_t port, String url, String f
} }
#endif #endif
void WebSocketsClient::beginSocketIO(const char *host, uint16_t port, const char * url, const char * protocol) {
begin(host, port, url, protocol);
_client.isSocketIO = true;
}
void WebSocketsClient::beginSocketIO(String host, uint16_t port, String url, String protocol) {
beginSocketIO(host.c_str(), port, url.c_str(), protocol.c_str());
}
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
void WebSocketsClient::beginSocketIOSSL(const char *host, uint16_t port, const char * url, const char * protocol) {
begin(host, port, url, protocol);
_client.isSocketIO = true;
_client.isSSL = true;
_fingerprint = "";
}
void WebSocketsClient::beginSocketIOSSL(String host, uint16_t port, String url, String protocol) {
beginSocketIOSSL(host.c_str(), port, url.c_str(), protocol.c_str());
}
#endif
#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) #if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
/** /**
@ -98,6 +124,10 @@ void WebSocketsClient::beginSSL(String host, uint16_t port, String url, String f
*/ */
void WebSocketsClient::loop(void) { void WebSocketsClient::loop(void) {
if(!clientIsConnected(&_client)) { if(!clientIsConnected(&_client)) {
// do not flood the server
if((millis() - _lastConnectionFail) < _reconnectInterval) {
return;
}
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) #if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
if(_client.isSSL) { if(_client.isSSL) {
@ -128,9 +158,11 @@ void WebSocketsClient::loop(void) {
if(_client.tcp->connect(_host.c_str(), _port)) { if(_client.tcp->connect(_host.c_str(), _port)) {
connectedCb(); connectedCb();
_lastConnectionFail = 0;
} else { } else {
connectFailedCb(); connectFailedCb();
delay(10); //some little delay to not flood the server _lastConnectionFail = millis();
} }
} else { } else {
handleClientData(); handleClientData();
@ -199,6 +231,24 @@ bool WebSocketsClient::sendBIN(const uint8_t * payload, size_t length) {
return sendBIN((uint8_t *) payload, length); return sendBIN((uint8_t *) payload, length);
} }
/**
* sends a WS ping to Server
* @param payload uint8_t *
* @param length size_t
* @return true if ping is send out
*/
bool WebSocketsClient::sendPing(uint8_t * payload, size_t length) {
if(clientIsConnected(&_client)) {
return sendFrame(&_client, WSop_ping, payload, length, true);
}
return false;
}
bool WebSocketsClient::sendPing(String & payload) {
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
@ -234,6 +284,25 @@ void WebSocketsClient::setAuthorization(const char * auth) {
} }
} }
/**
* set extra headers for the http request;
* separate headers by "\r\n"
* @param extraHeaders const char * extraHeaders
*/
void WebSocketsClient::setExtraHeaders(const char * extraHeaders) {
_client.extraHeaders = extraHeaders;
}
/**
* set the reconnect Interval
* how long to wait after a connection initiate failed
* @param time in ms
*/
void WebSocketsClient::setReconnectInterval(unsigned long time) {
_reconnectInterval = time;
}
//################################################################################# //#################################################################################
//################################################################################# //#################################################################################
//################################################################################# //#################################################################################
@ -243,21 +312,24 @@ void WebSocketsClient::setAuthorization(const char * auth) {
* @param client WSclient_t * ptr to the client struct * @param client WSclient_t * ptr to the client struct
* @param opcode WSopcode_t * @param opcode WSopcode_t
* @param payload uint8_t * * @param payload uint8_t *
* @param lenght size_t * @param length size_t
*/ */
void WebSocketsClient::messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t lenght) { void WebSocketsClient::messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin) {
WStype_t type = WStype_ERROR; WStype_t type = WStype_ERROR;
switch(opcode) { switch(opcode) {
case WSop_text: case WSop_text:
type = WStype_TEXT; type = fin ? WStype_TEXT : WStype_FRAGMENT_TEXT_START;
break; break;
case WSop_binary: case WSop_binary:
type = WStype_BIN; type = fin ? WStype_BIN : WStype_FRAGMENT_BIN_START;
break;
case WSop_continuation:
type = fin ? WStype_FRAGMENT_FIN : WStype_FRAGMENT;
break; break;
} }
runCbEvent(type, payload, lenght); runCbEvent(type, payload, length);
} }
@ -305,6 +377,7 @@ void WebSocketsClient::clientDisconnect(WSclient_t * client) {
client->cVersion = 0; client->cVersion = 0;
client->cIsUpgrade = false; client->cIsUpgrade = false;
client->cIsWebsocket = false; client->cIsWebsocket = false;
client->cSessionId = "";
client->status = WSC_NOT_CONNECTED; client->status = WSC_NOT_CONNECTED;
@ -373,12 +446,15 @@ void WebSocketsClient::handleClientData(void) {
} }
#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";
DEBUG_WEBSOCKETS("[WS-Client][sendHeader] sending header...\n"); DEBUG_WEBSOCKETS("[WS-Client][sendHeader] sending header...\n");
uint8_t randomKey[16] = { 0 }; uint8_t randomKey[16] = { 0 };
@ -393,34 +469,66 @@ void WebSocketsClient::sendHeader(WSclient_t * client) {
unsigned long start = micros(); unsigned long start = micros();
#endif #endif
String handshake = "GET " + client->cUrl + " HTTP/1.1\r\n" String handshake;
"Host: " + _host + ":" + _port + "\r\n" bool ws_header = true;
"Connection: Upgrade\r\n" String url = client->cUrl;
if(client->isSocketIO) {
if(client->cSessionId.length() == 0) {
url += WEBSOCKETS_STRING("&transport=polling");
ws_header = false;
} else {
url += WEBSOCKETS_STRING("&transport=websocket&sid=");
url += client->cSessionId;
}
}
handshake = WEBSOCKETS_STRING("GET ");
handshake += url + WEBSOCKETS_STRING(" HTTP/1.1\r\n"
"Host: ");
handshake += _host + ":" + _port + NEW_LINE;
if(ws_header) {
handshake += WEBSOCKETS_STRING("Connection: Upgrade\r\n"
"Upgrade: websocket\r\n" "Upgrade: websocket\r\n"
"Origin: file://\r\n"
"User-Agent: arduino-WebSocket-Client\r\n"
"Sec-WebSocket-Version: 13\r\n" "Sec-WebSocket-Version: 13\r\n"
"Sec-WebSocket-Key: " + client->cKey + "\r\n"; "Sec-WebSocket-Key: ");
handshake += client->cKey + NEW_LINE;
if(client->cProtocol.length() > 0) { if(client->cProtocol.length() > 0) {
handshake += "Sec-WebSocket-Protocol: " + client->cProtocol + "\r\n"; handshake += WEBSOCKETS_STRING("Sec-WebSocket-Protocol: ");
handshake +=client->cProtocol + NEW_LINE;
} }
if(client->cExtensions.length() > 0) { if(client->cExtensions.length() > 0) {
handshake += "Sec-WebSocket-Extensions: " + client->cExtensions + "\r\n"; handshake += WEBSOCKETS_STRING("Sec-WebSocket-Extensions: ");
handshake +=client->cExtensions + NEW_LINE;
}
} else {
handshake += WEBSOCKETS_STRING("Connection: keep-alive\r\n");
} }
// add extra headers; by default this includes "Origin: file://"
if (client->extraHeaders) {
handshake += client->extraHeaders + NEW_LINE;
}
handshake += WEBSOCKETS_STRING("User-Agent: arduino-WebSocket-Client\r\n");
if(client->base64Authorization.length() > 0) { if(client->base64Authorization.length() > 0) {
handshake += "Authorization: Basic " + client->base64Authorization + "\r\n"; handshake += WEBSOCKETS_STRING("Authorization: Basic ");
handshake += client->base64Authorization + NEW_LINE;
} }
if(client->plainAuthorization.length() > 0) { if(client->plainAuthorization.length() > 0) {
handshake += "Authorization: " + client->plainAuthorization + "\r\n"; handshake += WEBSOCKETS_STRING("Authorization: ");
handshake += client->plainAuthorization + NEW_LINE;
} }
handshake += "\r\n"; handshake += NEW_LINE;
client->tcp->write(handshake.c_str(), handshake.length()); DEBUG_WEBSOCKETS("[WS-Client][sendHeader] handshake %s", (uint8_t*)handshake.c_str());
write(client, (uint8_t*)handshake.c_str(), handshake.length());
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) #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)));
@ -441,30 +549,36 @@ void WebSocketsClient::handleHeader(WSclient_t * client, String * headerLine) {
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("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(':')) {
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(':') + 2);
if(headerName.equalsIgnoreCase("Connection")) { if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Connection"))) {
if(headerValue.equalsIgnoreCase("upgrade")) { if(headerValue.equalsIgnoreCase(WEBSOCKETS_STRING("upgrade"))) {
client->cIsUpgrade = true; client->cIsUpgrade = true;
} }
} else if(headerName.equalsIgnoreCase("Upgrade")) { } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Upgrade"))) {
if(headerValue.equalsIgnoreCase("websocket")) { if(headerValue.equalsIgnoreCase(WEBSOCKETS_STRING("websocket"))) {
client->cIsWebsocket = true; client->cIsWebsocket = true;
} }
} else if(headerName.equalsIgnoreCase("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("Sec-WebSocket-Protocol")) { } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Protocol"))) {
client->cProtocol = headerValue; client->cProtocol = headerValue;
} else if(headerName.equalsIgnoreCase("Sec-WebSocket-Extensions")) { } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Extensions"))) {
client->cExtensions = headerValue; client->cExtensions = headerValue;
} else if(headerName.equalsIgnoreCase("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"))) {
if (headerValue.indexOf(WEBSOCKETS_STRING("HttpOnly")) > -1) {
client->cSessionId = headerValue.substring(headerValue.indexOf('=') + 1, headerValue.indexOf(";"));
} else {
client->cSessionId = headerValue.substring(headerValue.indexOf('=') + 1);
}
} }
} else { } else {
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Header error (%s)\n", headerLine->c_str()); DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Header error (%s)\n", headerLine->c_str());
@ -490,6 +604,7 @@ void WebSocketsClient::handleHeader(WSclient_t * client, String * headerLine) {
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cProtocol: %s\n", client->cProtocol.c_str()); DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cProtocol: %s\n", client->cProtocol.c_str());
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cExtensions: %s\n", client->cExtensions.c_str()); DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cExtensions: %s\n", client->cExtensions.c_str());
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cVersion: %d\n", client->cVersion); DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cVersion: %d\n", client->cVersion);
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cSessionId: %s\n", client->cSessionId.c_str());
bool ok = (client->cIsUpgrade && client->cIsWebsocket); bool ok = (client->cIsUpgrade && client->cIsWebsocket);
@ -498,12 +613,17 @@ void WebSocketsClient::handleHeader(WSclient_t * client, String * headerLine) {
case 101: ///< Switching Protocols case 101: ///< Switching Protocols
break; break;
case 200:
if(client->isSocketIO) {
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;
} }
} }
@ -527,12 +647,16 @@ void WebSocketsClient::handleHeader(WSclient_t * client, String * headerLine) {
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());
} else if(clientIsConnected(client) && client->isSocketIO && client->cSessionId.length() > 0) {
sendHeader(client);
} else { } else {
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] no Websocket connection close.\n"); DEBUG_WEBSOCKETS("[WS-Client][handleHeader] no Websocket connection close.\n");
client->tcp->write("This is a webSocket client!"); _lastConnectionFail = millis();
if(clientIsConnected(client)) {
write(client, "This is a webSocket client!");
}
clientDisconnect(client); clientDisconnect(client);
} }
} }

View File

@ -25,7 +25,6 @@
#ifndef WEBSOCKETSCLIENT_H_ #ifndef WEBSOCKETSCLIENT_H_
#define WEBSOCKETSCLIENT_H_ #define WEBSOCKETSCLIENT_H_
#include <Arduino.h>
#include "WebSockets.h" #include "WebSockets.h"
class WebSocketsClient: private WebSockets { class WebSocketsClient: private WebSockets {
@ -48,6 +47,14 @@ class WebSocketsClient: private WebSockets {
void beginSSL(String host, uint16_t port, String url = "/", String fingerprint = "", String protocol = "arduino"); void beginSSL(String host, uint16_t port, String url = "/", String fingerprint = "", String protocol = "arduino");
#endif #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) #if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
void loop(void); void loop(void);
#else #else
@ -66,11 +73,18 @@ class WebSocketsClient: private WebSockets {
bool sendBIN(uint8_t * payload, size_t length, bool headerToPayload = false); bool sendBIN(uint8_t * payload, size_t length, bool headerToPayload = false);
bool sendBIN(const uint8_t * payload, size_t length); 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 disconnect(void);
void setAuthorization(const char * user, const char * password); void setAuthorization(const char * user, const char * password);
void setAuthorization(const char * auth); void setAuthorization(const char * auth);
void setExtraHeaders(const char * extraHeaders = NULL);
void setReconnectInterval(unsigned long time);
protected: protected:
String _host; String _host;
uint16_t _port; uint16_t _port;
@ -82,7 +96,10 @@ class WebSocketsClient: private WebSockets {
WebSocketClientEvent _cbEvent; WebSocketClientEvent _cbEvent;
void messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length); unsigned long _lastConnectionFail;
unsigned long _reconnectInterval;
void messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin);
void clientDisconnect(WSclient_t * client); void clientDisconnect(WSclient_t * client);
bool clientIsConnected(WSclient_t * client); bool clientIsConnected(WSclient_t * client);

View File

@ -283,6 +283,57 @@ bool WebSocketsServer::broadcastBIN(const uint8_t * payload, size_t length) {
return broadcastBIN((uint8_t *) payload, length); return broadcastBIN((uint8_t *) payload, length);
} }
/**
* sends a WS ping to Client
* @param num uint8_t client id
* @param payload uint8_t *
* @param length size_t
* @return true if ping is send out
*/
bool WebSocketsServer::sendPing(uint8_t num, uint8_t * payload, size_t length) {
if(num >= WEBSOCKETS_SERVER_CLIENT_MAX) {
return false;
}
WSclient_t * client = &_clients[num];
if(clientIsConnected(client)) {
return sendFrame(client, WSop_ping, payload, length);
}
return false;
}
bool WebSocketsServer::sendPing(uint8_t num, String & payload) {
return sendPing(num, (uint8_t *) payload.c_str(), payload.length());
}
/**
* sends a WS ping to all Client
* @param payload uint8_t *
* @param length size_t
* @return true if ping is send out
*/
bool WebSocketsServer::broadcastPing(uint8_t * payload, size_t length) {
WSclient_t * client;
bool ret = true;
for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
client = &_clients[i];
if(clientIsConnected(client)) {
if(!sendFrame(client, WSop_ping, payload, length)) {
ret = false;
}
}
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
delay(0);
#endif
}
return ret;
}
bool WebSocketsServer::broadcastPing(String & payload) {
return broadcastPing((uint8_t *) payload.c_str(), payload.length());
}
/** /**
* disconnect all clients * disconnect all clients
*/ */
@ -417,21 +468,24 @@ bool WebSocketsServer::newClient(WEBSOCKETS_NETWORK_CLASS * TCPclient) {
* @param client WSclient_t * ptr to the client struct * @param client WSclient_t * ptr to the client struct
* @param opcode WSopcode_t * @param opcode WSopcode_t
* @param payload uint8_t * * @param payload uint8_t *
* @param lenght size_t * @param length size_t
*/ */
void WebSocketsServer::messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t lenght) { void WebSocketsServer::messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin) {
WStype_t type = WStype_ERROR; WStype_t type = WStype_ERROR;
switch(opcode) { switch(opcode) {
case WSop_text: case WSop_text:
type = WStype_TEXT; type = fin ? WStype_TEXT : WStype_FRAGMENT_TEXT_START;
break; break;
case WSop_binary: case WSop_binary:
type = WStype_BIN; type = fin ? WStype_BIN : WStype_FRAGMENT_BIN_START;
break;
case WSop_continuation:
type = fin ? WStype_FRAGMENT_FIN : WStype_FRAGMENT;
break; break;
} }
runCbEvent(client->num, type, payload, lenght); runCbEvent(client->num, type, payload, length);
} }
@ -613,6 +667,7 @@ bool WebSocketsServer::hasMandatoryHeader(String headerName) {
return false; return false;
} }
/** /**
* handles http header reading for WebSocket upgrade * handles http header reading for WebSocket upgrade
* @param client WSclient_t * ///< pointer to the client struct * @param client WSclient_t * ///< pointer to the client struct
@ -620,6 +675,8 @@ bool WebSocketsServer::hasMandatoryHeader(String headerName) {
*/ */
void WebSocketsServer::handleHeader(WSclient_t * client, String * headerLine) { void WebSocketsServer::handleHeader(WSclient_t * client, String * headerLine) {
static const char * NEW_LINE = "\r\n";
headerLine->trim(); // remove \r headerLine->trim(); // remove \r
if(headerLine->length() > 0) { if(headerLine->length() > 0) {
@ -639,25 +696,25 @@ void WebSocketsServer::handleHeader(WSclient_t * client, String * headerLine) {
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(':') + 2);
if(headerName.equalsIgnoreCase("Connection")) { if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Connection"))) {
headerValue.toLowerCase(); headerValue.toLowerCase();
if(headerValue.indexOf("upgrade") >= 0) { if(headerValue.indexOf(WEBSOCKETS_STRING("upgrade")) >= 0) {
client->cIsUpgrade = true; client->cIsUpgrade = true;
} }
} else if(headerName.equalsIgnoreCase("Upgrade")) { } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Upgrade"))) {
if(headerValue.equalsIgnoreCase("websocket")) { if(headerValue.equalsIgnoreCase(WEBSOCKETS_STRING("websocket"))) {
client->cIsWebsocket = true; client->cIsWebsocket = true;
} }
} else if(headerName.equalsIgnoreCase("Sec-WebSocket-Version")) { } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Version"))) {
client->cVersion = headerValue.toInt(); client->cVersion = headerValue.toInt();
} else if(headerName.equalsIgnoreCase("Sec-WebSocket-Key")) { } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Key"))) {
client->cKey = headerValue; client->cKey = headerValue;
client->cKey.trim(); // see rfc6455 client->cKey.trim(); // see rfc6455
} else if(headerName.equalsIgnoreCase("Sec-WebSocket-Protocol")) { } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Protocol"))) {
client->cProtocol = headerValue; client->cProtocol = headerValue;
} else if(headerName.equalsIgnoreCase("Sec-WebSocket-Extensions")) { } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Extensions"))) {
client->cExtensions = headerValue; client->cExtensions = headerValue;
} else if(headerName.equalsIgnoreCase("Authorization")) { } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Authorization"))) {
client->base64Authorization = headerValue; client->base64Authorization = headerValue;
} else { } else {
client->cHttpHeadersValid &= execHttpHeaderValidation(headerName, headerValue); client->cHttpHeadersValid &= execHttpHeaderValidation(headerName, headerValue);
@ -709,17 +766,13 @@ void WebSocketsServer::handleHeader(WSclient_t * client, String * headerLine) {
} }
if(_base64Authorization.length() > 0) { if(_base64Authorization.length() > 0) {
if(client->base64Authorization.length() > 0) { String auth = WEBSOCKETS_STRING("Basic ");
String auth = "Basic ";
auth += _base64Authorization; auth += _base64Authorization;
if(auth != client->base64Authorization) { if(auth != client->base64Authorization) {
DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] HTTP Authorization failed!\n", client->num); DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] HTTP Authorization failed!\n", client->num);
handleAuthorizationFailed(client); handleAuthorizationFailed(client);
return; return;
} }
} else {
ok = false;
}
} }
if(ok) { if(ok) {
@ -733,32 +786,30 @@ void WebSocketsServer::handleHeader(WSclient_t * client, String * headerLine) {
client->status = WSC_CONNECTED; client->status = WSC_CONNECTED;
client->tcp->write("HTTP/1.1 101 Switching Protocols\r\n" String handshake = WEBSOCKETS_STRING("HTTP/1.1 101 Switching Protocols\r\n"
"Server: arduino-WebSocketsServer\r\n" "Server: arduino-WebSocketsServer\r\n"
"Upgrade: websocket\r\n" "Upgrade: websocket\r\n"
"Connection: Upgrade\r\n" "Connection: Upgrade\r\n"
"Sec-WebSocket-Version: 13\r\n" "Sec-WebSocket-Version: 13\r\n"
"Sec-WebSocket-Accept: "); "Sec-WebSocket-Accept: ");
client->tcp->write(sKey.c_str(), sKey.length()); handshake += sKey + NEW_LINE;
if(_origin.length() > 0) { if(_origin.length() > 0) {
String origin = "\r\nAccess-Control-Allow-Origin: "; handshake += WEBSOCKETS_STRING("Access-Control-Allow-Origin: ");
origin += _origin; handshake +=_origin + NEW_LINE;
origin += "\r\n";
client->tcp->write(origin.c_str(), origin.length());
} }
if(client->cProtocol.length() > 0) { if(client->cProtocol.length() > 0) {
String protocol = "\r\nSec-WebSocket-Protocol: "; handshake += WEBSOCKETS_STRING("Sec-WebSocket-Protocol: ");
protocol += _protocol; handshake +=_protocol + NEW_LINE;
protocol += "\r\n";
client->tcp->write(protocol.c_str(), protocol.length());
} else {
client->tcp->write("\r\n");
} }
// header end // header end
client->tcp->write("\r\n"); handshake += NEW_LINE;
DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] handshake %s", client->num, (uint8_t*)handshake.c_str());
write(client, (uint8_t*)handshake.c_str(), handshake.length());
headerDone(client); headerDone(client);

View File

@ -25,7 +25,6 @@
#ifndef WEBSOCKETSSERVER_H_ #ifndef WEBSOCKETSSERVER_H_
#define WEBSOCKETSSERVER_H_ #define WEBSOCKETSSERVER_H_
#include <Arduino.h>
#include "WebSockets.h" #include "WebSockets.h"
#define WEBSOCKETS_SERVER_CLIENT_MAX (5) #define WEBSOCKETS_SERVER_CLIENT_MAX (5)
@ -33,7 +32,7 @@
class WebSocketsServer: private WebSockets { class WebSocketsServer: protected WebSockets {
public: public:
#ifdef __AVR__ #ifdef __AVR__
@ -81,6 +80,12 @@ public:
bool broadcastBIN(uint8_t * payload, size_t length, bool headerToPayload = false); bool broadcastBIN(uint8_t * payload, size_t length, bool headerToPayload = false);
bool broadcastBIN(const uint8_t * payload, size_t length); 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(void);
void disconnect(uint8_t num); void disconnect(uint8_t num);
@ -108,7 +113,7 @@ protected:
bool newClient(WEBSOCKETS_NETWORK_CLASS * TCPclient); bool newClient(WEBSOCKETS_NETWORK_CLASS * TCPclient);
void messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length); void messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin);
void clientDisconnect(WSclient_t * client); void clientDisconnect(WSclient_t * client);
bool clientIsConnected(WSclient_t * client); bool clientIsConnected(WSclient_t * client);
@ -144,7 +149,6 @@ protected:
* @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("HTTP/1.1 401 Unauthorized\r\n"
"Server: arduino-WebSocket-Server\r\n" "Server: arduino-WebSocket-Server\r\n"
"Content-Type: text/plain\r\n" "Content-Type: text/plain\r\n"

24
travis/common.sh Normal file
View File

@ -0,0 +1,24 @@
#!/bin/bash
function build_sketches()
{
local arduino=$1
local srcpath=$2
local platform=$3
local sketches=$(find $srcpath -name *.ino)
for sketch in $sketches; do
local sketchdir=$(dirname $sketch)
if [[ -f "$sketchdir/.$platform.skip" ]]; then
echo -e "\n\n ------------ Skipping $sketch ------------ \n\n";
continue
fi
echo -e "\n\n ------------ Building $sketch ------------ \n\n";
$arduino --verify $sketch;
local result=$?
if [ $result -ne 0 ]; then
echo "Build failed ($1)"
return $result
fi
done
}