From 1eb1ac12055be3f3125e899399b4cb29536d9199 Mon Sep 17 00:00:00 2001 From: Zhiyong Li Date: Mon, 3 Jan 2022 19:21:03 -0800 Subject: [PATCH 1/7] Added random sec key generation and user agent header --- mbed_lib.json | 5 +++++ source/ws_client_base.h | 19 +++++++++++++------ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/mbed_lib.json b/mbed_lib.json index 96cac1b..357e109 100644 --- a/mbed_lib.json +++ b/mbed_lib.json @@ -5,6 +5,11 @@ "help": "Whether to use Mbed HTTP - if disabled, it will remove any code related to Mbed HTTP", "value": true, "macro_name": "MBED_WS_HAS_MBED_HTTP" + }, + "user-agent": { + "help": "User-Agent header presented to server", + "value": "Mbed-WS-Client", + "macro_name": "MBED_WS_USER_AGENT" } } } diff --git a/source/ws_client_base.h b/source/ws_client_base.h index bebc502..587d464 100644 --- a/source/ws_client_base.h +++ b/source/ws_client_base.h @@ -20,6 +20,7 @@ #include "mbed.h" #include "Socket.h" +#include "mbedtls/base64.h" #ifdef MBED_WS_HAS_MBED_HTTP #include "http_request.h" @@ -42,6 +43,10 @@ #define MBED_WS_PING_INTERVAL_MS 10000 #endif +#ifndef MBED_WS_USER_AGENT +#define MBED_WS_USER_AGENT "Mbed-WS-Client" +#endif + // #define MBED_WS_DEBUG 1 // this library returns nsapi_error_t codes, plus these @@ -174,22 +179,24 @@ class WebsocketClientBase { return r; } - // @todo: calculate new keys myself - // var key = 'L159VM0TWUzyDxwJEIEzjw==' - // var combined = 'L159VM0TWUzyDxwJEIEzjw==' + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11' - // var h = require('crypto').createHash('sha1') - // h.update(combined).digest('base64') - // This might seem weird... because we support both ws:// and wss:// // but we already have a good working socket with TLS connection, and so the only thing // we do is act on that socket. So it's fine to reference HttpRequest // the TCPSocket casting is also weird, but it's just setting pointers, so it's fine for now // This might break if Mbed HTTP changes inner workings though!! + + uint8_t randomBytes[16], wsSecKey[24]; + for (size_t i = 0; i < 16; i++) { + randomBytes[i] = rand(); + } + mbedtls_base64_encode(&wsSecKey[0], sizeof(wsSecKey), NULL, &randomBytes[0], sizeof(randomBytes)); + HttpRequest* req = new HttpRequest((TCPSocket*)_socket, HTTP_GET, _url); req->set_header("Upgrade", "Websocket"); req->set_header("Connection", "Upgrade"); req->set_header("Sec-WebSocket-Key", "L159VM0TWUzyDxwJEIEzjw=="); req->set_header("Sec-WebSocket-Version", "13"); + req->set_header("User-Agent", MBED_WS_USER_AGENT); HttpResponse* res = req->send(); if (!res) { From 8be6ff72b55e352b383d2527ba727bf336f3db77 Mon Sep 17 00:00:00 2001 From: Zhiyong Li Date: Tue, 4 Jan 2022 14:41:15 -0800 Subject: [PATCH 2/7] Added code to calculate Sec-WS-Accept from random sec key --- source/ws_client_base.h | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/source/ws_client_base.h b/source/ws_client_base.h index 587d464..5f11239 100644 --- a/source/ws_client_base.h +++ b/source/ws_client_base.h @@ -21,6 +21,8 @@ #include "mbed.h" #include "Socket.h" #include "mbedtls/base64.h" +#include "mbedtls/sha1.h" +#include "randLIB.h" #ifdef MBED_WS_HAS_MBED_HTTP #include "http_request.h" @@ -179,22 +181,25 @@ class WebsocketClientBase { return r; } + size_t key_len; + char random_bytes[16], ws_sec_key[25]; + for (size_t i = 0; i < 16; i++) { + random_bytes[i] = randLIB_get_8bit(); + } + mbedtls_base64_encode((unsigned char *)&ws_sec_key[0], sizeof(ws_sec_key), &key_len, (const unsigned char *)&random_bytes[0], sizeof(random_bytes)); + #ifdef MBED_WS_DEBUG + printf("Sec-WebSocket-Key: %s\n", ws_sec_key); + #endif + // This might seem weird... because we support both ws:// and wss:// // but we already have a good working socket with TLS connection, and so the only thing // we do is act on that socket. So it's fine to reference HttpRequest // the TCPSocket casting is also weird, but it's just setting pointers, so it's fine for now // This might break if Mbed HTTP changes inner workings though!! - - uint8_t randomBytes[16], wsSecKey[24]; - for (size_t i = 0; i < 16; i++) { - randomBytes[i] = rand(); - } - mbedtls_base64_encode(&wsSecKey[0], sizeof(wsSecKey), NULL, &randomBytes[0], sizeof(randomBytes)); - HttpRequest* req = new HttpRequest((TCPSocket*)_socket, HTTP_GET, _url); req->set_header("Upgrade", "Websocket"); req->set_header("Connection", "Upgrade"); - req->set_header("Sec-WebSocket-Key", "L159VM0TWUzyDxwJEIEzjw=="); + req->set_header("Sec-WebSocket-Key", string(ws_sec_key)); req->set_header("Sec-WebSocket-Version", "13"); req->set_header("User-Agent", MBED_WS_USER_AGENT); @@ -214,7 +219,16 @@ class WebsocketClientBase { bool has_valid_upgrade = false; bool has_valid_websocket_accept = false; + unsigned char ws_sec_accept_hash[20] = {0}; + unsigned char ws_sec_accept_buffer[61] = {0}; + const char guid_str[] = {"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"}; + char ws_sec_accept[29]; + sprintf((char*)ws_sec_accept_buffer,"%s%s", ws_sec_key, guid_str); + mbedtls_sha1(ws_sec_accept_buffer, 60, ws_sec_accept_hash); + mbedtls_base64_encode( (unsigned char *)&ws_sec_accept, sizeof(ws_sec_accept), &key_len, ws_sec_accept_hash, 20); + #ifdef MBED_WS_DEBUG + printf("Calculated Sec-Websocket-Accpet: %s\n", ws_sec_accept); printf("Headers:\n"); #endif for (size_t ix = 0; ix < res->get_headers_length(); ix++) { @@ -227,7 +241,7 @@ class WebsocketClientBase { has_valid_upgrade = true; } if (strcmp_insensitive(header_key, "Sec-WebSocket-Accept") == 0 && - strcmp_insensitive(header_value, "DdLWT/1JcX+nQFHebYP+rqEx5xI=") == 0) + strcmp_insensitive(header_value, ws_sec_accept) == 0) { has_valid_websocket_accept = true; } From 65238cc55873799b92639024aac8416cceeb5899 Mon Sep 17 00:00:00 2001 From: Zhiyong Li Date: Tue, 4 Jan 2022 15:40:11 -0800 Subject: [PATCH 3/7] fixed typo --- source/ws_client_base.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/ws_client_base.h b/source/ws_client_base.h index 5f11239..cbac552 100644 --- a/source/ws_client_base.h +++ b/source/ws_client_base.h @@ -228,7 +228,7 @@ class WebsocketClientBase { mbedtls_base64_encode( (unsigned char *)&ws_sec_accept, sizeof(ws_sec_accept), &key_len, ws_sec_accept_hash, 20); #ifdef MBED_WS_DEBUG - printf("Calculated Sec-Websocket-Accpet: %s\n", ws_sec_accept); + printf("Calculated Sec-WebSocket-Accept: %s\n", ws_sec_accept); printf("Headers:\n"); #endif for (size_t ix = 0; ix < res->get_headers_length(); ix++) { From 68a413d4ed5435c5e6ed03226dc914402e3a2d1f Mon Sep 17 00:00:00 2001 From: Zhiyong Li Date: Wed, 5 Jan 2022 00:39:25 -0800 Subject: [PATCH 4/7] Fixed default user-agent string escape --- mbed_lib.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mbed_lib.json b/mbed_lib.json index 357e109..9087b87 100644 --- a/mbed_lib.json +++ b/mbed_lib.json @@ -8,7 +8,7 @@ }, "user-agent": { "help": "User-Agent header presented to server", - "value": "Mbed-WS-Client", + "value": "\"Mbed-WS-Client\"", "macro_name": "MBED_WS_USER_AGENT" } } From bf660353e86d7bccc1f6eac31d6f8ecb77b734e4 Mon Sep 17 00:00:00 2001 From: Zhiyong Li Date: Mon, 13 Jun 2022 16:21:23 -0700 Subject: [PATCH 5/7] Added ping failure counter To allow occasional ping-pong out of sync in case unstable uplinks --- source/ws_client_base.h | 41 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/source/ws_client_base.h b/source/ws_client_base.h index cbac552..f867414 100644 --- a/source/ws_client_base.h +++ b/source/ws_client_base.h @@ -49,7 +49,7 @@ #define MBED_WS_USER_AGENT "Mbed-WS-Client" #endif -// #define MBED_WS_DEBUG 1 +//#define MBED_WS_DEBUG 1 // this library returns nsapi_error_t codes, plus these typedef enum { @@ -128,7 +128,9 @@ class WebsocketClientBase { _callbacks = nullptr; _ping_counter = 0; _pong_counter = 0; + _ping_failure_counter = 0; _ping_ev = 0; + _ping_counter_reset_ev = 0; } /** @@ -149,6 +151,10 @@ class WebsocketClientBase { if (_ping_ev != 0) { _queue->cancel(_ping_ev); } + + if(_ping_counter_reset_ev != 0) { + _queue->cancel(_ping_counter_reset_ev); + } } int connect(ws_callbacks_t *callbacks) { @@ -161,6 +167,7 @@ class WebsocketClientBase { _ping_counter = 0; _pong_counter = 0; + _ping_failure_counter = 0; #ifdef MBED_WS_HAS_MBED_HTTP if (!_network) { @@ -280,6 +287,7 @@ class WebsocketClientBase { // set ping interval _ping_ev = _queue->call_every(MBED_WS_PING_INTERVAL_MS, callback(this, &WebsocketClientBase::ping)); + _ping_counter_reset_ev = _queue->call_every(MBED_WS_PING_INTERVAL_MS * 3, callback(this, &WebsocketClientBase::resetPingFailureCounter)); return NSAPI_ERROR_OK; } @@ -344,6 +352,11 @@ class WebsocketClientBase { _ping_ev = 0; } + if(_ping_counter_reset_ev != 0) { + _queue->cancel(_ping_counter_reset_ev); + _ping_counter_reset_ev = 0; + } + _socket->close(); // ignore return value here... } @@ -362,6 +375,11 @@ class WebsocketClientBase { _queue->cancel(_ping_ev); _ping_ev = 0; } + + if(_ping_counter_reset_ev != 0) { + _queue->cancel(_ping_counter_reset_ev); + _ping_counter_reset_ev = 0; + } } /** @@ -375,9 +393,10 @@ class WebsocketClientBase { printf("ws resume_disconnect_checker\n"); #endif - _ping_counter = _pong_counter = 0; + _ping_counter = _pong_counter = _ping_failure_counter = 0; _ping_ev = _queue->call_every(MBED_WS_PING_INTERVAL_MS, callback(this, &WebsocketClientBase::ping)); + _ping_counter_reset_ev = _queue->call_every(MBED_WS_PING_INTERVAL_MS * 3, callback(this, &WebsocketClientBase::resetPingFailureCounter)); } protected: @@ -433,7 +452,11 @@ class WebsocketClientBase { #ifdef MBED_WS_DEBUG printf("Ping and pong out of sync: ping=%u pong=%u\n", _ping_counter, _pong_counter); #endif - handle_disconnect(); + _ping_counter = _pong_counter; + _ping_failure_counter++; + if(_ping_failure_counter > 2) { + handle_disconnect(); + } return; } @@ -449,6 +472,16 @@ class WebsocketClientBase { } } + void resetPingFailureCounter() { + + if(_ping_failure_counter > 0) { +#ifdef MBED_WS_DEBUG + printf("%llu: ws_ping_failure_counter = %d, reset to 0\n", time(nullptr), _ping_failure_counter); +#endif + _ping_failure_counter = 0; + } + } + WS_PARSING_STATE handle_rx_msg(rx_ws_message_t *msg, const uint8_t c) { #ifdef MBED_WS_DEBUG printf("handle_rx_msg state=%d\n", msg->state); @@ -668,7 +701,9 @@ class WebsocketClientBase { size_t _ping_counter; size_t _pong_counter; + size_t _ping_failure_counter; int _ping_ev; + int _ping_counter_reset_ev; rx_ws_message_t _curr_msg; }; From 5b3d33975dd6c5a9e5786a0f54bb0107fd323ff1 Mon Sep 17 00:00:00 2001 From: Zhiyong Li Date: Mon, 28 Oct 2024 10:50:03 -0700 Subject: [PATCH 6/7] Updated to work with Mbed OS 6.x --- source/ws_client.h | 7 ++++++- source/wss_client.h | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/source/ws_client.h b/source/ws_client.h index 478697f..aa32703 100644 --- a/source/ws_client.h +++ b/source/ws_client.h @@ -69,7 +69,12 @@ class WsClient : public WebsocketClientBase { if (r != NSAPI_ERROR_OK) { return r; } - return socket->connect(host, port); + + SocketAddress sockAddr; + _network->gethostbyname(host, &sockAddr); + sockAddr.set_port(port); + + return socket->connect(sockAddr); } }; diff --git a/source/wss_client.h b/source/wss_client.h index 5bb9ab7..af6831f 100644 --- a/source/wss_client.h +++ b/source/wss_client.h @@ -64,7 +64,12 @@ class WssClient : public WebsocketClientBase { if (r != NSAPI_ERROR_OK) { return r; } - return socket->connect(host, port); + + SocketAddress sockAddr; + _network->gethostbyname(host, &sockAddr); + sockAddr.set_port(port); + + return socket->connect(sockAddr); } private: From b8ee56c3a144b41b83cdfd5d23d17e60729e9c4c Mon Sep 17 00:00:00 2001 From: Zhiyong Li Date: Sat, 13 Jun 2026 20:07:37 -0700 Subject: [PATCH 7/7] Added support for CMake --- CMakeLists.txt | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..d92f4d4 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,11 @@ +#[[ Set name of library in lowcase]] +set(INTERNAL_LIBRARY_NAME "mbed-ws-client") + +add_library(${INTERNAL_LIBRARY_NAME} INTERFACE) + +#[[ Include .h files]] +target_include_directories(${INTERNAL_LIBRARY_NAME} INTERFACE ./source/) + +#[[Link library to mbed-core-flags but if the library needs to use RTOS + features, then have to be add also mbed-rtos-flags]] +target_link_libraries(${INTERNAL_LIBRARY_NAME} INTERFACE mbed-core-flags mbed-rtos-flags) \ No newline at end of file