Compare commits

...

15 Commits
1.3 ... ATmega

Author SHA1 Message Date
11273d9f69 Merge pull request #378 from Niklas04/patch-1
Update WebSocketsClient.cpp
2019-05-30 16:48:05 +02:00
82e3c57006 Update WebSocketsClient.cpp
fix a issue relating to a comparison that fails due to capitalisation of the word
2018-10-09 21:28:02 +02:00
3f5c7e1c46 add travis build test 2018-05-19 19:26:12 +02:00
7c5113fe2d fix #154 - _fingerprint not declared in this scope
fix #154 - _fingerprint not declared in this scope
2016-12-27 14:16:12 +01:00
21e959c013 fix #38 2016-01-07 01:21:09 +01:00
ccbef0fced Update library.json 2015-12-29 12:15:18 +01:00
431c5d17b9 Merge pull request #35 from ivankravets/patch-1
@PlatformIO Library Registry manifest file
2015-12-29 12:00:39 +01:00
5f9e30e908 @PlatformIO Library Registry manifest file
* This library in Web Registry: http://platformio.org/#!/lib/show/549/WebSockets
* Specification: [library.json](http://docs.platformio.org/en/latest/librarymanager/config.html)
* Library Manager:  http://docs.platformio.org/en/latest/librarymanager/index.html
2015-12-29 00:45:20 +02:00
21e092d179 code cleanup
improve readWait error handling
2015-12-28 16:33:54 +01:00
b24f021d33 update README.md 2015-12-28 03:25:01 +01:00
ff33056309 try to send data in one TCP packed if possible,
add random mask support for short frames.
may helps with #34
2015-12-28 03:19:45 +01:00
848979ecf0 use String to store fingerprint
(const char * can be invalidate based on which scope it coming from)
move _fingerprint to Client class only (server not need it)
2015-12-24 12:58:05 +01:00
167e61823c Merge pull request #31 from schokocappucino/master
verify ssl certificate fingerprint
2015-12-24 12:47:54 +01:00
07bd519940 disconnect on certificate mismatch 2015-12-24 04:52:05 +01:00
73680279f5 verify ssl certificate fingerprint 2015-12-24 04:47:11 +01:00
13 changed files with 213 additions and 30 deletions

39
.travis.yml Normal file
View File

@ -0,0 +1,39 @@
sudo: false
language: bash
os:
- linux
env:
matrix:
- CPU="esp8266" BOARD="esp8266com:esp8266:generic:CpuFrequency=80" IDE_VERSION=1.6.5
- CPU="esp8266" BOARD="esp8266com:esp8266:generic:CpuFrequency=80,FlashSize=1M0,FlashMode=qio,FlashFreq=80" IDE_VERSION=1.8.5
- CPU="esp8266" BOARD="esp8266com:esp8266:generic:CpuFrequency=80,Debug=Serial1" IDE_VERSION=1.6.5
- CPU="avr" BOARD="arduino:avr:mega:cpu=atmega2560" IDE_VERSION=1.6.5
script:
- /sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_1.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :1 -ac -screen 0 1280x1024x16
- sleep 3
- export DISPLAY=:1.0
- wget http://downloads.arduino.cc/arduino-$IDE_VERSION-linux64.tar.xz
- tar xf arduino-$IDE_VERSION-linux64.tar.xz
- mv arduino-$IDE_VERSION $HOME/arduino_ide
- export PATH="$HOME/arduino_ide:$PATH"
- which arduino
- mkdir -p $HOME/Arduino/libraries
- cp -r $TRAVIS_BUILD_DIR $HOME/Arduino/libraries/arduinoWebSockets
- source $TRAVIS_BUILD_DIR/travis/common.sh
- get_core $CPU
- cd $TRAVIS_BUILD_DIR
- arduino --board $BOARD --save-prefs
- arduino --get-pref sketchbook.path
- build_sketches arduino $HOME/Arduino/libraries/arduinoWebSockets/examples/$CPU $CPU
notifications:
email:
on_success: change
on_failure: change
webhooks:
urls:
- https://webhooks.gitter.im/e/1aa78fbe15080b0c2e37
on_success: change # options: [always|never|change] default: always
on_failure: always # options: [always|never|change] default: always
on_start: false # default: false

View File

@ -17,7 +17,7 @@ a WebSocket Server and Client for Arduino based on RFC6455.
##### Limitations #####
- max input length is limited to the ram size and the ```WEBSOCKETS_MAX_DATA_SIZE``` define
- max output length has no limit (the hardware is the limit)
- Client send masked frames always with mask 0x00000000
- Client send big frames with mask 0x00000000 (on AVR all frames)
##### Supported Hardware #####
- ESP8266 [Arduino for ESP8266](https://github.com/Links2004/Arduino)

19
library.json Normal file
View File

@ -0,0 +1,19 @@
{
"name": "WebSockets",
"keywords": "wifi, http, web, server, client, websocket",
"description": "WebSocket Server and Client for Arduino based on RFC6455",
"repository":
{
"type": "git",
"url": "https://github.com/Links2004/arduinoWebSockets.git"
},
"exclude": "tests",
"frameworks": "arduino",
"platforms": "*",
"authors":
{
"name": "Markus Sattler",
"url": "https://github.com/Links2004",
"maintainer": true
}
}

View File

@ -46,6 +46,8 @@ extern "C" {
#endif
#define WEBSOCKETS_MAX_HEADER_SIZE (14)
/**
*
* @param client WSclient_t * ptr to the client struct
@ -97,11 +99,13 @@ void WebSockets::sendFrame(WSclient_t * client, WSopcode_t opcode, uint8_t * pay
DEBUG_WEBSOCKETS("[WS][%d][sendFrame] text: %s\n", client->num, (payload + (headerToPayload ? 14 : 0)));
}
uint8_t maskKey[4] = { 0 };
uint8_t buffer[14] = { 0 };
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;
// calculate header Size
if(length < 126) {
@ -116,10 +120,26 @@ 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)
if(!headerToPayload && ((length > 0) && (length < 1400)) && (ESP.getFreeHeap() > 6000)) {
DEBUG_WEBSOCKETS("[WS][%d][sendFrame] pack to one TCP package...\n", client->num);
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 = (payload + (14 - headerSize));
headerPtr = (payloadPtr + (WEBSOCKETS_MAX_HEADER_SIZE - headerSize));
} else {
headerPtr = &buffer[0];
}
@ -131,7 +151,7 @@ void WebSockets::sendFrame(WSclient_t * client, WSopcode_t opcode, uint8_t * pay
if(fin) {
*headerPtr |= bit(7); ///< set Fin
}
*headerPtr |= opcode; ///< set opcode
*headerPtr |= opcode; ///< set opcode
headerPtr++;
// byte 1
@ -147,7 +167,7 @@ void WebSockets::sendFrame(WSclient_t * client, WSopcode_t opcode, uint8_t * pay
*headerPtr = ((length >> 8) & 0xFF); headerPtr++;
*headerPtr = (length & 0xFF); headerPtr++;
} else {
// normaly we never get here (to less memory)
// Normally we never get here (to less memory)
*headerPtr |= 127; headerPtr++;
*headerPtr = 0x00; headerPtr++;
*headerPtr = 0x00; headerPtr++;
@ -160,33 +180,61 @@ void WebSockets::sendFrame(WSclient_t * client, WSopcode_t opcode, uint8_t * pay
}
if(mask) {
// todo generate random mask key
for(uint8_t x = 0; x < sizeof(maskKey); x++) {
// maskKey[x] = random(0xFF);
maskKey[x] = 0x00; // fake xor (0x00 0x00 0x00 0x00)
*headerPtr = maskKey[x]; headerPtr++;
}
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++;
}
// todo encode XOR (note: using payload not working for static content from flash)
//for(size_t x = 0; x < length; x++) {
// payload[x] = (payload[x] ^ maskKey[x % 4]);
//}
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
client->tcp->write(&payload[(14 - headerSize)], (length + headerSize));
client->tcp->write(&payloadPtr[(WEBSOCKETS_MAX_HEADER_SIZE - headerSize)], (length + headerSize));
} else {
// send header
client->tcp->write(&buffer[0], headerSize);
if(payload && length > 0) {
if(payloadPtr && length > 0) {
// send payload
client->tcp->write(&payload[0], length);
client->tcp->write(&payloadPtr[0], length);
}
}
DEBUG_WEBSOCKETS("[WS][%d][sendFrame] sending Frame Done (%uus).\n", client->num, (micros() - start));
#ifdef WEBSOCKETS_USE_BIG_MEM
if(useInternBuffer && payloadPtr) {
free(payloadPtr);
}
#endif
}
/**
@ -235,7 +283,7 @@ void WebSockets::handleWebsocket(WSclient_t * client) {
}
payloadLen = buffer[0] << 8 | buffer[1];
} else if(payloadLen == 127) {
// read 64bit inteager as length
// read 64bit integer as length
if(!readWait(client, buffer, 8)) {
//timeout
clientDisconnect(client, 1002);
@ -349,7 +397,7 @@ String WebSockets::acceptKey(String clientKey) {
#ifdef ESP8266
sha1(clientKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11", &sha1HashBin[0]);
#else
clientKey =+ "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
clientKey += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
SHA1_CTX ctx;
SHA1Init(&ctx);
SHA1Update(&ctx, (const unsigned char*)clientKey.c_str(), clientKey.length());
@ -396,7 +444,12 @@ bool WebSockets::readWait(WSclient_t * client, uint8_t *out, size_t n) {
size_t len;
while(n > 0) {
if(client->tcp && !client->tcp->connected()) {
if(!client->tcp) {
DEBUG_WEBSOCKETS("[readWait] tcp is null!\n");
return false;
}
if(!client->tcp->connected()) {
DEBUG_WEBSOCKETS("[readWait] not connected!\n");
return false;
}

View File

@ -31,10 +31,12 @@
#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 (1024)

View File

@ -40,7 +40,10 @@ 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;
_client.tcp = NULL;
@ -71,13 +74,14 @@ void WebSocketsClient::begin(String host, uint16_t port, String url) {
}
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
void WebSocketsClient::beginSSL(const char *host, uint16_t port, const char * url) {
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) {
beginSSL(host.c_str(), port, url.c_str());
void WebSocketsClient::beginSSL(String host, uint16_t port, String url, String fingerprint) {
beginSSL(host.c_str(), port, url.c_str(), fingerprint.c_str());
}
#endif
@ -124,6 +128,14 @@ void WebSocketsClient::loop(void) {
#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
@ -346,7 +358,9 @@ void WebSocketsClient::sendHeader(WSclient_t * client) {
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"
@ -389,7 +403,7 @@ void WebSocketsClient::handleHeader(WSclient_t * client) {
String headerValue = headerLine.substring(headerLine.indexOf(':') + 2);
if(headerName.equalsIgnoreCase("Connection")) {
if(headerValue.indexOf("Upgrade") >= 0) {
if(headerValue.equalsIgnoreCase("Upgrade")) {
client->cIsUpgrade = true;
}
} else if(headerName.equalsIgnoreCase("Upgrade")) {
@ -438,7 +452,7 @@ void WebSocketsClient::handleHeader(WSclient_t * client) {
default: ///< Server dont unterstand requrst
ok = false;
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] serverCode is not 101 (%d)\n", client->cCode);
clientDisconnect(&_client);
clientDisconnect(client);
break;
}
}

View File

@ -40,8 +40,8 @@ class WebSocketsClient: private WebSockets {
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 = "/");
void beginSSL(String host, uint16_t port, String url = "/");
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);
@ -63,6 +63,9 @@ class WebSocketsClient: private WebSockets {
String _host;
uint16_t _port;
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
String _fingerprint;
#endif
WSclient_t _client;
WebSocketClientEvent _cbEvent;

53
travis/common.sh Normal file
View File

@ -0,0 +1,53 @@
#!/bin/bash
function build_sketches()
{
local arduino=$1
local srcpath=$2
local platform=$3
local sketches=$(find $srcpath -name *.ino)
for sketch in $sketches; do
local sketchdir=$(dirname $sketch)
if [[ -f "$sketchdir/.$platform.skip" ]]; then
echo -e "\n\n ------------ Skipping $sketch ------------ \n\n";
continue
fi
echo -e "\n\n ------------ Building $sketch ------------ \n\n";
$arduino --verify $sketch;
local result=$?
if [ $result -ne 0 ]; then
echo "Build failed ($sketch) build verbose..."
$arduino --verify --verbose --preserve-temp-files $sketch
result=$?
fi
if [ $result -ne 0 ]; then
echo "Build failed ($1) $sketch"
return $result
fi
done
}
function get_core()
{
echo Setup core for $1
cd $HOME/arduino_ide/hardware
if [ "$1" = "esp8266" ] ; then
mkdir esp8266com
cd esp8266com
git clone https://github.com/esp8266/Arduino.git esp8266
cd esp8266/tools
python get.py
fi
if [ "$1" = "esp32" ] ; then
mkdir espressif
cd espressif
git clone https://github.com/espressif/arduino-esp32.git esp32
cd esp32/tools
python get.py
fi
}