Skip to content

Commit

Permalink
Initial test code for improved sendPing and sendPong. (#1270)
Browse files Browse the repository at this point in the history
* Changed ping and pong to have automatic mask generation. If there is no payload the mask will be 0x0000.
  • Loading branch information
slaff authored Nov 22, 2017
1 parent 13fd385 commit f1e685f
Show file tree
Hide file tree
Showing 6 changed files with 151 additions and 52 deletions.
19 changes: 15 additions & 4 deletions Sming/SmingCore/Network/Http/Websocket/WebSocketConnection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,9 @@ int WebSocketConnection::staticOnControlBegin(void* userData, ws_frame_type_t ty
return -1;
}

connection->controlFrameType = type;
connection->controlFrame.type = type;
connection->controlFrame.payload = NULL;
connection->controlFrame.payloadLegth = 0;

if (type == WS_FRAME_CLOSE) {
connection->close();
Expand All @@ -136,6 +138,14 @@ int WebSocketConnection::staticOnControlBegin(void* userData, ws_frame_type_t ty

int WebSocketConnection::staticOnControlPayload(void* userData, const char *data, size_t length)
{
WebSocketConnection *connection = (WebSocketConnection *)userData;
if (connection == NULL) {
return -1;
}

connection->controlFrame.payload = (char *)data;
connection->controlFrame.payloadLegth = length;

return WS_OK;
}

Expand All @@ -146,9 +156,10 @@ int WebSocketConnection::staticOnControlEnd(void* userData)
return -1;
}

if(connection->controlFrameType == WS_FRAME_PING) {
// TODO: add control frame payload processing...
connection->send((const char* )NULL, 0, WS_PONG_FRAME);
if(connection->controlFrame.type == WS_FRAME_PING) {
connection->send((const char* )connection->controlFrame.payload,
connection->controlFrame.payloadLegth,
WS_PONG_FRAME);
}

return WS_OK;
Expand Down
8 changes: 7 additions & 1 deletion Sming/SmingCore/Network/Http/Websocket/WebSocketConnection.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ enum WsConnectionState
eWSCS_Closed
};

typedef struct {
ws_frame_type_t type;
char* payload;
size_t payloadLegth;
} WsFrameInfo;

class WebSocketConnection
{
public:
Expand Down Expand Up @@ -84,7 +90,7 @@ class WebSocketConnection
HttpServerConnection* connection = nullptr;

ws_frame_type_t frameType = WS_FRAME_TEXT;
ws_frame_type_t controlFrameType = WS_FRAME_PING;
WsFrameInfo controlFrame;

ws_parser_t parser;
ws_parser_callbacks_t parserSettings;
Expand Down
49 changes: 42 additions & 7 deletions Sming/SmingCore/Network/WebsocketClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -156,18 +156,16 @@ void WebsocketClient::onFinished(TcpClientState finishState)
TcpClient::onFinished(finishState);
}

void WebsocketClient::sendPing()
bool WebsocketClient::sendPing(const String& payload /* = "" */)
{
uint8_t buf[2] = { 0x89, 0x00 };
debugf("Sending PING");
send((char*) buf, 2, false);
return sendControlFrame(WSFrameType::ping, payload);
}

void WebsocketClient::sendPong()
bool WebsocketClient::sendPong(const String& payload /* = "" */)
{
uint8_t buf[2] = { 0x8A, 0x00 };
debugf("Sending PONG");
send((char*) buf, 2, false);
return sendControlFrame(WSFrameType::pong, payload);
}

void WebsocketClient::disconnect()
Expand Down Expand Up @@ -209,6 +207,43 @@ void WebsocketClient::sendMessage(const String& str)
_sendFrame(WSFrameType::text, (uint8_t*) str.c_str(), str.length() + 1);
}

bool WebsocketClient::sendControlFrame(WSFrameType frameType, const String& payload /* = "" */)
{
if(payload.length() > 127) {
debugf("Maximum length of payload is 127 bytes");
return false;
}

uint32_t mask = 0;
int size = 2 + payload.length() + 4 * mask;
uint8_t* buf = new uint8_t[size];

// if we have payload, generate random mask for it
if(payload.length()) {
mask = ESP8266_DREG(0x20E44); // See: http://esp8266-re.foogod.com/wiki/Random_Number_Generator
}

int pos = 0;
buf[pos++] = (uint8_t)frameType;
buf[pos++] = 0x00;
buf[pos] |= bit(7);

if(payload.length()) {
buf[pos] += payload.length();
}

buf[++pos] = (mask >> 24) & 0xFF;
buf[++pos] = (mask >> 16) & 0xFF;
buf[++pos] = (mask >> 8) & 0xFF;
buf[++pos] = (mask >> 0) & 0xFF;

WebsocketFrameClass::mask(payload, mask, (char *)(buf+pos+1));

send((char*) buf, size, false);

return true;
}

err_t WebsocketClient::onReceive(pbuf* buf)
{
if (buf == NULL)
Expand Down Expand Up @@ -280,7 +315,7 @@ err_t WebsocketClient::onReceive(pbuf* buf)
case WSFrameType::ping:
{
debugf("Got ping ...");
sendPong(); //Need to send Pong in response to Ping
sendPong(String((char*)wsFrame._payload, wsFrame._payloadLength)); //Need to send Pong in response to Ping
break;
}
case WSFrameType::pong:
Expand Down
65 changes: 45 additions & 20 deletions Sming/SmingCore/Network/WebsocketClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,54 +61,79 @@ class WebsocketClient: protected TcpClient
public:
WebsocketClient(bool autoDestruct = false) :TcpClient(autoDestruct) {};
virtual ~WebsocketClient() {};

/** @brief Set handler for websocket text messages
* @param handler Delegate callback to be run when text message received
*/
void setWebSocketMessageHandler(WebSocketClientMessageDelegate handler);
/** @brief Set handler for websocket text messages
* @param handler Delegate callback to be run when text message received
*/
void setWebSocketDisconnectedHandler(WebSocketClientDisconnectDelegate handler);

/** @brief Set handler for websocket disconnection event
* @param handler Delegate callback to be run when websocket disconnects
*/
void setWebSocketConnectedHandler(WebSocketClientConnectedDelegate handler);
void setWebSocketDisconnectedHandler(WebSocketClientDisconnectDelegate handler);

/** @brief Set handler for websocket connection event
* @param handler Delegate callback to be run when websocket connects
*/
void setWebSocketBinaryHandler(WebSocketClientBinaryDelegate handler);
void setWebSocketConnectedHandler(WebSocketClientConnectedDelegate handler);

/** @brief Set handler for websocket binary messages
* @param handler Delegate callback to be run when binary message received
*/
bool connect(String url, uint32_t sslOptions = 0);
/** @brief Connects websocket client to server
void setWebSocketBinaryHandler(WebSocketClientBinaryDelegate handler);

/** @brief Connects websocket client to server
* @param url URL address of websocket server
* @param sslOptions Specify the SSL options to be used when calling websocket server over SSL
*/
void sendPing();
bool connect(String url, uint32_t sslOptions = 0);

/** @brief Send websocket ping to server
*
* @param String payload - maximum 255 bytes
*
* @retval bool true if the data can be send, false otherwise
*/
void sendPong();
bool sendPing(const String& payload = "");

/** @brief Send websocket ping to server
* @param String& payload - maximum 255 bytes
*
* @retval bool true if the data can be send, false otherwise
*/
void disconnect();
/** @brief Disconnects websocket client from server
bool sendPong(const String& payload = "");

/** @brief Disconnects websocket client from server
*/
void sendMessage(char* msg, uint16_t length);
/** @brief Send text message to websocket server
void disconnect();

/** @brief Send text message to websocket server
* @param msg Pointer to NULL-terminated string buffer to be send to websocket server
* @param length length of the NULL-terminated string buffer
*/
void sendMessage(const String& str);
/** @brief Send text message to websocket server
void sendMessage(char* msg, uint16_t length);

/** @brief Send text message to websocket server
* @param C++ String to be send to websocket server
*/
void sendBinary(uint8_t* msg, uint16_t length);
/** @brief Send binary message to websocket server
void sendMessage(const String& str);

/** @brief Send binary message to websocket server
* @param msg Pointer to binary-data buffer to be send to websocket server
* @param length length of the binary-data buffer
*/
wsMode getWSMode();
/** @brief Get websocket client mode
void sendBinary(uint8_t* msg, uint16_t length);

/** @brief Send control frame to websocket server
* @param payload C++ String to be send to websocket server
*
*/
bool sendControlFrame(WSFrameType frameType, const String& payload = "");

/** @brief Get websocket client mode
* @retval Returnt websocket client mode
*/
wsMode getWSMode();

#ifdef ENABLE_SSL
using TcpClient::addSslOptions;
Expand Down
17 changes: 17 additions & 0 deletions Sming/SmingCore/Network/WebsocketFrame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -242,3 +242,20 @@ uint8_t WebsocketFrameClass::decodeFrame(uint8_t * buffer, size_t length)
}
return true;
}

int WebsocketFrameClass::mask(const String& payload, uint32_t key, char *data)
{
uint8_t pool[4] = {0};
int pos = 0;
pool[pos++] = (key >> 24) & 0xFF;
pool[pos++] = (key >> 16) & 0xFF;
pool[pos++] = (key >> 8) & 0xFF;
pool[pos++] = (key >> 0) & 0xFF;

int i;
for (i = 0; i < payload.length(); i++) {
data[i] = (payload[i] ^ pool[i % 4]);
}

return i;
}
45 changes: 25 additions & 20 deletions Sming/SmingCore/Network/WebsocketFrame.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,29 +54,34 @@ class WebsocketFrameClass
public:
WebsocketFrameClass() {};
virtual ~WebsocketFrameClass();

/** @brief Encode given buffer to valid websocket frame
* @param frameType Websocket frame type text or binary
* @param payload Pointer to buffer to be encoded as websocket frame
* @param length Length of buffer to be encoded as websocket frame
* @param mask If true websocket frame will be masked (required for client->server communication)
* @param fin If true produce ordinary websocket frame, not continuation. Currently MUST be true.
* @param headerToPayload If true try to create single buffer message with header and payload, otherwise produce separate header and payload buffers
* @retval Return true on success, false on error
*
* @details if successfully executed, check whether _header is not nullptr and either use _header and _payload or just _payload as websocket frame
*
*/
uint8_t encodeFrame(WSFrameType frameType, uint8_t * payload, size_t length, uint8_t mask, uint8_t fin, uint8_t headerToPayload = true);
/** @brief Encode given buffer to valid websocket frame
* @param frameType Websocket frame type text or binary
* @param payload Pointer to buffer to be encoded as websocket frame
* @param length Length of buffer to be encoded as websocket frame
* @param mask If true websocket frame will be masked (required for client->server communication)
* @param fin If true produce ordinary websocket frame, not continuation. Currently MUST be true.
* @param headerToPayload If true try to create single buffer message with header and payload, otherwise produce separate header and payload buffers
* @retval Return true on success, false on error
*
* @details if successfully executed, check whether _header is not nullptr and either use _header and _payload or just _payload as websocket frame
*
*/

/** @brief Decode given buffer containing websocket frame to payload
* @param buffer Pointer to buffer to be decoded as websocket frame
* @param length Length of buffer to be decoded as websocket frame
* @retval Return true on success, false on error
*
* @details if successfully executed, check _frameType to decide what to do with payload pointed by _payload
*
*/
uint8_t decodeFrame(uint8_t * buffer, size_t length);
/** @brief Decode given buffer containing websocket frame to payload
* @param buffer Pointer to buffer to be decoded as websocket frame
* @param length Length of buffer to be decoded as websocket frame
* @retval Return true on success, false on error
*
* @details if successfully executed, check _frameType to decide what to do with payload pointed by _payload
*
*/


static int mask(const String& payload, uint32_t key, char *data);

protected:
uint8_t* _payload = nullptr; // pointer to payload; in encode - will point to proper websocket frame payload, in decode - will point to websocket frame's decoded data
size_t _payloadLength = 0;
Expand Down

0 comments on commit f1e685f

Please sign in to comment.