Files
arduinoWebSockets/src/WebSockets.cpp

203 lines
6.9 KiB
C++
Raw Normal View History

2015-05-22 20:35:51 +02:00
/**
* @file WebSockets.cpp
* @date 20.05.2015
* @author Markus Sattler
*
* Copyright (c) 2015 Markus Sattler. All rights reserved.
* This file is part of the WebSockets for Arduino.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#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 ... |
+---------------------------------------------------------------+
*
*/
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;
/**
* handle the WebSocket stream
* @param client WSclient_t * ptr to the client struct
*/
void WebSockets::handleWebsocket(WSclient_t * client) {
uint8_t buffer[8] = { 0 };
bool fin;
bool rsv1;
bool rsv2;
bool rsv3;
WSopcode_t opCode;
bool mask;
size_t payloadLen;
uint8_t maskKey[4];
uint8_t * payload = NULL;
DEBUG_WEBSOCKETS("[WS-Server][%d][handleWebsocket] ------- read massage frame -------\n", client->num);
if(!readWait(client, buffer, 2)) {
//timeout
clientDisconnect(client);
return;
}
// split first 2 bytes in the data
fin = ((buffer[0] >> 7) & 0x01);
rsv1 = ((buffer[0] >> 6) & 0x01);
rsv2 = ((buffer[0] >> 5) & 0x01);
rsv3 = ((buffer[0] >> 4) & 0x01);
opCode = (WSopcode_t)(buffer[0] & 0x0F);
mask = ((buffer[1] >> 7) & 0x01);
payloadLen = (WSopcode_t)(buffer[1] & 0x7F);
if(payloadLen == 126) {
if(!readWait(client, buffer, 2)) {
//timeout
clientDisconnect(client);
return;
}
payloadLen = buffer[0] << 8 | buffer[1];
} else if(payloadLen == 127) {
// read 64bit inteager as Lenght
if(!readWait(client, buffer, 8)) {
//timeout
clientDisconnect(client);
return;
}
if(buffer[0] != 0 || buffer[1] != 0 || buffer[2] != 0 || buffer[3] != 0) {
// really to big!
payloadLen = 0xFFFFFFFF;
} else {
payloadLen = buffer[4] << 24 | buffer[5] << 16 | buffer[6] << 8 | buffer[7];
}
}
DEBUG_WEBSOCKETS("[WS-Server][%d][handleWebsocket] fin: %u rsv1: %u rsv2: %u rsv3 %u opCode: %u\n", client->num, fin, rsv1, rsv2, rsv3, opCode);
DEBUG_WEBSOCKETS("[WS-Server][%d][handleWebsocket] mask: %u payloadLen: %u\n", client->num, mask, payloadLen);
if(payloadLen > WEBSOCKETS_MAX_DATA_SIZE) {
DEBUG_WEBSOCKETS("[WS-Server][%d][handleWebsocket] payload to big! (%u)\n", client->num, payloadLen);
clientDisconnect(client);
return;
}
if(mask) {
client->tcp.read(maskKey, 4);
}
if(payloadLen > 0) {
// if text data we need one more
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);
return;
}
if(!readWait(client, payload, payloadLen)) {
DEBUG_WEBSOCKETS("[WS-Server][%d][handleWebsocket] missing data!\n", client->num);
clientDisconnect(client);
return;
}
if(mask) {
//decode XOR
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);
}
}
}
bool WebSockets::readWait(WSclient_t * client, uint8_t *out, size_t n) {
unsigned long t = millis();
size_t len;
while(n > 0) {
if(!client->tcp.connected()) {
DEBUG_WEBSOCKETS("[readWait] Receive not connected - 1!\n");
return false;
}
while(!client->tcp.available()) {
if(!client->tcp.connected()) {
DEBUG_WEBSOCKETS("[readWait] Receive not connected - 2!\n");
return false;
}
if((millis() - t) > WEBSOCKETS_TCP_TIMEOUT) {
DEBUG_WEBSOCKETS("[readWait] Receive TIMEOUT!\n");
return false;
}
delay(0);
}
len = client->tcp.read((uint8_t*) out, n);
if(len) {
t = millis();
out += len;
n -= len;
//DEBUG_WEBSOCKETS("Receive %d left %d!\n", len, n);
} else {
//DEBUG_WEBSOCKETS("Receive %d left %d!\n", len, n);
}
delay(0);
}
return true;
}