From a8d66b5ed9af8fce7f50ff975f8acd176d81073f Mon Sep 17 00:00:00 2001 From: pablorodiz Date: Thu, 9 Jun 2016 21:39:25 +0200 Subject: [PATCH 01/21] Removed waiting message without DEBUGGING definition --- WebSocketClient.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/WebSocketClient.cpp b/WebSocketClient.cpp index bad1b9f..d3957bf 100644 --- a/WebSocketClient.cpp +++ b/WebSocketClient.cpp @@ -88,7 +88,9 @@ bool WebSocketClient::analyzeRequest() { while (socket_client->connected() && !socket_client->available()) { delay(100); +#ifdef DEBUGGING Serial.println("Waiting..."); +#endif } // TODO: More robust string extraction From 2beba527ac4dfd1f9ead1bd86fb10e7942813948 Mon Sep 17 00:00:00 2001 From: pablorodiz Date: Thu, 9 Jun 2016 21:41:54 +0200 Subject: [PATCH 02/21] Avoid printing data lines when empty string (for pong) --- WebSocketClient.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/WebSocketClient.cpp b/WebSocketClient.cpp index d3957bf..9fee96f 100644 --- a/WebSocketClient.cpp +++ b/WebSocketClient.cpp @@ -256,8 +256,10 @@ bool WebSocketClient::getData(String& data, uint8_t *opcode) { void WebSocketClient::sendData(const char *str, uint8_t opcode) { #ifdef DEBUGGING - Serial.print(F("Sending data: ")); - Serial.println(str); + if((char)str[0]!=0) { + Serial.print(F("Sending data: ")); + Serial.println(str); + } #endif if (socket_client->connected()) { sendEncodedData(str, opcode); @@ -266,8 +268,10 @@ void WebSocketClient::sendData(const char *str, uint8_t opcode) { void WebSocketClient::sendData(String str, uint8_t opcode) { #ifdef DEBUGGING - Serial.print(F("Sending data: ")); - Serial.println(str); + if((char)str[0]!=0) { + Serial.print(F("Sending data: ")); + Serial.println(str); + } #endif if (socket_client->connected()) { sendEncodedData(str, opcode); From 366383d160d3df57a29c224502833fdb5704673a Mon Sep 17 00:00:00 2001 From: pablorodiz Date: Thu, 9 Jun 2016 21:46:31 +0200 Subject: [PATCH 03/21] With fast servers first websocket messages can be send in the payload of the handshake response. So I added headers end detection (empty line) and stop reading, so the remaining bytes of the packet will be considered part of the websocket communication --- WebSocketClient.cpp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/WebSocketClient.cpp b/WebSocketClient.cpp index 9fee96f..b80129c 100644 --- a/WebSocketClient.cpp +++ b/WebSocketClient.cpp @@ -99,14 +99,19 @@ bool WebSocketClient::analyzeRequest() { temp += (char)bite; if ((char)bite == '\n') { + if(temp.startsWith("\r\n")) { #ifdef DEBUGGING - Serial.print("Got Header: " + temp); -#endif - if (!foundupgrade && temp.startsWith("Upgrade: websocket")) { - foundupgrade = true; - } else if (temp.startsWith("Sec-WebSocket-Accept: ")) { - serverKey = temp.substring(22,temp.length() - 2); // Don't save last CR+LF - } + Serial.println("End of headers"); +#endif + break; + } else if (!foundupgrade && temp.startsWith("Upgrade: websocket")) { + foundupgrade = true; + } else if (temp.startsWith("Sec-WebSocket-Accept: ")) { + serverKey = temp.substring(22,temp.length() - 2); // Don't save last CR+LF + } +#ifdef DEBUGGING + Serial.print("Got Header: " + temp); +#endif temp = ""; } From 6fe7efbe6dc6ff6776cd9efa724e811be0ec1fc3 Mon Sep 17 00:00:00 2001 From: pablorodiz Date: Thu, 9 Jun 2016 21:50:04 +0200 Subject: [PATCH 04/21] Add the possibility to include additional headers (mainly for authenticaction) on the handshake request --- WebSocketClient.cpp | 5 +++++ WebSocketClient.h | 1 + 2 files changed, 6 insertions(+) diff --git a/WebSocketClient.cpp b/WebSocketClient.cpp index b80129c..6121004 100644 --- a/WebSocketClient.cpp +++ b/WebSocketClient.cpp @@ -6,6 +6,7 @@ #include "sha1.h" #include "base64.h" +char *WebSocketClient::headers = NULL; bool WebSocketClient::handshake(Client &client) { @@ -73,6 +74,10 @@ bool WebSocketClient::analyzeRequest() { socket_client->print(F("Host: ")); socket_client->print(host); socket_client->print(CRLF); + if(headers) { + socket_client->print(headers); + socket_client->print(CRLF); + } socket_client->print(F("Sec-WebSocket-Key: ")); socket_client->print(key); socket_client->print(CRLF); diff --git a/WebSocketClient.h b/WebSocketClient.h index 89b7c23..7c28f37 100644 --- a/WebSocketClient.h +++ b/WebSocketClient.h @@ -100,6 +100,7 @@ class WebSocketClient { char *path; char *host; char *protocol; + static char *headers; private: Client *socket_client; From f4e5a7d764b46bd2827bc385826c128f3a555747 Mon Sep 17 00:00:00 2001 From: pablorodiz Date: Thu, 9 Jun 2016 21:51:37 +0200 Subject: [PATCH 05/21] Added connected() function for the main system be able to check if connection is correct before calling any blocking-data-read function --- WebSocketClient.cpp | 4 ++++ WebSocketClient.h | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/WebSocketClient.cpp b/WebSocketClient.cpp index 6121004..4e13b6b 100644 --- a/WebSocketClient.cpp +++ b/WebSocketClient.cpp @@ -260,6 +260,10 @@ void WebSocketClient::disconnectStream() { socket_client->stop(); } +int WebSocketClient::connected(void) { + return socket_client->connected(); +} + bool WebSocketClient::getData(String& data, uint8_t *opcode) { return handleStream(data, opcode); } diff --git a/WebSocketClient.h b/WebSocketClient.h index 7c28f37..2d82349 100644 --- a/WebSocketClient.h +++ b/WebSocketClient.h @@ -89,7 +89,8 @@ class WebSocketClient { // Handle connection requests to validate and process/refuse // connections. bool handshake(Client &client); - + //Check if socket os connected + int connected(); // Get data off of the stream bool getData(String& data, uint8_t *opcode = NULL); From 2a40189e1ac383113e7c0ab350152d79d0b75c9a Mon Sep 17 00:00:00 2001 From: pablorodiz Date: Thu, 9 Jun 2016 21:52:36 +0200 Subject: [PATCH 06/21] Some corrections on the sha1 file to allow compilation for both, AVR and ESP8266 --- sha1.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sha1.cpp b/sha1.cpp index 770f6f5..aa404cb 100755 --- a/sha1.cpp +++ b/sha1.cpp @@ -1,6 +1,8 @@ #include +#ifdef __AVR__ #include #include +#endif #include "sha1.h" #define SHA1_K0 0x5a827999 @@ -8,7 +10,7 @@ #define SHA1_K40 0x8f1bbcdc #define SHA1_K60 0xca62c1d6 -uint8_t sha1InitState[] PROGMEM = { +const uint8_t sha1InitState[] PROGMEM = { 0x01,0x23,0x45,0x67, // H0 0x89,0xab,0xcd,0xef, // H1 0xfe,0xdc,0xba,0x98, // H2 From 455b40f218c89650fffe766055da7e515851e899 Mon Sep 17 00:00:00 2001 From: pablorodiz Date: Tue, 14 Jun 2016 22:16:35 +0200 Subject: [PATCH 07/21] sendEncodedData rewritten to speed up failure in case of socket disconnection --- WebSocketClient.cpp | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/WebSocketClient.cpp b/WebSocketClient.cpp index 4e13b6b..c697439 100644 --- a/WebSocketClient.cpp +++ b/WebSocketClient.cpp @@ -301,34 +301,34 @@ int WebSocketClient::timedRead() { } void WebSocketClient::sendEncodedData(char *str, uint8_t opcode) { - uint8_t mask[4]; + uint8_t header[8]; int size = strlen(str); + int i = 0; // Opcode; final fragment - socket_client->write(opcode | WS_FIN); + header[i++] = opcode | WS_FIN; + //socket_client->write(opcode | WS_FIN); // NOTE: no support for > 16-bit sized messages if (size > 125) { - socket_client->write(WS_SIZE16 | WS_MASK); - socket_client->write((uint8_t) (size >> 8)); - socket_client->write((uint8_t) (size & 0xFF)); + header[i++] = WS_SIZE16 | WS_MASK; + header[i++] = (uint8_t) (size >> 8); + header[i++] = (uint8_t) (size & 0xFF); } else { - socket_client->write((uint8_t) size | WS_MASK); + header[i++] = (uint8_t) size | WS_MASK; } - mask[0] = random(0, 256); - mask[1] = random(0, 256); - mask[2] = random(0, 256); - mask[3] = random(0, 256); - - socket_client->write(mask[0]); - socket_client->write(mask[1]); - socket_client->write(mask[2]); - socket_client->write(mask[3]); - - for (int i=0; iwrite(str[i] ^ mask[i % 4]); - } + int mask_base = i; + header[i++] = random(0, 256); + header[i++] = random(0, 256); + header[i++] = random(0, 256); + header[i++] = random(0, 256); + + if(socket_client->write(header, i)) { + for (int j=0; jwrite(str[j] ^ header[mask_base + (j % 4)]); + } + } } void WebSocketClient::sendEncodedData(String str, uint8_t opcode) { From abeeebc58f268f438883130e7bd10443138065fc Mon Sep 17 00:00:00 2001 From: pablorodiz Date: Mon, 20 Jun 2016 20:30:50 +0200 Subject: [PATCH 08/21] Added getData implementation based on char array instead of String, so user can completely avoid the use of Stings --- WebSocketClient.cpp | 97 ++++++++++++++++++++++++++++++--------------- WebSocketClient.h | 6 ++- 2 files changed, 69 insertions(+), 34 deletions(-) diff --git a/WebSocketClient.cpp b/WebSocketClient.cpp index c697439..914e4b9 100644 --- a/WebSocketClient.cpp +++ b/WebSocketClient.cpp @@ -145,31 +145,21 @@ bool WebSocketClient::analyzeRequest() { return serverKey.equals(String(b64Result)); } - -bool WebSocketClient::handleStream(String& data, uint8_t *opcode) { - uint8_t msgtype; - uint8_t bite; - unsigned int length; - uint8_t mask[4]; - uint8_t index; - unsigned int i; - bool hasMask = false; - - if (!socket_client->connected() || !socket_client->available()) - { +bool WebSocketClient::handleMessageHeader(uint8_t *msgtype, unsigned int *length, bool *hasMask, uint8_t *mask, uint8_t *opcode) { + if (!socket_client->connected() || !socket_client->available()) { return false; } - msgtype = timedRead(); + *msgtype = timedRead(); if (!socket_client->connected()) { return false; } - length = timedRead(); + *length = timedRead(); - if (length & WS_MASK) { - hasMask = true; - length = length & ~WS_MASK; + if (*length & WS_MASK) { + *hasMask = true; + *length = *length & ~WS_MASK; } @@ -177,27 +167,25 @@ bool WebSocketClient::handleStream(String& data, uint8_t *opcode) { return false; } - index = 6; - - if (length == WS_SIZE16) { - length = timedRead() << 8; + if (*length == WS_SIZE16) { + *length = timedRead() << 8; if (!socket_client->connected()) { return false; } - length |= timedRead(); + *length |= timedRead(); if (!socket_client->connected()) { return false; } - } else if (length == WS_SIZE64) { + } else if (*length == WS_SIZE64) { #ifdef DEBUGGING Serial.println(F("No support for over 16 bit sized messages")); #endif return false; } - if (hasMask) { + if (*hasMask) { // get the mask mask[0] = timedRead(); if (!socket_client->connected()) { @@ -220,23 +208,34 @@ bool WebSocketClient::handleStream(String& data, uint8_t *opcode) { return false; } } - - data = ""; - + if (opcode != NULL) { - *opcode = msgtype & ~WS_FIN; + *opcode = *msgtype & ~WS_FIN; } - + + return true; +} + +bool WebSocketClient::handleStream(String& data, uint8_t *opcode) { + uint8_t msgtype; + unsigned int length; + uint8_t mask[4]; + bool hasMask = false; + + if(!handleMessageHeader(&msgtype, &length, &hasMask, mask, opcode)) return false; + + data = ""; + if (hasMask) { - for (i=0; iconnected()) { return false; } } } else { - for (i=0; iconnected()) { return false; @@ -247,6 +246,35 @@ bool WebSocketClient::handleStream(String& data, uint8_t *opcode) { return true; } +bool WebSocketClient::handleStream(char *data, unsigned int dataLen, uint8_t *opcode) { + uint8_t msgtype; + unsigned int length; + uint8_t mask[4]; + bool hasMask = false; + + if(!handleMessageHeader(&msgtype, &length, &hasMask, mask, opcode)) return false; + + int i; + int limit = length>dataLen?dataLen:length; + if (hasMask) { + for (i=0; iconnected()) { + return false; + } + } + } else { + for (i=0; iconnected()) { + return false; + } + } + } + data[i] = '\0'; + return true; +} + void WebSocketClient::disconnectStream() { #ifdef DEBUGGING Serial.println(F("Terminating socket")); @@ -268,6 +296,10 @@ bool WebSocketClient::getData(String& data, uint8_t *opcode) { return handleStream(data, opcode); } +bool WebSocketClient::getData(char *data, unsigned int dataLen, uint8_t *opcode) { + return handleStream(data, dataLen, opcode); +} + void WebSocketClient::sendData(const char *str, uint8_t opcode) { #ifdef DEBUGGING if((char)str[0]!=0) { @@ -304,10 +336,9 @@ void WebSocketClient::sendEncodedData(char *str, uint8_t opcode) { uint8_t header[8]; int size = strlen(str); int i = 0; - + // Opcode; final fragment header[i++] = opcode | WS_FIN; - //socket_client->write(opcode | WS_FIN); // NOTE: no support for > 16-bit sized messages if (size > 125) { diff --git a/WebSocketClient.h b/WebSocketClient.h index 2d82349..367ab27 100644 --- a/WebSocketClient.h +++ b/WebSocketClient.h @@ -93,6 +93,7 @@ class WebSocketClient { int connected(); // Get data off of the stream bool getData(String& data, uint8_t *opcode = NULL); + bool getData(char *data, unsigned int dataLen, uint8_t *opcode = NULL); // Write data to the stream void sendData(const char *str, uint8_t opcode = WS_OPCODE_TEXT); @@ -113,8 +114,11 @@ class WebSocketClient { // websocket connection. bool analyzeRequest(); + bool handleMessageHeader(uint8_t *msgtype, unsigned int *length, bool *hasMask, uint8_t *mask, uint8_t *opcode); + bool handleStream(String& data, uint8_t *opcode); - + bool handleStream(char *data, unsigned int dataLen, uint8_t *opcode); + // Disconnect user gracefully. void disconnectStream(); From f775113e2655c2b480377ec7e9442c57fb116c27 Mon Sep 17 00:00:00 2001 From: pablorodiz Date: Fri, 1 Jul 2016 13:01:10 +0200 Subject: [PATCH 09/21] Added public function to disconnect websocket --- WebSocketClient.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/WebSocketClient.h b/WebSocketClient.h index 367ab27..5a7b120 100644 --- a/WebSocketClient.h +++ b/WebSocketClient.h @@ -99,6 +99,8 @@ class WebSocketClient { void sendData(const char *str, uint8_t opcode = WS_OPCODE_TEXT); void sendData(String str, uint8_t opcode = WS_OPCODE_TEXT); + void disconnect(void) {disconnectStream();}; + char *path; char *host; char *protocol; From 1a6838b1bb1a8b4076868d34bda8d54a5d5330f5 Mon Sep 17 00:00:00 2001 From: pablorodiz Date: Tue, 19 Jul 2016 21:30:34 +0200 Subject: [PATCH 10/21] Added buffered send method to speed up message send, specially in ESP8266 where byte-by-byte sending is extremely slow. Be aware that if using buffered send "sendData" just stores messages in the buffer. To perform the actual send the program needs to call the new function "process" periodically in the main loop. --- WebSocketClient.cpp | 41 +++++++++++++++++++++++++++++++++++++++-- WebSocketClient.h | 21 +++++++++++++++++++-- 2 files changed, 58 insertions(+), 4 deletions(-) diff --git a/WebSocketClient.cpp b/WebSocketClient.cpp index 914e4b9..d4dd705 100644 --- a/WebSocketClient.cpp +++ b/WebSocketClient.cpp @@ -8,6 +8,11 @@ char *WebSocketClient::headers = NULL; +#ifdef WS_BUFFERED_SEND +uint8_t WebSocketClient::buffer[MAX_FRAME_LENGTH]; +unsigned int WebSocketClient::bufferIndex = 0; +#endif + bool WebSocketClient::handshake(Client &client) { socket_client = &client; @@ -332,11 +337,38 @@ int WebSocketClient::timedRead() { return socket_client->read(); } +#ifdef WS_BUFFERED_SEND +int WebSocketClient::bufferedSend(uint8_t c) { + if(bufferIndex0) { + lastSend = millis(); + if(socket_client->write(buffer, bufferIndex)) { + bufferIndex = 0; + return 1; + } else { + //Error sending. Most probable thing is socket disconnection + //Serial.println("######################################################################SEND FAILED"); + return 0; + } + } +} +#endif + void WebSocketClient::sendEncodedData(char *str, uint8_t opcode) { uint8_t header[8]; int size = strlen(str); int i = 0; - + // Opcode; final fragment header[i++] = opcode | WS_FIN; @@ -355,11 +387,16 @@ void WebSocketClient::sendEncodedData(char *str, uint8_t opcode) { header[i++] = random(0, 256); header[i++] = random(0, 256); +#ifdef WS_BUFFERED_SEND + for(int k=0; kwrite(header, i)) { for (int j=0; jwrite(str[j] ^ header[mask_base + (j % 4)]); } - } + } +#endif /*WS_BUFFERED_SEND*/ } void WebSocketClient::sendEncodedData(String str, uint8_t opcode) { diff --git a/WebSocketClient.h b/WebSocketClient.h index 5a7b120..366b3d9 100644 --- a/WebSocketClient.h +++ b/WebSocketClient.h @@ -48,6 +48,10 @@ Currently based off of "The Web Socket protocol" draft (v 75): #include "String.h" #include "Client.h" +#ifdef ESP8266 +#define WS_BUFFERED_SEND +#endif + // CRLF characters to terminate lines/handshakes in headers. #define CRLF "\r\n" @@ -64,8 +68,12 @@ Currently based off of "The Web Socket protocol" draft (v 75): // Don't allow the client to send big frames of data. This will flood the Arduinos // memory and might even crash it. #ifndef MAX_FRAME_LENGTH +#ifdef ESP8266 +#define MAX_FRAME_LENGTH 2048 +#else #define MAX_FRAME_LENGTH 256 #endif +#endif #define SIZE(array) (sizeof(array) / sizeof(*array)) @@ -99,6 +107,10 @@ class WebSocketClient { void sendData(const char *str, uint8_t opcode = WS_OPCODE_TEXT); void sendData(String str, uint8_t opcode = WS_OPCODE_TEXT); +#ifdef WS_BUFFERED_SEND + int process(void); +#endif + void disconnect(void) {disconnectStream();}; char *path; @@ -108,10 +120,15 @@ class WebSocketClient { private: Client *socket_client; - unsigned long _startMillis; - + const char *socket_urlPrefix; +#ifdef WS_BUFFERED_SEND + static uint8_t buffer[MAX_FRAME_LENGTH]; + static unsigned int bufferIndex; + int bufferedSend(uint8_t c); +#endif + // Discovers if the client's header is requesting an upgrade to a // websocket connection. bool analyzeRequest(); From 19948e1370229aa21b276a96e5025ca94f603eed Mon Sep 17 00:00:00 2001 From: pablorodiz Date: Wed, 20 Jul 2016 12:50:57 +0200 Subject: [PATCH 11/21] Solved compilation error on last commit --- WebSocketClient.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/WebSocketClient.cpp b/WebSocketClient.cpp index d4dd705..f6ee8ab 100644 --- a/WebSocketClient.cpp +++ b/WebSocketClient.cpp @@ -351,7 +351,6 @@ int WebSocketClient::bufferedSend(uint8_t c) { } int WebSocketClient::process(void) { if(bufferIndex>0) { - lastSend = millis(); if(socket_client->write(buffer, bufferIndex)) { bufferIndex = 0; return 1; From 454b0c44691ee1a703ca8448af348dc18a9f9107 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Antonio=20Mart=C3=ADnez?= Date: Thu, 15 Dec 2016 18:21:43 +0100 Subject: [PATCH 12/21] Fixed capitalization in order to compile in linux environments --- WebSocketClient.cpp | 2 +- WebSocketClient.h | 4 ++-- WebSocketServer.cpp | 2 +- WebSocketServer.h | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/WebSocketClient.cpp b/WebSocketClient.cpp index f6ee8ab..54e47a0 100644 --- a/WebSocketClient.cpp +++ b/WebSocketClient.cpp @@ -4,7 +4,7 @@ #include "WebSocketClient.h" #include "sha1.h" -#include "base64.h" +#include "Base64.h" char *WebSocketClient::headers = NULL; diff --git a/WebSocketClient.h b/WebSocketClient.h index 366b3d9..e34c81f 100644 --- a/WebSocketClient.h +++ b/WebSocketClient.h @@ -45,7 +45,7 @@ Currently based off of "The Web Socket protocol" draft (v 75): #include #include -#include "String.h" +#include "string.h" #include "Client.h" #ifdef ESP8266 @@ -149,4 +149,4 @@ class WebSocketClient { -#endif \ No newline at end of file +#endif diff --git a/WebSocketServer.cpp b/WebSocketServer.cpp index bee55c5..a7cfc96 100644 --- a/WebSocketServer.cpp +++ b/WebSocketServer.cpp @@ -9,7 +9,7 @@ #endif #include "sha1.h" -#include "base64.h" +#include "Base64.h" bool WebSocketServer::handshake(Client &client) { diff --git a/WebSocketServer.h b/WebSocketServer.h index 6dcf052..078ab19 100644 --- a/WebSocketServer.h +++ b/WebSocketServer.h @@ -45,7 +45,7 @@ Currently based off of "The Web Socket protocol" draft (v 75): #include #include -#include "String.h" +#include "string.h" #include "Server.h" #include "Client.h" @@ -115,4 +115,4 @@ class WebSocketServer { -#endif \ No newline at end of file +#endif From b93f0491cc81224ed75d65885836d4fa919431f3 Mon Sep 17 00:00:00 2001 From: Pablo Rodiz Obaya Date: Thu, 15 Dec 2016 18:39:44 +0100 Subject: [PATCH 13/21] Update README.md Changed README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2eea2f0..4c10d6a 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,6 @@ Inside of the WebSocketServer class there is a compiler directive to turn on sup Because of limitations of the current Arduino platform (Uno at the time of this writing), this library does not support messages larger than 65535 characters. In addition, this library only supports single-frame text frames. It currently does not recognize continuation frames, binary frames, or ping/pong frames. ### Credits -Thank you to github user ejeklint for the excellent starting point for this library. From his original Hixie76-only code I was able to add support for RFC 6455 and create the WebSocket client. +Thank you to github user ejeklint and brandenhall for the excellent starting point for this library. From ejeklint's original Hixie76-only code brandenhall was able to add support for RFC 6455 and create the WebSocket client. Then I only had to improve stability and speed. -- Branden \ No newline at end of file +- Pablo From 527ef3ea553b4dc9ead8009c61acefb3e6d9aa84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Antonio=20Mart=C3=ADnez?= Date: Tue, 27 Dec 2016 21:18:55 +0100 Subject: [PATCH 14/21] Forced masked message data to contain only valid UTF-8 chars --- WebSocketClient.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/WebSocketClient.cpp b/WebSocketClient.cpp index 54e47a0..d8b0725 100644 --- a/WebSocketClient.cpp +++ b/WebSocketClient.cpp @@ -381,10 +381,10 @@ void WebSocketClient::sendEncodedData(char *str, uint8_t opcode) { } int mask_base = i; - header[i++] = random(0, 256); - header[i++] = random(0, 256); - header[i++] = random(0, 256); - header[i++] = random(0, 256); + header[i++] = random(0, 127); + header[i++] = random(0, 127); + header[i++] = random(0, 127); + header[i++] = random(0, 127); #ifdef WS_BUFFERED_SEND for(int k=0; k Date: Thu, 29 Dec 2016 13:22:57 +0100 Subject: [PATCH 15/21] Enabled WS_BUFFERED_SEND also for MKR1000 --- WebSocketClient.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/WebSocketClient.h b/WebSocketClient.h index e34c81f..bfea3d0 100644 --- a/WebSocketClient.h +++ b/WebSocketClient.h @@ -48,7 +48,7 @@ Currently based off of "The Web Socket protocol" draft (v 75): #include "string.h" #include "Client.h" -#ifdef ESP8266 +#if defined ESP8266 || defined ARDUINO_SAMD_MKR1000 #define WS_BUFFERED_SEND #endif @@ -68,11 +68,13 @@ Currently based off of "The Web Socket protocol" draft (v 75): // Don't allow the client to send big frames of data. This will flood the Arduinos // memory and might even crash it. #ifndef MAX_FRAME_LENGTH -#ifdef ESP8266 + +#if defined ESP8266 || defined ARDUINO_SAMD_MKR1000 #define MAX_FRAME_LENGTH 2048 #else #define MAX_FRAME_LENGTH 256 #endif + #endif #define SIZE(array) (sizeof(array) / sizeof(*array)) From 729bceeb4f7e0455d81a949641cc25b34642132d Mon Sep 17 00:00:00 2001 From: Pablo Rodiz Obaya Date: Thu, 2 Mar 2017 18:14:52 +0100 Subject: [PATCH 16/21] changed path, host, protocol and headers to internal private variables. Created setters for those variables and class constructor --- WebSocketClient.cpp | 37 ++++++++++++++++++++++++++++++------- WebSocketClient.h | 20 +++++++++++++------- 2 files changed, 43 insertions(+), 14 deletions(-) diff --git a/WebSocketClient.cpp b/WebSocketClient.cpp index d8b0725..95161dd 100644 --- a/WebSocketClient.cpp +++ b/WebSocketClient.cpp @@ -6,12 +6,31 @@ #include "sha1.h" #include "Base64.h" -char *WebSocketClient::headers = NULL; +//char *WebSocketClient::headers = NULL; +WebSocketClient::WebSocketClient(char *WsPath, char *WsHost, char *WsHeaders, char *WsProtocol){ + path = WsPath; + host = WsHost; + headers = WsHeaders; + protocol = WsProtocol; #ifdef WS_BUFFERED_SEND -uint8_t WebSocketClient::buffer[MAX_FRAME_LENGTH]; -unsigned int WebSocketClient::bufferIndex = 0; + bufferIndex = 0; #endif +} + +void WebSocketClient::setHost(char* WsHost) { + host = WsHost; +} +void WebSocketClient::setProtocol(char* WsProtocol) { + protocol = WsProtocol; +} +void WebSocketClient::setPath(char* WsPath) { + path = WsPath; +} +void WebSocketClient::setHeaders(char* WsHeaders) { + headers = WsHeaders; +} + bool WebSocketClient::handshake(Client &client) { @@ -86,9 +105,11 @@ bool WebSocketClient::analyzeRequest() { socket_client->print(F("Sec-WebSocket-Key: ")); socket_client->print(key); socket_client->print(CRLF); - socket_client->print(F("Sec-WebSocket-Protocol: ")); - socket_client->print(protocol); - socket_client->print(CRLF); + if(protocol!=NULL) { + socket_client->print(F("Sec-WebSocket-Protocol: ")); + socket_client->print(protocol); + socket_client->print(CRLF); + } socket_client->print(F("Sec-WebSocket-Version: 13\r\n")); socket_client->print(CRLF); @@ -102,7 +123,9 @@ bool WebSocketClient::analyzeRequest() { Serial.println("Waiting..."); #endif } - +#ifdef DEBUGGING + if(!socket_client->connected()) Serial.println("Error. Broken connection"); +#endif // TODO: More robust string extraction while ((bite = socket_client->read()) != -1) { diff --git a/WebSocketClient.h b/WebSocketClient.h index bfea3d0..e8b4985 100644 --- a/WebSocketClient.h +++ b/WebSocketClient.h @@ -48,6 +48,8 @@ Currently based off of "The Web Socket protocol" draft (v 75): #include "string.h" #include "Client.h" +#define DEBUGGING + #if defined ESP8266 || defined ARDUINO_SAMD_MKR1000 #define WS_BUFFERED_SEND #endif @@ -95,7 +97,11 @@ Currently based off of "The Web Socket protocol" draft (v 75): class WebSocketClient { public: - + WebSocketClient(char *WsPath = NULL, char *WsHost = NULL, char *WsHeaders = NULL, char *WsProtocol = NULL); + void setPath(char* WsPath); + void setHeaders(char *WsHeaders); + void setHost(char * WsHost); + void setProtocol(char * WsProtocol); // Handle connection requests to validate and process/refuse // connections. bool handshake(Client &client); @@ -115,19 +121,19 @@ class WebSocketClient { void disconnect(void) {disconnectStream();}; - char *path; - char *host; - char *protocol; - static char *headers; private: Client *socket_client; const char *socket_urlPrefix; + char *path; + char *host; + char *protocol; + char *headers; #ifdef WS_BUFFERED_SEND - static uint8_t buffer[MAX_FRAME_LENGTH]; - static unsigned int bufferIndex; + uint8_t buffer[MAX_FRAME_LENGTH]; + unsigned int bufferIndex; int bufferedSend(uint8_t c); #endif From c84e37fde794df4b180cabbfe9bf1021f6b3488d Mon Sep 17 00:00:00 2001 From: Pablo Rodiz Obaya Date: Thu, 2 Mar 2017 22:20:47 +0100 Subject: [PATCH 17/21] added some logs when debug output is activated and chnaged AnalyzeRequest and handshake return value to int so now the HTPP error code is returned. 200 is correct or any other value is for eeporting errors from the server --- WebSocketClient.cpp | 26 ++++++++++++++++++-------- WebSocketClient.h | 7 ++++--- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/WebSocketClient.cpp b/WebSocketClient.cpp index 95161dd..7494b45 100644 --- a/WebSocketClient.cpp +++ b/WebSocketClient.cpp @@ -32,7 +32,7 @@ void WebSocketClient::setHeaders(char* WsHeaders) { } -bool WebSocketClient::handshake(Client &client) { +int WebSocketClient::handshake(Client &client) { socket_client = &client; @@ -42,12 +42,13 @@ bool WebSocketClient::handshake(Client &client) { #ifdef DEBUGGING Serial.println(F("Client connected")); #endif - if (analyzeRequest()) { + int res = analyzeRequest(); + if (res==200) { #ifdef DEBUGGING Serial.println(F("Websocket established")); #endif - return true; + return res; } else { // Might just need to break until out of socket_client loop. @@ -56,19 +57,21 @@ bool WebSocketClient::handshake(Client &client) { #endif disconnectStream(); - return false; + return res; } } else { - return false; + return -1; } } -bool WebSocketClient::analyzeRequest() { +int WebSocketClient::analyzeRequest() { String temp; int bite; bool foundupgrade = false; unsigned long intkey[2]; + String sAnswerCode; + int answerCode=0; String serverKey; char keyStart[17]; char b64Key[25]; @@ -130,7 +133,6 @@ bool WebSocketClient::analyzeRequest() { while ((bite = socket_client->read()) != -1) { temp += (char)bite; - if ((char)bite == '\n') { if(temp.startsWith("\r\n")) { #ifdef DEBUGGING @@ -141,6 +143,13 @@ bool WebSocketClient::analyzeRequest() { foundupgrade = true; } else if (temp.startsWith("Sec-WebSocket-Accept: ")) { serverKey = temp.substring(22,temp.length() - 2); // Don't save last CR+LF + } else if(temp.startsWith("HTTP/1.1 ")){ + sAnswerCode = temp.substring(9,12); + answerCode = atoi(&sAnswerCode[0]); +#ifdef DEBUGGING + Serial.print("Answer Code: "); + Serial.println(answerCode); +#endif } #ifdef DEBUGGING Serial.print("Got Header: " + temp); @@ -170,7 +179,8 @@ bool WebSocketClient::analyzeRequest() { base64_encode(b64Result, result, 20); // if the keys match, good to go - return serverKey.equals(String(b64Result)); + if (answerCode!=0 && serverKey.equals(String(b64Result))||answerCode!=200) return answerCode; + else return -1; } bool WebSocketClient::handleMessageHeader(uint8_t *msgtype, unsigned int *length, bool *hasMask, uint8_t *mask, uint8_t *opcode) { diff --git a/WebSocketClient.h b/WebSocketClient.h index e8b4985..b0f54a9 100644 --- a/WebSocketClient.h +++ b/WebSocketClient.h @@ -48,7 +48,8 @@ Currently based off of "The Web Socket protocol" draft (v 75): #include "string.h" #include "Client.h" -#define DEBUGGING +//Uncoment the following line for debug output ot serial port +//#define DEBUGGING #if defined ESP8266 || defined ARDUINO_SAMD_MKR1000 #define WS_BUFFERED_SEND @@ -104,7 +105,7 @@ class WebSocketClient { void setProtocol(char * WsProtocol); // Handle connection requests to validate and process/refuse // connections. - bool handshake(Client &client); + int handshake(Client &client); //Check if socket os connected int connected(); // Get data off of the stream @@ -139,7 +140,7 @@ class WebSocketClient { // Discovers if the client's header is requesting an upgrade to a // websocket connection. - bool analyzeRequest(); + int analyzeRequest(); bool handleMessageHeader(uint8_t *msgtype, unsigned int *length, bool *hasMask, uint8_t *mask, uint8_t *opcode); From d56684c640d7d5bd8803cde0ea81eb5992e1a555 Mon Sep 17 00:00:00 2001 From: Pablo Rodiz Obaya Date: Sat, 4 Mar 2017 19:35:36 +0100 Subject: [PATCH 18/21] accelerated handshake by using send buffer. Also solved handshake error on evaluating response. Enhanced debugging by including logs on handshake and sending functions when debug is activated --- WebSocketClient.cpp | 116 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 101 insertions(+), 15 deletions(-) diff --git a/WebSocketClient.cpp b/WebSocketClient.cpp index 7494b45..e19086d 100644 --- a/WebSocketClient.cpp +++ b/WebSocketClient.cpp @@ -6,8 +6,6 @@ #include "sha1.h" #include "Base64.h" -//char *WebSocketClient::headers = NULL; - WebSocketClient::WebSocketClient(char *WsPath, char *WsHost, char *WsHeaders, char *WsProtocol){ path = WsPath; host = WsHost; @@ -43,7 +41,7 @@ int WebSocketClient::handshake(Client &client) { Serial.println(F("Client connected")); #endif int res = analyzeRequest(); - if (res==200) { + if (res==101) { #ifdef DEBUGGING Serial.println(F("Websocket established")); #endif @@ -92,7 +90,7 @@ int WebSocketClient::analyzeRequest() { #ifdef DEBUGGING Serial.println(F("Sending websocket upgrade headers")); #endif - +#ifndef WS_BUFFERED_SEND socket_client->print(F("GET ")); socket_client->print(path); socket_client->print(F(" HTTP/1.1\r\n")); @@ -100,22 +98,78 @@ int WebSocketClient::analyzeRequest() { socket_client->print(F("Connection: Upgrade\r\n")); socket_client->print(F("Host: ")); socket_client->print(host); - socket_client->print(CRLF); + socket_client->print(F(CRLF)); if(headers) { socket_client->print(headers); - socket_client->print(CRLF); + socket_client->print(F(CRLF)); } socket_client->print(F("Sec-WebSocket-Key: ")); socket_client->print(key); - socket_client->print(CRLF); + socket_client->print(F(CRLF)); if(protocol!=NULL) { socket_client->print(F("Sec-WebSocket-Protocol: ")); socket_client->print(protocol); - socket_client->print(CRLF); + socket_client->print(F(CRLF)); } socket_client->print(F("Sec-WebSocket-Version: 13\r\n")); - socket_client->print(CRLF); + socket_client->print(F(CRLF)); +#else + bufferIndex = 0; + strcpy_P((char *)&buffer[bufferIndex],(const char *)F("GET ")); + bufferIndex+=4; + strcpy((char *)&buffer[bufferIndex],path); + bufferIndex+=strlen(path); + strcpy_P((char *)&buffer[bufferIndex],(const char *)F(" HTTP/1.1\r\n")); + bufferIndex+=11; + strcpy_P((char *)&buffer[bufferIndex],(const char *)F("Upgrade: websocket\r\n")); + bufferIndex+=20; + strcpy_P((char *)&buffer[bufferIndex],(const char *)F("Connection: Upgrade\r\n")); + bufferIndex+=21; + strcpy_P((char *)&buffer[bufferIndex],(const char *)F("Host: ")); + bufferIndex+=6; + strcpy((char *)&buffer[bufferIndex],host); + bufferIndex+=strlen(host); + strcpy_P((char *)&buffer[bufferIndex],(const char *)F(CRLF)); + bufferIndex+=2; + if(headers) { + strcpy((char *)&buffer[bufferIndex],headers); + bufferIndex+=strlen(headers); + strcpy_P((char *)&buffer[bufferIndex],(const char *)F(CRLF)); + bufferIndex+=2; + } + strcpy_P((char *)&buffer[bufferIndex],(const char *)F("Sec-WebSocket-Key: ")); + bufferIndex+=19; + strcpy((char *)&buffer[bufferIndex],&key[0]); + bufferIndex+=key.length(); + strcpy_P((char *)&buffer[bufferIndex],(const char *)F(CRLF)); + bufferIndex+=2; + if(protocol!=NULL) { + strcpy_P((char *)&buffer[bufferIndex],(const char *)F("Sec-WebSocket-Protocol: ")); + bufferIndex+=24; + strcpy((char *)&buffer[bufferIndex],protocol); + bufferIndex+=strlen(protocol); + strcpy_P((char *)&buffer[bufferIndex],(const char *)F(CRLF)); + bufferIndex+=2; + } + strcpy_P((char *)&buffer[bufferIndex],(const char *)F("Sec-WebSocket-Version: 13\r\n")); + bufferIndex+=27; + strcpy_P((char *)&buffer[bufferIndex],(const char *)F(CRLF)); + bufferIndex+=2; + if(socket_client->write(buffer, bufferIndex)){ +#ifdef DEBUGGING + Serial.print("Sending: "); + int i; + for(i=0; i0) { if(socket_client->write(buffer, bufferIndex)) { +#ifdef DEBUGGING + Serial.print("Sending: "); + int i; + for (i=0; iwrite(header, i)) { for (int j=0; jwrite(str[j] ^ header[mask_base + (j % 4)]); + char c = str[j] ^ header[mask_base + (j % 4)]; +#ifdef DEBUGGING + Serial.write(c); +#endif + socket_client->write(c); } } +#ifdef DEBUGGING + Serial.println(); +#endif #endif /*WS_BUFFERED_SEND*/ } From 58cd2c91fe642b4f372ba78ded1e02b5e31093bd Mon Sep 17 00:00:00 2001 From: Pablo Rodiz Obaya Date: Mon, 6 Mar 2017 17:02:16 +0100 Subject: [PATCH 19/21] Found error on clearing buffer after fast handshake, and added some logs when receiving messages. --- WebSocketClient.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/WebSocketClient.cpp b/WebSocketClient.cpp index e19086d..d512495 100644 --- a/WebSocketClient.cpp +++ b/WebSocketClient.cpp @@ -160,8 +160,8 @@ int WebSocketClient::analyzeRequest() { Serial.print("Sending: "); int i; for(i=0; iavailable()) { delay(20); } - +#ifdef DEBUGGING + char c = socket_client->read(); + Serial.println(c); + return c; +#else return socket_client->read(); +#endif } #ifdef WS_BUFFERED_SEND From eb32a1decb4276518b6f885bca823496cdcc0c19 Mon Sep 17 00:00:00 2001 From: Pablo Rodiz Obaya Date: Wed, 8 Mar 2017 16:25:30 +0100 Subject: [PATCH 20/21] added some login --- WebSocketClient.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/WebSocketClient.cpp b/WebSocketClient.cpp index d512495..0a9095e 100644 --- a/WebSocketClient.cpp +++ b/WebSocketClient.cpp @@ -443,15 +443,10 @@ int WebSocketClient::bufferedSend(uint8_t c) { } else return 0; } } + int WebSocketClient::process(void) { if(bufferIndex>0) { if(socket_client->write(buffer, bufferIndex)) { -#ifdef DEBUGGING - Serial.print("Sending: "); - int i; - for (i=0; i Date: Mon, 10 Apr 2017 16:53:12 +0200 Subject: [PATCH 21/21] Code adapted to the requirements for working under ESP32-arduino library --- MD5.c | 5 +++-- MD5.h | 6 ++++-- WebSocketClient.cpp | 28 ++++++++++++++-------------- WebSocketClient.h | 4 ++-- WebSocketServer.cpp | 4 ++++ 5 files changed, 27 insertions(+), 20 deletions(-) diff --git a/MD5.c b/MD5.c index 5edaf5c..7d2a06f 100644 --- a/MD5.c +++ b/MD5.c @@ -20,7 +20,7 @@ * These notices must be retained in any copies of any part of this * documentation and/or software. */ - +#ifndef ESP32 #include "global.h" #include "MD5.h" @@ -300,4 +300,5 @@ void MD5(unsigned char strInputString[], unsigned char md5Digest[], unsigned int MD5Update(&ctx, strInputString, len); MD5Final(md5Digest, &ctx); -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/MD5.h b/MD5.h index c434fca..94475b5 100644 --- a/MD5.h +++ b/MD5.h @@ -19,7 +19,8 @@ * These notices must be retained in any copies of any part of this * documentation and/or software. */ - +#ifndef MD5_H +#define MD5_H /* MD5 context. */ typedef struct { UINT4 state[4]; /* state (ABCD) */ @@ -32,4 +33,5 @@ void MD5Update (MD5_CTX *, unsigned char *, unsigned int); void MD5Final (unsigned char [16], MD5_CTX *); /* Function used by Websockets implementation */ -void MD5 (unsigned char [], unsigned char [], unsigned int); \ No newline at end of file +void MD5 (unsigned char [], unsigned char [], unsigned int); +#endif /*MD5_H*/ \ No newline at end of file diff --git a/WebSocketClient.cpp b/WebSocketClient.cpp index 0a9095e..8100ed0 100644 --- a/WebSocketClient.cpp +++ b/WebSocketClient.cpp @@ -115,45 +115,45 @@ int WebSocketClient::analyzeRequest() { socket_client->print(F(CRLF)); #else bufferIndex = 0; - strcpy_P((char *)&buffer[bufferIndex],(const char *)F("GET ")); + strncpy_P((char *)&buffer[bufferIndex],(const char *)F("GET "), 4); bufferIndex+=4; strcpy((char *)&buffer[bufferIndex],path); bufferIndex+=strlen(path); - strcpy_P((char *)&buffer[bufferIndex],(const char *)F(" HTTP/1.1\r\n")); + strncpy_P((char *)&buffer[bufferIndex],(const char *)F(" HTTP/1.1\r\n"), 11); bufferIndex+=11; - strcpy_P((char *)&buffer[bufferIndex],(const char *)F("Upgrade: websocket\r\n")); + strncpy_P((char *)&buffer[bufferIndex],(const char *)F("Upgrade: websocket\r\n"), 20); bufferIndex+=20; - strcpy_P((char *)&buffer[bufferIndex],(const char *)F("Connection: Upgrade\r\n")); + strncpy_P((char *)&buffer[bufferIndex],(const char *)F("Connection: Upgrade\r\n"), 21); bufferIndex+=21; - strcpy_P((char *)&buffer[bufferIndex],(const char *)F("Host: ")); + strncpy_P((char *)&buffer[bufferIndex],(const char *)F("Host: "), 6); bufferIndex+=6; strcpy((char *)&buffer[bufferIndex],host); bufferIndex+=strlen(host); - strcpy_P((char *)&buffer[bufferIndex],(const char *)F(CRLF)); + strncpy_P((char *)&buffer[bufferIndex],(const char *)F(CRLF), 2); bufferIndex+=2; if(headers) { strcpy((char *)&buffer[bufferIndex],headers); bufferIndex+=strlen(headers); - strcpy_P((char *)&buffer[bufferIndex],(const char *)F(CRLF)); + strncpy_P((char *)&buffer[bufferIndex],(const char *)F(CRLF), 2); bufferIndex+=2; } - strcpy_P((char *)&buffer[bufferIndex],(const char *)F("Sec-WebSocket-Key: ")); + strncpy_P((char *)&buffer[bufferIndex],(const char *)F("Sec-WebSocket-Key: "), 19); bufferIndex+=19; strcpy((char *)&buffer[bufferIndex],&key[0]); bufferIndex+=key.length(); - strcpy_P((char *)&buffer[bufferIndex],(const char *)F(CRLF)); + strncpy_P((char *)&buffer[bufferIndex],(const char *)F(CRLF), 2); bufferIndex+=2; if(protocol!=NULL) { - strcpy_P((char *)&buffer[bufferIndex],(const char *)F("Sec-WebSocket-Protocol: ")); + strncpy_P((char *)&buffer[bufferIndex],(const char *)F("Sec-WebSocket-Protocol: "), 24); bufferIndex+=24; strcpy((char *)&buffer[bufferIndex],protocol); bufferIndex+=strlen(protocol); - strcpy_P((char *)&buffer[bufferIndex],(const char *)F(CRLF)); + strncpy_P((char *)&buffer[bufferIndex],(const char *)F(CRLF), 2); bufferIndex+=2; } - strcpy_P((char *)&buffer[bufferIndex],(const char *)F("Sec-WebSocket-Version: 13\r\n")); + strncpy_P((char *)&buffer[bufferIndex],(const char *)F("Sec-WebSocket-Version: 13\r\n"), 27); bufferIndex+=27; - strcpy_P((char *)&buffer[bufferIndex],(const char *)F(CRLF)); + strncpy_P((char *)&buffer[bufferIndex],(const char *)F(CRLF), 2); bufferIndex+=2; if(socket_client->write(buffer, bufferIndex)){ #ifdef DEBUGGING @@ -478,7 +478,7 @@ void WebSocketClient::sendEncodedData(char *str, uint8_t opcode) { Serial.print("Sending message. Header: "); int j; for(j=0; j +#endif #endif #include "sha1.h"