From ad07f3c66510696849bcbc77831e138978c942f8 Mon Sep 17 00:00:00 2001 From: Martin Becker Date: Tue, 18 Jul 2017 12:37:04 +0200 Subject: [PATCH 1/7] 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. --- src/WebSocketsClient.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/WebSocketsClient.cpp b/src/WebSocketsClient.cpp index 819af0e..1dfcfba 100644 --- a/src/WebSocketsClient.cpp +++ b/src/WebSocketsClient.cpp @@ -479,8 +479,12 @@ void WebSocketsClient::sendHeader(WSclient_t * client) { handshake += WEBSOCKETS_STRING("Connection: keep-alive\r\n"); } - handshake += WEBSOCKETS_STRING("Origin: file://\r\n" - "User-Agent: arduino-WebSocket-Client\r\n"); +#ifndef WEBSOCKET_HEADERS_NO_ORIGIN + // add origin header if requested + handshake += WEBSOCKETS_STRING("Origin: file://\r\n"); +#endif + + handshake += WEBSOCKETS_STRING("User-Agent: arduino-WebSocket-Client\r\n"); if(client->base64Authorization.length() > 0) { handshake += WEBSOCKETS_STRING("Authorization: Basic "); From 86d2e2400a10c88fa06e62490d54b2e355f1e04b Mon Sep 17 00:00:00 2001 From: Martin Becker Date: Tue, 18 Jul 2017 13:33:13 +0200 Subject: [PATCH 2/7] Add SockJS+Stomp example --- .../WebSocketClientSockJsAndStomp.ino | 155 ++++++++++++++++++ 1 file changed, 155 insertions(+) create mode 100644 examples/WebSocketClientSockJsAndStomp/WebSocketClientSockJsAndStomp.ino diff --git a/examples/WebSocketClientSockJsAndStomp/WebSocketClientSockJsAndStomp.ino b/examples/WebSocketClientSockJsAndStomp/WebSocketClientSockJsAndStomp.ino new file mode 100644 index 0000000..fb5d7bf --- /dev/null +++ b/examples/WebSocketClientSockJsAndStomp/WebSocketClientSockJsAndStomp.ino @@ -0,0 +1,155 @@ +/* + * 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 , contact: becker@informatik.uni-wuerzburg.de + */ + +// CONSTANTS AND MACROS + +#define DEBUG_WEBSOCKETS +#define DEBUG_WEBSOCKETS(...) Serial.printf( __VA_ARGS__ ) + +#define WEBSOCKETS_NETWORK_TYPE NETWORK_ESP8266 +#define WEBSOCKETS_HEADERS_NO_ORIGIN + +#define USE_SERIAL Serial + + +// LIBRARIES + +#include +#include + + +// SETTINGS + +const char* wlan_ssid = "yourssid"; +const char* wlan_password = "password"; + +const char* ws_host = "the.host.com"; +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://://<3digits>//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\\ncontent-length:33\\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(); + + for(uint8_t t = 4; t > 0; t--) { + USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t); + USE_SERIAL.flush(); + delay(1000); + } + + // 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.onEvent(webSocketEvent); +} + +void loop() { + webSocket.loop(); +} From 42ab3168c5eabbc407354dc2477c7ad7482891df Mon Sep 17 00:00:00 2001 From: Martin Becker Date: Wed, 19 Jul 2017 09:59:34 +0200 Subject: [PATCH 3/7] Switch to setting extra headers via function --- src/WebSockets.h | 2 ++ src/WebSocketsClient.cpp | 16 ++++++++++++---- src/WebSocketsClient.h | 3 +++ 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/WebSockets.h b/src/WebSockets.h index 1e62469..41cbe7d 100644 --- a/src/WebSockets.h +++ b/src/WebSockets.h @@ -226,6 +226,8 @@ typedef struct { String base64Authorization; ///< Base64 encoded Auth request String plainAuthorization; ///< Base64 encoded Auth request + String extraHeaders; + bool cHttpHeadersValid; ///< non-websocket http header validity indicator size_t cMandatoryHeadersCount; ///< non-websocket mandatory http headers present count diff --git a/src/WebSocketsClient.cpp b/src/WebSocketsClient.cpp index 1dfcfba..787b7a5 100644 --- a/src/WebSocketsClient.cpp +++ b/src/WebSocketsClient.cpp @@ -29,6 +29,7 @@ WebSocketsClient::WebSocketsClient() { _cbEvent = NULL; _client.num = 0; + _client.extraHeaders = "Origin: file://"; } WebSocketsClient::~WebSocketsClient() { @@ -274,6 +275,15 @@ 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; +} + //################################################################################# //################################################################################# //################################################################################# @@ -479,10 +489,8 @@ void WebSocketsClient::sendHeader(WSclient_t * client) { handshake += WEBSOCKETS_STRING("Connection: keep-alive\r\n"); } -#ifndef WEBSOCKET_HEADERS_NO_ORIGIN - // add origin header if requested - handshake += WEBSOCKETS_STRING("Origin: file://\r\n"); -#endif + // add extra headers; by default this includes "Origin: file://" + handshake += client->extraHeaders + NEW_LINE; handshake += WEBSOCKETS_STRING("User-Agent: arduino-WebSocket-Client\r\n"); diff --git a/src/WebSocketsClient.h b/src/WebSocketsClient.h index d8b6814..df9bbef 100644 --- a/src/WebSocketsClient.h +++ b/src/WebSocketsClient.h @@ -80,6 +80,9 @@ class WebSocketsClient: private WebSockets { void setAuthorization(const char * user, const char * password); void setAuthorization(const char * auth); + + void setExtraHeaders(char * extraHeaders); + void setExtraHeaders(const char * extraHeaders); protected: String _host; From 32cb052f23a0eb56f7e8cc885a175de4f4ce779c Mon Sep 17 00:00:00 2001 From: Martin Becker Date: Wed, 19 Jul 2017 10:19:32 +0200 Subject: [PATCH 4/7] Update SockJS+STOMP example --- .../WebSocketClientSockJsAndStomp.ino | 22 ++++++------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/examples/WebSocketClientSockJsAndStomp/WebSocketClientSockJsAndStomp.ino b/examples/WebSocketClientSockJsAndStomp/WebSocketClientSockJsAndStomp.ino index fb5d7bf..c8d990f 100644 --- a/examples/WebSocketClientSockJsAndStomp/WebSocketClientSockJsAndStomp.ino +++ b/examples/WebSocketClientSockJsAndStomp/WebSocketClientSockJsAndStomp.ino @@ -5,16 +5,10 @@ * 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 , contact: becker@informatik.uni-wuerzburg.de + * Author: Martin Becker , Contact: becker@informatik.uni-wuerzburg.de */ -// CONSTANTS AND MACROS - -#define DEBUG_WEBSOCKETS -#define DEBUG_WEBSOCKETS(...) Serial.printf( __VA_ARGS__ ) - -#define WEBSOCKETS_NETWORK_TYPE NETWORK_ESP8266 -#define WEBSOCKETS_HEADERS_NO_ORIGIN +// PRE #define USE_SERIAL Serial @@ -28,9 +22,9 @@ // SETTINGS const char* wlan_ssid = "yourssid"; -const char* wlan_password = "password"; +const char* wlan_password = "somepassword"; -const char* ws_host = "the.host.com"; +const char* ws_host = "the.host.net"; const int ws_port = 80; // base URL for SockJS (websocket) connection @@ -117,11 +111,6 @@ void setup() { 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); - } // connect to WiFi @@ -135,6 +124,7 @@ void setup() { } 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) @@ -147,6 +137,8 @@ void setup() { // 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); } From 669f0b489efc393307353ac2a4134e3638b52d1a Mon Sep 17 00:00:00 2001 From: Martin Becker Date: Wed, 19 Jul 2017 11:12:00 +0200 Subject: [PATCH 5/7] Fix empty header issue --- .../WebSocketClientSockJsAndStomp.ino | 2 +- src/WebSocketsClient.cpp | 8 +++++--- src/WebSocketsClient.h | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/examples/WebSocketClientSockJsAndStomp/WebSocketClientSockJsAndStomp.ino b/examples/WebSocketClientSockJsAndStomp/WebSocketClientSockJsAndStomp.ino index c8d990f..96ce679 100644 --- a/examples/WebSocketClientSockJsAndStomp/WebSocketClientSockJsAndStomp.ino +++ b/examples/WebSocketClientSockJsAndStomp/WebSocketClientSockJsAndStomp.ino @@ -137,7 +137,7 @@ void setup() { // 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(); // 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); } diff --git a/src/WebSocketsClient.cpp b/src/WebSocketsClient.cpp index 787b7a5..3faae99 100644 --- a/src/WebSocketsClient.cpp +++ b/src/WebSocketsClient.cpp @@ -29,7 +29,7 @@ WebSocketsClient::WebSocketsClient() { _cbEvent = NULL; _client.num = 0; - _client.extraHeaders = "Origin: file://"; + _client.extraHeaders = WEBSOCKETS_STRING("Origin: file://"); } WebSocketsClient::~WebSocketsClient() { @@ -490,8 +490,10 @@ void WebSocketsClient::sendHeader(WSclient_t * client) { } // add extra headers; by default this includes "Origin: file://" - handshake += client->extraHeaders + NEW_LINE; - + if (client->extraHeaders) { + handshake += client->extraHeaders + NEW_LINE; + } + handshake += WEBSOCKETS_STRING("User-Agent: arduino-WebSocket-Client\r\n"); if(client->base64Authorization.length() > 0) { diff --git a/src/WebSocketsClient.h b/src/WebSocketsClient.h index df9bbef..b097b68 100644 --- a/src/WebSocketsClient.h +++ b/src/WebSocketsClient.h @@ -81,8 +81,8 @@ class WebSocketsClient: private WebSockets { void setAuthorization(const char * user, const char * password); void setAuthorization(const char * auth); + void setExtraHeaders(const char * extraHeaders = NULL); void setExtraHeaders(char * extraHeaders); - void setExtraHeaders(const char * extraHeaders); protected: String _host; From fcb623ce9167f1e6a380b359d94f500fecffce94 Mon Sep 17 00:00:00 2001 From: Martin Becker Date: Wed, 19 Jul 2017 15:50:25 +0200 Subject: [PATCH 6/7] Simplified example --- .../WebSocketClientSockJsAndStomp.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/WebSocketClientSockJsAndStomp/WebSocketClientSockJsAndStomp.ino b/examples/WebSocketClientSockJsAndStomp/WebSocketClientSockJsAndStomp.ino index 96ce679..f5397db 100644 --- a/examples/WebSocketClientSockJsAndStomp/WebSocketClientSockJsAndStomp.ino +++ b/examples/WebSocketClientSockJsAndStomp/WebSocketClientSockJsAndStomp.ino @@ -82,7 +82,7 @@ void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) { // and send a message - msg = "[\"SEND\\ndestination:/app/message\\ncontent-length:33\\n\\n{\\\"user\\\":\\\"esp\\\",\\\"message\\\":\\\"Hello!\\\"}\\u0000\"]"; + msg = "[\"SEND\\ndestination:/app/message\\n\\n{\\\"user\\\":\\\"esp\\\",\\\"message\\\":\\\"Hello!\\\"}\\u0000\"]"; webSocket.sendTXT(msg); delay(1000); } From d0ab6c4fd1e26f97ab5e8857d33edee96526c923 Mon Sep 17 00:00:00 2001 From: Martin Becker Date: Thu, 20 Jul 2017 08:21:29 +0200 Subject: [PATCH 7/7] Remove redundant method header and fix indent --- .../WebSocketClientSockJsAndStomp.ino | 94 +++++++++---------- src/WebSocketsClient.h | 1 - 2 files changed, 47 insertions(+), 48 deletions(-) diff --git a/examples/WebSocketClientSockJsAndStomp/WebSocketClientSockJsAndStomp.ino b/examples/WebSocketClientSockJsAndStomp/WebSocketClientSockJsAndStomp.ino index f5397db..078b547 100644 --- a/examples/WebSocketClientSockJsAndStomp/WebSocketClientSockJsAndStomp.ino +++ b/examples/WebSocketClientSockJsAndStomp/WebSocketClientSockJsAndStomp.ino @@ -1,12 +1,12 @@ /* - * 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 , Contact: becker@informatik.uni-wuerzburg.de - */ + 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 , Contact: becker@informatik.uni-wuerzburg.de +*/ // PRE @@ -28,7 +28,7 @@ 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): +// The complete URL will look something like this(cf. http://sockjs.github.io/sockjs-protocol/sockjs-protocol-0.3.3.html#section-36): // ws://://<3digits>//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 "/" !!! @@ -43,7 +43,7 @@ WebSocketsClient webSocket; void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) { - switch(type) { + switch (type) { case WStype_DISCONNECTED: USE_SERIAL.printf("[WSc] Disconnected!\n"); break; @@ -52,43 +52,43 @@ void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) { USE_SERIAL.printf("[WSc] Connected to url: %s\n", payload); } break; - case WStype_TEXT: { + case WStype_TEXT: + { + // ##################### + // handle STOMP protocol + // ##################### - // ##################### - // handle STOMP protocol - // ##################### + String text = (char*) payload; - String text = (char*) payload; - - USE_SERIAL.printf("[WSc] get text: %s\n", payload); + USE_SERIAL.printf("[WSc] get text: %s\n", payload); - if (payload[0] == 'h') { - - USE_SERIAL.println("Heartbeat!"); - - } else if (payload[0] == 'o') { + if (payload[0] == 'h') { - // 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")) { + USE_SERIAL.println("Heartbeat!"); - // subscribe to some channels - - char *msg = "[\"SUBSCRIBE\\nid:sub-0\\ndestination:/user/queue/messages\\n\\n\\u0000\"]"; - webSocket.sendTXT(msg); - delay(1000); + } else if (payload[0] == 'o') { - // and send a message + // on open connection + char *msg = "[\"CONNECT\\naccept-version:1.1,1.0\\nheart-beat:10000,10000\\n\\n\\u0000\"]"; + webSocket.sendTXT(msg); - msg = "[\"SEND\\ndestination:/app/message\\n\\n{\\\"user\\\":\\\"esp\\\",\\\"message\\\":\\\"Hello!\\\"}\\u0000\"]"; - webSocket.sendTXT(msg); - delay(1000); + } 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; } - - break; - } case WStype_BIN: USE_SERIAL.printf("[WSc] get binary length: %u\n", length); hexdump(payload, length); @@ -107,7 +107,7 @@ void setup() { // USE_SERIAL.begin(921600); USE_SERIAL.begin(115200); -// USE_SERIAL.setDebugOutput(true); + // USE_SERIAL.setDebugOutput(true); USE_SERIAL.println(); @@ -117,28 +117,28 @@ void setup() { 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("."); + 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 += random(0, 999); socketUrl += "/"; - socketUrl += random(0,999999); // should be a random string, but this works (see ) + 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.setExtraHeaders("foo: I am so funny\r\nbar: not"); // some headers, in case you feel funny webSocket.onEvent(webSocketEvent); } diff --git a/src/WebSocketsClient.h b/src/WebSocketsClient.h index b097b68..7390a76 100644 --- a/src/WebSocketsClient.h +++ b/src/WebSocketsClient.h @@ -82,7 +82,6 @@ class WebSocketsClient: private WebSockets { void setAuthorization(const char * auth); void setExtraHeaders(const char * extraHeaders = NULL); - void setExtraHeaders(char * extraHeaders); protected: String _host;