Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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)
5 changes: 5 additions & 0 deletions mbed_lib.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
}
7 changes: 6 additions & 1 deletion source/ws_client.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
};

Expand Down
76 changes: 66 additions & 10 deletions source/ws_client_base.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@

#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"
Expand All @@ -42,7 +45,11 @@
#define MBED_WS_PING_INTERVAL_MS 10000
#endif

// #define MBED_WS_DEBUG 1
#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
typedef enum {
Expand Down Expand Up @@ -121,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;
}

/**
Expand All @@ -142,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) {
Expand All @@ -154,6 +167,7 @@ class WebsocketClientBase {

_ping_counter = 0;
_pong_counter = 0;
_ping_failure_counter = 0;

#ifdef MBED_WS_HAS_MBED_HTTP
if (!_network) {
Expand All @@ -174,11 +188,15 @@ 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')
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
Expand All @@ -188,8 +206,9 @@ class WebsocketClientBase {
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);

HttpResponse* res = req->send();
if (!res) {
Expand All @@ -207,7 +226,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-Accept: %s\n", ws_sec_accept);
printf("Headers:\n");
#endif
for (size_t ix = 0; ix < res->get_headers_length(); ix++) {
Expand All @@ -220,7 +248,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;
}
Expand Down Expand Up @@ -259,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;
}
Expand Down Expand Up @@ -323,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...
}

Expand All @@ -341,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;
}
}

/**
Expand All @@ -354,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:
Expand Down Expand Up @@ -412,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;
}

Expand All @@ -428,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);
Expand Down Expand Up @@ -647,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;
};
Expand Down
7 changes: 6 additions & 1 deletion source/wss_client.h
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down