Initial migration to platformIO
This commit is contained in:
parent
d9209b854c
commit
e711668f90
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
.pio
|
||||||
|
.vscode/.browse.c_cpp.db*
|
||||||
|
.vscode/c_cpp_properties.json
|
||||||
|
.vscode/launch.json
|
||||||
|
.vscode/ipch
|
39
include/README
Normal file
39
include/README
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
|
||||||
|
This directory is intended for project header files.
|
||||||
|
|
||||||
|
A header file is a file containing C declarations and macro definitions
|
||||||
|
to be shared between several project source files. You request the use of a
|
||||||
|
header file in your project source file (C, C++, etc) located in `src` folder
|
||||||
|
by including it, with the C preprocessing directive `#include'.
|
||||||
|
|
||||||
|
```src/main.c
|
||||||
|
|
||||||
|
#include "header.h"
|
||||||
|
|
||||||
|
int main (void)
|
||||||
|
{
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Including a header file produces the same results as copying the header file
|
||||||
|
into each source file that needs it. Such copying would be time-consuming
|
||||||
|
and error-prone. With a header file, the related declarations appear
|
||||||
|
in only one place. If they need to be changed, they can be changed in one
|
||||||
|
place, and programs that include the header file will automatically use the
|
||||||
|
new version when next recompiled. The header file eliminates the labor of
|
||||||
|
finding and changing all the copies as well as the risk that a failure to
|
||||||
|
find one copy will result in inconsistencies within a program.
|
||||||
|
|
||||||
|
In C, the usual convention is to give header files names that end with `.h'.
|
||||||
|
It is most portable to use only letters, digits, dashes, and underscores in
|
||||||
|
header file names, and at most one dot.
|
||||||
|
|
||||||
|
Read more about using header files in official GCC documentation:
|
||||||
|
|
||||||
|
* Include Syntax
|
||||||
|
* Include Operation
|
||||||
|
* Once-Only Headers
|
||||||
|
* Computed Includes
|
||||||
|
|
||||||
|
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
|
46
lib/README
Normal file
46
lib/README
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
|
||||||
|
This directory is intended for project specific (private) libraries.
|
||||||
|
PlatformIO will compile them to static libraries and link into executable file.
|
||||||
|
|
||||||
|
The source code of each library should be placed in a an own separate directory
|
||||||
|
("lib/your_library_name/[here are source files]").
|
||||||
|
|
||||||
|
For example, see a structure of the following two libraries `Foo` and `Bar`:
|
||||||
|
|
||||||
|
|--lib
|
||||||
|
| |
|
||||||
|
| |--Bar
|
||||||
|
| | |--docs
|
||||||
|
| | |--examples
|
||||||
|
| | |--src
|
||||||
|
| | |- Bar.c
|
||||||
|
| | |- Bar.h
|
||||||
|
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
|
||||||
|
| |
|
||||||
|
| |--Foo
|
||||||
|
| | |- Foo.c
|
||||||
|
| | |- Foo.h
|
||||||
|
| |
|
||||||
|
| |- README --> THIS FILE
|
||||||
|
|
|
||||||
|
|- platformio.ini
|
||||||
|
|--src
|
||||||
|
|- main.c
|
||||||
|
|
||||||
|
and a contents of `src/main.c`:
|
||||||
|
```
|
||||||
|
#include <Foo.h>
|
||||||
|
#include <Bar.h>
|
||||||
|
|
||||||
|
int main (void)
|
||||||
|
{
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
PlatformIO Library Dependency Finder will find automatically dependent
|
||||||
|
libraries scanning project source files.
|
||||||
|
|
||||||
|
More information about PlatformIO Library Dependency Finder
|
||||||
|
- https://docs.platformio.org/page/librarymanager/ldf.html
|
16
platformio.ini
Normal file
16
platformio.ini
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
; PlatformIO Project Configuration File
|
||||||
|
;
|
||||||
|
; Build options: build flags, source filter
|
||||||
|
; Upload options: custom upload port, speed and extra flags
|
||||||
|
; Library options: dependencies, extra library storages
|
||||||
|
; Advanced options: extra scripting
|
||||||
|
;
|
||||||
|
; Please visit documentation for the other options and examples
|
||||||
|
; https://docs.platformio.org/page/projectconf.html
|
||||||
|
|
||||||
|
[env:megaatmega2560]
|
||||||
|
platform = atmelavr
|
||||||
|
board = megaatmega2560
|
||||||
|
framework = arduino
|
||||||
|
lib_deps = arduino-libraries/Ethernet@^2.0.2
|
||||||
|
build_flags -DHOST_NAME=DepartureSign
|
@ -1,80 +0,0 @@
|
|||||||
#include "ProtocolDL.hpp"
|
|
||||||
|
|
||||||
ProtocolDL::ProtocolDL(Image& img):
|
|
||||||
image(&img)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void ProtocolDL::newByte(uint8_t data)
|
|
||||||
{
|
|
||||||
switch(cnt)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
complete = false;
|
|
||||||
source.width = (data << 8);
|
|
||||||
cnt = 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 1:
|
|
||||||
source.width |= data;
|
|
||||||
cnt = 2;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 2:
|
|
||||||
source.height = (data << 8);
|
|
||||||
cnt = 3;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 3:
|
|
||||||
source.height |= data;
|
|
||||||
cnt = 4;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 4:
|
|
||||||
source.delay = (data << 8);
|
|
||||||
cnt = 5;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 5:
|
|
||||||
source.delay |= data;
|
|
||||||
image->set_size(source.width, source.height);
|
|
||||||
source.x = 0;
|
|
||||||
source.y = 0;
|
|
||||||
cnt = 6;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
for (int shift = 7; shift >= 0; shift--) {
|
|
||||||
byte pixel = (data >> shift) & 1;
|
|
||||||
image->set_pixel(source.x, source.y, pixel);
|
|
||||||
|
|
||||||
if(source.x == (source.width - 1) && source.y == (source.height - 1))
|
|
||||||
{
|
|
||||||
//this was the last pixel
|
|
||||||
complete = true;
|
|
||||||
cnt = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
source.x++;
|
|
||||||
if (source.x >= source.width) {
|
|
||||||
source.x = 0;
|
|
||||||
source.y++;
|
|
||||||
if (source.y >= source.height) {
|
|
||||||
source.y = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ProtocolDL::isComplete()
|
|
||||||
{
|
|
||||||
return complete;
|
|
||||||
}
|
|
@ -1,35 +0,0 @@
|
|||||||
#ifndef PROTOCOL_DL_HPP
|
|
||||||
#define PROTOCOL_DL_HPP
|
|
||||||
|
|
||||||
#include "image.hpp"
|
|
||||||
|
|
||||||
class ProtocolDL
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ProtocolDL(Image& img);
|
|
||||||
void newByte(uint8_t data);
|
|
||||||
bool isComplete();
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
|
|
||||||
struct source_t {
|
|
||||||
// width and height of current frame
|
|
||||||
unsigned int width;
|
|
||||||
unsigned int height;
|
|
||||||
|
|
||||||
// delay until next frame is shown
|
|
||||||
unsigned int delay;
|
|
||||||
|
|
||||||
// position of current pixel
|
|
||||||
int x;
|
|
||||||
int y;
|
|
||||||
};
|
|
||||||
|
|
||||||
source_t source;
|
|
||||||
uint16_t cnt = 0;
|
|
||||||
bool complete = true;
|
|
||||||
Image* image;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,592 +0,0 @@
|
|||||||
/**
|
|
||||||
* @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"
|
|
||||||
|
|
||||||
#define LEGACY_SHA1
|
|
||||||
|
|
||||||
#ifdef ESP8266
|
|
||||||
#include <core_esp8266_features.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
#ifdef CORE_HAS_LIBB64
|
|
||||||
#include <libb64/cencode.h>
|
|
||||||
#else
|
|
||||||
#include "libb64/cencode_inc.h"
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef ESP8266
|
|
||||||
#include <Hash.h>
|
|
||||||
#else
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef LEGACY_SHA1
|
|
||||||
#include "sha1/sha1.h"
|
|
||||||
#else
|
|
||||||
extern "C" {
|
|
||||||
#include "libsha1/libsha1.h"
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define WEBSOCKETS_MAX_HEADER_SIZE (14)
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param client WSclient_t * ptr to the client struct
|
|
||||||
* @param code uint16_t see RFC
|
|
||||||
* @param reason
|
|
||||||
* @param reasonLen
|
|
||||||
*/
|
|
||||||
void WebSockets::clientDisconnect(WSclient_t * client, uint16_t code, char * reason, size_t reasonLen) {
|
|
||||||
DEBUG_WEBSOCKETS("[WS][");
|
|
||||||
DEBUG_WEBSOCKETS(client->num);
|
|
||||||
DEBUG_WEBSOCKETS("][handleWebsocket] clientDisconnect code: ");
|
|
||||||
DEBUG_WEBSOCKETS(code);
|
|
||||||
DEBUG_WEBSOCKETS("\n");
|
|
||||||
if(client->status == WSC_CONNECTED && code) {
|
|
||||||
if(reason) {
|
|
||||||
sendFrame(client, WSop_close, (uint8_t *) reason, reasonLen);
|
|
||||||
} else {
|
|
||||||
uint8_t buffer[2];
|
|
||||||
buffer[0] = ((code >> 8) & 0xFF);
|
|
||||||
buffer[1] = (code & 0xFF);
|
|
||||||
sendFrame(client, WSop_close, &buffer[0], 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
clientDisconnect(client);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param client WSclient_t * ptr to the client struct
|
|
||||||
* @param opcode WSopcode_t
|
|
||||||
* @param payload uint8_t *
|
|
||||||
* @param length size_t
|
|
||||||
* @param mask bool add dummy mask to the frame (needed for web browser)
|
|
||||||
* @param fin bool can be used to send data in more then one frame (set fin on the last frame)
|
|
||||||
* @param headerToPayload bool set true if the payload has reserved 14 Byte at the beginning to dynamically add the Header (payload neet to be in RAM!)
|
|
||||||
*/
|
|
||||||
bool WebSockets::sendFrame(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool mask, bool fin, bool headerToPayload) {
|
|
||||||
|
|
||||||
if(client->tcp && !client->tcp->connected()) {
|
|
||||||
DEBUG_WEBSOCKETS("[WS][");
|
|
||||||
DEBUG_WEBSOCKETS(client->num);
|
|
||||||
DEBUG_WEBSOCKETS("][sendFrame] not Connected!?\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(client->status != WSC_CONNECTED) {
|
|
||||||
DEBUG_WEBSOCKETS("[WS][");
|
|
||||||
DEBUG_WEBSOCKETS(client->num);
|
|
||||||
DEBUG_WEBSOCKETS("][sendFrame] not in WSC_CONNECTED state!?\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEBUG_WEBSOCKETS("[WS][");
|
|
||||||
DEBUG_WEBSOCKETS(client->num);
|
|
||||||
DEBUG_WEBSOCKETS("][sendFrame] ------- send massage frame -------\n");
|
|
||||||
|
|
||||||
DEBUG_WEBSOCKETS("[WS][");
|
|
||||||
DEBUG_WEBSOCKETS(client->num);
|
|
||||||
DEBUG_WEBSOCKETS("][sendFrame] fin: ");
|
|
||||||
DEBUG_WEBSOCKETS(fin);
|
|
||||||
DEBUG_WEBSOCKETS(" opCode: ");
|
|
||||||
DEBUG_WEBSOCKETS(opcode);
|
|
||||||
DEBUG_WEBSOCKETS(" mask: ");
|
|
||||||
DEBUG_WEBSOCKETS(mask);
|
|
||||||
DEBUG_WEBSOCKETS(" length: ");
|
|
||||||
DEBUG_WEBSOCKETS(length);
|
|
||||||
DEBUG_WEBSOCKETS(" headerToPayload: ");
|
|
||||||
DEBUG_WEBSOCKETS(headerToPayload);
|
|
||||||
DEBUG_WEBSOCKETS("\n");
|
|
||||||
|
|
||||||
if(opcode == WSop_text) {
|
|
||||||
DEBUG_WEBSOCKETS("[WS][");
|
|
||||||
DEBUG_WEBSOCKETS(client->num);
|
|
||||||
DEBUG_WEBSOCKETS("][sendFrame] text: ");
|
|
||||||
DEBUG_WEBSOCKETS((char*) (payload + (headerToPayload ? 14 : 0)));
|
|
||||||
DEBUG_WEBSOCKETS("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t maskKey[4] = { 0x00, 0x00, 0x00, 0x00 };
|
|
||||||
uint8_t buffer[WEBSOCKETS_MAX_HEADER_SIZE] = { 0 };
|
|
||||||
|
|
||||||
uint8_t headerSize;
|
|
||||||
uint8_t * headerPtr;
|
|
||||||
uint8_t * payloadPtr = payload;
|
|
||||||
bool useInternBuffer = false;
|
|
||||||
bool ret = true;
|
|
||||||
|
|
||||||
// calculate header Size
|
|
||||||
if(length < 126) {
|
|
||||||
headerSize = 2;
|
|
||||||
} else if(length < 0xFFFF) {
|
|
||||||
headerSize = 4;
|
|
||||||
} else {
|
|
||||||
headerSize = 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(mask) {
|
|
||||||
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)
|
|
||||||
if(!headerToPayload && ((length > 0) && (length < 1400)) && (ESP.getFreeHeap() > 6000)) {
|
|
||||||
DEBUG_WEBSOCKETS("[WS][");
|
|
||||||
DEBUG_WEBSOCKETS(client->num);
|
|
||||||
DEBUG_WEBSOCKETS("][sendFrame] pack to one TCP package...\n");
|
|
||||||
|
|
||||||
uint8_t * dataPtr = (uint8_t *) malloc(length + WEBSOCKETS_MAX_HEADER_SIZE);
|
|
||||||
if(dataPtr) {
|
|
||||||
memcpy((dataPtr + WEBSOCKETS_MAX_HEADER_SIZE), payload, length);
|
|
||||||
headerToPayload = true;
|
|
||||||
useInternBuffer = true;
|
|
||||||
payloadPtr = dataPtr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// set Header Pointer
|
|
||||||
if(headerToPayload) {
|
|
||||||
// calculate offset in payload
|
|
||||||
headerPtr = (payloadPtr + (WEBSOCKETS_MAX_HEADER_SIZE - headerSize));
|
|
||||||
} else {
|
|
||||||
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(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) {
|
|
||||||
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]);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
*headerPtr = maskKey[0]; headerPtr++;
|
|
||||||
*headerPtr = maskKey[1]; headerPtr++;
|
|
||||||
*headerPtr = maskKey[2]; headerPtr++;
|
|
||||||
*headerPtr = maskKey[3]; headerPtr++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef NODEBUG_WEBSOCKETS
|
|
||||||
unsigned long start = micros();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if(headerToPayload) {
|
|
||||||
// header has be added to payload
|
|
||||||
// payload is forced to reserved 14 Byte but we may not need all based on the length and mask settings
|
|
||||||
// offset in payload is calculatetd 14 - headerSize
|
|
||||||
if (client->tcp->write(&payloadPtr[(WEBSOCKETS_MAX_HEADER_SIZE - headerSize)], (length + headerSize)) != (length + headerSize)) {
|
|
||||||
ret = false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// send header
|
|
||||||
if (client->tcp->write(&buffer[0], headerSize) != headerSize) {
|
|
||||||
ret = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(payloadPtr && length > 0) {
|
|
||||||
// send payload
|
|
||||||
if(client->tcp->write(&payloadPtr[0], length) != length) {
|
|
||||||
ret = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DEBUG_WEBSOCKETS("[WS][");
|
|
||||||
DEBUG_WEBSOCKETS(client->num);
|
|
||||||
DEBUG_WEBSOCKETS("][sendFrame] sending Frame Done (");
|
|
||||||
DEBUG_WEBSOCKETS((micros() - start));
|
|
||||||
DEBUG_WEBSOCKETS("us).\n");
|
|
||||||
|
|
||||||
#ifdef WEBSOCKETS_USE_BIG_MEM
|
|
||||||
if(useInternBuffer && payloadPtr) {
|
|
||||||
free(payloadPtr);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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][");
|
|
||||||
DEBUG_WEBSOCKETS(client->num);
|
|
||||||
DEBUG_WEBSOCKETS("][handleWebsocket] ------- read massage frame -------\n");
|
|
||||||
|
|
||||||
if(!readWait(client, buffer, 2)) {
|
|
||||||
//timeout
|
|
||||||
clientDisconnect(client, 1002);
|
|
||||||
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, 1002);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
payloadLen = buffer[0] << 8 | buffer[1];
|
|
||||||
} else if(payloadLen == 127) {
|
|
||||||
// read 64bit integer as length
|
|
||||||
if(!readWait(client, buffer, 8)) {
|
|
||||||
//timeout
|
|
||||||
clientDisconnect(client, 1002);
|
|
||||||
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][");
|
|
||||||
DEBUG_WEBSOCKETS(client->num);
|
|
||||||
DEBUG_WEBSOCKETS("][handleWebsocket] fin: ");
|
|
||||||
DEBUG_WEBSOCKETS(fin);
|
|
||||||
DEBUG_WEBSOCKETS(" rsv1: ");
|
|
||||||
DEBUG_WEBSOCKETS(rsv1);
|
|
||||||
DEBUG_WEBSOCKETS(" rsv2: ");
|
|
||||||
DEBUG_WEBSOCKETS(rsv2);
|
|
||||||
DEBUG_WEBSOCKETS(" rsv3: ");
|
|
||||||
DEBUG_WEBSOCKETS(rsv3);
|
|
||||||
DEBUG_WEBSOCKETS(" opCode: ");
|
|
||||||
DEBUG_WEBSOCKETS(opCode);
|
|
||||||
DEBUG_WEBSOCKETS("\n");
|
|
||||||
|
|
||||||
DEBUG_WEBSOCKETS("[WS][");
|
|
||||||
DEBUG_WEBSOCKETS(client->num);
|
|
||||||
DEBUG_WEBSOCKETS("][handleWebsocket] mask: ");
|
|
||||||
DEBUG_WEBSOCKETS(mask);
|
|
||||||
DEBUG_WEBSOCKETS(" payloadLen: ");
|
|
||||||
DEBUG_WEBSOCKETS(payloadLen);
|
|
||||||
DEBUG_WEBSOCKETS("\n");
|
|
||||||
|
|
||||||
if(payloadLen > WEBSOCKETS_MAX_DATA_SIZE) {
|
|
||||||
DEBUG_WEBSOCKETS("[WS][");
|
|
||||||
DEBUG_WEBSOCKETS(client->num);
|
|
||||||
DEBUG_WEBSOCKETS("][handleWebsocket] payload to big! (");
|
|
||||||
DEBUG_WEBSOCKETS(payloadLen);
|
|
||||||
DEBUG_WEBSOCKETS(")\n");
|
|
||||||
clientDisconnect(client, 1009);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(mask) {
|
|
||||||
if(!readWait(client, maskKey, 4)) {
|
|
||||||
//timeout
|
|
||||||
clientDisconnect(client, 1002);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(payloadLen > 0) {
|
|
||||||
// if text data we need one more
|
|
||||||
payload = (uint8_t *) malloc(payloadLen + 1);
|
|
||||||
|
|
||||||
if(!payload) {
|
|
||||||
DEBUG_WEBSOCKETS("[WS][");
|
|
||||||
DEBUG_WEBSOCKETS(client->num);
|
|
||||||
DEBUG_WEBSOCKETS("][handleWebsocket] to less memory to handle payload ");
|
|
||||||
DEBUG_WEBSOCKETS(payloadLen);
|
|
||||||
DEBUG_WEBSOCKETS("!\n");
|
|
||||||
clientDisconnect(client, 1011);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!readWait(client, payload, payloadLen)) {
|
|
||||||
DEBUG_WEBSOCKETS("[WS][");
|
|
||||||
DEBUG_WEBSOCKETS(client->num);
|
|
||||||
DEBUG_WEBSOCKETS("][handleWebsocket] missing data!\n");
|
|
||||||
free(payload);
|
|
||||||
clientDisconnect(client, 1002);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
payload[payloadLen] = 0x00;
|
|
||||||
|
|
||||||
if(mask) {
|
|
||||||
//decode XOR
|
|
||||||
for(size_t i = 0; i < payloadLen; i++) {
|
|
||||||
payload[i] = (payload[i] ^ maskKey[i % 4]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch(opCode) {
|
|
||||||
case WSop_text:
|
|
||||||
DEBUG_WEBSOCKETS("[WS][");
|
|
||||||
DEBUG_WEBSOCKETS(client->num);
|
|
||||||
DEBUG_WEBSOCKETS("][handleWebsocket] text: ");
|
|
||||||
DEBUG_WEBSOCKETS((char*) payload);
|
|
||||||
DEBUG_WEBSOCKETS("\n");
|
|
||||||
// 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][");
|
|
||||||
DEBUG_WEBSOCKETS(client->num);
|
|
||||||
DEBUG_WEBSOCKETS("][handleWebsocket] get pong (");
|
|
||||||
DEBUG_WEBSOCKETS((char*) payload);
|
|
||||||
DEBUG_WEBSOCKETS(")\n");
|
|
||||||
break;
|
|
||||||
case WSop_close:
|
|
||||||
{
|
|
||||||
uint16_t reasonCode = 1000;
|
|
||||||
if(payloadLen >= 2) {
|
|
||||||
reasonCode = payload[0] << 8 | payload[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
DEBUG_WEBSOCKETS("[WS][");
|
|
||||||
DEBUG_WEBSOCKETS(client->num);
|
|
||||||
DEBUG_WEBSOCKETS("][handleWebsocket] get ask for close. Code: ");
|
|
||||||
DEBUG_WEBSOCKETS(reasonCode);
|
|
||||||
if(payloadLen > 2) {
|
|
||||||
DEBUG_WEBSOCKETS(" (");
|
|
||||||
DEBUG_WEBSOCKETS((char*) (payload+2));
|
|
||||||
DEBUG_WEBSOCKETS(")\n");
|
|
||||||
} else {
|
|
||||||
DEBUG_WEBSOCKETS("\n");
|
|
||||||
}
|
|
||||||
clientDisconnect(client, 1000);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case WSop_continuation:
|
|
||||||
// continuation is not supported
|
|
||||||
clientDisconnect(client, 1003);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
clientDisconnect(client, 1002);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(payload) {
|
|
||||||
free(payload);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* generate the key for Sec-WebSocket-Accept
|
|
||||||
* @param clientKey String
|
|
||||||
* @return String Accept Key
|
|
||||||
*/
|
|
||||||
String WebSockets::acceptKey(String clientKey) {
|
|
||||||
uint8_t sha1HashBin[20] = { 0 };
|
|
||||||
#ifdef ESP8266
|
|
||||||
sha1(clientKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11", &sha1HashBin[0]);
|
|
||||||
#else
|
|
||||||
clientKey += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
|
||||||
#ifdef LEGACY_SHA1
|
|
||||||
// Miha Nahtigal
|
|
||||||
char cbuff[clientKey.length()+1];
|
|
||||||
clientKey.toCharArray(cbuff, clientKey.length()+1);//Converts String into character array
|
|
||||||
Sha1.init();
|
|
||||||
Sha1.print(cbuff);
|
|
||||||
strcpy(sha1HashBin, Sha1.result());
|
|
||||||
Serial.println(cbuff);
|
|
||||||
//old:uint8_t *sha1HashBin = Sha1.result();
|
|
||||||
#else
|
|
||||||
SHA1_CTX ctx;
|
|
||||||
SHA1Init(&ctx);
|
|
||||||
SHA1Update(&ctx, (const unsigned char*)clientKey.c_str(), clientKey.length());
|
|
||||||
SHA1Final(&sha1HashBin[0], &ctx);
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
String key = base64_encode(sha1HashBin, 20);
|
|
||||||
key.trim();
|
|
||||||
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* base64_encode
|
|
||||||
* @param data uint8_t *
|
|
||||||
* @param length size_t
|
|
||||||
* @return base64 encoded String
|
|
||||||
*/
|
|
||||||
String WebSockets::base64_encode(uint8_t * data, size_t length) {
|
|
||||||
size_t size = ((length*1.6f)+1);
|
|
||||||
char * buffer = (char *) malloc(size);
|
|
||||||
if(buffer) {
|
|
||||||
base64_encodestate _state;
|
|
||||||
base64_init_encodestate(&_state);
|
|
||||||
int len = base64_encode_block((const char *) &data[0], length, &buffer[0], &_state);
|
|
||||||
len = base64_encode_blockend((buffer + len), &_state);
|
|
||||||
|
|
||||||
String base64 = String(buffer);
|
|
||||||
free(buffer);
|
|
||||||
return base64;
|
|
||||||
}
|
|
||||||
return String("-FAIL-");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
|
|
||||||
while(n > 0) {
|
|
||||||
if(!client->tcp) {
|
|
||||||
DEBUG_WEBSOCKETS("[readWait] tcp is null!\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!client->tcp->connected()) {
|
|
||||||
DEBUG_WEBSOCKETS("[readWait] not connected!\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if((millis() - t) > WEBSOCKETS_TCP_TIMEOUT) {
|
|
||||||
DEBUG_WEBSOCKETS("[readWait] receive TIMEOUT!\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!client->tcp->available()) {
|
|
||||||
#ifdef ESP8266
|
|
||||||
delay(0);
|
|
||||||
#endif
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
//DEBUG_WEBSOCKETS("Receive ");
|
|
||||||
//DEBUG_WEBSOCKETS(len);
|
|
||||||
//DEBUG_WEBSOCKETS("left ");
|
|
||||||
//DEBUG_WEBSOCKETS(n);
|
|
||||||
//DEBUG_WEBSOCKETS("!\n");
|
|
||||||
#ifdef ESP8266
|
|
||||||
delay(0);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
159
src/WebSockets.h
159
src/WebSockets.h
@ -1,159 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file WebSockets.h
|
|
||||||
* @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
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef WEBSOCKETS_H_
|
|
||||||
#define WEBSOCKETS_H_
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
|
|
||||||
//#define DEBUG_WEBSOCKETS(...) Serial.print( __VA_ARGS__ )
|
|
||||||
|
|
||||||
#ifndef DEBUG_WEBSOCKETS
|
|
||||||
#define DEBUG_WEBSOCKETS(...)
|
|
||||||
#define NODEBUG_WEBSOCKETS
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef ESP8266
|
|
||||||
#define WEBSOCKETS_MAX_DATA_SIZE (15*1024)
|
|
||||||
#define WEBSOCKETS_USE_BIG_MEM
|
|
||||||
#else
|
|
||||||
//atmega328p has only 2KB ram!
|
|
||||||
#define WEBSOCKETS_MAX_DATA_SIZE (2048)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define WEBSOCKETS_TCP_TIMEOUT (1500)
|
|
||||||
|
|
||||||
#define NETWORK_ESP8266 (1)
|
|
||||||
#define NETWORK_W5100 (2)
|
|
||||||
#define NETWORK_ENC28J60 (3)
|
|
||||||
|
|
||||||
|
|
||||||
// select Network type based
|
|
||||||
#ifdef ESP8266
|
|
||||||
#define WEBSOCKETS_NETWORK_TYPE NETWORK_ESP8266
|
|
||||||
#else
|
|
||||||
#define WEBSOCKETS_NETWORK_TYPE NETWORK_W5100
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
|
||||||
|
|
||||||
#ifndef ESP8266
|
|
||||||
#error "network type ESP8266 only possible on the ESP mcu!"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <ESP8266WiFi.h>
|
|
||||||
#define WEBSOCKETS_NETWORK_CLASS WiFiClient
|
|
||||||
#define WEBSOCKETS_NETWORK_SERVER_CLASS WiFiServer
|
|
||||||
|
|
||||||
#elif (WEBSOCKETS_NETWORK_TYPE == NETWORK_W5100)
|
|
||||||
|
|
||||||
#include <Ethernet.h>
|
|
||||||
#include <SPI.h>
|
|
||||||
#define WEBSOCKETS_NETWORK_CLASS EthernetClient
|
|
||||||
#define WEBSOCKETS_NETWORK_SERVER_CLASS EthernetServer
|
|
||||||
|
|
||||||
#elif (WEBSOCKETS_NETWORK_TYPE == NETWORK_ENC28J60)
|
|
||||||
|
|
||||||
#include <UIPEthernet.h>
|
|
||||||
#define WEBSOCKETS_NETWORK_CLASS UIPClient
|
|
||||||
#define WEBSOCKETS_NETWORK_SERVER_CLASS UIPServer
|
|
||||||
|
|
||||||
#else
|
|
||||||
#error "no network type selected!"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
WSC_NOT_CONNECTED,
|
|
||||||
WSC_HEADER,
|
|
||||||
WSC_CONNECTED
|
|
||||||
} WSclientsStatus_t;
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
WStype_ERROR,
|
|
||||||
WStype_DISCONNECTED,
|
|
||||||
WStype_CONNECTED,
|
|
||||||
WStype_TEXT,
|
|
||||||
WStype_BIN
|
|
||||||
} WStype_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
|
|
||||||
|
|
||||||
WSclientsStatus_t status;
|
|
||||||
|
|
||||||
WEBSOCKETS_NETWORK_CLASS * tcp;
|
|
||||||
|
|
||||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
|
||||||
bool isSSL; ///< run in ssl mode
|
|
||||||
WiFiClientSecure * ssl;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
String cUrl; ///< http url
|
|
||||||
uint16_t cCode; ///< http code
|
|
||||||
|
|
||||||
bool cIsUpgrade; ///< Connection == Upgrade
|
|
||||||
bool cIsWebsocket; ///< Upgrade == websocket
|
|
||||||
|
|
||||||
String cKey; ///< client Sec-WebSocket-Key
|
|
||||||
String cAccept; ///< client Sec-WebSocket-Accept
|
|
||||||
String cProtocol; ///< client Sec-WebSocket-Protocol
|
|
||||||
String cExtensions; ///< client Sec-WebSocket-Extensions
|
|
||||||
uint16_t cVersion; ///< client Sec-WebSocket-Version
|
|
||||||
|
|
||||||
} WSclient_t;
|
|
||||||
|
|
||||||
class WebSockets {
|
|
||||||
protected:
|
|
||||||
virtual void clientDisconnect(WSclient_t * client);
|
|
||||||
virtual bool clientIsConnected(WSclient_t * client);
|
|
||||||
|
|
||||||
virtual void messageRecived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length);
|
|
||||||
|
|
||||||
void clientDisconnect(WSclient_t * client, uint16_t code, char * reason = NULL, size_t reasonLen = 0);
|
|
||||||
bool 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 handleWebsocket(WSclient_t * client);
|
|
||||||
|
|
||||||
bool readWait(WSclient_t * client, uint8_t *out, size_t n);
|
|
||||||
|
|
||||||
String acceptKey(String clientKey);
|
|
||||||
String base64_encode(uint8_t * data, size_t length);
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* WEBSOCKETS_H_ */
|
|
@ -1,506 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file WebSocketsClient.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"
|
|
||||||
#include "WebSocketsClient.h"
|
|
||||||
|
|
||||||
WebSocketsClient::WebSocketsClient() {
|
|
||||||
_cbEvent = NULL;
|
|
||||||
_client.num = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
WebSocketsClient::~WebSocketsClient() {
|
|
||||||
disconnect();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* calles to init the Websockets server
|
|
||||||
*/
|
|
||||||
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;
|
|
||||||
_client.tcp = NULL;
|
|
||||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
|
||||||
_client.isSSL = false;
|
|
||||||
_client.ssl = NULL;
|
|
||||||
#endif
|
|
||||||
_client.cUrl = url;
|
|
||||||
_client.cCode = 0;
|
|
||||||
_client.cIsUpgrade = false;
|
|
||||||
_client.cIsWebsocket = true;
|
|
||||||
_client.cKey = "";
|
|
||||||
_client.cAccept = "";
|
|
||||||
_client.cProtocol = "";
|
|
||||||
_client.cExtensions = "";
|
|
||||||
_client.cVersion = 0;
|
|
||||||
|
|
||||||
#ifdef ESP8266
|
|
||||||
randomSeed(RANDOM_REG32);
|
|
||||||
#else
|
|
||||||
// todo find better seed
|
|
||||||
randomSeed(millis());
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void WebSocketsClient::begin(String host, uint16_t port, String url) {
|
|
||||||
begin(host.c_str(), port, url.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
|
||||||
void WebSocketsClient::beginSSL(const char *host, uint16_t port, const char * url, const char * fingerprint) {
|
|
||||||
begin(host, port, url);
|
|
||||||
_client.isSSL = true;
|
|
||||||
_fingerprint = fingerprint;
|
|
||||||
}
|
|
||||||
|
|
||||||
void WebSocketsClient::beginSSL(String host, uint16_t port, String url, String fingerprint) {
|
|
||||||
beginSSL(host.c_str(), port, url.c_str(), fingerprint.c_str());
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
|
||||||
* called in arduino loop
|
|
||||||
*/
|
|
||||||
void WebSocketsClient::loop(void) {
|
|
||||||
if(!clientIsConnected(&_client)) {
|
|
||||||
|
|
||||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
|
||||||
if(_client.isSSL) {
|
|
||||||
DEBUG_WEBSOCKETS("[WS-Client] connect wss...\n");
|
|
||||||
if(_client.ssl) {
|
|
||||||
delete _client.ssl;
|
|
||||||
_client.ssl = NULL;
|
|
||||||
_client.tcp = NULL;
|
|
||||||
}
|
|
||||||
_client.ssl = new WiFiClientSecure();
|
|
||||||
_client.tcp = _client.ssl;
|
|
||||||
} else {
|
|
||||||
DEBUG_WEBSOCKETS("[WS-Client] connect ws...\n");
|
|
||||||
if(_client.tcp) {
|
|
||||||
delete _client.tcp;
|
|
||||||
_client.tcp = NULL;
|
|
||||||
}
|
|
||||||
_client.tcp = new WiFiClient();
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
_client.tcp = new WEBSOCKETS_NETWORK_CLASS();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if(!_client.tcp) {
|
|
||||||
DEBUG_WEBSOCKETS("[WS-Client] creating Network class failed!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(_client.tcp->connect(_host.c_str(), _port)) {
|
|
||||||
DEBUG_WEBSOCKETS("[WS-Client] connected to %s:%u.\n");
|
|
||||||
//DEBUG_WEBSOCKETS(_host.c_str());
|
|
||||||
//DEBUG_WEBSOCKETS(_port);
|
|
||||||
|
|
||||||
_client.status = WSC_HEADER;
|
|
||||||
|
|
||||||
// set Timeout for readBytesUntil and readStringUntil
|
|
||||||
_client.tcp->setTimeout(WEBSOCKETS_TCP_TIMEOUT);
|
|
||||||
|
|
||||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
|
||||||
_client.tcp->setNoDelay(true);
|
|
||||||
|
|
||||||
if(_client.isSSL && _fingerprint.length()) {
|
|
||||||
if(!_client.ssl->verify(_fingerprint.c_str(), _host.c_str())) {
|
|
||||||
DEBUG_WEBSOCKETS("[WS-Client] certificate mismatch\n");
|
|
||||||
WebSockets::clientDisconnect(&_client, 1000);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// send Header to Server
|
|
||||||
sendHeader(&_client);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
DEBUG_WEBSOCKETS("[WS-Client] connection to %s:%u Faild\n");
|
|
||||||
///EBUG_WEBSOCKETS(_host.c_str());
|
|
||||||
//DEBUG_WEBSOCKETS(_port);
|
|
||||||
delay(10); //some litle delay to not flood the server
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
handleClientData();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* set callback function
|
|
||||||
* @param cbEvent WebSocketServerEvent
|
|
||||||
*/
|
|
||||||
void WebSocketsClient::onEvent(WebSocketClientEvent cbEvent) {
|
|
||||||
_cbEvent = cbEvent;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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)
|
|
||||||
*/
|
|
||||||
void WebSocketsClient::sendTXT(uint8_t * payload, size_t length, bool headerToPayload) {
|
|
||||||
if(length == 0) {
|
|
||||||
length = strlen((const char *) payload);
|
|
||||||
}
|
|
||||||
if(clientIsConnected(&_client)) {
|
|
||||||
sendFrame(&_client, WSop_text, payload, length, true, true, headerToPayload);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void WebSocketsClient::sendTXT(const uint8_t * payload, size_t length) {
|
|
||||||
sendTXT((uint8_t *) payload, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
void WebSocketsClient::sendTXT(char * payload, size_t length, bool headerToPayload) {
|
|
||||||
sendTXT((uint8_t *) payload, length, headerToPayload);
|
|
||||||
}
|
|
||||||
|
|
||||||
void WebSocketsClient::sendTXT(const char * payload, size_t length) {
|
|
||||||
sendTXT((uint8_t *) payload, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
void WebSocketsClient::sendTXT(String payload) {
|
|
||||||
sendTXT((uint8_t *) payload.c_str(), payload.length());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* send binary 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)
|
|
||||||
*/
|
|
||||||
void WebSocketsClient::sendBIN(uint8_t * payload, size_t length, bool headerToPayload) {
|
|
||||||
if(clientIsConnected(&_client)) {
|
|
||||||
sendFrame(&_client, WSop_binary, payload, length, true, true, headerToPayload);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void WebSocketsClient::sendBIN(const uint8_t * payload, size_t length) {
|
|
||||||
sendBIN((uint8_t *) payload, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* disconnect one client
|
|
||||||
* @param num uint8_t client id
|
|
||||||
*/
|
|
||||||
void WebSocketsClient::disconnect(void) {
|
|
||||||
if(clientIsConnected(&_client)) {
|
|
||||||
WebSockets::clientDisconnect(&_client, 1000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//#################################################################################
|
|
||||||
//#################################################################################
|
|
||||||
//#################################################################################
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param client WSclient_t * ptr to the client struct
|
|
||||||
* @param opcode WSopcode_t
|
|
||||||
* @param payload uint8_t *
|
|
||||||
* @param lenght size_t
|
|
||||||
*/
|
|
||||||
void WebSocketsClient::messageRecived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t lenght) {
|
|
||||||
WStype_t type = WStype_ERROR;
|
|
||||||
|
|
||||||
switch(opcode) {
|
|
||||||
case WSop_text:
|
|
||||||
type = WStype_TEXT;
|
|
||||||
break;
|
|
||||||
case WSop_binary:
|
|
||||||
type = WStype_BIN;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
runCbEvent(type, payload, lenght);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Disconnect an client
|
|
||||||
* @param client WSclient_t * ptr to the client struct
|
|
||||||
*/
|
|
||||||
void WebSocketsClient::clientDisconnect(WSclient_t * client) {
|
|
||||||
|
|
||||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
|
||||||
if(client->isSSL && client->ssl) {
|
|
||||||
if(client->ssl->connected()) {
|
|
||||||
client->ssl->flush();
|
|
||||||
client->ssl->stop();
|
|
||||||
}
|
|
||||||
delete client->ssl;
|
|
||||||
client->ssl = NULL;
|
|
||||||
client->tcp = NULL;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if(client->tcp) {
|
|
||||||
if(client->tcp->connected()) {
|
|
||||||
client->tcp->flush();
|
|
||||||
client->tcp->stop();
|
|
||||||
}
|
|
||||||
delete client->tcp;
|
|
||||||
client->tcp = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
client->cCode = 0;
|
|
||||||
client->cKey = "";
|
|
||||||
client->cAccept = "";
|
|
||||||
client->cProtocol = "";
|
|
||||||
client->cVersion = 0;
|
|
||||||
client->cIsUpgrade = false;
|
|
||||||
client->cIsWebsocket = false;
|
|
||||||
|
|
||||||
client->status = WSC_NOT_CONNECTED;
|
|
||||||
|
|
||||||
DEBUG_WEBSOCKETS("[WS-Client] client disconnected.\n");
|
|
||||||
|
|
||||||
runCbEvent(WStype_DISCONNECTED, NULL, 0);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* get client state
|
|
||||||
* @param client WSclient_t * ptr to the client struct
|
|
||||||
* @return true = conneted
|
|
||||||
*/
|
|
||||||
bool WebSocketsClient::clientIsConnected(WSclient_t * client) {
|
|
||||||
|
|
||||||
if(!client->tcp) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(client->tcp->connected()) {
|
|
||||||
if(client->status != WSC_NOT_CONNECTED) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// client lost
|
|
||||||
if(client->status != WSC_NOT_CONNECTED) {
|
|
||||||
DEBUG_WEBSOCKETS("[WS-Client] connection lost.\n");
|
|
||||||
// do cleanup
|
|
||||||
clientDisconnect(client);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(client->tcp) {
|
|
||||||
// do cleanup
|
|
||||||
clientDisconnect(client);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handel incomming data from Client
|
|
||||||
*/
|
|
||||||
void WebSocketsClient::handleClientData(void) {
|
|
||||||
int len = _client.tcp->available();
|
|
||||||
if(len > 0) {
|
|
||||||
switch(_client.status) {
|
|
||||||
case WSC_HEADER:
|
|
||||||
handleHeader(&_client);
|
|
||||||
break;
|
|
||||||
case WSC_CONNECTED:
|
|
||||||
WebSockets::handleWebsocket(&_client);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
WebSockets::clientDisconnect(&_client, 1002);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#ifdef ESP8266
|
|
||||||
delay(0);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* send the WebSocket header to Server
|
|
||||||
* @param client WSclient_t * ptr to the client struct
|
|
||||||
*/
|
|
||||||
void WebSocketsClient::sendHeader(WSclient_t * client) {
|
|
||||||
|
|
||||||
DEBUG_WEBSOCKETS("[WS-Client][sendHeader] sending header...\n");
|
|
||||||
|
|
||||||
uint8_t randomKey[16] = { 0 };
|
|
||||||
|
|
||||||
for(uint8_t i = 0; i < sizeof(randomKey); i++) {
|
|
||||||
randomKey[i] = random(0xFF);
|
|
||||||
}
|
|
||||||
|
|
||||||
client->cKey = base64_encode(&randomKey[0], 16);
|
|
||||||
|
|
||||||
#ifndef NODEBUG_WEBSOCKETS
|
|
||||||
unsigned long start = micros();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
String handshake = "GET " + client->cUrl + " HTTP/1.1\r\n"
|
|
||||||
"Host: " + _host + "\r\n"
|
|
||||||
"Upgrade: websocket\r\n"
|
|
||||||
"Connection: Upgrade\r\n"
|
|
||||||
"User-Agent: arduino-WebSocket-Client\r\n"
|
|
||||||
"Sec-WebSocket-Version: 13\r\n"
|
|
||||||
"Sec-WebSocket-Protocol: arduino\r\n"
|
|
||||||
"Sec-WebSocket-Key: " + client->cKey + "\r\n";
|
|
||||||
|
|
||||||
if(client->cExtensions.length() > 0) {
|
|
||||||
handshake += "Sec-WebSocket-Extensions: " + client->cExtensions + "\r\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
handshake += "\r\n";
|
|
||||||
|
|
||||||
client->tcp->write(handshake.c_str(), handshake.length());
|
|
||||||
|
|
||||||
DEBUG_WEBSOCKETS("[WS-Client][sendHeader] sending header... Done (%uus).\n");
|
|
||||||
//DEBUG_WEBSOCKETS((micros() - start));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* handle the WebSocket header reading
|
|
||||||
* @param client WSclient_t * ptr to the client struct
|
|
||||||
*/
|
|
||||||
void WebSocketsClient::handleHeader(WSclient_t * client) {
|
|
||||||
|
|
||||||
String headerLine = client->tcp->readStringUntil('\n');
|
|
||||||
headerLine.trim(); // remove \r
|
|
||||||
|
|
||||||
if(headerLine.length() > 0) {
|
|
||||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] RX: %s\n");
|
|
||||||
DEBUG_WEBSOCKETS(headerLine.c_str());
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
if(headerName.equalsIgnoreCase("Connection")) {
|
|
||||||
if(headerValue.indexOf("Upgrade") >= 0) {
|
|
||||||
client->cIsUpgrade = true;
|
|
||||||
}
|
|
||||||
} else if(headerName.equalsIgnoreCase("Upgrade")) {
|
|
||||||
if(headerValue.equalsIgnoreCase("websocket")) {
|
|
||||||
client->cIsWebsocket = true;
|
|
||||||
}
|
|
||||||
} else if(headerName.equalsIgnoreCase("Sec-WebSocket-Accept")) {
|
|
||||||
client->cAccept = headerValue;
|
|
||||||
client->cAccept.trim(); // see rfc6455
|
|
||||||
} else if(headerName.equalsIgnoreCase("Sec-WebSocket-Protocol")) {
|
|
||||||
client->cProtocol = headerValue;
|
|
||||||
} else if(headerName.equalsIgnoreCase("Sec-WebSocket-Extensions")) {
|
|
||||||
client->cExtensions = headerValue;
|
|
||||||
} else if(headerName.equalsIgnoreCase("Sec-WebSocket-Version")) {
|
|
||||||
client->cVersion = headerValue.toInt();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Header error (%s)\n");
|
|
||||||
DEBUG_WEBSOCKETS(headerLine.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Header read fin.\n");
|
|
||||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Client settings:\n");
|
|
||||||
|
|
||||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cURL: %s\n");
|
|
||||||
//DEBUG_WEBSOCKETS(client->cUrl.c_str());
|
|
||||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cKey: %s\n");
|
|
||||||
//DEBUG_WEBSOCKETS(client->cKey.c_str());
|
|
||||||
|
|
||||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Server header:\n");
|
|
||||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cCode: %d\n");
|
|
||||||
//DEBUG_WEBSOCKETS(client->cCode);
|
|
||||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cIsUpgrade: %d\n");
|
|
||||||
//DEBUG_WEBSOCKETS(client->cIsUpgrade);
|
|
||||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cIsWebsocket: %d\n");
|
|
||||||
//DEBUG_WEBSOCKETS(client->cIsWebsocket);
|
|
||||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cAccept: %s\n");
|
|
||||||
//DEBUG_WEBSOCKETS(client->cAccept.c_str());
|
|
||||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cProtocol: %s\n");
|
|
||||||
//DEBUG_WEBSOCKETS(client->cProtocol.c_str());
|
|
||||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cExtensions: %s\n");
|
|
||||||
//DEBUG_WEBSOCKETS(client->cExtensions.c_str());
|
|
||||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cVersion: %d\n");
|
|
||||||
//DEBUG_WEBSOCKETS(client->cVersion);
|
|
||||||
|
|
||||||
bool ok = (client->cIsUpgrade && client->cIsWebsocket);
|
|
||||||
|
|
||||||
if(ok) {
|
|
||||||
switch(client->cCode) {
|
|
||||||
case 101: ///< Switching Protocols
|
|
||||||
|
|
||||||
break;
|
|
||||||
case 403: ///< Forbidden
|
|
||||||
// todo handle login
|
|
||||||
default: ///< Server dont unterstand requrst
|
|
||||||
ok = false;
|
|
||||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] serverCode is not 101 (%d)\n");
|
|
||||||
//DEBUG_WEBSOCKETS(client->cCode);
|
|
||||||
clientDisconnect(client);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(ok) {
|
|
||||||
if(client->cAccept.length() == 0) {
|
|
||||||
ok = false;
|
|
||||||
} else {
|
|
||||||
// generate Sec-WebSocket-Accept key for check
|
|
||||||
String sKey = acceptKey(client->cKey);
|
|
||||||
if(sKey != client->cAccept) {
|
|
||||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Sec-WebSocket-Accept is wrong\n");
|
|
||||||
ok = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(ok) {
|
|
||||||
|
|
||||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Websocket connection init done.\n");
|
|
||||||
|
|
||||||
client->status = WSC_CONNECTED;
|
|
||||||
|
|
||||||
runCbEvent(WStype_CONNECTED, (uint8_t *) client->cUrl.c_str(), client->cUrl.length());
|
|
||||||
|
|
||||||
} else {
|
|
||||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] no Websocket connection close.\n");
|
|
||||||
client->tcp->write("This is a webSocket client!");
|
|
||||||
clientDisconnect(client);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,98 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file WebSocketsClient.h
|
|
||||||
* @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
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef WEBSOCKETSCLIENT_H_
|
|
||||||
#define WEBSOCKETSCLIENT_H_
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
#include "WebSockets.h"
|
|
||||||
|
|
||||||
class WebSocketsClient: private WebSockets {
|
|
||||||
public:
|
|
||||||
|
|
||||||
typedef void (*WebSocketClientEvent)(WStype_t type, uint8_t * payload, size_t length);
|
|
||||||
|
|
||||||
WebSocketsClient(void);
|
|
||||||
~WebSocketsClient(void);
|
|
||||||
|
|
||||||
void begin(const char *host, uint16_t port, const char * url = "/");
|
|
||||||
void begin(String host, uint16_t port, String url = "/");
|
|
||||||
|
|
||||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
|
||||||
void beginSSL(const char *host, uint16_t port, const char * url = "/", const char * = "");
|
|
||||||
void beginSSL(String host, uint16_t port, String url = "/", String fingerprint = "");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void loop(void);
|
|
||||||
|
|
||||||
void onEvent(WebSocketClientEvent cbEvent);
|
|
||||||
|
|
||||||
void sendTXT(uint8_t * payload, size_t length = 0, bool headerToPayload = false);
|
|
||||||
void sendTXT(const uint8_t * payload, size_t length = 0);
|
|
||||||
void sendTXT(char * payload, size_t length = 0, bool headerToPayload = false);
|
|
||||||
void sendTXT(const char * payload, size_t length = 0);
|
|
||||||
void sendTXT(String payload);
|
|
||||||
|
|
||||||
void sendBIN(uint8_t * payload, size_t length, bool headerToPayload = false);
|
|
||||||
void sendBIN(const uint8_t * payload, size_t length);
|
|
||||||
|
|
||||||
void disconnect(void);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
String _host;
|
|
||||||
uint16_t _port;
|
|
||||||
|
|
||||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
|
||||||
String _fingerprint;
|
|
||||||
#endif
|
|
||||||
WSclient_t _client;
|
|
||||||
|
|
||||||
WebSocketClientEvent _cbEvent;
|
|
||||||
|
|
||||||
void messageRecived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length);
|
|
||||||
|
|
||||||
void clientDisconnect(WSclient_t * client);
|
|
||||||
bool clientIsConnected(WSclient_t * client);
|
|
||||||
|
|
||||||
void handleNewClients(void);
|
|
||||||
void handleClientData(void);
|
|
||||||
|
|
||||||
void sendHeader(WSclient_t * client);
|
|
||||||
void handleHeader(WSclient_t * client);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* called for sending a Event to the app
|
|
||||||
* @param type WStype_t
|
|
||||||
* @param payload uint8_t *
|
|
||||||
* @param length size_t
|
|
||||||
*/
|
|
||||||
virtual void runCbEvent(WStype_t type, uint8_t * payload, size_t length) {
|
|
||||||
if(_cbEvent) {
|
|
||||||
_cbEvent(type, payload, length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* WEBSOCKETSCLIENT_H_ */
|
|
@ -1,714 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file WebSocketsServer.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"
|
|
||||||
#include "WebSocketsServer.h"
|
|
||||||
|
|
||||||
WebSocketsServer::WebSocketsServer(uint16_t port) {
|
|
||||||
_port = port;
|
|
||||||
_server = new WEBSOCKETS_NETWORK_SERVER_CLASS(port);
|
|
||||||
|
|
||||||
_cbEvent = NULL;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
WebSocketsServer::~WebSocketsServer() {
|
|
||||||
// disconnect all clients
|
|
||||||
disconnect();
|
|
||||||
|
|
||||||
// TODO how to close server?
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* calles to init the Websockets server
|
|
||||||
*/
|
|
||||||
void WebSocketsServer::begin(void) {
|
|
||||||
WSclient_t * client;
|
|
||||||
|
|
||||||
// init client storage
|
|
||||||
for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
|
|
||||||
client = &_clients[i];
|
|
||||||
|
|
||||||
client->num = i;
|
|
||||||
client->status = WSC_NOT_CONNECTED;
|
|
||||||
client->tcp = NULL;
|
|
||||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
|
||||||
client->isSSL = false;
|
|
||||||
client->ssl = NULL;
|
|
||||||
#endif
|
|
||||||
client->cUrl = "";
|
|
||||||
client->cCode = 0;
|
|
||||||
client->cKey = "";
|
|
||||||
client->cProtocol = "";
|
|
||||||
client->cVersion = 0;
|
|
||||||
client->cIsUpgrade = false;
|
|
||||||
client->cIsWebsocket = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef ESP8266
|
|
||||||
randomSeed(RANDOM_REG32);
|
|
||||||
#else
|
|
||||||
// TODO find better seed
|
|
||||||
randomSeed(millis());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
_server->begin();
|
|
||||||
|
|
||||||
DEBUG_WEBSOCKETS("[WS-Server] Server Started.\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* called in arduino loop
|
|
||||||
*/
|
|
||||||
void WebSocketsServer::loop(void) {
|
|
||||||
handleNewClients();
|
|
||||||
handleClientData();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* set callback function
|
|
||||||
* @param cbEvent WebSocketServerEvent
|
|
||||||
*/
|
|
||||||
void WebSocketsServer::onEvent(WebSocketServerEvent cbEvent) {
|
|
||||||
_cbEvent = cbEvent;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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)
|
|
||||||
*/
|
|
||||||
void WebSocketsServer::sendTXT(uint8_t num, uint8_t * payload, size_t length, bool headerToPayload) {
|
|
||||||
if(num >= WEBSOCKETS_SERVER_CLIENT_MAX) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if(length == 0) {
|
|
||||||
length = strlen((const char *) payload);
|
|
||||||
}
|
|
||||||
WSclient_t * client = &_clients[num];
|
|
||||||
if(clientIsConnected(client)) {
|
|
||||||
sendFrame(client, WSop_text, payload, length, false, true, headerToPayload);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void WebSocketsServer::sendTXT(uint8_t num, const uint8_t * payload, size_t length) {
|
|
||||||
sendTXT(num, (uint8_t *) payload, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
void WebSocketsServer::sendTXT(uint8_t num, char * payload, size_t length, bool headerToPayload) {
|
|
||||||
sendTXT(num, (uint8_t *) payload, length, headerToPayload);
|
|
||||||
}
|
|
||||||
|
|
||||||
void WebSocketsServer::sendTXT(uint8_t num, const char * payload, size_t length) {
|
|
||||||
sendTXT(num, (uint8_t *) payload, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
void WebSocketsServer::sendTXT(uint8_t num, String payload) {
|
|
||||||
sendTXT(num, (uint8_t *) payload.c_str(), payload.length());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* send text data to client all
|
|
||||||
* @param payload uint8_t *
|
|
||||||
* @param length size_t
|
|
||||||
* @param headerToPayload bool (see sendFrame for more details)
|
|
||||||
*/
|
|
||||||
void WebSocketsServer::broadcastTXT(uint8_t * payload, size_t length, bool headerToPayload) {
|
|
||||||
WSclient_t * client;
|
|
||||||
if(length == 0) {
|
|
||||||
length = strlen((const char *) payload);
|
|
||||||
}
|
|
||||||
|
|
||||||
for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
|
|
||||||
client = &_clients[i];
|
|
||||||
if(clientIsConnected(client)) {
|
|
||||||
sendFrame(client, WSop_text, payload, length, false, true, headerToPayload);
|
|
||||||
}
|
|
||||||
#ifdef ESP8266
|
|
||||||
delay(0);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void WebSocketsServer::broadcastTXT(const uint8_t * payload, size_t length) {
|
|
||||||
broadcastTXT((uint8_t *) payload, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
void WebSocketsServer::broadcastTXT(char * payload, size_t length, bool headerToPayload) {
|
|
||||||
broadcastTXT((uint8_t *) payload, length, headerToPayload);
|
|
||||||
}
|
|
||||||
|
|
||||||
void WebSocketsServer::broadcastTXT(const char * payload, size_t length) {
|
|
||||||
broadcastTXT((uint8_t *) payload, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
void WebSocketsServer::broadcastTXT(String payload) {
|
|
||||||
broadcastTXT((uint8_t *) payload.c_str(), payload.length());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* send binary 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)
|
|
||||||
*/
|
|
||||||
void WebSocketsServer::sendBIN(uint8_t num, uint8_t * payload, size_t length, bool headerToPayload) {
|
|
||||||
if(num >= WEBSOCKETS_SERVER_CLIENT_MAX) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
WSclient_t * client = &_clients[num];
|
|
||||||
if(clientIsConnected(client)) {
|
|
||||||
sendFrame(client, WSop_binary, payload, length, false, true, headerToPayload);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void WebSocketsServer::sendBIN(uint8_t num, const uint8_t * payload, size_t length) {
|
|
||||||
sendBIN(num, (uint8_t *) payload, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* send binary data to client all
|
|
||||||
* @param payload uint8_t *
|
|
||||||
* @param length size_t
|
|
||||||
* @param headerToPayload bool (see sendFrame for more details)
|
|
||||||
*/
|
|
||||||
void WebSocketsServer::broadcastBIN(uint8_t * payload, size_t length, bool headerToPayload) {
|
|
||||||
WSclient_t * client;
|
|
||||||
for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
|
|
||||||
client = &_clients[i];
|
|
||||||
if(clientIsConnected(client)) {
|
|
||||||
sendFrame(client, WSop_binary, payload, length, false, true, headerToPayload);
|
|
||||||
}
|
|
||||||
#ifdef ESP8266
|
|
||||||
delay(0);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void WebSocketsServer::broadcastBIN(const uint8_t * payload, size_t length) {
|
|
||||||
broadcastBIN((uint8_t *) payload, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* sends a WS ping to Client
|
|
||||||
* @param num uint8_t client id
|
|
||||||
* @param payload uint8_t *
|
|
||||||
* @param length size_t
|
|
||||||
* @return true if ping is send out
|
|
||||||
*/
|
|
||||||
bool WebSocketsServer::sendPing(uint8_t num, uint8_t * payload, size_t length) {
|
|
||||||
if(num >= WEBSOCKETS_SERVER_CLIENT_MAX) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
WSclient_t * client;
|
|
||||||
client = &_clients[num];
|
|
||||||
if(clientIsConnected(client)) {
|
|
||||||
return sendFrame(client, WSop_ping, payload, length);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool WebSocketsServer::sendPing(uint8_t num, String & payload) {
|
|
||||||
return sendPing(num, (uint8_t *) payload.c_str(), payload.length());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* sends a WS ping to all Client
|
|
||||||
* @param payload uint8_t *
|
|
||||||
* @param length size_t
|
|
||||||
* @return true if ping is send out
|
|
||||||
*/
|
|
||||||
bool WebSocketsServer::broadcastPing(uint8_t * payload, size_t length) {
|
|
||||||
WSclient_t * client;
|
|
||||||
bool ret = true;
|
|
||||||
for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
|
|
||||||
client = &_clients[i];
|
|
||||||
if(clientIsConnected(client)) {
|
|
||||||
if(!sendFrame(client, WSop_ping, payload, length)) {
|
|
||||||
ret = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
|
||||||
delay(0);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool WebSocketsServer::broadcastPing(String & payload) {
|
|
||||||
return broadcastPing((uint8_t *) payload.c_str(), payload.length());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool WebSocketsServer::clientConnected(uint8_t num) {
|
|
||||||
WSclient_t * client;
|
|
||||||
client = &_clients[num];
|
|
||||||
return clientIsConnected(client);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* disconnect all clients
|
|
||||||
*/
|
|
||||||
void WebSocketsServer::disconnect(void) {
|
|
||||||
WSclient_t * client;
|
|
||||||
for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
|
|
||||||
client = &_clients[i];
|
|
||||||
if(clientIsConnected(client)) {
|
|
||||||
WebSockets::clientDisconnect(client, 1000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* disconnect one client
|
|
||||||
* @param num uint8_t client id
|
|
||||||
*/
|
|
||||||
void WebSocketsServer::disconnect(uint8_t num) {
|
|
||||||
if(num >= WEBSOCKETS_SERVER_CLIENT_MAX) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
WSclient_t * client = &_clients[num];
|
|
||||||
if(clientIsConnected(client)) {
|
|
||||||
WebSockets::clientDisconnect(client, 1000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
|
||||||
/**
|
|
||||||
* get an IP for a client
|
|
||||||
* @param num uint8_t client id
|
|
||||||
* @return IPAddress
|
|
||||||
*/
|
|
||||||
IPAddress WebSocketsServer::remoteIP(uint8_t num) {
|
|
||||||
if(num < WEBSOCKETS_SERVER_CLIENT_MAX) {
|
|
||||||
WSclient_t * client = &_clients[num];
|
|
||||||
if(clientIsConnected(client)) {
|
|
||||||
return client->tcp->remoteIP();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return IPAddress();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
//#################################################################################
|
|
||||||
//#################################################################################
|
|
||||||
//#################################################################################
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param client WSclient_t * ptr to the client struct
|
|
||||||
* @param opcode WSopcode_t
|
|
||||||
* @param payload uint8_t *
|
|
||||||
* @param lenght size_t
|
|
||||||
*/
|
|
||||||
void WebSocketsServer::messageRecived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t lenght) {
|
|
||||||
WStype_t type = WStype_ERROR;
|
|
||||||
|
|
||||||
switch(opcode) {
|
|
||||||
case WSop_text:
|
|
||||||
type = WStype_TEXT;
|
|
||||||
break;
|
|
||||||
case WSop_binary:
|
|
||||||
type = WStype_BIN;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
runCbEvent(client->num, type, payload, lenght);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Disconnect an client
|
|
||||||
* @param client WSclient_t * ptr to the client struct
|
|
||||||
*/
|
|
||||||
void WebSocketsServer::clientDisconnect(WSclient_t * client) {
|
|
||||||
|
|
||||||
|
|
||||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
|
||||||
if(client->isSSL && client->ssl) {
|
|
||||||
if(client->ssl->connected()) {
|
|
||||||
client->ssl->flush();
|
|
||||||
client->ssl->stop();
|
|
||||||
}
|
|
||||||
delete client->ssl;
|
|
||||||
client->ssl = NULL;
|
|
||||||
client->tcp = NULL;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if(client->tcp) {
|
|
||||||
if(client->tcp->connected()) {
|
|
||||||
client->tcp->flush();
|
|
||||||
client->tcp->stop();
|
|
||||||
}
|
|
||||||
delete client->tcp;
|
|
||||||
client->tcp = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
client->cUrl = "";
|
|
||||||
client->cKey = "";
|
|
||||||
client->cProtocol = "";
|
|
||||||
client->cVersion = 0;
|
|
||||||
client->cIsUpgrade = false;
|
|
||||||
client->cIsWebsocket = false;
|
|
||||||
|
|
||||||
client->status = WSC_NOT_CONNECTED;
|
|
||||||
|
|
||||||
DEBUG_WEBSOCKETS("[WS-Server][");DEBUG_WEBSOCKETS(client->num);DEBUG_WEBSOCKETS("]");
|
|
||||||
DEBUG_WEBSOCKETS(" client disconnected.\n");
|
|
||||||
|
|
||||||
|
|
||||||
runCbEvent(client->num, WStype_DISCONNECTED, NULL, 0);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* get client state
|
|
||||||
* @param client WSclient_t * ptr to the client struct
|
|
||||||
* @return true = conneted
|
|
||||||
*/
|
|
||||||
bool WebSocketsServer::clientIsConnected(WSclient_t * client) {
|
|
||||||
|
|
||||||
if(!client->tcp) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(client->tcp->connected()) {
|
|
||||||
if(client->status != WSC_NOT_CONNECTED) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// client lost
|
|
||||||
DEBUG_WEBSOCKETS(client->status);
|
|
||||||
if(client->status != WSC_NOT_CONNECTED) {
|
|
||||||
DEBUG_WEBSOCKETS("[WS-Server][");DEBUG_WEBSOCKETS(client->num);DEBUG_WEBSOCKETS("]");
|
|
||||||
DEBUG_WEBSOCKETS(" client connection lost.\n");
|
|
||||||
|
|
||||||
// do cleanup
|
|
||||||
clientDisconnect(client);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(client->tcp) {
|
|
||||||
// do cleanup
|
|
||||||
clientDisconnect(client);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266)
|
|
||||||
bool WebSocketsServer::clientExists(const WEBSOCKETS_NETWORK_CLASS &c)
|
|
||||||
{
|
|
||||||
WSclient_t * client;
|
|
||||||
for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
|
|
||||||
client = &_clients[i];
|
|
||||||
if (clientIsConnected(client) && (*client->tcp == c))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
|
||||||
WEBSOCKETS_NETWORK_CLASS newClient = _server->available();
|
|
||||||
if (newClient && !clientExists(newClient)) {
|
|
||||||
client->tcp = new WEBSOCKETS_NETWORK_CLASS(newClient);
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
//if (!client->tcp->connected() || !client->tcp->available()) {
|
|
||||||
// DEBUG_WEBSOCKETS("[WS-Client] Not Connected!\n");
|
|
||||||
// return;
|
|
||||||
//}
|
|
||||||
#endif
|
|
||||||
if(!client->tcp) {
|
|
||||||
DEBUG_WEBSOCKETS("[WS-Server] 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][");DEBUG_WEBSOCKETS(client->num);DEBUG_WEBSOCKETS("]");
|
|
||||||
DEBUG_WEBSOCKETS(" new client from %d.%d.%d.%d\n");
|
|
||||||
|
|
||||||
//DEBUG_WEBSOCKETS(ip[0]);
|
|
||||||
//DEBUG_WEBSOCKETS(ip[1]);
|
|
||||||
//DEBUG_WEBSOCKETS(ip[2]);
|
|
||||||
//DEBUG_WEBSOCKETS(ip[3]);
|
|
||||||
#else
|
|
||||||
DEBUG_WEBSOCKETS("[WS-Server][");DEBUG_WEBSOCKETS(client->num);DEBUG_WEBSOCKETS("]");
|
|
||||||
DEBUG_WEBSOCKETS(" new client\n");
|
|
||||||
#endif
|
|
||||||
ok = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
DEBUG_WEBSOCKETS("[WS-Server] no free space new client from %d.%d.%d.%d\n");
|
|
||||||
//DEBUG_WEBSOCKETS(ip[0]);
|
|
||||||
//DEBUG_WEBSOCKETS(ip[1]);
|
|
||||||
//DEBUG_WEBSOCKETS(ip[2]);
|
|
||||||
//DEBUG_WEBSOCKETS(ip[3]);
|
|
||||||
#else
|
|
||||||
DEBUG_WEBSOCKETS("[WS-Server] no free space new client\n");
|
|
||||||
#endif
|
|
||||||
tcpClient.stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef ESP8266
|
|
||||||
delay(0);
|
|
||||||
#endif
|
|
||||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handel incomming data from Client
|
|
||||||
*/
|
|
||||||
void WebSocketsServer::handleClientData(void) {
|
|
||||||
|
|
||||||
WSclient_t * client;
|
|
||||||
for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
|
|
||||||
client = &_clients[i];
|
|
||||||
if(clientIsConnected(client)) {
|
|
||||||
int len = client->tcp->available();
|
|
||||||
if(len > 0) {
|
|
||||||
switch(client->status) {
|
|
||||||
case WSC_HEADER:
|
|
||||||
handleHeader(client);
|
|
||||||
break;
|
|
||||||
case WSC_CONNECTED:
|
|
||||||
WebSockets::handleWebsocket(client);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
WebSockets::clientDisconnect(client, 1002);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#ifdef ESP8266
|
|
||||||
delay(0);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* handle the WebSocket header reading
|
|
||||||
* @param client WSclient_t * ptr to the client struct
|
|
||||||
*/
|
|
||||||
void WebSocketsServer::handleHeader(WSclient_t * client) {
|
|
||||||
|
|
||||||
String headerLine = client->tcp->readStringUntil('\n');
|
|
||||||
headerLine.trim(); // remove \r
|
|
||||||
|
|
||||||
if(headerLine.length() > 0) {
|
|
||||||
DEBUG_WEBSOCKETS("[WS-Server][");DEBUG_WEBSOCKETS(client->num);DEBUG_WEBSOCKETS("]");
|
|
||||||
DEBUG_WEBSOCKETS("[handleHeader] RX: ");
|
|
||||||
DEBUG_WEBSOCKETS(headerLine.c_str());
|
|
||||||
DEBUG_WEBSOCKETS("\n");
|
|
||||||
|
|
||||||
// websocket request starts allways with GET see rfc6455
|
|
||||||
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);
|
|
||||||
//Serial.print(headerName); Serial.print(":"); Serial.println(headerValue);
|
|
||||||
|
|
||||||
if(headerName.equalsIgnoreCase("Connection")) {
|
|
||||||
if(headerValue.indexOf("Upgrade") >= 0) {
|
|
||||||
client->cIsUpgrade = true;
|
|
||||||
}
|
|
||||||
} else if(headerName.equalsIgnoreCase("Upgrade")) {
|
|
||||||
if(headerValue.equalsIgnoreCase("websocket")) {
|
|
||||||
client->cIsWebsocket = true;
|
|
||||||
}
|
|
||||||
} else if(headerName.equalsIgnoreCase("Sec-WebSocket-Version")) {
|
|
||||||
client->cVersion = headerValue.toInt();
|
|
||||||
} else if(headerName.equalsIgnoreCase("Sec-WebSocket-Key")) {
|
|
||||||
client->cKey = headerValue;
|
|
||||||
client->cKey.trim(); // see rfc6455
|
|
||||||
} else if(headerName.equalsIgnoreCase("Sec-WebSocket-Protocol")) {
|
|
||||||
client->cProtocol = headerValue;
|
|
||||||
} else if(headerName.equalsIgnoreCase("Sec-WebSocket-Extensions")) {
|
|
||||||
client->cExtensions = headerValue;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Serial.print("[WS-Client][handleHeader] Header error (%s)\n");
|
|
||||||
Serial.print(headerLine.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
DEBUG_WEBSOCKETS("[WS-Server][");DEBUG_WEBSOCKETS(client->num);DEBUG_WEBSOCKETS("]");
|
|
||||||
DEBUG_WEBSOCKETS("[handleHeader] Header read fin.\n");
|
|
||||||
|
|
||||||
DEBUG_WEBSOCKETS("[WS-Server][");DEBUG_WEBSOCKETS(client->num);DEBUG_WEBSOCKETS("]");
|
|
||||||
DEBUG_WEBSOCKETS("[handleHeader] - cURL: ");
|
|
||||||
DEBUG_WEBSOCKETS(client->cUrl.c_str());
|
|
||||||
DEBUG_WEBSOCKETS("\n");
|
|
||||||
|
|
||||||
DEBUG_WEBSOCKETS("[WS-Server][");DEBUG_WEBSOCKETS(client->num);DEBUG_WEBSOCKETS("]");
|
|
||||||
DEBUG_WEBSOCKETS("[handleHeader] - cIsUpgrade: ");
|
|
||||||
DEBUG_WEBSOCKETS(client->cIsUpgrade);
|
|
||||||
DEBUG_WEBSOCKETS("\n");
|
|
||||||
|
|
||||||
DEBUG_WEBSOCKETS("[WS-Server][");DEBUG_WEBSOCKETS(client->num);DEBUG_WEBSOCKETS("]");
|
|
||||||
DEBUG_WEBSOCKETS("[handleHeader] - cIsWebsocket: ");
|
|
||||||
DEBUG_WEBSOCKETS(client->cIsWebsocket);
|
|
||||||
DEBUG_WEBSOCKETS("\n");
|
|
||||||
|
|
||||||
DEBUG_WEBSOCKETS("[WS-Server][");DEBUG_WEBSOCKETS(client->num);DEBUG_WEBSOCKETS("]");
|
|
||||||
DEBUG_WEBSOCKETS("[handleHeader] - cKey: ");
|
|
||||||
DEBUG_WEBSOCKETS(client->cKey.c_str());
|
|
||||||
DEBUG_WEBSOCKETS("\n");
|
|
||||||
|
|
||||||
DEBUG_WEBSOCKETS("[WS-Server][");DEBUG_WEBSOCKETS(client->num);DEBUG_WEBSOCKETS("]");
|
|
||||||
DEBUG_WEBSOCKETS("[handleHeader] - cProtocol: ");
|
|
||||||
DEBUG_WEBSOCKETS(client->cProtocol.c_str());
|
|
||||||
DEBUG_WEBSOCKETS("\n");
|
|
||||||
|
|
||||||
DEBUG_WEBSOCKETS("[WS-Server][");DEBUG_WEBSOCKETS(client->num);DEBUG_WEBSOCKETS("]");
|
|
||||||
DEBUG_WEBSOCKETS("[handleHeader] - cExtensions: ");
|
|
||||||
DEBUG_WEBSOCKETS(client->cExtensions.c_str());
|
|
||||||
DEBUG_WEBSOCKETS("\n");
|
|
||||||
|
|
||||||
DEBUG_WEBSOCKETS("[WS-Server][");DEBUG_WEBSOCKETS(client->num);DEBUG_WEBSOCKETS("]");
|
|
||||||
DEBUG_WEBSOCKETS("[handleHeader] - cVersion: ");
|
|
||||||
DEBUG_WEBSOCKETS(client->cVersion);
|
|
||||||
DEBUG_WEBSOCKETS("\n\n");
|
|
||||||
|
|
||||||
bool ok = (client->cIsUpgrade && client->cIsWebsocket);
|
|
||||||
Serial.println("here0");
|
|
||||||
|
|
||||||
if (!client->cIsUpgrade) {
|
|
||||||
DEBUG_WEBSOCKETS("[WS-Server][");DEBUG_WEBSOCKETS(client->num);DEBUG_WEBSOCKETS("]");
|
|
||||||
DEBUG_WEBSOCKETS("[handleHeader] Error! Not an Upgrade\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!client->cIsWebsocket) {
|
|
||||||
DEBUG_WEBSOCKETS("[WS-Server][");DEBUG_WEBSOCKETS(client->num);DEBUG_WEBSOCKETS("]");
|
|
||||||
DEBUG_WEBSOCKETS("[handleHeader] Error! Not a Websocket\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if(ok) {
|
|
||||||
Serial.println("here1");
|
|
||||||
DEBUG_WEBSOCKETS("[WS-Server][");DEBUG_WEBSOCKETS(client->num);DEBUG_WEBSOCKETS("]");
|
|
||||||
DEBUG_WEBSOCKETS("[handleHeader] First ok test success.\n");
|
|
||||||
|
|
||||||
if(client->cUrl.length() == 0) {
|
|
||||||
Serial.println("here2");
|
|
||||||
ok = false;
|
|
||||||
}
|
|
||||||
if(client->cKey.length() == 0) {
|
|
||||||
Serial.println("here3");
|
|
||||||
ok = false;
|
|
||||||
}
|
|
||||||
if(client->cVersion != 13) {
|
|
||||||
Serial.println("here4");
|
|
||||||
ok = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(ok) {
|
|
||||||
Serial.println("here5");
|
|
||||||
DEBUG_WEBSOCKETS("[WS-Server][");DEBUG_WEBSOCKETS(client->num);DEBUG_WEBSOCKETS("]");
|
|
||||||
DEBUG_WEBSOCKETS("[handleHeader] Websocket connection incomming.\n");
|
|
||||||
|
|
||||||
|
|
||||||
// generate Sec-WebSocket-Accept key
|
|
||||||
String sKey = acceptKey(client->cKey);
|
|
||||||
|
|
||||||
DEBUG_WEBSOCKETS("[WS-Server][");DEBUG_WEBSOCKETS(client->num);DEBUG_WEBSOCKETS("]");
|
|
||||||
DEBUG_WEBSOCKETS("[handleHeader] - sKey: ");
|
|
||||||
DEBUG_WEBSOCKETS(sKey.c_str());
|
|
||||||
DEBUG_WEBSOCKETS("\n");
|
|
||||||
|
|
||||||
client->status = WSC_CONNECTED;
|
|
||||||
|
|
||||||
client->tcp->write("HTTP/1.1 101 Switching Protocols\r\n"
|
|
||||||
"Server: arduino-WebSocketsServer\r\n"
|
|
||||||
"Upgrade: websocket\r\n"
|
|
||||||
"Connection: Upgrade\r\n"
|
|
||||||
"Sec-WebSocket-Version: 13\r\n"
|
|
||||||
"Sec-WebSocket-Accept: ");
|
|
||||||
client->tcp->write(sKey.c_str(), sKey.length());
|
|
||||||
client->tcp->write("\r\n");
|
|
||||||
|
|
||||||
if(client->cProtocol.length() > 0) {
|
|
||||||
// TODO add api to set Protocol of Server
|
|
||||||
client->tcp->write("Sec-WebSocket-Protocol: arduino\r\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
// header end
|
|
||||||
client->tcp->write("\r\n");
|
|
||||||
|
|
||||||
// send ping
|
|
||||||
WebSockets::sendFrame(client, WSop_ping);
|
|
||||||
|
|
||||||
runCbEvent(client->num, WStype_CONNECTED, (uint8_t *) client->cUrl.c_str(), client->cUrl.length());
|
|
||||||
|
|
||||||
} else {
|
|
||||||
handleNonWebsocketConnection(client);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,143 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file WebSocketsServer.h
|
|
||||||
* @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
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef WEBSOCKETSSERVER_H_
|
|
||||||
#define WEBSOCKETSSERVER_H_
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
#include "WebSockets.h"
|
|
||||||
|
|
||||||
#define WEBSOCKETS_SERVER_CLIENT_MAX (5)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class WebSocketsServer: private WebSockets {
|
|
||||||
public:
|
|
||||||
|
|
||||||
typedef void (*WebSocketServerEvent)(uint8_t num, WStype_t type, uint8_t * payload, size_t length);
|
|
||||||
|
|
||||||
WebSocketsServer(uint16_t port);
|
|
||||||
~WebSocketsServer(void);
|
|
||||||
|
|
||||||
void begin(void);
|
|
||||||
void loop(void);
|
|
||||||
|
|
||||||
void onEvent(WebSocketServerEvent cbEvent);
|
|
||||||
|
|
||||||
|
|
||||||
void sendTXT(uint8_t num, uint8_t * payload, size_t length = 0, bool headerToPayload = false);
|
|
||||||
void sendTXT(uint8_t num, const uint8_t * payload, size_t length = 0);
|
|
||||||
void sendTXT(uint8_t num, char * payload, size_t length = 0, bool headerToPayload = false);
|
|
||||||
void sendTXT(uint8_t num, const char * payload, size_t length = 0);
|
|
||||||
void sendTXT(uint8_t num, String payload);
|
|
||||||
|
|
||||||
void broadcastTXT(uint8_t * payload, size_t length = 0, bool headerToPayload = false);
|
|
||||||
void broadcastTXT(const uint8_t * payload, size_t length = 0);
|
|
||||||
void broadcastTXT(char * payload, size_t length = 0, bool headerToPayload = false);
|
|
||||||
void broadcastTXT(const char * payload, size_t length = 0);
|
|
||||||
void broadcastTXT(String payload);
|
|
||||||
|
|
||||||
void sendBIN(uint8_t num, uint8_t * payload, size_t length, bool headerToPayload = false);
|
|
||||||
void sendBIN(uint8_t num, const uint8_t * payload, size_t length);
|
|
||||||
|
|
||||||
bool sendPing(uint8_t num, uint8_t * payload = NULL, size_t length = 0);
|
|
||||||
bool sendPing(uint8_t num, String & payload);
|
|
||||||
|
|
||||||
bool broadcastPing(uint8_t * payload = NULL, size_t length = 0);
|
|
||||||
bool broadcastPing(String & payload);
|
|
||||||
|
|
||||||
void broadcastBIN(uint8_t * payload, size_t length, bool headerToPayload = false);
|
|
||||||
void broadcastBIN(const uint8_t * payload, size_t length);
|
|
||||||
|
|
||||||
void disconnect(void);
|
|
||||||
void disconnect(uint8_t num);
|
|
||||||
|
|
||||||
bool clientConnected(uint8_t num);
|
|
||||||
|
|
||||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
|
||||||
IPAddress remoteIP(uint8_t num);
|
|
||||||
#else
|
|
||||||
bool clientExists(const WEBSOCKETS_NETWORK_CLASS &c);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
protected:
|
|
||||||
uint16_t _port;
|
|
||||||
|
|
||||||
WEBSOCKETS_NETWORK_SERVER_CLASS * _server;
|
|
||||||
|
|
||||||
WSclient_t _clients[WEBSOCKETS_SERVER_CLIENT_MAX];
|
|
||||||
|
|
||||||
WebSocketServerEvent _cbEvent;
|
|
||||||
|
|
||||||
void messageRecived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length);
|
|
||||||
|
|
||||||
void clientDisconnect(WSclient_t * client);
|
|
||||||
bool clientIsConnected(WSclient_t * client);
|
|
||||||
|
|
||||||
void handleNewClients(void);
|
|
||||||
void handleClientData(void);
|
|
||||||
|
|
||||||
void handleHeader(WSclient_t * client);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* called if a non Websocket connection is comming in.
|
|
||||||
* Note: can be overrided
|
|
||||||
* @param client WSclient_t * ptr to the client struct
|
|
||||||
*/
|
|
||||||
virtual void handleNonWebsocketConnection(WSclient_t * client) {
|
|
||||||
DEBUG_WEBSOCKETS("[WS-Server][");
|
|
||||||
DEBUG_WEBSOCKETS(client->num);
|
|
||||||
DEBUG_WEBSOCKETS("][handleHeader] no Websocket connection close.\n");
|
|
||||||
|
|
||||||
client->tcp->write("HTTP/1.1 400 Bad Request\r\n"
|
|
||||||
"Server: arduino-WebSocket-Server\r\n"
|
|
||||||
"Content-Type: text/plain\r\n"
|
|
||||||
"Content-Length: 32\r\n"
|
|
||||||
"Connection: close\r\n"
|
|
||||||
"Sec-WebSocket-Version: 13\r\n"
|
|
||||||
"\r\n"
|
|
||||||
"This is a Websocket server only!");
|
|
||||||
clientDisconnect(client);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* called for sending a Event to the app
|
|
||||||
* @param num uint8_t
|
|
||||||
* @param type WStype_t
|
|
||||||
* @param payload uint8_t *
|
|
||||||
* @param length size_t
|
|
||||||
*/
|
|
||||||
virtual void runCbEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
|
|
||||||
if(_cbEvent) {
|
|
||||||
_cbEvent(num, type, payload, length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif /* WEBSOCKETSSERVER_H_ */
|
|
Loading…
Reference in New Issue
Block a user