Merge branch 'gpn18' into socketio

This commit is contained in:
Links
2019-06-10 13:23:15 +02:00
6 changed files with 384 additions and 76 deletions

168
src/SocketIOclient.cpp Normal file
View File

@ -0,0 +1,168 @@
/*
* SocketIOclient.cpp
*
* Created on: May 12, 2018
* Author: links
*/
#include "WebSockets.h"
#include "WebSocketsClient.h"
#include "SocketIOclient.h"
SocketIOclient::SocketIOclient() {
}
SocketIOclient::~SocketIOclient() {
}
void SocketIOclient::begin(const char *host, uint16_t port, const char * url, const char * protocol) {
WebSocketsClient::beginSocketIO(host, port, url, protocol);
}
void SocketIOclient::begin(String host, uint16_t port, String url, String protocol) {
WebSocketsClient::beginSocketIO(host, port, url, protocol);
}
bool SocketIOclient::isConnected(void) {
return WebSocketsClient::isConnected();
}
/**
* send text data to client
* @param num uint8_t client id
* @param payload uint8_t *
* @param length size_t
* @param headerToPayload bool (see sendFrame for more details)
* @return true if ok
*/
bool SocketIOclient::sendEVENT(uint8_t * payload, size_t length, bool headerToPayload) {
bool ret = false;
if(length == 0) {
length = strlen((const char *) payload);
}
if(clientIsConnected(&_client)) {
if(!headerToPayload) {
// webSocket Header
ret = WebSocketsClient::sendFrameHeader(&_client, WSop_text, length + 2, true);
// Engine.IO / Socket.IO Header
if(ret) {
uint8_t buf[3] = { eIOtype_MESSAGE, sIOtype_EVENT, 0x00 };
ret = WebSocketsClient::write(&_client, buf, 2);
}
if(ret) {
ret = WebSocketsClient::write(&_client, payload, length );
}
return ret;
} else {
// TODO implement
}
// return WebSocketsClient::sendFrame(&_client, WSop_text, payload, length, true, true, headerToPayload);
}
return false;
}
bool SocketIOclient::sendEVENT(const uint8_t * payload, size_t length) {
return sendEVENT((uint8_t *) payload, length);
}
bool SocketIOclient::sendEVENT(char * payload, size_t length, bool headerToPayload) {
return sendEVENT((uint8_t *) payload, length, headerToPayload);
}
bool SocketIOclient::sendEVENT(const char * payload, size_t length) {
return sendEVENT((uint8_t *) payload, length);
}
bool SocketIOclient::sendEVENT(String & payload) {
return sendEVENT((uint8_t *) payload.c_str(), payload.length());
}
void SocketIOclient::loop(void) {
WebSocketsClient::loop();
unsigned long t = millis();
if((t - _lastConnectionFail) > EIO_HEARTBEAT_INTERVAL) {
_lastConnectionFail = t;
//WebSocketsClient::sendTXT(eIOtype_PING);
}
}
void SocketIOclient::runCbEvent(WStype_t type, uint8_t * payload, size_t length) {
switch(type) {
case WStype_DISCONNECTED:
DEBUG_WEBSOCKETS("[wsIOc] Disconnected!\n");
break;
case WStype_CONNECTED: {
DEBUG_WEBSOCKETS("[wsIOc] Connected to url: %s\n", payload);
// send message to server when Connected
// Engine.io upgrade confirmation message (required)
WebSocketsClient::sendTXT(eIOtype_UPGRADE);
}
break;
case WStype_TEXT: {
if(length < 1) {
break;
}
engineIOmessageType_t eType = (engineIOmessageType_t) payload[0];
switch(eType) {
case eIOtype_PING:
payload[0] = eIOtype_PONG;
DEBUG_WEBSOCKETS("[wsIOc] get ping send pong (%s)\n", payload);
WebSocketsClient::sendTXT(payload, length, false);
break;
case eIOtype_PONG:
DEBUG_WEBSOCKETS("[wsIOc] get pong\n");
break;
case eIOtype_MESSAGE: {
if(length < 2) {
break;
}
socketIOmessageType_t ioType = (socketIOmessageType_t) payload[1];
uint8_t * data = &payload[2];
size_t lData = length - 2;
switch(ioType) {
case sIOtype_EVENT:
DEBUG_WEBSOCKETS("[wsIOc] get event (%d): %s\n", lData, data);
break;
case sIOtype_CONNECT:
case sIOtype_DISCONNECT:
case sIOtype_ACK:
case sIOtype_ERROR:
case sIOtype_BINARY_EVENT:
case sIOtype_BINARY_ACK:
default:
DEBUG_WEBSOCKETS("[wsIOc] Socket.IO Message Type %c (%02X) is not implemented\n", ioType, ioType);
DEBUG_WEBSOCKETS("[wsIOc] get text: %s\n", payload);
break;
}
}
break;
case eIOtype_OPEN:
case eIOtype_CLOSE:
case eIOtype_UPGRADE:
case eIOtype_NOOP:
default:
DEBUG_WEBSOCKETS("[wsIOc] Engine.IO Message Type %c (%02X) is not implemented\n", eType, eType);
DEBUG_WEBSOCKETS("[wsIOc] get text: %s\n", payload);
break;
}
// send message to server
// webSocket.sendTXT("message here");
}
break;
case WStype_BIN:
DEBUG_WEBSOCKETS("[wsIOc] get binary length: %u\n", length);
// hexdump(payload, length);
// send data to server
// webSocket.sendBIN(payload, length);
break;
}
}

69
src/SocketIOclient.h Normal file
View File

@ -0,0 +1,69 @@
/*
* SocketIOclient.h
*
* Created on: May 12, 2018
* Author: links
*/
#ifndef SOCKETIOCLIENT_H_
#define SOCKETIOCLIENT_H_
#include "WebSockets.h"
#define EIO_HEARTBEAT_INTERVAL 10000
#define EIO_MAX_HEADER_SIZE (WEBSOCKETS_MAX_HEADER_SIZE + 1)
#define SIO_MAX_HEADER_SIZE (EIO_MAX_HEADER_SIZE + 1)
typedef enum {
eIOtype_OPEN = '0', ///< Sent from the server when a new transport is opened (recheck)
eIOtype_CLOSE = '1', ///< Request the close of this transport but does not shutdown the connection itself.
eIOtype_PING = '2', ///< Sent by the client. Server should answer with a pong packet containing the same data
eIOtype_PONG = '3', ///< Sent by the server to respond to ping packets.
eIOtype_MESSAGE = '4', ///< actual message, client and server should call their callbacks with the data
eIOtype_UPGRADE = '5', ///< Before engine.io switches a transport, it tests, if server and client can communicate over this transport. If this test succeed, the client sends an upgrade packets which requests the server to flush its cache on the old transport and switch to the new transport.
eIOtype_NOOP = '6', ///< A noop packet. Used primarily to force a poll cycle when an incoming websocket connection is received.
} engineIOmessageType_t;
typedef enum {
sIOtype_CONNECT = '0',
sIOtype_DISCONNECT = '1',
sIOtype_EVENT = '2',
sIOtype_ACK = '3',
sIOtype_ERROR = '4',
sIOtype_BINARY_EVENT = '5',
sIOtype_BINARY_ACK = '6',
} socketIOmessageType_t;
class SocketIOclient: protected WebSocketsClient {
public:
#ifdef __AVR__
typedef void (*SocketIOclientEvent)(WStype_t type, uint8_t * payload, size_t length);
#else
typedef std::function<void (WStype_t type, uint8_t * payload, size_t length)> SocketIOclientEvent;
#endif
SocketIOclient(void);
virtual ~SocketIOclient(void);
void begin(const char *host, uint16_t port, const char * url = "/socket.io/?EIO=3", const char * protocol = "arduino");
void begin(String host, uint16_t port, String url = "/socket.io/?EIO=3", String protocol = "arduino");
bool isConnected(void);
bool sendEVENT(uint8_t * payload, size_t length = 0, bool headerToPayload = false);
bool sendEVENT(const uint8_t * payload, size_t length = 0);
bool sendEVENT(char * payload, size_t length = 0, bool headerToPayload = false);
bool sendEVENT(const char * payload, size_t length = 0);
bool sendEVENT(String & payload);
void loop(void);
protected:
void runCbEvent(WStype_t type, uint8_t * payload, size_t length);
uint64_t _lastHeartbeat = 0;
};
#endif /* SOCKETIOCLIENT_H_ */

View File

@ -70,6 +70,112 @@ void WebSockets::clientDisconnect(WSclient_t * client, uint16_t code, char * rea
clientDisconnect(client);
}
/**
*
* @param buf uint8_t * ptr to the buffer for writing
* @param opcode WSopcode_t
* @param length size_t length of the payload
* @param mask bool add dummy mask to the frame (needed for web browser)
* @param maskkey uint8_t[4] key used for payload
* @param fin bool can be used to send data in more then one frame (set fin on the last frame)
*/
uint8_t WebSockets::createHeader(uint8_t * headerPtr, WSopcode_t opcode, size_t length, bool mask, uint8_t maskKey[4], bool fin) {
uint8_t headerSize;
// calculate header Size
if(length < 126) {
headerSize = 2;
} else if(length < 0xFFFF) {
headerSize = 4;
} else {
headerSize = 10;
}
if(mask) {
headerSize += 4;
}
// create header
// byte 0
*headerPtr = 0x00;
if(fin) {
*headerPtr |= bit(7); ///< set Fin
}
*headerPtr |= opcode; ///< set opcode
headerPtr++;
// byte 1
*headerPtr = 0x00;
if(mask) {
*headerPtr |= bit(7); ///< set mask
}
if(length < 126) {
*headerPtr |= length;
headerPtr++;
} else if(length < 0xFFFF) {
*headerPtr |= 126;
headerPtr++;
*headerPtr = ((length >> 8) & 0xFF);
headerPtr++;
*headerPtr = (length & 0xFF);
headerPtr++;
} else {
// Normally we never get here (to less memory)
*headerPtr |= 127;
headerPtr++;
*headerPtr = 0x00;
headerPtr++;
*headerPtr = 0x00;
headerPtr++;
*headerPtr = 0x00;
headerPtr++;
*headerPtr = 0x00;
headerPtr++;
*headerPtr = ((length >> 24) & 0xFF);
headerPtr++;
*headerPtr = ((length >> 16) & 0xFF);
headerPtr++;
*headerPtr = ((length >> 8) & 0xFF);
headerPtr++;
*headerPtr = (length & 0xFF);
headerPtr++;
}
if(mask) {
*headerPtr = maskKey[0];
headerPtr++;
*headerPtr = maskKey[1];
headerPtr++;
*headerPtr = maskKey[2];
headerPtr++;
*headerPtr = maskKey[3];
headerPtr++;
}
return headerSize;
}
/**
*
* @param client WSclient_t * ptr to the client struct
* @param opcode WSopcode_t
* @param length size_t length of the payload
* @param fin bool can be used to send data in more then one frame (set fin on the last frame)
* @return true if ok
*/
bool WebSockets::sendFrameHeader(WSclient_t * client, WSopcode_t opcode, size_t length, bool fin) {
uint8_t maskKey[4] = { 0x00, 0x00, 0x00, 0x00 };
uint8_t buffer[WEBSOCKETS_MAX_HEADER_SIZE] = { 0 };
uint8_t headerSize = createHeader(&buffer[0], opcode, length, client->cIsClient, maskKey, fin);
if(write(client, &buffer[0], headerSize) != headerSize) {
return false;
}
return true;
}
/**
*
* @param client WSclient_t * ptr to the client struct
@ -143,85 +249,27 @@ bool WebSockets::sendFrame(WSclient_t * client, WSopcode_t opcode, uint8_t * pay
headerPtr = &buffer[0];
}
// create header
// byte 0
*headerPtr = 0x00;
if(fin) {
*headerPtr |= bit(7); ///< set Fin
}
*headerPtr |= opcode; ///< set opcode
headerPtr++;
// byte 1
*headerPtr = 0x00;
if(client->cIsClient) {
*headerPtr |= bit(7); ///< set mask
if(client->cIsClient && useInternBuffer) {
// if we use a Intern Buffer we can modify the data
// by this fact its possible the do the masking
for(uint8_t x = 0; x < sizeof(maskKey); x++) {
maskKey[x] = random(0xFF);
}
}
if(length < 126) {
*headerPtr |= length;
headerPtr++;
} else if(length < 0xFFFF) {
*headerPtr |= 126;
headerPtr++;
*headerPtr = ((length >> 8) & 0xFF);
headerPtr++;
*headerPtr = (length & 0xFF);
headerPtr++;
} else {
// Normally we never get here (to less memory)
*headerPtr |= 127;
headerPtr++;
*headerPtr = 0x00;
headerPtr++;
*headerPtr = 0x00;
headerPtr++;
*headerPtr = 0x00;
headerPtr++;
*headerPtr = 0x00;
headerPtr++;
*headerPtr = ((length >> 24) & 0xFF);
headerPtr++;
*headerPtr = ((length >> 16) & 0xFF);
headerPtr++;
*headerPtr = ((length >> 8) & 0xFF);
headerPtr++;
*headerPtr = (length & 0xFF);
headerPtr++;
}
createHeader(headerPtr, opcode, length, client->cIsClient, maskKey, fin);
if(client->cIsClient) {
if(useInternBuffer) {
// if we use a Intern Buffer we can modify the data
// by this fact its possible the do the masking
for(uint8_t x = 0; x < sizeof(maskKey); x++) {
maskKey[x] = random(0xFF);
*headerPtr = maskKey[x];
headerPtr++;
}
uint8_t * dataMaskPtr;
if(headerToPayload) {
dataMaskPtr = (payloadPtr + WEBSOCKETS_MAX_HEADER_SIZE);
} else {
dataMaskPtr = payloadPtr;
}
for(size_t x = 0; x < length; x++) {
dataMaskPtr[x] = (dataMaskPtr[x] ^ maskKey[x % 4]);
}
if(client->cIsClient && useInternBuffer) {
uint8_t * dataMaskPtr;
if(headerToPayload) {
dataMaskPtr = (payloadPtr + WEBSOCKETS_MAX_HEADER_SIZE);
} else {
*headerPtr = maskKey[0];
headerPtr++;
*headerPtr = maskKey[1];
headerPtr++;
*headerPtr = maskKey[2];
headerPtr++;
*headerPtr = maskKey[3];
headerPtr++;
dataMaskPtr = payloadPtr;
}
for(size_t x = 0; x < length; x++) {
dataMaskPtr[x] = (dataMaskPtr[x] ^ maskKey[x % 4]);
}
}

View File

@ -298,9 +298,12 @@ class WebSockets {
virtual void clientDisconnect(WSclient_t * client) = 0;
virtual bool clientIsConnected(WSclient_t * client) = 0;
void clientDisconnect(WSclient_t * client, uint16_t code, char * reason = NULL, size_t reasonLen = 0);
virtual void messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin) = 0;
void clientDisconnect(WSclient_t * client, uint16_t code, char * reason = NULL, size_t reasonLen = 0);
uint8_t createHeader(uint8_t * buf, WSopcode_t opcode, size_t length, bool mask, uint8_t maskKey[4], bool fin);
bool sendFrameHeader(WSclient_t * client, WSopcode_t opcode, size_t length = 0, bool fin = true);
bool sendFrame(WSclient_t * client, WSopcode_t opcode, uint8_t * payload = NULL, size_t length = 0, bool fin = true, bool headerToPayload = false);
void headerDone(WSclient_t * client);

View File

@ -251,6 +251,12 @@ bool WebSocketsClient::sendTXT(String & payload) {
return sendTXT((uint8_t *)payload.c_str(), payload.length());
}
bool WebSocketsClient::sendTXT(char payload) {
uint8_t buf[WEBSOCKETS_MAX_HEADER_SIZE + 2] = { 0x00 };
buf[WEBSOCKETS_MAX_HEADER_SIZE] = payload;
return sendTXT(buf, 1, true);
}
/**
* send binary data to client
* @param num uint8_t client id
@ -343,6 +349,10 @@ void WebSocketsClient::setReconnectInterval(unsigned long time) {
_reconnectInterval = time;
}
bool WebSocketsClient::isConnected(void) {
return (_client.status == WSC_CONNECTED);
}
//#################################################################################
//#################################################################################
//#################################################################################
@ -694,6 +704,13 @@ void WebSocketsClient::handleHeader(WSclient_t * client, String * headerLine) {
runCbEvent(WStype_CONNECTED, (uint8_t *)client->cUrl.c_str(), client->cUrl.length());
} else if(clientIsConnected(client) && client->isSocketIO && client->cSessionId.length() > 0) {
if(_client.tcp->available()) {
// read not needed data
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] still data in buffer (%d), clean up.\n", _client.tcp->available());
while(_client.tcp->available() > 0) {
_client.tcp->read();
}
}
sendHeader(client);
} else {
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] no Websocket connection close.\n");

View File

@ -27,7 +27,7 @@
#include "WebSockets.h"
class WebSocketsClient : private WebSockets {
class WebSocketsClient : protected WebSockets {
public:
#ifdef __AVR__
typedef void (*WebSocketClientEvent)(WStype_t type, uint8_t * payload, size_t length);
@ -71,6 +71,7 @@ class WebSocketsClient : private WebSockets {
bool sendTXT(char * payload, size_t length = 0, bool headerToPayload = false);
bool sendTXT(const char * payload, size_t length = 0);
bool sendTXT(String & payload);
bool sendTXT(char payload);
bool sendBIN(uint8_t * payload, size_t length, bool headerToPayload = false);
bool sendBIN(const uint8_t * payload, size_t length);
@ -94,6 +95,8 @@ class WebSocketsClient : private WebSockets {
String _host;
uint16_t _port;
bool isConnected(void);
#if defined(HAS_SSL)
String _fingerprint;
const char * _CA_cert;