first commit
This commit is contained in:
67
libraries/Arduino-Websocket-Fast/README.md
Normal file
67
libraries/Arduino-Websocket-Fast/README.md
Normal file
@@ -0,0 +1,67 @@
|
||||
## Websocket client for Arduino, with fast data send
|
||||
|
||||
This is a simple library that implements a Websocket client running on an Arduino.
|
||||
|
||||
### Rationale
|
||||
|
||||
For our IoT prototype project based on Arduino, we needed a reliable data transmission protocol, and we thought of Websocket. We then searched for existing client implementations for Arduino, something that was able to handle any subclass of the `Client` one provided by Arduino, and that was essential in implementation but complete as well. We found the excellent code here <https://github.com/brandenhall/Arduino-Websocket>. However, some modifications were needed for our purpose. In particular, we needed max throughput possible, approaching 100 messages/s.
|
||||
|
||||
### Features
|
||||
I added the following:
|
||||
|
||||
- Faster data send (`client.sendData(..., true)`, default behaviour): instead of sending a TCP packet per char, everything is sent
|
||||
in one shot in a single TCP packet. This makes the implementation much faster. However, take into consideration max string length when using `WiFiClient.write()` method (around 90 bytes, from users experience when googled). Example:
|
||||
|
||||
```C++
|
||||
webSocketClient.sendData("my string to send", WS_OPCODE_TEXT, true);
|
||||
```
|
||||
|
||||
- For method `client.getData()`, I created a pure C string implementation, to avoid chances of heap fragmentation due to `String`
|
||||
class. Example:
|
||||
|
||||
```C++
|
||||
char msg_in[100]; // should be long enough to hold the longest arriving message
|
||||
uint8_t opcode_in;
|
||||
...
|
||||
webSocketClient.getData(msg_in, &opcode_in)
|
||||
```
|
||||
|
||||
### Tests
|
||||
The optimized code was tested for:
|
||||
|
||||
- `WiFiClient` (`<WiFi.h>` and `<WiFi101.h>`)
|
||||
- Arduino UNO and ZERO
|
||||
- WiFi shield (retired) on Arduino UNO and WiFi shield 101 on Arduino ZERO
|
||||
- `ws` as Node.js websocket server
|
||||
|
||||
We were able to obtain to reach the target throughput indicated above, with a message length of around 70 bytes (\*):
|
||||
|
||||
(\*) In order to reach that speed, we had to apply the following hack:
|
||||
|
||||
1. <https://gist.github.com/u0078867/9df30eb7da64d8f43422faa70b1a9e52>
|
||||
|
||||
We did not want to get the `loop()` stuck if the TCP message was not sent (via WiFi), and we could afford some data lost randomly; although, we wanted our data to be reliable and in time order on the server side, so we excluded UDP packets.
|
||||
|
||||
2. After point 1, we had to manually disable the mask flag for websocket messages, by replacing this line in src /WebSocketClient.h:
|
||||
|
||||
```C++
|
||||
#define WS_MASK 0x80
|
||||
```
|
||||
|
||||
with this one:
|
||||
|
||||
```C++
|
||||
#define WS_MASK 0x00
|
||||
```
|
||||
|
||||
This modification disables the message mask, which normally is **compulsory**. `ws` tolerates it however.
|
||||
|
||||
### MCU compatibility
|
||||
- Tested: Arduino UNO, ZERO
|
||||
- Not tested: Arduino DUE; howerer, by searching similar C++ repos on GitHub (`arduino websocket due in:readme,name,description fork:true`), it seems that the conditional inclusion (in src/sha1.cpp) of `#include <avr/io.h>` and `#include <avr/pgmspace.h>` needed for ZERO board, would also fix compilation for DUE board. Any good-soul tester is welcome to feedback.
|
||||
|
||||
### Notes
|
||||
See the original code from Branden for additional notes.
|
||||
|
||||
### Credits
|
||||
This is an optimized version of the client code from the excellent job in <https://github.com/brandenhall/Arduino-Websocket>. Most of the credit goes to Branden.
|
||||
@@ -0,0 +1,109 @@
|
||||
#include <SPI.h>
|
||||
#include <SC16IS750.h>
|
||||
#include <WiFly.h>
|
||||
|
||||
// Here we define a maximum framelength to 64 bytes. Default is 256.
|
||||
#define MAX_FRAME_LENGTH 64
|
||||
|
||||
// Define how many callback functions you have. Default is 1.
|
||||
#define CALLBACK_FUNCTIONS 1
|
||||
|
||||
#include <WebSocketClient.h>
|
||||
|
||||
WiFlyClient client = WiFlyClient();
|
||||
WebSocketClient webSocketClient;
|
||||
|
||||
void setup() {
|
||||
|
||||
|
||||
Serial.begin(9600);
|
||||
SC16IS750.begin();
|
||||
|
||||
WiFly.setUart(&SC16IS750);
|
||||
|
||||
WiFly.begin();
|
||||
|
||||
// This is for an unsecured network
|
||||
// For a WPA1/2 network use auth 3, and in another command send 'set wlan phrase PASSWORD'
|
||||
// For a WEP network use auth 2, and in another command send 'set wlan key KEY'
|
||||
WiFly.sendCommand(F("set wlan auth 1"));
|
||||
WiFly.sendCommand(F("set wlan channel 0"));
|
||||
WiFly.sendCommand(F("set ip dhcp 1"));
|
||||
|
||||
Serial.println(F("Joining WiFi network..."));
|
||||
|
||||
|
||||
// Here is where you set the network name to join
|
||||
if (!WiFly.sendCommand(F("join arduino_wifi"), "Associated!", 20000, false)) {
|
||||
Serial.println(F("Association failed."));
|
||||
while (1) {
|
||||
// Hang on failure.
|
||||
}
|
||||
}
|
||||
|
||||
if (!WiFly.waitForResponse("DHCP in", 10000)) {
|
||||
Serial.println(F("DHCP failed."));
|
||||
while (1) {
|
||||
// Hang on failure.
|
||||
}
|
||||
}
|
||||
|
||||
// This is how you get the local IP as an IPAddress object
|
||||
Serial.println(WiFly.localIP());
|
||||
|
||||
// This delay is needed to let the WiFly respond properly
|
||||
delay(100);
|
||||
|
||||
// Connect to the websocket server
|
||||
if (client.connect("echo.websocket.org", 80)) {
|
||||
Serial.println("Connected");
|
||||
} else {
|
||||
Serial.println("Connection failed.");
|
||||
while(1) {
|
||||
// Hang on failure
|
||||
}
|
||||
}
|
||||
|
||||
// Handshake with the server
|
||||
webSocketClient.path = "/";
|
||||
webSocketClient.host = "echo.websocket.org";
|
||||
|
||||
if (webSocketClient.handshake(client)) {
|
||||
Serial.println("Handshake successful");
|
||||
} else {
|
||||
Serial.println("Handshake failed.");
|
||||
while(1) {
|
||||
// Hang on failure
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
String data;
|
||||
|
||||
if (client.connected()) {
|
||||
|
||||
webSocketClient.getData(data);
|
||||
|
||||
if (data.length() > 0) {
|
||||
Serial.print("Received data: ");
|
||||
Serial.println(data);
|
||||
}
|
||||
|
||||
// capture the value of analog 1, send it along
|
||||
pinMode(1, INPUT);
|
||||
data = String(analogRead(1));
|
||||
|
||||
webSocketClient.sendData(data);
|
||||
|
||||
} else {
|
||||
|
||||
Serial.println("Client disconnected.");
|
||||
while (1) {
|
||||
// Hang on disconnect.
|
||||
}
|
||||
}
|
||||
|
||||
// wait to fully let the client disconnect
|
||||
delay(3000);
|
||||
}
|
||||
24
libraries/Arduino-Websocket-Fast/keywords.txt
Normal file
24
libraries/Arduino-Websocket-Fast/keywords.txt
Normal file
@@ -0,0 +1,24 @@
|
||||
#######################################
|
||||
# Syntax Coloring Map WebsocketFast
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Datatypes (KEYWORD1)
|
||||
#######################################
|
||||
|
||||
WebSocketClient KEYWORD1
|
||||
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
#######################################
|
||||
handshake KEYWORD2
|
||||
getData KEYWORD2
|
||||
sendData KEYWORD2
|
||||
path KEYWORD2
|
||||
host KEYWORD2
|
||||
protocol KEYWORD2
|
||||
|
||||
#######################################
|
||||
# Constants (LITERAL1)
|
||||
#######################################
|
||||
|
||||
9
libraries/Arduino-Websocket-Fast/library.properties
Normal file
9
libraries/Arduino-Websocket-Fast/library.properties
Normal file
@@ -0,0 +1,9 @@
|
||||
name=Arduino-Websocket-Fast
|
||||
version=1.0.0
|
||||
author=Davide Monari (KULeuven)
|
||||
maintainer=Davide Monari <dvidemnr@gmail.com>
|
||||
sentence=Websocket client library (fast data sending).
|
||||
paragraph=The library can wrap around a generic Arduino Client() class or similar interface (e.g. EthernetClient(), WiFiClient(), WiflyClient(), ...) and is optimized in speed for data sending.
|
||||
category=Communication
|
||||
url=https://github.com/u0078867/Arduino-Websocket-Fast
|
||||
architectures=avr,samd
|
||||
133
libraries/Arduino-Websocket-Fast/src/Base64.cpp
Normal file
133
libraries/Arduino-Websocket-Fast/src/Base64.cpp
Normal file
@@ -0,0 +1,133 @@
|
||||
#include "Base64.h"
|
||||
|
||||
const char b64_alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789+/";
|
||||
|
||||
/* 'Private' declarations */
|
||||
inline void a3_to_a4(unsigned char * a4, unsigned char * a3);
|
||||
inline void a4_to_a3(unsigned char * a3, unsigned char * a4);
|
||||
inline unsigned char b64_lookup(char c);
|
||||
|
||||
int base64_encode(char *output, char *input, int inputLen) {
|
||||
int i = 0, j = 0;
|
||||
int encLen = 0;
|
||||
unsigned char a3[3];
|
||||
unsigned char a4[4];
|
||||
|
||||
while(inputLen--) {
|
||||
a3[i++] = *(input++);
|
||||
if(i == 3) {
|
||||
a3_to_a4(a4, a3);
|
||||
|
||||
for(i = 0; i < 4; i++) {
|
||||
output[encLen++] = b64_alphabet[a4[i]];
|
||||
}
|
||||
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if(i) {
|
||||
for(j = i; j < 3; j++) {
|
||||
a3[j] = '\0';
|
||||
}
|
||||
|
||||
a3_to_a4(a4, a3);
|
||||
|
||||
for(j = 0; j < i + 1; j++) {
|
||||
output[encLen++] = b64_alphabet[a4[j]];
|
||||
}
|
||||
|
||||
while((i++ < 3)) {
|
||||
output[encLen++] = '=';
|
||||
}
|
||||
}
|
||||
output[encLen] = '\0';
|
||||
return encLen;
|
||||
}
|
||||
|
||||
int base64_decode(char * output, char * input, int inputLen) {
|
||||
int i = 0, j = 0;
|
||||
int decLen = 0;
|
||||
unsigned char a3[3];
|
||||
unsigned char a4[4];
|
||||
|
||||
|
||||
while (inputLen--) {
|
||||
if(*input == '=') {
|
||||
break;
|
||||
}
|
||||
|
||||
a4[i++] = *(input++);
|
||||
if (i == 4) {
|
||||
for (i = 0; i <4; i++) {
|
||||
a4[i] = b64_lookup(a4[i]);
|
||||
}
|
||||
|
||||
a4_to_a3(a3,a4);
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
output[decLen++] = a3[i];
|
||||
}
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (i) {
|
||||
for (j = i; j < 4; j++) {
|
||||
a4[j] = '\0';
|
||||
}
|
||||
|
||||
for (j = 0; j <4; j++) {
|
||||
a4[j] = b64_lookup(a4[j]);
|
||||
}
|
||||
|
||||
a4_to_a3(a3,a4);
|
||||
|
||||
for (j = 0; j < i - 1; j++) {
|
||||
output[decLen++] = a3[j];
|
||||
}
|
||||
}
|
||||
output[decLen] = '\0';
|
||||
return decLen;
|
||||
}
|
||||
|
||||
int base64_enc_len(int plainLen) {
|
||||
int n = plainLen;
|
||||
return (n + 2 - ((n + 2) % 3)) / 3 * 4;
|
||||
}
|
||||
|
||||
int base64_dec_len(char * input, int inputLen) {
|
||||
int i = 0;
|
||||
int numEq = 0;
|
||||
for(i = inputLen - 1; input[i] == '='; i--) {
|
||||
numEq++;
|
||||
}
|
||||
|
||||
return ((6 * inputLen) / 8) - numEq;
|
||||
}
|
||||
|
||||
inline void a3_to_a4(unsigned char * a4, unsigned char * a3) {
|
||||
a4[0] = (a3[0] & 0xfc) >> 2;
|
||||
a4[1] = ((a3[0] & 0x03) << 4) + ((a3[1] & 0xf0) >> 4);
|
||||
a4[2] = ((a3[1] & 0x0f) << 2) + ((a3[2] & 0xc0) >> 6);
|
||||
a4[3] = (a3[2] & 0x3f);
|
||||
}
|
||||
|
||||
inline void a4_to_a3(unsigned char * a3, unsigned char * a4) {
|
||||
a3[0] = (a4[0] << 2) + ((a4[1] & 0x30) >> 4);
|
||||
a3[1] = ((a4[1] & 0xf) << 4) + ((a4[2] & 0x3c) >> 2);
|
||||
a3[2] = ((a4[2] & 0x3) << 6) + a4[3];
|
||||
}
|
||||
|
||||
inline unsigned char b64_lookup(char c) {
|
||||
int i;
|
||||
for(i = 0; i < 64; i++) {
|
||||
if(b64_alphabet[i] == c) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
75
libraries/Arduino-Websocket-Fast/src/Base64.h
Normal file
75
libraries/Arduino-Websocket-Fast/src/Base64.h
Normal file
@@ -0,0 +1,75 @@
|
||||
#ifndef _BASE64_H
|
||||
#define _BASE64_H
|
||||
|
||||
/* b64_alphabet:
|
||||
* Description: Base64 alphabet table, a mapping between integers
|
||||
* and base64 digits
|
||||
* Notes: This is an extern here but is defined in Base64.c
|
||||
*/
|
||||
extern const char b64_alphabet[];
|
||||
|
||||
/* base64_encode:
|
||||
* Description:
|
||||
* Encode a string of characters as base64
|
||||
* Parameters:
|
||||
* output: the output buffer for the encoding, stores the encoded string
|
||||
* input: the input buffer for the encoding, stores the binary to be encoded
|
||||
* inputLen: the length of the input buffer, in bytes
|
||||
* Return value:
|
||||
* Returns the length of the encoded string
|
||||
* Requirements:
|
||||
* 1. output must not be null or empty
|
||||
* 2. input must not be null
|
||||
* 3. inputLen must be greater than or equal to 0
|
||||
*/
|
||||
int base64_encode(char *output, char *input, int inputLen);
|
||||
|
||||
/* base64_decode:
|
||||
* Description:
|
||||
* Decode a base64 encoded string into bytes
|
||||
* Parameters:
|
||||
* output: the output buffer for the decoding,
|
||||
* stores the decoded binary
|
||||
* input: the input buffer for the decoding,
|
||||
* stores the base64 string to be decoded
|
||||
* inputLen: the length of the input buffer, in bytes
|
||||
* Return value:
|
||||
* Returns the length of the decoded string
|
||||
* Requirements:
|
||||
* 1. output must not be null or empty
|
||||
* 2. input must not be null
|
||||
* 3. inputLen must be greater than or equal to 0
|
||||
*/
|
||||
int base64_decode(char *output, char *input, int inputLen);
|
||||
|
||||
/* base64_enc_len:
|
||||
* Description:
|
||||
* Returns the length of a base64 encoded string whose decoded
|
||||
* form is inputLen bytes long
|
||||
* Parameters:
|
||||
* inputLen: the length of the decoded string
|
||||
* Return value:
|
||||
* The length of a base64 encoded string whose decoded form
|
||||
* is inputLen bytes long
|
||||
* Requirements:
|
||||
* None
|
||||
*/
|
||||
int base64_enc_len(int inputLen);
|
||||
|
||||
/* base64_dec_len:
|
||||
* Description:
|
||||
* Returns the length of the decoded form of a
|
||||
* base64 encoded string
|
||||
* Parameters:
|
||||
* input: the base64 encoded string to be measured
|
||||
* inputLen: the length of the base64 encoded string
|
||||
* Return value:
|
||||
* Returns the length of the decoded form of a
|
||||
* base64 encoded string
|
||||
* Requirements:
|
||||
* 1. input must not be null
|
||||
* 2. input must be greater than or equal to zero
|
||||
*/
|
||||
int base64_dec_len(char *input, int inputLen);
|
||||
|
||||
#endif // _BASE64_H
|
||||
511
libraries/Arduino-Websocket-Fast/src/WebSocketClient.cpp
Normal file
511
libraries/Arduino-Websocket-Fast/src/WebSocketClient.cpp
Normal file
@@ -0,0 +1,511 @@
|
||||
//#define DEBUGGING
|
||||
|
||||
#include "global.h"
|
||||
#include "WebSocketClient.h"
|
||||
|
||||
#include "sha1.h"
|
||||
#include "base64.h"
|
||||
|
||||
|
||||
bool WebSocketClient::handshake(Client &client) {
|
||||
|
||||
socket_client = &client;
|
||||
|
||||
// If there is a connected client->
|
||||
if (socket_client->connected()) {
|
||||
// Check request and look for websocket handshake
|
||||
#ifdef DEBUGGING
|
||||
Serial.println(F("Client connected"));
|
||||
#endif
|
||||
if (analyzeRequest()) {
|
||||
#ifdef DEBUGGING
|
||||
Serial.println(F("Websocket established"));
|
||||
#endif
|
||||
|
||||
return true;
|
||||
|
||||
} else {
|
||||
// Might just need to break until out of socket_client loop.
|
||||
#ifdef DEBUGGING
|
||||
Serial.println(F("Invalid handshake"));
|
||||
#endif
|
||||
disconnectStream();
|
||||
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool WebSocketClient::analyzeRequest() {
|
||||
String temp;
|
||||
|
||||
int bite;
|
||||
bool foundupgrade = false;
|
||||
unsigned long intkey[2];
|
||||
String serverKey;
|
||||
char keyStart[17];
|
||||
char b64Key[25];
|
||||
String key = "------------------------";
|
||||
|
||||
randomSeed(analogRead(0));
|
||||
|
||||
for (int i=0; i<16; ++i) {
|
||||
keyStart[i] = (char)random(1, 256);
|
||||
}
|
||||
|
||||
base64_encode(b64Key, keyStart, 16);
|
||||
|
||||
for (int i=0; i<24; ++i) {
|
||||
key[i] = b64Key[i];
|
||||
}
|
||||
|
||||
#ifdef DEBUGGING
|
||||
Serial.println(F("Sending websocket upgrade headers"));
|
||||
#endif
|
||||
|
||||
socket_client->print(F("GET "));
|
||||
socket_client->print(path);
|
||||
socket_client->print(F(" HTTP/1.1\r\n"));
|
||||
socket_client->print(F("Upgrade: websocket\r\n"));
|
||||
socket_client->print(F("Connection: Upgrade\r\n"));
|
||||
socket_client->print(F("Host: "));
|
||||
socket_client->print(host);
|
||||
socket_client->print(CRLF);
|
||||
socket_client->print(F("Sec-WebSocket-Key: "));
|
||||
socket_client->print(key);
|
||||
socket_client->print(CRLF);
|
||||
socket_client->print(F("Sec-WebSocket-Protocol: "));
|
||||
socket_client->print(protocol);
|
||||
socket_client->print(CRLF);
|
||||
socket_client->print(F("Sec-WebSocket-Version: 13\r\n"));
|
||||
socket_client->print(CRLF);
|
||||
|
||||
#ifdef DEBUGGING
|
||||
Serial.println(F("Analyzing response headers"));
|
||||
#endif
|
||||
|
||||
while (socket_client->connected() && !socket_client->available()) {
|
||||
delay(100);
|
||||
Serial.println("Waiting...");
|
||||
}
|
||||
|
||||
// TODO: More robust string extraction
|
||||
while ((bite = socket_client->read()) != -1) {
|
||||
|
||||
temp += (char)bite;
|
||||
|
||||
if ((char)bite == '\n') {
|
||||
#ifdef DEBUGGING
|
||||
Serial.print("Got Header: " + temp);
|
||||
#endif
|
||||
if (!foundupgrade && temp.startsWith("Upgrade: websocket")) {
|
||||
foundupgrade = true;
|
||||
} else if (temp.startsWith("Sec-WebSocket-Accept: ")) {
|
||||
serverKey = temp.substring(22,temp.length() - 2); // Don't save last CR+LF
|
||||
}
|
||||
temp = "";
|
||||
}
|
||||
|
||||
if (!socket_client->available()) {
|
||||
delay(20);
|
||||
}
|
||||
}
|
||||
|
||||
key += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
||||
uint8_t *hash;
|
||||
char result[21];
|
||||
char b64Result[30];
|
||||
|
||||
Sha1.init();
|
||||
Sha1.print(key);
|
||||
hash = Sha1.result();
|
||||
|
||||
for (int i=0; i<20; ++i) {
|
||||
result[i] = (char)hash[i];
|
||||
}
|
||||
result[20] = '\0';
|
||||
|
||||
base64_encode(b64Result, result, 20);
|
||||
|
||||
// if the keys match, good to go
|
||||
return serverKey.equals(String(b64Result));
|
||||
}
|
||||
|
||||
|
||||
bool WebSocketClient::handleStream(String& data, uint8_t *opcode) {
|
||||
uint8_t msgtype;
|
||||
uint8_t bite;
|
||||
unsigned int length;
|
||||
uint8_t mask[4];
|
||||
uint8_t index;
|
||||
unsigned int i;
|
||||
bool hasMask = false;
|
||||
|
||||
if (!socket_client->connected() || !socket_client->available())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
msgtype = timedRead();
|
||||
if (!socket_client->connected()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
length = timedRead();
|
||||
|
||||
if (length & WS_MASK) {
|
||||
hasMask = true;
|
||||
length = length & ~WS_MASK;
|
||||
}
|
||||
|
||||
|
||||
if (!socket_client->connected()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
index = 6;
|
||||
|
||||
if (length == WS_SIZE16) {
|
||||
length = timedRead() << 8;
|
||||
if (!socket_client->connected()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
length |= timedRead();
|
||||
if (!socket_client->connected()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
} else if (length == WS_SIZE64) {
|
||||
#ifdef DEBUGGING
|
||||
Serial.println(F("No support for over 16 bit sized messages"));
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hasMask) {
|
||||
// get the mask
|
||||
mask[0] = timedRead();
|
||||
if (!socket_client->connected()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mask[1] = timedRead();
|
||||
if (!socket_client->connected()) {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
mask[2] = timedRead();
|
||||
if (!socket_client->connected()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mask[3] = timedRead();
|
||||
if (!socket_client->connected()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
data = "";
|
||||
|
||||
if (opcode != NULL)
|
||||
{
|
||||
*opcode = msgtype & ~WS_FIN;
|
||||
}
|
||||
|
||||
if (hasMask) {
|
||||
for (i=0; i<length; ++i) {
|
||||
data += (char) (timedRead() ^ mask[i % 4]);
|
||||
if (!socket_client->connected()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (i=0; i<length; ++i) {
|
||||
data += (char) timedRead();
|
||||
if (!socket_client->connected()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WebSocketClient::handleStream(char *data, uint8_t *opcode) {
|
||||
uint8_t msgtype;
|
||||
uint8_t bite;
|
||||
unsigned int length;
|
||||
uint8_t mask[4];
|
||||
uint8_t index;
|
||||
unsigned int i;
|
||||
bool hasMask = false;
|
||||
|
||||
if (!socket_client->connected() || !socket_client->available())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
msgtype = timedRead();
|
||||
if (!socket_client->connected()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
length = timedRead();
|
||||
|
||||
if (length & WS_MASK) {
|
||||
hasMask = true;
|
||||
length = length & ~WS_MASK;
|
||||
}
|
||||
|
||||
|
||||
if (!socket_client->connected()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
index = 6;
|
||||
|
||||
if (length == WS_SIZE16) {
|
||||
length = timedRead() << 8;
|
||||
if (!socket_client->connected()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
length |= timedRead();
|
||||
if (!socket_client->connected()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
} else if (length == WS_SIZE64) {
|
||||
#ifdef DEBUGGING
|
||||
Serial.println(F("No support for over 16 bit sized messages"));
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hasMask) {
|
||||
// get the mask
|
||||
mask[0] = timedRead();
|
||||
if (!socket_client->connected()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mask[1] = timedRead();
|
||||
if (!socket_client->connected()) {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
mask[2] = timedRead();
|
||||
if (!socket_client->connected()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mask[3] = timedRead();
|
||||
if (!socket_client->connected()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
strcpy(data, "");
|
||||
|
||||
if (opcode != NULL)
|
||||
{
|
||||
*opcode = msgtype & ~WS_FIN;
|
||||
}
|
||||
|
||||
if (hasMask) {
|
||||
for (i=0; i<length; ++i) {
|
||||
sprintf(data, "%s%c", data, (char) (timedRead() ^ mask[i % 4]));
|
||||
if (!socket_client->connected()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (i=0; i<length; ++i) {
|
||||
sprintf(data, "%s%c", data, (char) timedRead());
|
||||
if (!socket_client->connected()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void WebSocketClient::disconnectStream() {
|
||||
#ifdef DEBUGGING
|
||||
Serial.println(F("Terminating socket"));
|
||||
#endif
|
||||
// Should send 0x8700 to server to tell it I'm quitting here.
|
||||
socket_client->write((uint8_t) 0x87);
|
||||
socket_client->write((uint8_t) 0x00);
|
||||
|
||||
socket_client->flush();
|
||||
delay(10);
|
||||
socket_client->stop();
|
||||
}
|
||||
|
||||
bool WebSocketClient::getData(String& data, uint8_t *opcode) {
|
||||
return handleStream(data, opcode);
|
||||
}
|
||||
|
||||
bool WebSocketClient::getData(char *data, uint8_t *opcode) {
|
||||
return handleStream(data, opcode);
|
||||
}
|
||||
|
||||
void WebSocketClient::sendData(const char *str, uint8_t opcode, bool fast) {
|
||||
#ifdef DEBUGGING
|
||||
Serial.print(F("Sending data: "));
|
||||
Serial.println(str);
|
||||
#endif
|
||||
if (socket_client->connected()) {
|
||||
if (fast) {
|
||||
sendEncodedDataFast(str, opcode);
|
||||
} else {
|
||||
sendEncodedData(str, opcode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WebSocketClient::sendData(String str, uint8_t opcode, bool fast) {
|
||||
#ifdef DEBUGGING
|
||||
Serial.print(F("Sending data: "));
|
||||
Serial.println(str);
|
||||
#endif
|
||||
if (socket_client->connected()) {
|
||||
if (fast) {
|
||||
sendEncodedDataFast(str, opcode);
|
||||
} else {
|
||||
sendEncodedData(str, opcode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int WebSocketClient::timedRead() {
|
||||
while (!socket_client->available()) {
|
||||
//delay(20);
|
||||
}
|
||||
|
||||
return socket_client->read();
|
||||
}
|
||||
|
||||
void WebSocketClient::sendEncodedData(char *str, uint8_t opcode) {
|
||||
uint8_t mask[4];
|
||||
int size = strlen(str);
|
||||
|
||||
// Opcode; final fragment
|
||||
socket_client->write(opcode | WS_FIN);
|
||||
|
||||
// NOTE: no support for > 16-bit sized messages
|
||||
if (size > 125) {
|
||||
socket_client->write(WS_SIZE16 | WS_MASK);
|
||||
socket_client->write((uint8_t) (size >> 8));
|
||||
socket_client->write((uint8_t) (size & 0xFF));
|
||||
} else {
|
||||
socket_client->write((uint8_t) size | WS_MASK);
|
||||
}
|
||||
|
||||
if (WS_MASK > 0) {
|
||||
//Serial.println("MASK");
|
||||
mask[0] = random(0, 256);
|
||||
mask[1] = random(0, 256);
|
||||
mask[2] = random(0, 256);
|
||||
mask[3] = random(0, 256);
|
||||
|
||||
socket_client->write(mask[0]);
|
||||
socket_client->write(mask[1]);
|
||||
socket_client->write(mask[2]);
|
||||
socket_client->write(mask[3]);
|
||||
}
|
||||
|
||||
for (int i=0; i<size; ++i) {
|
||||
if (WS_MASK > 0) {
|
||||
//Serial.println("send with MASK");
|
||||
//delay(20);
|
||||
socket_client->write(str[i] ^ mask[i % 4]);
|
||||
} else {
|
||||
socket_client->write(str[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WebSocketClient::sendEncodedDataFast(char *str, uint8_t opcode) {
|
||||
uint8_t mask[4];
|
||||
int size = strlen(str);
|
||||
int size_buf = size + 1;
|
||||
if (size > 125) {
|
||||
size_buf += 3;
|
||||
} else {
|
||||
size_buf += 1;
|
||||
}
|
||||
if (WS_MASK > 0) {
|
||||
size_buf += 4;
|
||||
}
|
||||
|
||||
char buf[size_buf];
|
||||
char tmp[2];
|
||||
|
||||
// Opcode; final fragment
|
||||
sprintf(tmp, "%c", (char)(opcode | WS_FIN));
|
||||
strcpy(buf, tmp);
|
||||
|
||||
// NOTE: no support for > 16-bit sized messages
|
||||
if (size > 125) {
|
||||
sprintf(tmp, "%c", (char)(WS_SIZE16 | WS_MASK));
|
||||
strcat(buf, tmp);
|
||||
sprintf(tmp, "%c", (char) (size >> 8));
|
||||
strcat(buf, tmp);
|
||||
sprintf(tmp, "%c", (char) (size & 0xFF));
|
||||
strcat(buf, tmp);
|
||||
} else {
|
||||
sprintf(tmp, "%c", (char) size | WS_MASK);
|
||||
strcat(buf, tmp);
|
||||
}
|
||||
|
||||
if (WS_MASK > 0) {
|
||||
mask[0] = random(0, 256);
|
||||
mask[1] = random(0, 256);
|
||||
mask[2] = random(0, 256);
|
||||
mask[3] = random(0, 256);
|
||||
|
||||
sprintf(tmp, "%c", (char) mask[0]);
|
||||
strcat(buf, tmp);
|
||||
sprintf(tmp, "%c", (char) mask[1]);
|
||||
strcat(buf, tmp);
|
||||
sprintf(tmp, "%c", (char) mask[2]);
|
||||
strcat(buf, tmp);
|
||||
sprintf(tmp, "%c", (char) mask[3]);
|
||||
strcat(buf, tmp);
|
||||
|
||||
for (int i=0; i<size; ++i) {
|
||||
str[i] = str[i] ^ mask[i % 4];
|
||||
}
|
||||
}
|
||||
|
||||
strcat(buf, str);
|
||||
socket_client->write((uint8_t*)buf, size_buf);
|
||||
}
|
||||
|
||||
|
||||
void WebSocketClient::sendEncodedData(String str, uint8_t opcode) {
|
||||
int size = str.length() + 1;
|
||||
char cstr[size];
|
||||
|
||||
str.toCharArray(cstr, size);
|
||||
|
||||
sendEncodedData(cstr, opcode);
|
||||
}
|
||||
|
||||
|
||||
void WebSocketClient::sendEncodedDataFast(String str, uint8_t opcode) {
|
||||
int size = str.length() + 1;
|
||||
char cstr[size];
|
||||
|
||||
str.toCharArray(cstr, size);
|
||||
|
||||
sendEncodedDataFast(cstr, opcode);
|
||||
}
|
||||
135
libraries/Arduino-Websocket-Fast/src/WebSocketClient.h
Normal file
135
libraries/Arduino-Websocket-Fast/src/WebSocketClient.h
Normal file
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
Websocket-Arduino, a websocket implementation for Arduino
|
||||
Copyright 2016 Brendan Hall
|
||||
|
||||
Based on previous implementations by
|
||||
Copyright 2011 Brendan Hall
|
||||
and
|
||||
Copyright 2010 Ben Swanson
|
||||
and
|
||||
Copyright 2010 Randall Brewer
|
||||
and
|
||||
Copyright 2010 Oliver Smith
|
||||
|
||||
Some code and concept based off of Webduino library
|
||||
Copyright 2009 Ben Combee, Ran Talbott
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
-------------
|
||||
Now based off
|
||||
http://www.whatwg.org/specs/web-socket-protocol/
|
||||
|
||||
- OLD -
|
||||
Currently based off of "The Web Socket protocol" draft (v 75):
|
||||
http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-75
|
||||
*/
|
||||
|
||||
|
||||
#ifndef WEBSOCKETCLIENT_H_
|
||||
#define WEBSOCKETCLIENT_H_
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <Stream.h>
|
||||
#include "String.h"
|
||||
#include "Client.h"
|
||||
|
||||
// CRLF characters to terminate lines/handshakes in headers.
|
||||
#define CRLF "\r\n"
|
||||
|
||||
// Amount of time (in ms) a user may be connected before getting disconnected
|
||||
// for timing out (i.e. not sending any data to the server).
|
||||
#define TIMEOUT_IN_MS 10000
|
||||
|
||||
// ACTION_SPACE is how many actions are allowed in a program. Defaults to
|
||||
// 5 unless overwritten by user.
|
||||
#ifndef CALLBACK_FUNCTIONS
|
||||
#define CALLBACK_FUNCTIONS 1
|
||||
#endif
|
||||
|
||||
// Don't allow the client to send big frames of data. This will flood the Arduinos
|
||||
// memory and might even crash it.
|
||||
#ifndef MAX_FRAME_LENGTH
|
||||
#define MAX_FRAME_LENGTH 256
|
||||
#endif
|
||||
|
||||
#define SIZE(array) (sizeof(array) / sizeof(*array))
|
||||
|
||||
// WebSocket protocol constants
|
||||
// First byte
|
||||
#define WS_FIN 0x80
|
||||
#define WS_OPCODE_TEXT 0x01
|
||||
#define WS_OPCODE_BINARY 0x02
|
||||
#define WS_OPCODE_CLOSE 0x08
|
||||
#define WS_OPCODE_PING 0x09
|
||||
#define WS_OPCODE_PONG 0x0a
|
||||
// Second byte
|
||||
#define WS_MASK 0x80
|
||||
//#define WS_MASK 0x00
|
||||
#define WS_SIZE16 126
|
||||
#define WS_SIZE64 127
|
||||
|
||||
|
||||
class WebSocketClient {
|
||||
public:
|
||||
|
||||
// Handle connection requests to validate and process/refuse
|
||||
// connections.
|
||||
bool handshake(Client &client);
|
||||
|
||||
// Get data off of the stream
|
||||
bool getData(String& data, uint8_t *opcode = NULL);
|
||||
bool getData(char *data, uint8_t *opcode = NULL);
|
||||
|
||||
// Write data to the stream
|
||||
void sendData(const char *str, uint8_t opcode = WS_OPCODE_TEXT, bool fast = true);
|
||||
void sendData(String str, uint8_t opcode = WS_OPCODE_TEXT, bool fast = true);
|
||||
|
||||
char *path;
|
||||
char *host;
|
||||
char *protocol;
|
||||
|
||||
private:
|
||||
Client *socket_client;
|
||||
unsigned long _startMillis;
|
||||
|
||||
const char *socket_urlPrefix;
|
||||
|
||||
// Discovers if the client's header is requesting an upgrade to a
|
||||
// websocket connection.
|
||||
bool analyzeRequest();
|
||||
|
||||
bool handleStream(String& data, uint8_t *opcode);
|
||||
bool handleStream(char *data, uint8_t *opcode);
|
||||
|
||||
// Disconnect user gracefully.
|
||||
void disconnectStream();
|
||||
|
||||
int timedRead();
|
||||
|
||||
void sendEncodedData(char *str, uint8_t opcode);
|
||||
void sendEncodedData(String str, uint8_t opcode);
|
||||
|
||||
void sendEncodedDataFast(char *str, uint8_t opcode);
|
||||
void sendEncodedDataFast(String str, uint8_t opcode);
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
33
libraries/Arduino-Websocket-Fast/src/global.h
Normal file
33
libraries/Arduino-Websocket-Fast/src/global.h
Normal file
@@ -0,0 +1,33 @@
|
||||
/* GLOBAL.H - RSAREF types and constants */
|
||||
|
||||
/* PROTOTYPES should be set to one if and only if the compiler
|
||||
* supports function argument prototyping.
|
||||
* The following makes PROTOTYPES default to 0 if it has not already
|
||||
* been defined with C compiler flags.
|
||||
*/
|
||||
#ifndef PROTOTYPES
|
||||
#define PROTOTYPES 0
|
||||
#endif
|
||||
|
||||
/*Modified by MMoore http://mikestechspot.blogspot.com
|
||||
Changed typedefs to be fully compatible w/ Arduino 08/09/2010 */
|
||||
|
||||
/* POINTER defines a generic pointer type */
|
||||
typedef unsigned char *POINTER;
|
||||
|
||||
/* UINT2 defines a two byte word */
|
||||
typedef unsigned int UINT2;
|
||||
|
||||
/* UINT4 defines a four byte word */
|
||||
typedef unsigned long UINT4;
|
||||
|
||||
/* PROTO_LIST is defined depending on how PROTOTYPES is defined above.
|
||||
* If using PROTOTYPES, then PROTO_LIST returns the list, otherwise it
|
||||
* returns an empty list.
|
||||
*/
|
||||
#if PROTOTYPES
|
||||
#define PROTO_LIST(list) list
|
||||
#else
|
||||
#define PROTO_LIST(list) ()
|
||||
#endif
|
||||
|
||||
156
libraries/Arduino-Websocket-Fast/src/sha1.cpp
Normal file
156
libraries/Arduino-Websocket-Fast/src/sha1.cpp
Normal file
@@ -0,0 +1,156 @@
|
||||
#include <string.h>
|
||||
//#ifdef ARDUINO_SAMD_ZERO
|
||||
//#else
|
||||
#ifdef __AVR__
|
||||
#include <avr/io.h>
|
||||
#include <avr/pgmspace.h>
|
||||
#endif
|
||||
#include "sha1.h"
|
||||
|
||||
#define SHA1_K0 0x5a827999
|
||||
#define SHA1_K20 0x6ed9eba1
|
||||
#define SHA1_K40 0x8f1bbcdc
|
||||
#define SHA1_K60 0xca62c1d6
|
||||
|
||||
const uint8_t sha1InitState[] PROGMEM = {
|
||||
0x01,0x23,0x45,0x67, // H0
|
||||
0x89,0xab,0xcd,0xef, // H1
|
||||
0xfe,0xdc,0xba,0x98, // H2
|
||||
0x76,0x54,0x32,0x10, // H3
|
||||
0xf0,0xe1,0xd2,0xc3 // H4
|
||||
};
|
||||
|
||||
void Sha1Class::init(void) {
|
||||
memcpy_P(state.b,sha1InitState,HASH_LENGTH);
|
||||
byteCount = 0;
|
||||
bufferOffset = 0;
|
||||
}
|
||||
|
||||
uint32_t Sha1Class::rol32(uint32_t number, uint8_t bits) {
|
||||
return ((number << bits) | (number >> (32-bits)));
|
||||
}
|
||||
|
||||
void Sha1Class::hashBlock() {
|
||||
uint8_t i;
|
||||
uint32_t a,b,c,d,e,t;
|
||||
|
||||
a=state.w[0];
|
||||
b=state.w[1];
|
||||
c=state.w[2];
|
||||
d=state.w[3];
|
||||
e=state.w[4];
|
||||
for (i=0; i<80; i++) {
|
||||
if (i>=16) {
|
||||
t = buffer.w[(i+13)&15] ^ buffer.w[(i+8)&15] ^ buffer.w[(i+2)&15] ^ buffer.w[i&15];
|
||||
buffer.w[i&15] = rol32(t,1);
|
||||
}
|
||||
if (i<20) {
|
||||
t = (d ^ (b & (c ^ d))) + SHA1_K0;
|
||||
} else if (i<40) {
|
||||
t = (b ^ c ^ d) + SHA1_K20;
|
||||
} else if (i<60) {
|
||||
t = ((b & c) | (d & (b | c))) + SHA1_K40;
|
||||
} else {
|
||||
t = (b ^ c ^ d) + SHA1_K60;
|
||||
}
|
||||
t+=rol32(a,5) + e + buffer.w[i&15];
|
||||
e=d;
|
||||
d=c;
|
||||
c=rol32(b,30);
|
||||
b=a;
|
||||
a=t;
|
||||
}
|
||||
state.w[0] += a;
|
||||
state.w[1] += b;
|
||||
state.w[2] += c;
|
||||
state.w[3] += d;
|
||||
state.w[4] += e;
|
||||
}
|
||||
|
||||
void Sha1Class::addUncounted(uint8_t data) {
|
||||
buffer.b[bufferOffset ^ 3] = data;
|
||||
bufferOffset++;
|
||||
if (bufferOffset == BLOCK_LENGTH) {
|
||||
hashBlock();
|
||||
bufferOffset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
size_t Sha1Class::write(uint8_t data) {
|
||||
++byteCount;
|
||||
addUncounted(data);
|
||||
|
||||
return sizeof(data);
|
||||
}
|
||||
|
||||
void Sha1Class::pad() {
|
||||
// Implement SHA-1 padding (fips180-2 §5.1.1)
|
||||
|
||||
// Pad with 0x80 followed by 0x00 until the end of the block
|
||||
addUncounted(0x80);
|
||||
while (bufferOffset != 56) addUncounted(0x00);
|
||||
|
||||
// Append length in the last 8 bytes
|
||||
addUncounted(0); // We're only using 32 bit lengths
|
||||
addUncounted(0); // But SHA-1 supports 64 bit lengths
|
||||
addUncounted(0); // So zero pad the top bits
|
||||
addUncounted(byteCount >> 29); // Shifting to multiply by 8
|
||||
addUncounted(byteCount >> 21); // as SHA-1 supports bitstreams as well as
|
||||
addUncounted(byteCount >> 13); // byte.
|
||||
addUncounted(byteCount >> 5);
|
||||
addUncounted(byteCount << 3);
|
||||
}
|
||||
|
||||
|
||||
uint8_t* Sha1Class::result(void) {
|
||||
// Pad to complete the last block
|
||||
pad();
|
||||
|
||||
// Swap byte order back
|
||||
for (int i=0; i<5; i++) {
|
||||
uint32_t a,b;
|
||||
a=state.w[i];
|
||||
b=a<<24;
|
||||
b|=(a<<8) & 0x00ff0000;
|
||||
b|=(a>>8) & 0x0000ff00;
|
||||
b|=a>>24;
|
||||
state.w[i]=b;
|
||||
}
|
||||
|
||||
// Return pointer to hash (20 characters)
|
||||
return state.b;
|
||||
}
|
||||
|
||||
#define HMAC_IPAD 0x36
|
||||
#define HMAC_OPAD 0x5c
|
||||
|
||||
void Sha1Class::initHmac(const uint8_t* key, int keyLength) {
|
||||
uint8_t i;
|
||||
memset(keyBuffer,0,BLOCK_LENGTH);
|
||||
if (keyLength > BLOCK_LENGTH) {
|
||||
// Hash long keys
|
||||
init();
|
||||
for (;keyLength--;) write(*key++);
|
||||
memcpy(keyBuffer,result(),HASH_LENGTH);
|
||||
} else {
|
||||
// Block length keys are used as is
|
||||
memcpy(keyBuffer,key,keyLength);
|
||||
}
|
||||
// Start inner hash
|
||||
init();
|
||||
for (i=0; i<BLOCK_LENGTH; i++) {
|
||||
write(keyBuffer[i] ^ HMAC_IPAD);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t* Sha1Class::resultHmac(void) {
|
||||
uint8_t i;
|
||||
// Complete inner hash
|
||||
memcpy(innerHash,result(),HASH_LENGTH);
|
||||
// Calculate outer hash
|
||||
init();
|
||||
for (i=0; i<BLOCK_LENGTH; i++) write(keyBuffer[i] ^ HMAC_OPAD);
|
||||
for (i=0; i<HASH_LENGTH; i++) write(innerHash[i]);
|
||||
return result();
|
||||
}
|
||||
Sha1Class Sha1;
|
||||
43
libraries/Arduino-Websocket-Fast/src/sha1.h
Normal file
43
libraries/Arduino-Websocket-Fast/src/sha1.h
Normal file
@@ -0,0 +1,43 @@
|
||||
#ifndef Sha1_h
|
||||
#define Sha1_h
|
||||
|
||||
#include <inttypes.h>
|
||||
#include "Print.h"
|
||||
|
||||
#define HASH_LENGTH 20
|
||||
#define BLOCK_LENGTH 64
|
||||
|
||||
union _buffer {
|
||||
uint8_t b[BLOCK_LENGTH];
|
||||
uint32_t w[BLOCK_LENGTH/4];
|
||||
};
|
||||
union _state {
|
||||
uint8_t b[HASH_LENGTH];
|
||||
uint32_t w[HASH_LENGTH/4];
|
||||
};
|
||||
|
||||
class Sha1Class : public Print
|
||||
{
|
||||
public:
|
||||
void init(void);
|
||||
void initHmac(const uint8_t* secret, int secretLength);
|
||||
uint8_t* result(void);
|
||||
uint8_t* resultHmac(void);
|
||||
virtual size_t write(uint8_t);
|
||||
using Print::write;
|
||||
private:
|
||||
void pad();
|
||||
void addUncounted(uint8_t data);
|
||||
void hashBlock();
|
||||
uint32_t rol32(uint32_t number, uint8_t bits);
|
||||
_buffer buffer;
|
||||
uint8_t bufferOffset;
|
||||
_state state;
|
||||
uint32_t byteCount;
|
||||
uint8_t keyBuffer[BLOCK_LENGTH];
|
||||
uint8_t innerHash[HASH_LENGTH];
|
||||
|
||||
};
|
||||
extern Sha1Class Sha1;
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user