diff --git a/src/WebSockets.cpp b/src/WebSockets.cpp index 4ec9a1f..d45748e 100644 --- a/src/WebSockets.cpp +++ b/src/WebSockets.cpp @@ -30,9 +30,9 @@ extern "C" { #ifdef CORE_HAS_LIBB64 - #include +#include #else - #include "libb64/cencode_inc.h" +#include "libb64/cencode_inc.h" #endif } @@ -46,8 +46,6 @@ extern "C" { #endif -#define WEBSOCKETS_MAX_HEADER_SIZE (14) - /** * * @param client WSclient_t * ptr to the client struct @@ -120,7 +118,6 @@ void WebSockets::sendFrame(WSclient_t * client, WSopcode_t opcode, uint8_t * pay headerSize += 4; } - #ifdef WEBSOCKETS_USE_BIG_MEM // only for ESP since AVR has less HEAP // try to send data in one TCP package (only if some free Heap is there) @@ -161,22 +158,35 @@ void WebSockets::sendFrame(WSclient_t * client, WSopcode_t opcode, uint8_t * pay } if(length < 126) { - *headerPtr |= length; headerPtr++; + *headerPtr |= length; + headerPtr++; } else if(length < 0xFFFF) { - *headerPtr |= 126; headerPtr++; - *headerPtr = ((length >> 8) & 0xFF); headerPtr++; - *headerPtr = (length & 0xFF); headerPtr++; + *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++; + *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) { @@ -185,7 +195,8 @@ void WebSockets::sendFrame(WSclient_t * client, WSopcode_t opcode, uint8_t * pay // 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++; + *headerPtr = maskKey[x]; + headerPtr++; } uint8_t * dataMaskPtr; @@ -201,10 +212,14 @@ void WebSockets::sendFrame(WSclient_t * client, WSopcode_t opcode, uint8_t * pay } } else { - *headerPtr = maskKey[0]; headerPtr++; - *headerPtr = maskKey[1]; headerPtr++; - *headerPtr = maskKey[2]; headerPtr++; - *headerPtr = maskKey[3]; headerPtr++; + *headerPtr = maskKey[0]; + headerPtr++; + *headerPtr = maskKey[1]; + headerPtr++; + *headerPtr = maskKey[2]; + headerPtr++; + *headerPtr = maskKey[3]; + headerPtr++; } } @@ -237,154 +252,211 @@ void WebSockets::sendFrame(WSclient_t * client, WSopcode_t opcode, uint8_t * pay } +/** + * callen when HTTP header is done + * @param client WSclient_t * ptr to the client struct + */ +void WebSockets::headerDone(WSclient_t * client) { + client->status = WSC_CONNECTED; + client->cWsRXsize = 0; + DEBUG_WEBSOCKETS("[WS][%d][headerDone] Header Handling Done (%uus).\n", client->num); +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + client->cHttpLine = ""; + handleWebsocket(client); +#endif +} + /** * handle the WebSocket stream * @param client WSclient_t * ptr to the client struct */ void WebSockets::handleWebsocket(WSclient_t * client) { + if(client->cWsRXsize == 0) { + handleWebsocketCb(client); + } +} - uint8_t buffer[8] = { 0 }; +/** + * wait for + * @param client + * @param size + */ +bool WebSockets::handleWebsocketWaitFor(WSclient_t * client, size_t size) { + if(size > WEBSOCKETS_MAX_HEADER_SIZE) { + DEBUG_WEBSOCKETS("[WS][%d][handleWebsocketWaitFor] size: %d to big!\n", client->num, size); + return false; + } - bool fin; - bool rsv1; - bool rsv2; - bool rsv3; - WSopcode_t opCode; - bool mask; - size_t payloadLen; + if(client->cWsRXsize >= size) { + return true; + } - uint8_t maskKey[4]; + DEBUG_WEBSOCKETS("[WS][%d][handleWebsocketWaitFor] size: %d cWsRXsize: %d\n", client->num, size, client->cWsRXsize); + readCb(client, &client->cWsHeader[client->cWsRXsize], (size - client->cWsRXsize), std::bind([](WebSockets * server, size_t size, WSclient_t * client, bool ok) { + DEBUG_WEBSOCKETS("[WS][%d][handleWebsocketWaitFor][readCb] size: %d ok: %d\n", client->num, size, ok); + if(ok) { + client->cWsRXsize = size; + server->handleWebsocketCb(client); + } else { + DEBUG_WEBSOCKETS("[WS][%d][readCb] failed.\n", client->num); + client->cWsRXsize = 0; + // timeout or error + server->clientDisconnect(client, 1002); + } + }, this, size, std::placeholders::_1, std::placeholders::_2)); + return false; +} +void WebSockets::handleWebsocketCb(WSclient_t * client) { + + uint8_t * buffer = client->cWsHeader; + + WSMessageHeader_t * header = &client->cWsHeaderDecode; uint8_t * payload = NULL; - DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] ------- read massage frame -------\n", client->num); + uint8_t headerLen = 2; - if(!readWait(client, buffer, 2)) { - //timeout - clientDisconnect(client, 1002); + if(!handleWebsocketWaitFor(client, headerLen)) { 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); + header->fin = ((*buffer >> 7) & 0x01); + header->rsv1 = ((*buffer >> 6) & 0x01); + header->rsv2 = ((*buffer >> 5) & 0x01); + header->rsv3 = ((*buffer >> 4) & 0x01); + header->opCode = (WSopcode_t) (*buffer & 0x0F); + buffer++; - mask = ((buffer[1] >> 7) & 0x01); - payloadLen = (WSopcode_t) (buffer[1] & 0x7F); + header->mask = ((*buffer >> 7) & 0x01); + header->payloadLen = (WSopcode_t) (*buffer & 0x7F); + buffer++; - if(payloadLen == 126) { - if(!readWait(client, buffer, 2)) { - //timeout - clientDisconnect(client, 1002); + if(header->payloadLen == 126) { + headerLen += 4; + if(!handleWebsocketWaitFor(client, headerLen)) { return; } - payloadLen = buffer[0] << 8 | buffer[1]; - } else if(payloadLen == 127) { + header->payloadLen = buffer[0] << 8 | buffer[1]; + buffer += 2; + } else if(header->payloadLen == 127) { + headerLen += 8; // read 64bit integer as length - if(!readWait(client, buffer, 8)) { - //timeout - clientDisconnect(client, 1002); + if(!handleWebsocketWaitFor(client, headerLen)) { return; } if(buffer[0] != 0 || buffer[1] != 0 || buffer[2] != 0 || buffer[3] != 0) { // really to big! - payloadLen = 0xFFFFFFFF; + header->payloadLen = 0xFFFFFFFF; } else { - payloadLen = buffer[4] << 24 | buffer[5] << 16 | buffer[6] << 8 | buffer[7]; + header->payloadLen = buffer[4] << 24 | buffer[5] << 16 | buffer[6] << 8 | buffer[7]; } + buffer += 8; } - DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] fin: %u rsv1: %u rsv2: %u rsv3 %u opCode: %u\n", client->num, fin, rsv1, rsv2, rsv3, opCode); - DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] mask: %u payloadLen: %u\n", client->num, mask, payloadLen); + DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] ------- read massage frame -------\n", client->num); + DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] fin: %u rsv1: %u rsv2: %u rsv3 %u opCode: %u\n", client->num, header->fin, header->rsv1, header->rsv2, header->rsv3, header->opCode); + DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] mask: %u payloadLen: %u\n", client->num, header->mask, header->payloadLen); - if(payloadLen > WEBSOCKETS_MAX_DATA_SIZE) { - DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] payload to big! (%u)\n", client->num, payloadLen); + if(header->payloadLen > WEBSOCKETS_MAX_DATA_SIZE) { + DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] payload to big! (%u)\n", client->num, header->payloadLen); clientDisconnect(client, 1009); return; } - if(mask) { - if(!readWait(client, maskKey, 4)) { - //timeout - clientDisconnect(client, 1002); + if(header->mask) { + headerLen += 4; + if(!handleWebsocketWaitFor(client, headerLen)) { return; } + header->maskKey = buffer; + buffer += 4; } - if(payloadLen > 0) { + if(header->payloadLen > 0) { // if text data we need one more - payload = (uint8_t *) malloc(payloadLen + 1); + payload = (uint8_t *) malloc(header->payloadLen + 1); if(!payload) { - DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] to less memory to handle payload %d!\n", client->num, payloadLen); + DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] to less memory to handle payload %d!\n", client->num, header->payloadLen); clientDisconnect(client, 1011); return; } + readCb(client, payload, header->payloadLen, std::bind(&WebSockets::handleWebsocketPayloadCb, this, std::placeholders::_1, std::placeholders::_2, payload)); + } else { + handleWebsocketPayloadCb(client, true, NULL); + } +} - if(!readWait(client, payload, payloadLen)) { - DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] missing data!\n", client->num); - free(payload); - clientDisconnect(client, 1002); - return; - } +void WebSockets::handleWebsocketPayloadCb(WSclient_t * client, bool ok, uint8_t * payload) { + WSMessageHeader_t * header = &client->cWsHeaderDecode; + if(ok) { + if(header->payloadLen > 0) { + payload[header->payloadLen] = 0x00; - payload[payloadLen] = 0x00; - - if(mask) { - //decode XOR - for(size_t i = 0; i < payloadLen; i++) { - payload[i] = (payload[i] ^ maskKey[i % 4]); + if(header->mask) { + //decode XOR + for(size_t i = 0; i < header->payloadLen; i++) { + payload[i] = (payload[i] ^ header->maskKey[i % 4]); + } } } - } - switch(opCode) { - case WSop_text: - DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] text: %s\n", client->num, payload); - // no break here! - case WSop_binary: - messageRecived(client, opCode, payload, payloadLen); - break; - case WSop_ping: - // send pong back - sendFrame(client, WSop_pong, payload, payloadLen); - break; - case WSop_pong: - DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] get pong (%s)\n", client->num, payload); - break; - case WSop_close: - { + switch(header->opCode) { + case WSop_text: + DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] text: %s\n", client->num, payload); + // no break here! + case WSop_binary: + messageRecived(client, header->opCode, payload, header->payloadLen); + break; + case WSop_ping: + // send pong back + sendFrame(client, WSop_pong, payload, header->payloadLen); + break; + case WSop_pong: + DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] get pong (%s)\n", client->num, payload); + break; + case WSop_close: { uint16_t reasonCode = 1000; - if(payloadLen >= 2) { + if(header->payloadLen >= 2) { reasonCode = payload[0] << 8 | payload[1]; } DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] get ask for close. Code: %d", client->num, reasonCode); - if(payloadLen > 2) { - DEBUG_WEBSOCKETS(" (%s)\n", (payload+2)); + if(header->payloadLen > 2) { + DEBUG_WEBSOCKETS(" (%s)\n", (payload + 2)); } else { DEBUG_WEBSOCKETS("\n"); } clientDisconnect(client, 1000); } - break; - case WSop_continuation: - // continuation is not supported - clientDisconnect(client, 1003); - break; - default: - clientDisconnect(client, 1002); - break; - } + break; + case WSop_continuation: + // continuation is not supported + clientDisconnect(client, 1003); + break; + default: + clientDisconnect(client, 1002); + break; + } - if(payload) { + if(payload) { + free(payload); + } + + // reset input + client->cWsRXsize = 0; +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + //register callback for next message + handleWebsocketWaitFor(client, 2); +#endif + + } else { + DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] missing data!\n", client->num); free(payload); + clientDisconnect(client, 1002); } - } /** @@ -417,7 +489,7 @@ String WebSockets::acceptKey(String clientKey) { * @return base64 encoded String */ String WebSockets::base64_encode(uint8_t * data, size_t length) { - size_t size = ((length*1.6f)+1); + size_t size = ((length * 1.6f) + 1); char * buffer = (char *) malloc(size); if(buffer) { base64_encodestate _state; @@ -439,28 +511,46 @@ String WebSockets::base64_encode(uint8_t * data, size_t length) { * @param n size_t byte count * @return true if ok */ -bool WebSockets::readWait(WSclient_t * client, uint8_t *out, size_t n) { +bool WebSockets::readCb(WSclient_t * client, uint8_t * out, size_t n, WSreadWaitCb cb) { +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + + client->tcp->readBytes(out, n, std::bind([](WSclient_t * client, bool ok, WSreadWaitCb cb) { + if(cb) { + cb(client, ok); + } + }, client, std::placeholders::_1, cb)); + +#else unsigned long t = millis(); size_t len; - + DEBUG_WEBSOCKETS("[readCb] n: %d t: %d\n", n, t); while(n > 0) { - if(!client->tcp) { - DEBUG_WEBSOCKETS("[readWait] tcp is null!\n"); + if(client->tcp == NULL) { + DEBUG_WEBSOCKETS("[readCb] tcp is null!\n"); + if(cb) { + cb(client, false); + } return false; } if(!client->tcp->connected()) { - DEBUG_WEBSOCKETS("[readWait] not connected!\n"); + DEBUG_WEBSOCKETS("[readCb] not connected!\n"); + if(cb) { + cb(client, false); + } return false; } if((millis() - t) > WEBSOCKETS_TCP_TIMEOUT) { - DEBUG_WEBSOCKETS("[readWait] receive TIMEOUT!\n"); + DEBUG_WEBSOCKETS("[readCb] receive TIMEOUT! %d\n", (millis() - t)); + if(cb) { + cb(client, false); + } return false; } if(!client->tcp->available()) { -#ifdef ESP8266 +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) delay(0); #endif continue; @@ -475,9 +565,14 @@ bool WebSockets::readWait(WSclient_t * client, uint8_t *out, size_t n) { } else { //DEBUG_WEBSOCKETS("Receive %d left %d!\n", len, n); } -#ifdef ESP8266 +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) delay(0); #endif } + if(cb) { + cb(client, true); + } +#endif return true; } + diff --git a/src/WebSockets.h b/src/WebSockets.h index ffccf80..96cf09c 100644 --- a/src/WebSockets.h +++ b/src/WebSockets.h @@ -27,7 +27,7 @@ #include -//#define DEBUG_WEBSOCKETS(...) Serial1.printf( __VA_ARGS__ ) +//#define DEBUG_WEBSOCKETS(...) os_printf( __VA_ARGS__ ) #ifndef DEBUG_WEBSOCKETS #define DEBUG_WEBSOCKETS(...) @@ -44,20 +44,39 @@ #define WEBSOCKETS_TCP_TIMEOUT (1500) -#define NETWORK_ESP8266 (1) -#define NETWORK_W5100 (2) -#define NETWORK_ENC28J60 (3) +#define NETWORK_ESP8266_ASYNC (0) +#define NETWORK_ESP8266 (1) +#define NETWORK_W5100 (2) +#define NETWORK_ENC28J60 (3) +// max size of the WS Message Header +#define WEBSOCKETS_MAX_HEADER_SIZE (14) // select Network type based #ifdef ESP8266 -#define WEBSOCKETS_NETWORK_TYPE NETWORK_ESP8266 +//#define WEBSOCKETS_NETWORK_TYPE NETWORK_ESP8266 +#define WEBSOCKETS_NETWORK_TYPE NETWORK_ESP8266_ASYNC #else #define WEBSOCKETS_NETWORK_TYPE NETWORK_W5100 #endif +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) -#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) +// Note: +// No SSL/WSS support for client in Async mode +// TLS lib need a sync interface! + +#ifndef ESP8266 +#error "network type ESP8266 ASYNC only possible on the ESP mcu!" +#endif + +#include +#include +#include +#define WEBSOCKETS_NETWORK_CLASS AsyncTCPbuffer +#define WEBSOCKETS_NETWORK_SERVER_CLASS AsyncServer + +#elif (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) #ifndef ESP8266 #error "network type ESP8266 only possible on the ESP mcu!" @@ -110,6 +129,21 @@ typedef enum { ///< %xB-F are reserved for further control frames } WSopcode_t; +typedef struct { + + bool fin; + bool rsv1; + bool rsv2; + bool rsv3; + + WSopcode_t opCode; + bool mask; + + size_t payloadLen; + + uint8_t * maskKey; +} WSMessageHeader_t; + typedef struct { uint8_t num; ///< connection number @@ -134,10 +168,23 @@ typedef struct { String cExtensions; ///< client Sec-WebSocket-Extensions uint16_t cVersion; ///< client Sec-WebSocket-Version + uint8_t cWsRXsize; ///< State of the RX + uint8_t cWsHeader[WEBSOCKETS_MAX_HEADER_SIZE]; ///< RX WS Message buffer + WSMessageHeader_t cWsHeaderDecode; + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + String cHttpLine; ///< HTTP header lines +#endif + } WSclient_t; + + class WebSockets { protected: + + typedef std::function WSreadWaitCb; + virtual void clientDisconnect(WSclient_t * client); virtual bool clientIsConnected(WSclient_t * client); @@ -146,14 +193,20 @@ class WebSockets { 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 length = 0, bool mask = false, bool fin = true, bool headerToPayload = false); + void headerDone(WSclient_t * client); void handleWebsocket(WSclient_t * client); - bool readWait(WSclient_t * client, uint8_t *out, size_t n); + bool handleWebsocketWaitFor(WSclient_t * client, size_t size); + void handleWebsocketCb(WSclient_t * client); + void handleWebsocketPayloadCb(WSclient_t * client, bool ok, uint8_t * payload); String acceptKey(String clientKey); String base64_encode(uint8_t * data, size_t length); + bool readCb(WSclient_t * client, uint8_t *out, size_t n, WSreadWaitCb cb); + + }; #endif /* WEBSOCKETS_H_ */ diff --git a/src/WebSocketsClient.cpp b/src/WebSocketsClient.cpp index 3a0a039..0351012 100644 --- a/src/WebSocketsClient.cpp +++ b/src/WebSocketsClient.cpp @@ -40,7 +40,9 @@ WebSocketsClient::~WebSocketsClient() { void WebSocketsClient::begin(const char *host, uint16_t port, const char * url) { _host = host; _port = port; +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) _fingerprint = ""; +#endif _client.num = 0; _client.status = WSC_NOT_CONNECTED; @@ -141,10 +143,12 @@ void WebSocketsClient::loop(void) { } else { DEBUG_WEBSOCKETS("[WS-Client] connection to %s:%u Faild\n", _host.c_str(), _port); - delay(10); //some litle delay to not flood the server + delay(10); //some little delay to not flood the server } } else { +#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) handleClientData(); +#endif } } @@ -248,12 +252,15 @@ void WebSocketsClient::messageRecived(WSclient_t * client, WSopcode_t opcode, ui */ void WebSocketsClient::clientDisconnect(WSclient_t * client) { + bool event = false; + #if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) if(client->isSSL && client->ssl) { if(client->ssl->connected()) { client->ssl->flush(); client->ssl->stop(); } + event = true; delete client->ssl; client->ssl = NULL; client->tcp = NULL; @@ -265,6 +272,7 @@ void WebSocketsClient::clientDisconnect(WSclient_t * client) { client->tcp->flush(); client->tcp->stop(); } + event = true; delete client->tcp; client->tcp = NULL; } @@ -280,9 +288,9 @@ void WebSocketsClient::clientDisconnect(WSclient_t * client) { client->status = WSC_NOT_CONNECTED; DEBUG_WEBSOCKETS("[WS-Client] client disconnected.\n"); - - runCbEvent(WStype_DISCONNECTED, NULL, 0); - + if(event) { + runCbEvent(WStype_DISCONNECTED, NULL, 0); + } } /** @@ -316,7 +324,7 @@ bool WebSocketsClient::clientIsConnected(WSclient_t * client) { return false; } - +#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) /** * Handel incomming data from Client */ @@ -325,7 +333,10 @@ void WebSocketsClient::handleClientData(void) { if(len > 0) { switch(_client.status) { case WSC_HEADER: - handleHeader(&_client); + { + String headerLine = _client->tcp->readStringUntil('\n'); + handleHeader(&_client, &headerLine); + } break; case WSC_CONNECTED: WebSockets::handleWebsocket(&_client); @@ -335,10 +346,11 @@ void WebSocketsClient::handleClientData(void) { break; } } -#ifdef ESP8266 +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) delay(0); #endif } +#endif /** * send the WebSocket header to Server @@ -377,6 +389,10 @@ void WebSocketsClient::sendHeader(WSclient_t * client) { client->tcp->write(handshake.c_str(), handshake.length()); +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + client->tcp->readStringUntil('\n', &(client->cHttpLine), std::bind(&WebSocketsClient::handleHeader, this, client, &(client->cHttpLine))); +#endif + DEBUG_WEBSOCKETS("[WS-Client][sendHeader] sending header... Done (%uus).\n", (micros() - start)); } @@ -385,20 +401,19 @@ void WebSocketsClient::sendHeader(WSclient_t * client) { * handle the WebSocket header reading * @param client WSclient_t * ptr to the client struct */ -void WebSocketsClient::handleHeader(WSclient_t * client) { +void WebSocketsClient::handleHeader(WSclient_t * client, String * headerLine) { - String headerLine = client->tcp->readStringUntil('\n'); - headerLine.trim(); // remove \r + headerLine->trim(); // remove \r - if(headerLine.length() > 0) { - DEBUG_WEBSOCKETS("[WS-Client][handleHeader] RX: %s\n", headerLine.c_str()); + if(headerLine->length() > 0) { + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] RX: %s\n", headerLine->c_str()); - if(headerLine.startsWith("HTTP/1.")) { + if(headerLine->startsWith("HTTP/1.")) { // "HTTP/1.1 101 Switching Protocols" - client->cCode = headerLine.substring(9, headerLine.indexOf(' ', 9)).toInt(); - } else if(headerLine.indexOf(':')) { - String headerName = headerLine.substring(0, headerLine.indexOf(':')); - String headerValue = headerLine.substring(headerLine.indexOf(':') + 2); + client->cCode = headerLine->substring(9, headerLine->indexOf(' ', 9)).toInt(); + } else if(headerLine->indexOf(':')) { + String headerName = headerLine->substring(0, headerLine->indexOf(':')); + String headerValue = headerLine->substring(headerLine->indexOf(':') + 2); if(headerName.equalsIgnoreCase("Connection")) { if(headerValue.indexOf("Upgrade") >= 0) { @@ -419,9 +434,14 @@ void WebSocketsClient::handleHeader(WSclient_t * client) { client->cVersion = headerValue.toInt(); } } else { - DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Header error (%s)\n", headerLine.c_str()); + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Header error (%s)\n", headerLine->c_str()); } + (*headerLine) = ""; +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + client->tcp->readStringUntil('\n', &(client->cHttpLine), std::bind(&WebSocketsClient::handleHeader, this, client, &(client->cHttpLine))); +#endif + } else { DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Header read fin.\n"); DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Client settings:\n"); @@ -456,6 +476,7 @@ void WebSocketsClient::handleHeader(WSclient_t * client) { } if(ok) { + if(client->cAccept.length() == 0) { ok = false; } else { @@ -471,8 +492,8 @@ void WebSocketsClient::handleHeader(WSclient_t * client) { if(ok) { DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Websocket connection init done.\n"); + headerDone(client); - client->status = WSC_CONNECTED; runCbEvent(WStype_CONNECTED, (uint8_t *) client->cUrl.c_str(), client->cUrl.length()); diff --git a/src/WebSocketsClient.h b/src/WebSocketsClient.h index 589a0d7..719da66 100644 --- a/src/WebSocketsClient.h +++ b/src/WebSocketsClient.h @@ -75,11 +75,12 @@ class WebSocketsClient: private WebSockets { void clientDisconnect(WSclient_t * client); bool clientIsConnected(WSclient_t * client); - void handleNewClients(void); +#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) void handleClientData(void); +#endif void sendHeader(WSclient_t * client); - void handleHeader(WSclient_t * client); + void handleHeader(WSclient_t * client, String * headerLine); /** * called for sending a Event to the app diff --git a/src/WebSocketsServer.cpp b/src/WebSocketsServer.cpp index 1dfcdcd..1d82605 100644 --- a/src/WebSocketsServer.cpp +++ b/src/WebSocketsServer.cpp @@ -32,10 +32,17 @@ WebSocketsServer::WebSocketsServer(uint16_t port, String origin, String protocol _server = new WEBSOCKETS_NETWORK_SERVER_CLASS(port); +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + _server->onClient([](void *s, AsyncClient* c){ + ((WebSocketsServer*)s)->newClient(new AsyncTCPbuffer(c)); + }, this); +#endif + _cbEvent = NULL; } + WebSocketsServer::~WebSocketsServer() { // disconnect all clients disconnect(); @@ -72,6 +79,11 @@ void WebSocketsServer::begin(void) { client->cVersion = 0; client->cIsUpgrade = false; client->cIsWebsocket = false; + + client->cWsRXsize = 0; +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + client->cHttpLine = ""; +#endif } #ifdef ESP8266 @@ -90,8 +102,10 @@ void WebSocketsServer::begin(void) { * called in arduino loop */ void WebSocketsServer::loop(void) { +#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) handleNewClients(); handleClientData(); +#endif } /** @@ -155,7 +169,7 @@ void WebSocketsServer::broadcastTXT(uint8_t * payload, size_t length, bool heade if(clientIsConnected(client)) { sendFrame(client, WSop_text, payload, length, false, true, headerToPayload); } -#ifdef ESP8266 +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) delay(0); #endif } @@ -211,7 +225,7 @@ void WebSocketsServer::broadcastBIN(uint8_t * payload, size_t length, bool heade if(clientIsConnected(client)) { sendFrame(client, WSop_binary, payload, length, false, true, headerToPayload); } -#ifdef ESP8266 +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) delay(0); #endif } @@ -248,7 +262,7 @@ void WebSocketsServer::disconnect(uint8_t num) { } } -#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) /** * get an IP for a client * @param num uint8_t client id @@ -270,6 +284,48 @@ IPAddress WebSocketsServer::remoteIP(uint8_t num) { //################################################################################# //################################################################################# +/** + * handle new client connection + * @param client + */ +bool WebSocketsServer::newClient(WEBSOCKETS_NETWORK_CLASS * TCPclient) { + WSclient_t * client; + // search free list entry for client + for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) { + client = &_clients[i]; + + // state is not connected or tcp connection is lost + if(!clientIsConnected(client)) { + client->tcp = TCPclient; + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) + client->isSSL = false; + client->tcp->setNoDelay(true); +#endif +#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) + // set Timeout for readBytesUntil and readStringUntil + client->tcp->setTimeout(WEBSOCKETS_TCP_TIMEOUT); +#endif + client->status = WSC_HEADER; +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + IPAddress ip = client->tcp->remoteIP(); + DEBUG_WEBSOCKETS("[WS-Server][%d] new client from %d.%d.%d.%d\n", client->num, ip[0], ip[1], ip[2], ip[3]); +#else + DEBUG_WEBSOCKETS("[WS-Server][%d] new client\n", client->num); +#endif + + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + client->tcp->readStringUntil('\n', &(client->cHttpLine), std::bind(&WebSocketsServer::handleHeader, this, client, &(client->cHttpLine))); +#endif + + return true; + break; + } + } + return false; +} + /** * * @param client WSclient_t * ptr to the client struct @@ -314,7 +370,9 @@ void WebSocketsServer::clientDisconnect(WSclient_t * client) { if(client->tcp) { if(client->tcp->connected()) { +#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) client->tcp->flush(); +#endif client->tcp->stop(); } delete client->tcp; @@ -328,6 +386,11 @@ void WebSocketsServer::clientDisconnect(WSclient_t * client) { client->cIsUpgrade = false; client->cIsWebsocket = false; + client->cWsRXsize = 0; +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + client->cHttpLine = ""; +#endif + client->status = WSC_NOT_CONNECTED; DEBUG_WEBSOCKETS("[WS-Server][%d] client disconnected.\n", client->num); @@ -367,71 +430,50 @@ bool WebSocketsServer::clientIsConnected(WSclient_t * client) { return false; } - +#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) /** * Handle incomming Connection Request */ void WebSocketsServer::handleNewClients(void) { - WSclient_t * client; + #if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) while(_server->hasClient()) { #endif bool ok = false; - // search free list entry for client - for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) { - client = &_clients[i]; - - // state is not connected or tcp connection is lost - if(!clientIsConnected(client)) { -#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) - // store new connection - client->tcp = new WEBSOCKETS_NETWORK_CLASS(_server->available()); -#else - client->tcp = new WEBSOCKETS_NETWORK_CLASS(_server->available()); -#endif - if(!client->tcp) { - DEBUG_WEBSOCKETS("[WS-Client] creating Network class failed!"); - return; - } #if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) - client->isSSL = false; - client->tcp->setNoDelay(true); -#endif - // set Timeout for readBytesUntil and readStringUntil - client->tcp->setTimeout(WEBSOCKETS_TCP_TIMEOUT); - client->status = WSC_HEADER; -#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) - IPAddress ip = client->tcp->remoteIP(); - DEBUG_WEBSOCKETS("[WS-Server][%d] new client from %d.%d.%d.%d\n", client->num, ip[0], ip[1], ip[2], ip[3]); + // store new connection + WEBSOCKETS_NETWORK_CLASS * tcpClient = new WEBSOCKETS_NETWORK_CLASS(_server->available()); #else - DEBUG_WEBSOCKETS("[WS-Server][%d] new client\n", client->num); + WEBSOCKETS_NETWORK_CLASS * tcpClient = new WEBSOCKETS_NETWORK_CLASS(_server->available()); #endif - ok = true; - break; - } + + if(!tcpClient) { + DEBUG_WEBSOCKETS("[WS-Client] creating Network class failed!"); + return; } + ok = newClient(tcpClient); + if(!ok) { // no free space to handle client - WEBSOCKETS_NETWORK_CLASS tcpClient = _server->available(); #if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) - IPAddress ip = client->tcp->remoteIP(); + IPAddress ip = tcpClient->remoteIP(); DEBUG_WEBSOCKETS("[WS-Server] no free space new client from %d.%d.%d.%d\n", ip[0], ip[1], ip[2], ip[3]); #else DEBUG_WEBSOCKETS("[WS-Server] no free space new client\n"); #endif - tcpClient.stop(); + tcpClient->stop(); } -#ifdef ESP8266 - delay(0); -#endif #if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) + delay(0); } #endif + } + /** * Handel incomming data from Client */ @@ -443,10 +485,13 @@ void WebSocketsServer::handleClientData(void) { if(clientIsConnected(client)) { int len = client->tcp->available(); if(len > 0) { - + //DEBUG_WEBSOCKETS("[WS-Server][%d][handleClientData] len: %d\n", client->num, len); switch(client->status) { case WSC_HEADER: - handleHeader(client); + { + String headerLine = client->tcp->readStringUntil('\n'); + handleHeader(client, &headerLine); + } break; case WSC_CONNECTED: WebSockets::handleWebsocket(client); @@ -457,31 +502,32 @@ void WebSocketsServer::handleClientData(void) { } } } -#ifdef ESP8266 +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) delay(0); #endif } } +#endif + /** * handle the WebSocket header reading * @param client WSclient_t * ptr to the client struct */ -void WebSocketsServer::handleHeader(WSclient_t * client) { +void WebSocketsServer::handleHeader(WSclient_t * client, String * headerLine) { - String headerLine = client->tcp->readStringUntil('\n'); - headerLine.trim(); // remove \r + headerLine->trim(); // remove \r - if(headerLine.length() > 0) { - DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] RX: %s\n", client->num, headerLine.c_str()); + if(headerLine->length() > 0) { + DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] RX: %s\n", client->num, headerLine->c_str()); // websocket request starts allways with GET see rfc6455 - if(headerLine.startsWith("GET ")) { + if(headerLine->startsWith("GET ")) { // cut URL out - client->cUrl = headerLine.substring(4, headerLine.indexOf(' ', 4)); - } else if(headerLine.indexOf(':')) { - String headerName = headerLine.substring(0, headerLine.indexOf(':')); - String headerValue = headerLine.substring(headerLine.indexOf(':') + 2); + client->cUrl = headerLine->substring(4, headerLine->indexOf(' ', 4)); + } else if(headerLine->indexOf(':')) { + String headerName = headerLine->substring(0, headerLine->indexOf(':')); + String headerValue = headerLine->substring(headerLine->indexOf(':') + 2); if(headerName.equalsIgnoreCase("Connection")) { if(headerValue.indexOf("Upgrade") >= 0) { @@ -502,9 +548,13 @@ void WebSocketsServer::handleHeader(WSclient_t * client) { client->cExtensions = headerValue; } } else { - DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Header error (%s)\n", headerLine.c_str()); + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Header error (%s)\n", headerLine->c_str()); } + (*headerLine) = ""; +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + client->tcp->readStringUntil('\n', &(client->cHttpLine), std::bind(&WebSocketsServer::handleHeader, this, client, &(client->cHttpLine))); +#endif } else { DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] Header read fin.\n", client->num); @@ -532,7 +582,7 @@ void WebSocketsServer::handleHeader(WSclient_t * client) { if(ok) { - DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] Websocket connection incomming.\n", client->num); + DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] Websocket connection incoming.\n", client->num); // generate Sec-WebSocket-Accept key String sKey = acceptKey(client->cKey); @@ -568,6 +618,8 @@ void WebSocketsServer::handleHeader(WSclient_t * client) { // header end client->tcp->write("\r\n"); + headerDone(client); + // send ping WebSockets::sendFrame(client, WSop_ping); diff --git a/src/WebSocketsServer.h b/src/WebSocketsServer.h index a4155da..f1840cc 100644 --- a/src/WebSocketsServer.h +++ b/src/WebSocketsServer.h @@ -68,7 +68,7 @@ public: void disconnect(void); void disconnect(uint8_t num); -#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) IPAddress remoteIP(uint8_t num); #endif @@ -83,15 +83,20 @@ protected: WebSocketServerEvent _cbEvent; + bool newClient(WEBSOCKETS_NETWORK_CLASS * TCPclient); + void messageRecived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length); void clientDisconnect(WSclient_t * client); bool clientIsConnected(WSclient_t * client); +#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) void handleNewClients(void); void handleClientData(void); +#endif + + void handleHeader(WSclient_t * client, String * headerLine); - void handleHeader(WSclient_t * client); /** * called if a non Websocket connection is comming in. diff --git a/tests/webSocket.html b/tests/webSocket.html index 1760886..1dc4f6b 100644 --- a/tests/webSocket.html +++ b/tests/webSocket.html @@ -8,10 +8,11 @@ connection.onopen = function () { connection.send('Message from Browser to ESP8266 yay its Working!! ' + new Date()); connection.send('ping'); - setInterval(function() { +/* setInterval(function() { connection.send('Time: ' + new Date()); }, 20); - +*/ +connection.send('Time: ' + new Date()); }; connection.onerror = function (error) { @@ -20,6 +21,7 @@ connection.onerror = function (error) { connection.onmessage = function (e) { console.log('Server: ', e.data); + connection.send('Time: ' + new Date()); }; function sendRGB() { diff --git a/tests/webSocketServer/index.js b/tests/webSocketServer/index.js index 07a3fe9..5fc3606 100644 --- a/tests/webSocketServer/index.js +++ b/tests/webSocketServer/index.js @@ -41,11 +41,11 @@ wsServer.on('request', function(request) { connection.on('message', function(message) { if (message.type === 'utf8') { console.log('Received Message: ' + message.utf8Data); - connection.sendUTF(message.utf8Data); + // connection.sendUTF(message.utf8Data); } else if (message.type === 'binary') { console.log('Received Binary Message of ' + message.binaryData.length + ' bytes'); - connection.sendBytes(message.binaryData); + //connection.sendBytes(message.binaryData); } });