diff --git a/src/WebSockets.cpp b/src/WebSockets.cpp index 03db50a..b976994 100644 --- a/src/WebSockets.cpp +++ b/src/WebSockets.cpp @@ -25,39 +25,70 @@ #include "WebSockets.h" /** - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-------+-+-------------+-------------------------------+ - |F|R|R|R| opcode|M| Payload len | Extended payload length | - |I|S|S|S| (4) |A| (7) | (16/64) | - |N|V|V|V| |S| | (if payload len==126/127) | - | |1|2|3| |K| | | - +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + - | Extended payload length continued, if payload len == 127 | - + - - - - - - - - - - - - - - - +-------------------------------+ - | |Masking-key, if MASK set to 1 | - +-------------------------------+-------------------------------+ - | Masking-key (continued) | Payload Data | - +-------------------------------- - - - - - - - - - - - - - - - + - : Payload Data continued ... : - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - | Payload Data continued ... | - +---------------------------------------------------------------+ * + * @param client WSclient_t * ptr to the client struct + * @param code + * @param reason + * @param reasonLen */ +void WebSockets::clientDisconnect(WSclient_t * client, uint16_t code, char * reason, size_t reasonLen) { + if(client->status == WSC_CONNECTED && code) { + //todo send reason to client -typedef enum { - WSop_continuation = 0x00, ///< %x0 denotes a continuation frame - WSop_text = 0x01, ///< %x1 denotes a text frame - WSop_binary = 0x02, ///< %x2 denotes a binary frame - ///< %x3-7 are reserved for further non-control frames - WSop_close = 0x08, ///< %x8 denotes a connection close - WSop_ping = 0x09, ///< %x9 denotes a ping - WSop_pong = 0x0A ///< %xA denotes a pong - ///< %xB-F are reserved for further control frames -} WSopcode_t; + if(reasonLen > 0 && reason) { + } else { + } + } + clientDisconnect(client); +} + +/** + * + * @param client WSclient_t * ptr to the client struct + * @param opcode WSopcode_t + * @param payload uint8_t * + * @param lenght size_t + */ +void WebSockets::sendFrame(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t lenght) { + + uint8_t buffer[16] = { 0 }; + uint8_t i = 0; + + //create header + + buffer[i] = bit(7); // set Fin + buffer[i++] |= opcode; // set opcode + + if(lenght < 126) { + buffer[i++] = lenght; + + } else if(lenght < 0xFFFF) { + buffer[i++] = 126; + buffer[i++] = ((lenght >> 8) & 0xFF); + buffer[i++] = (lenght & 0xFF); + } else { + buffer[i++] = 127; + buffer[i++] = 0x00; + buffer[i++] = 0x00; + buffer[i++] = 0x00; + buffer[i++] = 0x00; + buffer[i++] = ((lenght >> 24) & 0xFF); + buffer[i++] = ((lenght >> 16) & 0xFF); + buffer[i++] = ((lenght >> 8) & 0xFF); + buffer[i++] = (lenght & 0xFF); + } + + // send header + client->tcp.write(&buffer[0], i); + + if(payload && lenght > 0) { + // send payload + client->tcp.write(&payload[0], lenght); + } + +} /** * handle the WebSocket stream @@ -83,7 +114,7 @@ void WebSockets::handleWebsocket(WSclient_t * client) { if(!readWait(client, buffer, 2)) { //timeout - clientDisconnect(client); + clientDisconnect(client, 1002); return; } @@ -92,15 +123,15 @@ void WebSockets::handleWebsocket(WSclient_t * client) { rsv1 = ((buffer[0] >> 6) & 0x01); rsv2 = ((buffer[0] >> 5) & 0x01); rsv3 = ((buffer[0] >> 4) & 0x01); - opCode = (WSopcode_t)(buffer[0] & 0x0F); + opCode = (WSopcode_t) (buffer[0] & 0x0F); mask = ((buffer[1] >> 7) & 0x01); - payloadLen = (WSopcode_t)(buffer[1] & 0x7F); + payloadLen = (WSopcode_t) (buffer[1] & 0x7F); if(payloadLen == 126) { if(!readWait(client, buffer, 2)) { //timeout - clientDisconnect(client); + clientDisconnect(client, 1002); return; } payloadLen = buffer[0] << 8 | buffer[1]; @@ -108,7 +139,7 @@ void WebSockets::handleWebsocket(WSclient_t * client) { // read 64bit inteager as Lenght if(!readWait(client, buffer, 8)) { //timeout - clientDisconnect(client); + clientDisconnect(client, 1002); return; } @@ -125,7 +156,7 @@ void WebSockets::handleWebsocket(WSclient_t * client) { if(payloadLen > WEBSOCKETS_MAX_DATA_SIZE) { DEBUG_WEBSOCKETS("[WS-Server][%d][handleWebsocket] payload to big! (%u)\n", client->num, payloadLen); - clientDisconnect(client); + clientDisconnect(client, 1009); return; } @@ -135,37 +166,77 @@ void WebSockets::handleWebsocket(WSclient_t * client) { if(payloadLen > 0) { // if text data we need one more - payload = (uint8_t *) malloc(payloadLen+1); + payload = (uint8_t *) malloc(payloadLen + 1); if(!payload) { DEBUG_WEBSOCKETS("[WS-Server][%d][handleWebsocket] to less memory to handle payload %d!\n", client->num, payloadLen); - clientDisconnect(client); + clientDisconnect(client, 1011); return; } if(!readWait(client, payload, payloadLen)) { DEBUG_WEBSOCKETS("[WS-Server][%d][handleWebsocket] missing data!\n", client->num); - clientDisconnect(client); + clientDisconnect(client, 1002); return; } + payload[payloadLen] = 0x00; + if(mask) { //decode XOR - for (size_t i = 0; i < payloadLen; i++) { + for(size_t i = 0; i < payloadLen; i++) { payload[i] = (payload[i] ^ maskKey[i % 4]); } } - if(opCode == WSop_text) { - payload[payloadLen] = 0x00; - DEBUG_WEBSOCKETS("[WS-Server][%d][handleWebsocket] Text: %s\n", client->num, payload); - } else { - clientDisconnect(client); + switch(opCode) { + case WSop_text: + DEBUG_WEBSOCKETS("[WS-Server][%d][handleWebsocket] text: %s\n", client->num, payload) + ; + // todo API for user to get message may callback + + // send the frame back! + sendFrame(client, WSop_text, payload, payloadLen); + + break; + case WSop_binary: + // todo API for user to get message may callback + break; + case WSop_ping: + // todo send pong + break; + case WSop_pong: + DEBUG_WEBSOCKETS("[WS-Server][%d][handleWebsocket] get pong from Client (%s)\n", client->num, payload) + ; + break; + case WSop_close: { + uint16_t reasonCode = buffer[0] << 8 | buffer[1]; + + DEBUG_WEBSOCKETS("[WS-Server][%d][handleWebsocket] client ask for close. Code: %d (%s)\n", client->num, reasonCode, (payload + 2)); + + // todo send confimation to client + clientDisconnect(client, 1000, (char *) (payload + 2), payloadLen - 2); + } + break; + case WSop_continuation: + // continuation is not supported + clientDisconnect(client, 1003); + break; + default: + clientDisconnect(client, 1002); + break; } } } +/** + * read x byte from tcp or get timeout + * @param client WSclient_t * + * @param out uint8_t * data buffer + * @param n size_t byte count + * @return true if ok + */ bool WebSockets::readWait(WSclient_t * client, uint8_t *out, size_t n) { unsigned long t = millis(); size_t len; diff --git a/src/WebSockets.h b/src/WebSockets.h index 580ef4e..6ac19f3 100644 --- a/src/WebSockets.h +++ b/src/WebSockets.h @@ -52,6 +52,17 @@ typedef enum { WSC_CONNECTED } WSclientsStatus_t; +typedef enum { + WSop_continuation = 0x00, ///< %x0 denotes a continuation frame + WSop_text = 0x01, ///< %x1 denotes a text frame + WSop_binary = 0x02, ///< %x2 denotes a binary frame + ///< %x3-7 are reserved for further non-control frames + WSop_close = 0x08, ///< %x8 denotes a connection close + WSop_ping = 0x09, ///< %x9 denotes a ping + WSop_pong = 0x0A ///< %xA denotes a pong + ///< %xB-F are reserved for further control frames +} WSopcode_t; + typedef struct { uint8_t num; ///< connection number @@ -81,12 +92,16 @@ typedef struct { class WebSockets { protected: - - void handleWebsocket(WSclient_t * client); - virtual void clientDisconnect(WSclient_t * client); virtual bool clientIsConnected(WSclient_t * client); + + void clientDisconnect(WSclient_t * client, uint16_t code, char * reason = NULL, size_t reasonLen = 0); + void sendFrame(WSclient_t * client, WSopcode_t opcode, uint8_t * payload = NULL, size_t lenght = 0); + + + void handleWebsocket(WSclient_t * client); + bool readWait(WSclient_t * client, uint8_t *out, size_t n); };