+#endif
+#include "ESPAsyncWebServer.h"
+
+DNSServer dnsServer;
+AsyncWebServer server(80);
+
+class CaptiveRequestHandler : public AsyncWebHandler {
+public:
+ CaptiveRequestHandler() {}
+ virtual ~CaptiveRequestHandler() {}
+
+ bool canHandle(AsyncWebServerRequest *request){
+ //request->addInterestingHeader("ANY");
+ return true;
+ }
+
+ void handleRequest(AsyncWebServerRequest *request) {
+ AsyncResponseStream *response = request->beginResponseStream("text/html");
+ response->print("Captive Portal");
+ response->print("This is out captive portal front page.
");
+ response->printf("You were trying to reach: http://%s%s
", request->host().c_str(), request->url().c_str());
+ response->printf("Try opening this link instead
", WiFi.softAPIP().toString().c_str());
+ response->print("");
+ request->send(response);
+ }
+};
+
+
+void setup(){
+ //your other setup stuff...
+ WiFi.softAP("esp-captive");
+ dnsServer.start(53, "*", WiFi.softAPIP());
+ server.addHandler(new CaptiveRequestHandler()).setFilter(ON_AP_FILTER);//only when requested from AP
+ //more handlers...
+ server.begin();
+}
+
+void loop(){
+ dnsServer.processNextRequest();
+}
diff --git a/lib/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/ESP_AsyncFSBrowser.ino b/lib/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/ESP_AsyncFSBrowser.ino
new file mode 100644
index 00000000..bf42d003
--- /dev/null
+++ b/lib/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/ESP_AsyncFSBrowser.ino
@@ -0,0 +1,221 @@
+#include
+#ifdef ESP32
+#include
+#include
+#include
+#include
+#include
+#elif defined(ESP8266)
+#include
+#include
+#include
+#endif
+#include
+#include
+
+// SKETCH BEGIN
+AsyncWebServer server(80);
+AsyncWebSocket ws("/ws");
+AsyncEventSource events("/events");
+
+void onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len){
+ if(type == WS_EVT_CONNECT){
+ Serial.printf("ws[%s][%u] connect\n", server->url(), client->id());
+ client->printf("Hello Client %u :)", client->id());
+ client->ping();
+ } else if(type == WS_EVT_DISCONNECT){
+ Serial.printf("ws[%s][%u] disconnect\n", server->url(), client->id());
+ } else if(type == WS_EVT_ERROR){
+ Serial.printf("ws[%s][%u] error(%u): %s\n", server->url(), client->id(), *((uint16_t*)arg), (char*)data);
+ } else if(type == WS_EVT_PONG){
+ Serial.printf("ws[%s][%u] pong[%u]: %s\n", server->url(), client->id(), len, (len)?(char*)data:"");
+ } else if(type == WS_EVT_DATA){
+ AwsFrameInfo * info = (AwsFrameInfo*)arg;
+ String msg = "";
+ if(info->final && info->index == 0 && info->len == len){
+ //the whole message is in a single frame and we got all of it's data
+ Serial.printf("ws[%s][%u] %s-message[%llu]: ", server->url(), client->id(), (info->opcode == WS_TEXT)?"text":"binary", info->len);
+
+ if(info->opcode == WS_TEXT){
+ for(size_t i=0; i < info->len; i++) {
+ msg += (char) data[i];
+ }
+ } else {
+ char buff[3];
+ for(size_t i=0; i < info->len; i++) {
+ sprintf(buff, "%02x ", (uint8_t) data[i]);
+ msg += buff ;
+ }
+ }
+ Serial.printf("%s\n",msg.c_str());
+
+ if(info->opcode == WS_TEXT)
+ client->text("I got your text message");
+ else
+ client->binary("I got your binary message");
+ } else {
+ //message is comprised of multiple frames or the frame is split into multiple packets
+ if(info->index == 0){
+ if(info->num == 0)
+ Serial.printf("ws[%s][%u] %s-message start\n", server->url(), client->id(), (info->message_opcode == WS_TEXT)?"text":"binary");
+ Serial.printf("ws[%s][%u] frame[%u] start[%llu]\n", server->url(), client->id(), info->num, info->len);
+ }
+
+ Serial.printf("ws[%s][%u] frame[%u] %s[%llu - %llu]: ", server->url(), client->id(), info->num, (info->message_opcode == WS_TEXT)?"text":"binary", info->index, info->index + len);
+
+ if(info->opcode == WS_TEXT){
+ for(size_t i=0; i < len; i++) {
+ msg += (char) data[i];
+ }
+ } else {
+ char buff[3];
+ for(size_t i=0; i < len; i++) {
+ sprintf(buff, "%02x ", (uint8_t) data[i]);
+ msg += buff ;
+ }
+ }
+ Serial.printf("%s\n",msg.c_str());
+
+ if((info->index + len) == info->len){
+ Serial.printf("ws[%s][%u] frame[%u] end[%llu]\n", server->url(), client->id(), info->num, info->len);
+ if(info->final){
+ Serial.printf("ws[%s][%u] %s-message end\n", server->url(), client->id(), (info->message_opcode == WS_TEXT)?"text":"binary");
+ if(info->message_opcode == WS_TEXT)
+ client->text("I got your text message");
+ else
+ client->binary("I got your binary message");
+ }
+ }
+ }
+ }
+}
+
+
+const char* ssid = "*******";
+const char* password = "*******";
+const char * hostName = "esp-async";
+const char* http_username = "admin";
+const char* http_password = "admin";
+
+void setup(){
+ Serial.begin(115200);
+ Serial.setDebugOutput(true);
+ WiFi.mode(WIFI_AP_STA);
+ WiFi.softAP(hostName);
+ WiFi.begin(ssid, password);
+ if (WiFi.waitForConnectResult() != WL_CONNECTED) {
+ Serial.printf("STA: Failed!\n");
+ WiFi.disconnect(false);
+ delay(1000);
+ WiFi.begin(ssid, password);
+ }
+
+ //Send OTA events to the browser
+ ArduinoOTA.onStart([]() { events.send("Update Start", "ota"); });
+ ArduinoOTA.onEnd([]() { events.send("Update End", "ota"); });
+ ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
+ char p[32];
+ sprintf(p, "Progress: %u%%\n", (progress/(total/100)));
+ events.send(p, "ota");
+ });
+ ArduinoOTA.onError([](ota_error_t error) {
+ if(error == OTA_AUTH_ERROR) events.send("Auth Failed", "ota");
+ else if(error == OTA_BEGIN_ERROR) events.send("Begin Failed", "ota");
+ else if(error == OTA_CONNECT_ERROR) events.send("Connect Failed", "ota");
+ else if(error == OTA_RECEIVE_ERROR) events.send("Recieve Failed", "ota");
+ else if(error == OTA_END_ERROR) events.send("End Failed", "ota");
+ });
+ ArduinoOTA.setHostname(hostName);
+ ArduinoOTA.begin();
+
+ MDNS.addService("http","tcp",80);
+
+ SPIFFS.begin();
+
+ ws.onEvent(onWsEvent);
+ server.addHandler(&ws);
+
+ events.onConnect([](AsyncEventSourceClient *client){
+ client->send("hello!",NULL,millis(),1000);
+ });
+ server.addHandler(&events);
+
+#ifdef ESP32
+ server.addHandler(new SPIFFSEditor(SPIFFS, http_username,http_password));
+#elif defined(ESP8266)
+ server.addHandler(new SPIFFSEditor(http_username,http_password));
+#endif
+
+ server.on("/heap", HTTP_GET, [](AsyncWebServerRequest *request){
+ request->send(200, "text/plain", String(ESP.getFreeHeap()));
+ });
+
+ server.serveStatic("/", SPIFFS, "/").setDefaultFile("index.htm");
+
+ server.onNotFound([](AsyncWebServerRequest *request){
+ Serial.printf("NOT_FOUND: ");
+ if(request->method() == HTTP_GET)
+ Serial.printf("GET");
+ else if(request->method() == HTTP_POST)
+ Serial.printf("POST");
+ else if(request->method() == HTTP_DELETE)
+ Serial.printf("DELETE");
+ else if(request->method() == HTTP_PUT)
+ Serial.printf("PUT");
+ else if(request->method() == HTTP_PATCH)
+ Serial.printf("PATCH");
+ else if(request->method() == HTTP_HEAD)
+ Serial.printf("HEAD");
+ else if(request->method() == HTTP_OPTIONS)
+ Serial.printf("OPTIONS");
+ else
+ Serial.printf("UNKNOWN");
+ Serial.printf(" http://%s%s\n", request->host().c_str(), request->url().c_str());
+
+ if(request->contentLength()){
+ Serial.printf("_CONTENT_TYPE: %s\n", request->contentType().c_str());
+ Serial.printf("_CONTENT_LENGTH: %u\n", request->contentLength());
+ }
+
+ int headers = request->headers();
+ int i;
+ for(i=0;igetHeader(i);
+ Serial.printf("_HEADER[%s]: %s\n", h->name().c_str(), h->value().c_str());
+ }
+
+ int params = request->params();
+ for(i=0;igetParam(i);
+ if(p->isFile()){
+ Serial.printf("_FILE[%s]: %s, size: %u\n", p->name().c_str(), p->value().c_str(), p->size());
+ } else if(p->isPost()){
+ Serial.printf("_POST[%s]: %s\n", p->name().c_str(), p->value().c_str());
+ } else {
+ Serial.printf("_GET[%s]: %s\n", p->name().c_str(), p->value().c_str());
+ }
+ }
+
+ request->send(404);
+ });
+ server.onFileUpload([](AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final){
+ if(!index)
+ Serial.printf("UploadStart: %s\n", filename.c_str());
+ Serial.printf("%s", (const char*)data);
+ if(final)
+ Serial.printf("UploadEnd: %s (%u)\n", filename.c_str(), index+len);
+ });
+ server.onRequestBody([](AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total){
+ if(!index)
+ Serial.printf("BodyStart: %u\n", total);
+ Serial.printf("%s", (const char*)data);
+ if(index + len == total)
+ Serial.printf("BodyEnd: %u\n", total);
+ });
+ server.begin();
+}
+
+void loop(){
+ ArduinoOTA.handle();
+ ws.cleanupClients();
+}
diff --git a/lib/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/.exclude.files b/lib/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/.exclude.files
new file mode 100644
index 00000000..955397fa
--- /dev/null
+++ b/lib/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/.exclude.files
@@ -0,0 +1,2 @@
+/*.js.gz
+/.exclude.files
diff --git a/lib/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/ace.js.gz b/lib/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/ace.js.gz
new file mode 100644
index 00000000..7b175c1c
Binary files /dev/null and b/lib/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/ace.js.gz differ
diff --git a/lib/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/ext-searchbox.js.gz b/lib/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/ext-searchbox.js.gz
new file mode 100644
index 00000000..cf5b49f6
Binary files /dev/null and b/lib/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/ext-searchbox.js.gz differ
diff --git a/lib/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/favicon.ico b/lib/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/favicon.ico
new file mode 100644
index 00000000..71b25fe6
Binary files /dev/null and b/lib/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/favicon.ico differ
diff --git a/lib/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/index.htm b/lib/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/index.htm
new file mode 100644
index 00000000..28f47e99
--- /dev/null
+++ b/lib/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/index.htm
@@ -0,0 +1,131 @@
+
+
+
+
+
+ WebSocketTester
+
+
+
+
+
+
+ $
+
+
+
diff --git a/lib/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/mode-css.js.gz b/lib/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/mode-css.js.gz
new file mode 100644
index 00000000..ebd6fe94
Binary files /dev/null and b/lib/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/mode-css.js.gz differ
diff --git a/lib/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/mode-html.js.gz b/lib/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/mode-html.js.gz
new file mode 100644
index 00000000..26b53532
Binary files /dev/null and b/lib/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/mode-html.js.gz differ
diff --git a/lib/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/mode-javascript.js.gz b/lib/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/mode-javascript.js.gz
new file mode 100644
index 00000000..c0451c1c
Binary files /dev/null and b/lib/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/mode-javascript.js.gz differ
diff --git a/lib/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/worker-html.js.gz b/lib/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/worker-html.js.gz
new file mode 100644
index 00000000..ec8aa87a
Binary files /dev/null and b/lib/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/worker-html.js.gz differ
diff --git a/lib/ESPAsyncWebServer/examples/regex_patterns/.test.build_flags b/lib/ESPAsyncWebServer/examples/regex_patterns/.test.build_flags
new file mode 100644
index 00000000..9ea3bb74
--- /dev/null
+++ b/lib/ESPAsyncWebServer/examples/regex_patterns/.test.build_flags
@@ -0,0 +1 @@
+-DASYNCWEBSERVER_REGEX=1
diff --git a/lib/ESPAsyncWebServer/examples/regex_patterns/regex_patterns.ino b/lib/ESPAsyncWebServer/examples/regex_patterns/regex_patterns.ino
new file mode 100644
index 00000000..fb013063
--- /dev/null
+++ b/lib/ESPAsyncWebServer/examples/regex_patterns/regex_patterns.ino
@@ -0,0 +1,77 @@
+//
+// A simple server implementation with regex routes:
+// * serve static messages
+// * read GET and POST parameters
+// * handle missing pages / 404s
+//
+
+// Add buildflag ASYNCWEBSERVER_REGEX to enable the regex support
+
+// For platformio: platformio.ini:
+// build_flags =
+// -DASYNCWEBSERVER_REGEX
+
+// For arduino IDE: create/update platform.local.txt
+// Windows: C:\Users\(username)\AppData\Local\Arduino15\packages\espxxxx\hardware\espxxxx\{version}\platform.local.txt
+// Linux: ~/.arduino15/packages/espxxxx/hardware/espxxxx/{version}/platform.local.txt
+//
+// compiler.cpp.extra_flags=-DASYNCWEBSERVER_REGEX=1
+
+#include
+#ifdef ESP32
+#include
+#include
+#elif defined(ESP8266)
+#include
+#include
+#endif
+#include
+
+AsyncWebServer server(80);
+
+const char* ssid = "YOUR_SSID";
+const char* password = "YOUR_PASSWORD";
+
+const char* PARAM_MESSAGE = "message";
+
+void notFound(AsyncWebServerRequest *request) {
+ request->send(404, "text/plain", "Not found");
+}
+
+void setup() {
+
+ Serial.begin(115200);
+ WiFi.mode(WIFI_STA);
+ WiFi.begin(ssid, password);
+ if (WiFi.waitForConnectResult() != WL_CONNECTED) {
+ Serial.printf("WiFi Failed!\n");
+ return;
+ }
+
+ Serial.print("IP Address: ");
+ Serial.println(WiFi.localIP());
+
+ server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
+ request->send(200, "text/plain", "Hello, world");
+ });
+
+ // Send a GET request to /sensor/
+ server.on("^\\/sensor\\/([0-9]+)$", HTTP_GET, [] (AsyncWebServerRequest *request) {
+ String sensorNumber = request->pathArg(0);
+ request->send(200, "text/plain", "Hello, sensor: " + sensorNumber);
+ });
+
+ // Send a GET request to /sensor//action/
+ server.on("^\\/sensor\\/([0-9]+)\\/action\\/([a-zA-Z0-9]+)$", HTTP_GET, [] (AsyncWebServerRequest *request) {
+ String sensorNumber = request->pathArg(0);
+ String action = request->pathArg(1);
+ request->send(200, "text/plain", "Hello, sensor: " + sensorNumber + ", with action: " + action);
+ });
+
+ server.onNotFound(notFound);
+
+ server.begin();
+}
+
+void loop() {
+}
diff --git a/lib/ESPAsyncWebServer/examples/simple_server/simple_server.ino b/lib/ESPAsyncWebServer/examples/simple_server/simple_server.ino
new file mode 100644
index 00000000..bdbcf60d
--- /dev/null
+++ b/lib/ESPAsyncWebServer/examples/simple_server/simple_server.ino
@@ -0,0 +1,74 @@
+//
+// A simple server implementation showing how to:
+// * serve static messages
+// * read GET and POST parameters
+// * handle missing pages / 404s
+//
+
+#include
+#ifdef ESP32
+#include
+#include
+#elif defined(ESP8266)
+#include
+#include
+#endif
+#include
+
+AsyncWebServer server(80);
+
+const char* ssid = "YOUR_SSID";
+const char* password = "YOUR_PASSWORD";
+
+const char* PARAM_MESSAGE = "message";
+
+void notFound(AsyncWebServerRequest *request) {
+ request->send(404, "text/plain", "Not found");
+}
+
+void setup() {
+
+ Serial.begin(115200);
+ WiFi.mode(WIFI_STA);
+ WiFi.begin(ssid, password);
+ if (WiFi.waitForConnectResult() != WL_CONNECTED) {
+ Serial.printf("WiFi Failed!\n");
+ return;
+ }
+
+ Serial.print("IP Address: ");
+ Serial.println(WiFi.localIP());
+
+ server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
+ request->send(200, "text/plain", "Hello, world");
+ });
+
+ // Send a GET request to /get?message=
+ server.on("/get", HTTP_GET, [] (AsyncWebServerRequest *request) {
+ String message;
+ if (request->hasParam(PARAM_MESSAGE)) {
+ message = request->getParam(PARAM_MESSAGE)->value();
+ } else {
+ message = "No message sent";
+ }
+ request->send(200, "text/plain", "Hello, GET: " + message);
+ });
+
+ // Send a POST request to /post with a form field message set to
+ server.on("/post", HTTP_POST, [](AsyncWebServerRequest *request){
+ String message;
+ if (request->hasParam(PARAM_MESSAGE, true)) {
+ message = request->getParam(PARAM_MESSAGE, true)->value();
+ } else {
+ message = "No message sent";
+ }
+ request->send(200, "text/plain", "Hello, POST: " + message);
+ });
+
+ server.onNotFound(notFound);
+
+ server.begin();
+}
+
+void loop() {
+}
\ No newline at end of file
diff --git a/lib/ESPAsyncWebServer/keywords.txt b/lib/ESPAsyncWebServer/keywords.txt
new file mode 100644
index 00000000..c391e6c4
--- /dev/null
+++ b/lib/ESPAsyncWebServer/keywords.txt
@@ -0,0 +1,3 @@
+JsonArray KEYWORD1
+add KEYWORD2
+createArray KEYWORD3
diff --git a/lib/ESPAsyncWebServer/library.json b/lib/ESPAsyncWebServer/library.json
new file mode 100644
index 00000000..55659279
--- /dev/null
+++ b/lib/ESPAsyncWebServer/library.json
@@ -0,0 +1,33 @@
+{
+ "name":"ESP Async WebServer",
+ "description":"Asynchronous HTTP and WebSocket Server Library for ESP8266 and ESP32",
+ "keywords":"http,async,websocket,webserver",
+ "authors":
+ {
+ "name": "Hristo Gochkov",
+ "maintainer": true
+ },
+ "repository":
+ {
+ "type": "git",
+ "url": "https://github.com/me-no-dev/ESPAsyncWebServer.git"
+ },
+ "version": "1.2.3",
+ "license": "LGPL-3.0",
+ "frameworks": "arduino",
+ "platforms": ["espressif8266", "espressif32"],
+ "dependencies": [
+ {
+ "name": "ESPAsyncTCP",
+ "platforms": "espressif8266"
+ },
+ {
+ "name": "AsyncTCP",
+ "platforms": "espressif32"
+ },
+ {
+ "name": "Hash",
+ "platforms": "espressif8266"
+ }
+ ]
+}
diff --git a/lib/ESPAsyncWebServer/library.properties b/lib/ESPAsyncWebServer/library.properties
new file mode 100644
index 00000000..401b0417
--- /dev/null
+++ b/lib/ESPAsyncWebServer/library.properties
@@ -0,0 +1,9 @@
+name=ESP Async WebServer
+version=1.2.3
+author=Me-No-Dev
+maintainer=Me-No-Dev
+sentence=Async Web Server for ESP8266 and ESP31B
+paragraph=Async Web Server for ESP8266 and ESP31B
+category=Other
+url=https://github.com/me-no-dev/ESPAsyncWebServer
+architectures=*
diff --git a/lib/ESPAsyncWebServer/src/AsyncEventSource.cpp b/lib/ESPAsyncWebServer/src/AsyncEventSource.cpp
new file mode 100644
index 00000000..f2914df5
--- /dev/null
+++ b/lib/ESPAsyncWebServer/src/AsyncEventSource.cpp
@@ -0,0 +1,368 @@
+/*
+ Asynchronous WebServer library for Espressif MCUs
+
+ Copyright (c) 2016 Hristo Gochkov. All rights reserved.
+
+ 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 "Arduino.h"
+#include "AsyncEventSource.h"
+
+static String generateEventMessage(const char *message, const char *event, uint32_t id, uint32_t reconnect){
+ String ev = "";
+
+ if(reconnect){
+ ev += "retry: ";
+ ev += String(reconnect);
+ ev += "\r\n";
+ }
+
+ if(id){
+ ev += "id: ";
+ ev += String(id);
+ ev += "\r\n";
+ }
+
+ if(event != NULL){
+ ev += "event: ";
+ ev += String(event);
+ ev += "\r\n";
+ }
+
+ if(message != NULL){
+ size_t messageLen = strlen(message);
+ char * lineStart = (char *)message;
+ char * lineEnd;
+ do {
+ char * nextN = strchr(lineStart, '\n');
+ char * nextR = strchr(lineStart, '\r');
+ if(nextN == NULL && nextR == NULL){
+ size_t llen = ((char *)message + messageLen) - lineStart;
+ char * ldata = (char *)malloc(llen+1);
+ if(ldata != NULL){
+ memcpy(ldata, lineStart, llen);
+ ldata[llen] = 0;
+ ev += "data: ";
+ ev += ldata;
+ ev += "\r\n\r\n";
+ free(ldata);
+ }
+ lineStart = (char *)message + messageLen;
+ } else {
+ char * nextLine = NULL;
+ if(nextN != NULL && nextR != NULL){
+ if(nextR < nextN){
+ lineEnd = nextR;
+ if(nextN == (nextR + 1))
+ nextLine = nextN + 1;
+ else
+ nextLine = nextR + 1;
+ } else {
+ lineEnd = nextN;
+ if(nextR == (nextN + 1))
+ nextLine = nextR + 1;
+ else
+ nextLine = nextN + 1;
+ }
+ } else if(nextN != NULL){
+ lineEnd = nextN;
+ nextLine = nextN + 1;
+ } else {
+ lineEnd = nextR;
+ nextLine = nextR + 1;
+ }
+
+ size_t llen = lineEnd - lineStart;
+ char * ldata = (char *)malloc(llen+1);
+ if(ldata != NULL){
+ memcpy(ldata, lineStart, llen);
+ ldata[llen] = 0;
+ ev += "data: ";
+ ev += ldata;
+ ev += "\r\n";
+ free(ldata);
+ }
+ lineStart = nextLine;
+ if(lineStart == ((char *)message + messageLen))
+ ev += "\r\n";
+ }
+ } while(lineStart < ((char *)message + messageLen));
+ }
+
+ return ev;
+}
+
+// Message
+
+AsyncEventSourceMessage::AsyncEventSourceMessage(const char * data, size_t len)
+: _data(nullptr), _len(len), _sent(0), _acked(0)
+{
+ _data = (uint8_t*)malloc(_len+1);
+ if(_data == nullptr){
+ _len = 0;
+ } else {
+ memcpy(_data, data, len);
+ _data[_len] = 0;
+ }
+}
+
+AsyncEventSourceMessage::~AsyncEventSourceMessage() {
+ if(_data != NULL)
+ free(_data);
+}
+
+size_t AsyncEventSourceMessage::ack(size_t len, uint32_t time) {
+ (void)time;
+ // If the whole message is now acked...
+ if(_acked + len > _len){
+ // Return the number of extra bytes acked (they will be carried on to the next message)
+ const size_t extra = _acked + len - _len;
+ _acked = _len;
+ return extra;
+ }
+ // Return that no extra bytes left.
+ _acked += len;
+ return 0;
+}
+
+size_t AsyncEventSourceMessage::send(AsyncClient *client) {
+ const size_t len = _len - _sent;
+ if(client->space() < len){
+ return 0;
+ }
+ size_t sent = client->add((const char *)_data, len);
+ if(client->canSend())
+ client->send();
+ _sent += sent;
+ return sent;
+}
+
+// Client
+
+AsyncEventSourceClient::AsyncEventSourceClient(AsyncWebServerRequest *request, AsyncEventSource *server)
+: _messageQueue(LinkedList([](AsyncEventSourceMessage *m){ delete m; }))
+{
+ _client = request->client();
+ _server = server;
+ _lastId = 0;
+ if(request->hasHeader("Last-Event-ID"))
+ _lastId = atoi(request->getHeader("Last-Event-ID")->value().c_str());
+
+ _client->setRxTimeout(0);
+ _client->onError(NULL, NULL);
+ _client->onAck([](void *r, AsyncClient* c, size_t len, uint32_t time){ (void)c; ((AsyncEventSourceClient*)(r))->_onAck(len, time); }, this);
+ _client->onPoll([](void *r, AsyncClient* c){ (void)c; ((AsyncEventSourceClient*)(r))->_onPoll(); }, this);
+ _client->onData(NULL, NULL);
+ _client->onTimeout([this](void *r, AsyncClient* c __attribute__((unused)), uint32_t time){ ((AsyncEventSourceClient*)(r))->_onTimeout(time); }, this);
+ _client->onDisconnect([this](void *r, AsyncClient* c){ ((AsyncEventSourceClient*)(r))->_onDisconnect(); delete c; }, this);
+
+ _server->_addClient(this);
+ delete request;
+}
+
+AsyncEventSourceClient::~AsyncEventSourceClient(){
+ _messageQueue.free();
+ close();
+}
+
+void AsyncEventSourceClient::_queueMessage(AsyncEventSourceMessage *dataMessage){
+ if(dataMessage == NULL)
+ return;
+ if(!connected()){
+ delete dataMessage;
+ return;
+ }
+ if(_messageQueue.length() >= SSE_MAX_QUEUED_MESSAGES){
+ ets_printf("ERROR: Too many messages queued\n");
+ delete dataMessage;
+ } else {
+ _messageQueue.add(dataMessage);
+ }
+ if(_client->canSend())
+ _runQueue();
+}
+
+void AsyncEventSourceClient::_onAck(size_t len, uint32_t time){
+ while(len && !_messageQueue.isEmpty()){
+ len = _messageQueue.front()->ack(len, time);
+ if(_messageQueue.front()->finished())
+ _messageQueue.remove(_messageQueue.front());
+ }
+
+ _runQueue();
+}
+
+void AsyncEventSourceClient::_onPoll(){
+ if(!_messageQueue.isEmpty()){
+ _runQueue();
+ }
+}
+
+
+void AsyncEventSourceClient::_onTimeout(uint32_t time __attribute__((unused))){
+ _client->close(true);
+}
+
+void AsyncEventSourceClient::_onDisconnect(){
+ _client = NULL;
+ _server->_handleDisconnect(this);
+}
+
+void AsyncEventSourceClient::close(){
+ if(_client != NULL)
+ _client->close();
+}
+
+void AsyncEventSourceClient::write(const char * message, size_t len){
+ _queueMessage(new AsyncEventSourceMessage(message, len));
+}
+
+void AsyncEventSourceClient::send(const char *message, const char *event, uint32_t id, uint32_t reconnect){
+ String ev = generateEventMessage(message, event, id, reconnect);
+ _queueMessage(new AsyncEventSourceMessage(ev.c_str(), ev.length()));
+}
+
+void AsyncEventSourceClient::_runQueue(){
+ while(!_messageQueue.isEmpty() && _messageQueue.front()->finished()){
+ _messageQueue.remove(_messageQueue.front());
+ }
+
+ for(auto i = _messageQueue.begin(); i != _messageQueue.end(); ++i)
+ {
+ if(!(*i)->sent())
+ (*i)->send(_client);
+ }
+}
+
+
+// Handler
+
+AsyncEventSource::AsyncEventSource(const String& url)
+ : _url(url)
+ , _clients(LinkedList([](AsyncEventSourceClient *c){ delete c; }))
+ , _connectcb(NULL)
+{}
+
+AsyncEventSource::~AsyncEventSource(){
+ close();
+}
+
+void AsyncEventSource::onConnect(ArEventHandlerFunction cb){
+ _connectcb = cb;
+}
+
+void AsyncEventSource::_addClient(AsyncEventSourceClient * client){
+ /*char * temp = (char *)malloc(2054);
+ if(temp != NULL){
+ memset(temp+1,' ',2048);
+ temp[0] = ':';
+ temp[2049] = '\r';
+ temp[2050] = '\n';
+ temp[2051] = '\r';
+ temp[2052] = '\n';
+ temp[2053] = 0;
+ client->write((const char *)temp, 2053);
+ free(temp);
+ }*/
+
+ _clients.add(client);
+ if(_connectcb)
+ _connectcb(client);
+}
+
+void AsyncEventSource::_handleDisconnect(AsyncEventSourceClient * client){
+ _clients.remove(client);
+}
+
+void AsyncEventSource::close(){
+ for(const auto &c: _clients){
+ if(c->connected())
+ c->close();
+ }
+}
+
+// pmb fix
+size_t AsyncEventSource::avgPacketsWaiting() const {
+ if(_clients.isEmpty())
+ return 0;
+
+ size_t aql=0;
+ uint32_t nConnectedClients=0;
+
+ for(const auto &c: _clients){
+ if(c->connected()) {
+ aql+=c->packetsWaiting();
+ ++nConnectedClients;
+ }
+ }
+// return aql / nConnectedClients;
+ return ((aql) + (nConnectedClients/2))/(nConnectedClients); // round up
+}
+
+void AsyncEventSource::send(const char *message, const char *event, uint32_t id, uint32_t reconnect){
+
+
+ String ev = generateEventMessage(message, event, id, reconnect);
+ for(const auto &c: _clients){
+ if(c->connected()) {
+ c->write(ev.c_str(), ev.length());
+ }
+ }
+}
+
+size_t AsyncEventSource::count() const {
+ return _clients.count_if([](AsyncEventSourceClient *c){
+ return c->connected();
+ });
+}
+
+bool AsyncEventSource::canHandle(AsyncWebServerRequest *request){
+ if(request->method() != HTTP_GET || !request->url().equals(_url)) {
+ return false;
+ }
+ request->addInterestingHeader("Last-Event-ID");
+ return true;
+}
+
+void AsyncEventSource::handleRequest(AsyncWebServerRequest *request){
+ if((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str()))
+ return request->requestAuthentication();
+ request->send(new AsyncEventSourceResponse(this));
+}
+
+// Response
+
+AsyncEventSourceResponse::AsyncEventSourceResponse(AsyncEventSource *server){
+ _server = server;
+ _code = 200;
+ _contentType = "text/event-stream";
+ _sendContentLength = false;
+ addHeader("Cache-Control", "no-cache");
+ addHeader("Connection","keep-alive");
+}
+
+void AsyncEventSourceResponse::_respond(AsyncWebServerRequest *request){
+ String out = _assembleHead(request->version());
+ request->client()->write(out.c_str(), _headLength);
+ _state = RESPONSE_WAIT_ACK;
+}
+
+size_t AsyncEventSourceResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time __attribute__((unused))){
+ if(len){
+ new AsyncEventSourceClient(request, _server);
+ }
+ return 0;
+}
+
diff --git a/lib/ESPAsyncWebServer/src/AsyncEventSource.h b/lib/ESPAsyncWebServer/src/AsyncEventSource.h
new file mode 100644
index 00000000..b097fa62
--- /dev/null
+++ b/lib/ESPAsyncWebServer/src/AsyncEventSource.h
@@ -0,0 +1,133 @@
+/*
+ Asynchronous WebServer library for Espressif MCUs
+
+ Copyright (c) 2016 Hristo Gochkov. All rights reserved.
+
+ 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 ASYNCEVENTSOURCE_H_
+#define ASYNCEVENTSOURCE_H_
+
+#include
+#ifdef ESP32
+#include
+#define SSE_MAX_QUEUED_MESSAGES 32
+#else
+#include
+#define SSE_MAX_QUEUED_MESSAGES 8
+#endif
+#include
+
+#include "AsyncWebSynchronization.h"
+
+#ifdef ESP8266
+#include
+#ifdef CRYPTO_HASH_h // include Hash.h from espressif framework if the first include was from the crypto library
+#include <../src/Hash.h>
+#endif
+#endif
+
+#ifdef ESP32
+#define DEFAULT_MAX_SSE_CLIENTS 8
+#else
+#define DEFAULT_MAX_SSE_CLIENTS 4
+#endif
+
+class AsyncEventSource;
+class AsyncEventSourceResponse;
+class AsyncEventSourceClient;
+typedef std::function ArEventHandlerFunction;
+
+class AsyncEventSourceMessage {
+ private:
+ uint8_t * _data;
+ size_t _len;
+ size_t _sent;
+ //size_t _ack;
+ size_t _acked;
+ public:
+ AsyncEventSourceMessage(const char * data, size_t len);
+ ~AsyncEventSourceMessage();
+ size_t ack(size_t len, uint32_t time __attribute__((unused)));
+ size_t send(AsyncClient *client);
+ bool finished(){ return _acked == _len; }
+ bool sent() { return _sent == _len; }
+};
+
+class AsyncEventSourceClient {
+ private:
+ AsyncClient *_client;
+ AsyncEventSource *_server;
+ uint32_t _lastId;
+ LinkedList _messageQueue;
+ void _queueMessage(AsyncEventSourceMessage *dataMessage);
+ void _runQueue();
+
+ public:
+
+ AsyncEventSourceClient(AsyncWebServerRequest *request, AsyncEventSource *server);
+ ~AsyncEventSourceClient();
+
+ AsyncClient* client(){ return _client; }
+ void close();
+ void write(const char * message, size_t len);
+ void send(const char *message, const char *event=NULL, uint32_t id=0, uint32_t reconnect=0);
+ bool connected() const { return (_client != NULL) && _client->connected(); }
+ uint32_t lastId() const { return _lastId; }
+ size_t packetsWaiting() const { return _messageQueue.length(); }
+
+ //system callbacks (do not call)
+ void _onAck(size_t len, uint32_t time);
+ void _onPoll();
+ void _onTimeout(uint32_t time);
+ void _onDisconnect();
+};
+
+class AsyncEventSource: public AsyncWebHandler {
+ private:
+ String _url;
+ LinkedList _clients;
+ ArEventHandlerFunction _connectcb;
+ public:
+ AsyncEventSource(const String& url);
+ ~AsyncEventSource();
+
+ const char * url() const { return _url.c_str(); }
+ void close();
+ void onConnect(ArEventHandlerFunction cb);
+ void send(const char *message, const char *event=NULL, uint32_t id=0, uint32_t reconnect=0);
+ size_t count() const; //number clinets connected
+ size_t avgPacketsWaiting() const;
+
+ //system callbacks (do not call)
+ void _addClient(AsyncEventSourceClient * client);
+ void _handleDisconnect(AsyncEventSourceClient * client);
+ virtual bool canHandle(AsyncWebServerRequest *request) override final;
+ virtual void handleRequest(AsyncWebServerRequest *request) override final;
+};
+
+class AsyncEventSourceResponse: public AsyncWebServerResponse {
+ private:
+ String _content;
+ AsyncEventSource *_server;
+ public:
+ AsyncEventSourceResponse(AsyncEventSource *server);
+ void _respond(AsyncWebServerRequest *request);
+ size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time);
+ bool _sourceValid() const { return true; }
+};
+
+
+#endif /* ASYNCEVENTSOURCE_H_ */
diff --git a/lib/ESPAsyncWebServer/src/AsyncJson.h b/lib/ESPAsyncWebServer/src/AsyncJson.h
new file mode 100644
index 00000000..27b4a26f
--- /dev/null
+++ b/lib/ESPAsyncWebServer/src/AsyncJson.h
@@ -0,0 +1,252 @@
+// AsyncJson.h
+/*
+ Async Response to use with ArduinoJson and AsyncWebServer
+ Written by Andrew Melvin (SticilFace) with help from me-no-dev and BBlanchon.
+
+ Example of callback in use
+
+ server.on("/json", HTTP_ANY, [](AsyncWebServerRequest * request) {
+
+ AsyncJsonResponse * response = new AsyncJsonResponse();
+ JsonObject& root = response->getRoot();
+ root["key1"] = "key number one";
+ JsonObject& nested = root.createNestedObject("nested");
+ nested["key1"] = "key number one";
+
+ response->setLength();
+ request->send(response);
+ });
+
+ --------------------
+
+ Async Request to use with ArduinoJson and AsyncWebServer
+ Written by Arsène von Wyss (avonwyss)
+
+ Example
+
+ AsyncCallbackJsonWebHandler* handler = new AsyncCallbackJsonWebHandler("/rest/endpoint");
+ handler->onRequest([](AsyncWebServerRequest *request, JsonVariant &json) {
+ JsonObject& jsonObj = json.as();
+ // ...
+ });
+ server.addHandler(handler);
+
+*/
+#ifndef ASYNC_JSON_H_
+#define ASYNC_JSON_H_
+#include
+#include
+#include
+
+#if ARDUINOJSON_VERSION_MAJOR == 5
+ #define ARDUINOJSON_5_COMPATIBILITY
+#else
+ #define DYNAMIC_JSON_DOCUMENT_SIZE 1024
+#endif
+
+constexpr const char* JSON_MIMETYPE = "application/json";
+
+/*
+ * Json Response
+ * */
+
+class ChunkPrint : public Print {
+ private:
+ uint8_t* _destination;
+ size_t _to_skip;
+ size_t _to_write;
+ size_t _pos;
+ public:
+ ChunkPrint(uint8_t* destination, size_t from, size_t len)
+ : _destination(destination), _to_skip(from), _to_write(len), _pos{0} {}
+ virtual ~ChunkPrint(){}
+ size_t write(uint8_t c){
+ if (_to_skip > 0) {
+ _to_skip--;
+ return 1;
+ } else if (_to_write > 0) {
+ _to_write--;
+ _destination[_pos++] = c;
+ return 1;
+ }
+ return 0;
+ }
+ size_t write(const uint8_t *buffer, size_t size)
+ {
+ return this->Print::write(buffer, size);
+ }
+};
+
+class AsyncJsonResponse: public AsyncAbstractResponse {
+ protected:
+
+#ifdef ARDUINOJSON_5_COMPATIBILITY
+ DynamicJsonBuffer _jsonBuffer;
+#else
+ DynamicJsonDocument _jsonBuffer;
+#endif
+
+ JsonVariant _root;
+ bool _isValid;
+
+ public:
+
+#ifdef ARDUINOJSON_5_COMPATIBILITY
+ AsyncJsonResponse(bool isArray=false): _isValid{false} {
+ _code = 200;
+ _contentType = JSON_MIMETYPE;
+ if(isArray)
+ _root = _jsonBuffer.createArray();
+ else
+ _root = _jsonBuffer.createObject();
+ }
+#else
+ AsyncJsonResponse(bool isArray=false, size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE) : _jsonBuffer(maxJsonBufferSize), _isValid{false} {
+ _code = 200;
+ _contentType = JSON_MIMETYPE;
+ if(isArray)
+ _root = _jsonBuffer.createNestedArray();
+ else
+ _root = _jsonBuffer.createNestedObject();
+ }
+#endif
+
+ ~AsyncJsonResponse() {}
+ JsonVariant & getRoot() { return _root; }
+ bool _sourceValid() const { return _isValid; }
+ size_t setLength() {
+
+#ifdef ARDUINOJSON_5_COMPATIBILITY
+ _contentLength = _root.measureLength();
+#else
+ _contentLength = measureJson(_root);
+#endif
+
+ if (_contentLength) { _isValid = true; }
+ return _contentLength;
+ }
+
+ size_t getSize() { return _jsonBuffer.size(); }
+
+ size_t _fillBuffer(uint8_t *data, size_t len){
+ ChunkPrint dest(data, _sentLength, len);
+
+#ifdef ARDUINOJSON_5_COMPATIBILITY
+ _root.printTo( dest ) ;
+#else
+ serializeJson(_root, dest);
+#endif
+ return len;
+ }
+};
+
+class PrettyAsyncJsonResponse: public AsyncJsonResponse {
+public:
+#ifdef ARDUINOJSON_5_COMPATIBILITY
+ PrettyAsyncJsonResponse (bool isArray=false) : AsyncJsonResponse{isArray} {}
+#else
+ PrettyAsyncJsonResponse (bool isArray=false, size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE) : AsyncJsonResponse{isArray, maxJsonBufferSize} {}
+#endif
+ size_t setLength () {
+#ifdef ARDUINOJSON_5_COMPATIBILITY
+ _contentLength = _root.measurePrettyLength ();
+#else
+ _contentLength = measureJsonPretty(_root);
+#endif
+ if (_contentLength) {_isValid = true;}
+ return _contentLength;
+ }
+ size_t _fillBuffer (uint8_t *data, size_t len) {
+ ChunkPrint dest (data, _sentLength, len);
+#ifdef ARDUINOJSON_5_COMPATIBILITY
+ _root.prettyPrintTo (dest);
+#else
+ serializeJsonPretty(_root, dest);
+#endif
+ return len;
+ }
+};
+
+typedef std::function ArJsonRequestHandlerFunction;
+
+class AsyncCallbackJsonWebHandler: public AsyncWebHandler {
+private:
+protected:
+ const String _uri;
+ WebRequestMethodComposite _method;
+ ArJsonRequestHandlerFunction _onRequest;
+ size_t _contentLength;
+#ifndef ARDUINOJSON_5_COMPATIBILITY
+ const size_t maxJsonBufferSize;
+#endif
+ size_t _maxContentLength;
+public:
+#ifdef ARDUINOJSON_5_COMPATIBILITY
+ AsyncCallbackJsonWebHandler(const String& uri, ArJsonRequestHandlerFunction onRequest)
+ : _uri(uri), _method(HTTP_POST|HTTP_PUT|HTTP_PATCH), _onRequest(onRequest), _maxContentLength(16384) {}
+#else
+ AsyncCallbackJsonWebHandler(const String& uri, ArJsonRequestHandlerFunction onRequest, size_t maxJsonBufferSize=DYNAMIC_JSON_DOCUMENT_SIZE)
+ : _uri(uri), _method(HTTP_POST|HTTP_PUT|HTTP_PATCH), _onRequest(onRequest), maxJsonBufferSize(maxJsonBufferSize), _maxContentLength(16384) {}
+#endif
+
+ void setMethod(WebRequestMethodComposite method){ _method = method; }
+ void setMaxContentLength(int maxContentLength){ _maxContentLength = maxContentLength; }
+ void onRequest(ArJsonRequestHandlerFunction fn){ _onRequest = fn; }
+
+ virtual bool canHandle(AsyncWebServerRequest *request) override final{
+ if(!_onRequest)
+ return false;
+
+ if(!(_method & request->method()))
+ return false;
+
+ if(_uri.length() && (_uri != request->url() && !request->url().startsWith(_uri+"/")))
+ return false;
+
+ if ( !request->contentType().equalsIgnoreCase(JSON_MIMETYPE) )
+ return false;
+
+ request->addInterestingHeader("ANY");
+ return true;
+ }
+
+ virtual void handleRequest(AsyncWebServerRequest *request) override final {
+ if(_onRequest) {
+ if (request->_tempObject != NULL) {
+
+#ifdef ARDUINOJSON_5_COMPATIBILITY
+ DynamicJsonBuffer jsonBuffer;
+ JsonVariant json = jsonBuffer.parse((uint8_t*)(request->_tempObject));
+ if (json.success()) {
+#else
+ DynamicJsonDocument jsonBuffer(this->maxJsonBufferSize);
+ DeserializationError error = deserializeJson(jsonBuffer, (uint8_t*)(request->_tempObject));
+ if(!error) {
+ JsonVariant json = jsonBuffer.as();
+#endif
+
+ _onRequest(request, json);
+ return;
+ }
+ }
+ request->send(_contentLength > _maxContentLength ? 413 : 400);
+ } else {
+ request->send(500);
+ }
+ }
+ virtual void handleUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) override final {
+ }
+ virtual void handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) override final {
+ if (_onRequest) {
+ _contentLength = total;
+ if (total > 0 && request->_tempObject == NULL && total < _maxContentLength) {
+ request->_tempObject = malloc(total);
+ }
+ if (request->_tempObject != NULL) {
+ memcpy((uint8_t*)(request->_tempObject) + index, data, len);
+ }
+ }
+ }
+ virtual bool isRequestHandlerTrivial() override final {return _onRequest ? false : true;}
+};
+#endif
diff --git a/lib/ESPAsyncWebServer/src/AsyncWebSocket.cpp b/lib/ESPAsyncWebServer/src/AsyncWebSocket.cpp
new file mode 100644
index 00000000..52dcd75f
--- /dev/null
+++ b/lib/ESPAsyncWebServer/src/AsyncWebSocket.cpp
@@ -0,0 +1,1303 @@
+/*
+ Asynchronous WebServer library for Espressif MCUs
+
+ Copyright (c) 2016 Hristo Gochkov. All rights reserved.
+ This file is part of the esp8266 core for Arduino environment.
+
+ 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 "Arduino.h"
+#include "AsyncWebSocket.h"
+
+#include
+
+#ifndef ESP8266
+extern "C" {
+typedef struct {
+ uint32_t state[5];
+ uint32_t count[2];
+ unsigned char buffer[64];
+} SHA1_CTX;
+
+void SHA1Transform(uint32_t state[5], const unsigned char buffer[64]);
+void SHA1Init(SHA1_CTX* context);
+void SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len);
+void SHA1Final(unsigned char digest[20], SHA1_CTX* context);
+}
+#else
+#include
+#endif
+
+#define MAX_PRINTF_LEN 64
+
+size_t webSocketSendFrameWindow(AsyncClient *client){
+ if(!client->canSend())
+ return 0;
+ size_t space = client->space();
+ if(space < 9)
+ return 0;
+ return space - 8;
+}
+
+size_t webSocketSendFrame(AsyncClient *client, bool final, uint8_t opcode, bool mask, uint8_t *data, size_t len){
+ if(!client->canSend())
+ return 0;
+ size_t space = client->space();
+ if(space < 2)
+ return 0;
+ uint8_t mbuf[4] = {0,0,0,0};
+ uint8_t headLen = 2;
+ if(len && mask){
+ headLen += 4;
+ mbuf[0] = rand() % 0xFF;
+ mbuf[1] = rand() % 0xFF;
+ mbuf[2] = rand() % 0xFF;
+ mbuf[3] = rand() % 0xFF;
+ }
+ if(len > 125)
+ headLen += 2;
+ if(space < headLen)
+ return 0;
+ space -= headLen;
+
+ if(len > space) len = space;
+
+ uint8_t *buf = (uint8_t*)malloc(headLen);
+ if(buf == NULL){
+ //os_printf("could not malloc %u bytes for frame header\n", headLen);
+ return 0;
+ }
+
+ buf[0] = opcode & 0x0F;
+ if(final)
+ buf[0] |= 0x80;
+ if(len < 126)
+ buf[1] = len & 0x7F;
+ else {
+ buf[1] = 126;
+ buf[2] = (uint8_t)((len >> 8) & 0xFF);
+ buf[3] = (uint8_t)(len & 0xFF);
+ }
+ if(len && mask){
+ buf[1] |= 0x80;
+ memcpy(buf + (headLen - 4), mbuf, 4);
+ }
+ if(client->add((const char *)buf, headLen) != headLen){
+ //os_printf("error adding %lu header bytes\n", headLen);
+ free(buf);
+ return 0;
+ }
+ free(buf);
+
+ if(len){
+ if(len && mask){
+ size_t i;
+ for(i=0;iadd((const char *)data, len) != len){
+ //os_printf("error adding %lu data bytes\n", len);
+ return 0;
+ }
+ }
+ if(!client->send()){
+ //os_printf("error sending frame: %lu\n", headLen+len);
+ return 0;
+ }
+ return len;
+}
+
+
+/*
+ * AsyncWebSocketMessageBuffer
+ */
+
+
+
+AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer()
+ :_data(nullptr)
+ ,_len(0)
+ ,_lock(false)
+ ,_count(0)
+{
+
+}
+
+AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(uint8_t * data, size_t size)
+ :_data(nullptr)
+ ,_len(size)
+ ,_lock(false)
+ ,_count(0)
+{
+
+ if (!data) {
+ return;
+ }
+
+ _data = new uint8_t[_len + 1];
+
+ if (_data) {
+ memcpy(_data, data, _len);
+ _data[_len] = 0;
+ }
+}
+
+
+AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(size_t size)
+ :_data(nullptr)
+ ,_len(size)
+ ,_lock(false)
+ ,_count(0)
+{
+ _data = new uint8_t[_len + 1];
+
+ if (_data) {
+ _data[_len] = 0;
+ }
+
+}
+
+AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(const AsyncWebSocketMessageBuffer & copy)
+ :_data(nullptr)
+ ,_len(0)
+ ,_lock(false)
+ ,_count(0)
+{
+ _len = copy._len;
+ _lock = copy._lock;
+ _count = 0;
+
+ if (_len) {
+ _data = new uint8_t[_len + 1];
+ _data[_len] = 0;
+ }
+
+ if (_data) {
+ memcpy(_data, copy._data, _len);
+ _data[_len] = 0;
+ }
+
+}
+
+AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(AsyncWebSocketMessageBuffer && copy)
+ :_data(nullptr)
+ ,_len(0)
+ ,_lock(false)
+ ,_count(0)
+{
+ _len = copy._len;
+ _lock = copy._lock;
+ _count = 0;
+
+ if (copy._data) {
+ _data = copy._data;
+ copy._data = nullptr;
+ }
+
+}
+
+AsyncWebSocketMessageBuffer::~AsyncWebSocketMessageBuffer()
+{
+ if (_data) {
+ delete[] _data;
+ }
+}
+
+bool AsyncWebSocketMessageBuffer::reserve(size_t size)
+{
+ _len = size;
+
+ if (_data) {
+ delete[] _data;
+ _data = nullptr;
+ }
+
+ _data = new uint8_t[_len + 1];
+
+ if (_data) {
+ _data[_len] = 0;
+ return true;
+ } else {
+ return false;
+ }
+
+}
+
+
+
+/*
+ * Control Frame
+ */
+
+class AsyncWebSocketControl {
+ private:
+ uint8_t _opcode;
+ uint8_t *_data;
+ size_t _len;
+ bool _mask;
+ bool _finished;
+ public:
+ AsyncWebSocketControl(uint8_t opcode, uint8_t *data=NULL, size_t len=0, bool mask=false)
+ :_opcode(opcode)
+ ,_len(len)
+ ,_mask(len && mask)
+ ,_finished(false)
+ {
+ if(data == NULL)
+ _len = 0;
+ if(_len){
+ if(_len > 125)
+ _len = 125;
+ _data = (uint8_t*)malloc(_len);
+ if(_data == NULL)
+ _len = 0;
+ else memcpy(_data, data, len);
+ } else _data = NULL;
+ }
+ virtual ~AsyncWebSocketControl(){
+ if(_data != NULL)
+ free(_data);
+ }
+ virtual bool finished() const { return _finished; }
+ uint8_t opcode(){ return _opcode; }
+ uint8_t len(){ return _len + 2; }
+ size_t send(AsyncClient *client){
+ _finished = true;
+ return webSocketSendFrame(client, true, _opcode & 0x0F, _mask, _data, _len);
+ }
+};
+
+/*
+ * Basic Buffered Message
+ */
+
+
+AsyncWebSocketBasicMessage::AsyncWebSocketBasicMessage(const char * data, size_t len, uint8_t opcode, bool mask)
+ :_len(len)
+ ,_sent(0)
+ ,_ack(0)
+ ,_acked(0)
+{
+ _opcode = opcode & 0x07;
+ _mask = mask;
+ _data = (uint8_t*)malloc(_len+1);
+ if(_data == NULL){
+ _len = 0;
+ _status = WS_MSG_ERROR;
+ } else {
+ _status = WS_MSG_SENDING;
+ memcpy(_data, data, _len);
+ _data[_len] = 0;
+ }
+}
+AsyncWebSocketBasicMessage::AsyncWebSocketBasicMessage(uint8_t opcode, bool mask)
+ :_len(0)
+ ,_sent(0)
+ ,_ack(0)
+ ,_acked(0)
+ ,_data(NULL)
+{
+ _opcode = opcode & 0x07;
+ _mask = mask;
+
+}
+
+
+AsyncWebSocketBasicMessage::~AsyncWebSocketBasicMessage() {
+ if(_data != NULL)
+ free(_data);
+}
+
+ void AsyncWebSocketBasicMessage::ack(size_t len, uint32_t time) {
+ (void)time;
+ _acked += len;
+ if(_sent == _len && _acked == _ack){
+ _status = WS_MSG_SENT;
+ }
+}
+ size_t AsyncWebSocketBasicMessage::send(AsyncClient *client) {
+ if(_status != WS_MSG_SENDING)
+ return 0;
+ if(_acked < _ack){
+ return 0;
+ }
+ if(_sent == _len){
+ if(_acked == _ack)
+ _status = WS_MSG_SENT;
+ return 0;
+ }
+ if(_sent > _len){
+ _status = WS_MSG_ERROR;
+ return 0;
+ }
+
+ size_t toSend = _len - _sent;
+ size_t window = webSocketSendFrameWindow(client);
+
+ if(window < toSend) {
+ toSend = window;
+ }
+
+ _sent += toSend;
+ _ack += toSend + ((toSend < 126)?2:4) + (_mask * 4);
+
+ bool final = (_sent == _len);
+ uint8_t* dPtr = (uint8_t*)(_data + (_sent - toSend));
+ uint8_t opCode = (toSend && _sent == toSend)?_opcode:(uint8_t)WS_CONTINUATION;
+
+ size_t sent = webSocketSendFrame(client, final, opCode, _mask, dPtr, toSend);
+ _status = WS_MSG_SENDING;
+ if(toSend && sent != toSend){
+ _sent -= (toSend - sent);
+ _ack -= (toSend - sent);
+ }
+ return sent;
+}
+
+// bool AsyncWebSocketBasicMessage::reserve(size_t size) {
+// if (size) {
+// _data = (uint8_t*)malloc(size +1);
+// if (_data) {
+// memset(_data, 0, size);
+// _len = size;
+// _status = WS_MSG_SENDING;
+// return true;
+// }
+// }
+// return false;
+// }
+
+
+/*
+ * AsyncWebSocketMultiMessage Message
+ */
+
+
+AsyncWebSocketMultiMessage::AsyncWebSocketMultiMessage(AsyncWebSocketMessageBuffer * buffer, uint8_t opcode, bool mask)
+ :_len(0)
+ ,_sent(0)
+ ,_ack(0)
+ ,_acked(0)
+ ,_WSbuffer(nullptr)
+{
+
+ _opcode = opcode & 0x07;
+ _mask = mask;
+
+ if (buffer) {
+ _WSbuffer = buffer;
+ (*_WSbuffer)++;
+ _data = buffer->get();
+ _len = buffer->length();
+ _status = WS_MSG_SENDING;
+ //ets_printf("M: %u\n", _len);
+ } else {
+ _status = WS_MSG_ERROR;
+ }
+
+}
+
+
+AsyncWebSocketMultiMessage::~AsyncWebSocketMultiMessage() {
+ if (_WSbuffer) {
+ (*_WSbuffer)--; // decreases the counter.
+ }
+}
+
+ void AsyncWebSocketMultiMessage::ack(size_t len, uint32_t time) {
+ (void)time;
+ _acked += len;
+ if(_sent >= _len && _acked >= _ack){
+ _status = WS_MSG_SENT;
+ }
+ //ets_printf("A: %u\n", len);
+}
+ size_t AsyncWebSocketMultiMessage::send(AsyncClient *client) {
+ if(_status != WS_MSG_SENDING)
+ return 0;
+ if(_acked < _ack){
+ return 0;
+ }
+ if(_sent == _len){
+ _status = WS_MSG_SENT;
+ return 0;
+ }
+ if(_sent > _len){
+ _status = WS_MSG_ERROR;
+ //ets_printf("E: %u > %u\n", _sent, _len);
+ return 0;
+ }
+
+ size_t toSend = _len - _sent;
+ size_t window = webSocketSendFrameWindow(client);
+
+ if(window < toSend) {
+ toSend = window;
+ }
+
+ _sent += toSend;
+ _ack += toSend + ((toSend < 126)?2:4) + (_mask * 4);
+
+ //ets_printf("W: %u %u\n", _sent - toSend, toSend);
+
+ bool final = (_sent == _len);
+ uint8_t* dPtr = (uint8_t*)(_data + (_sent - toSend));
+ uint8_t opCode = (toSend && _sent == toSend)?_opcode:(uint8_t)WS_CONTINUATION;
+
+ size_t sent = webSocketSendFrame(client, final, opCode, _mask, dPtr, toSend);
+ _status = WS_MSG_SENDING;
+ if(toSend && sent != toSend){
+ //ets_printf("E: %u != %u\n", toSend, sent);
+ _sent -= (toSend - sent);
+ _ack -= (toSend - sent);
+ }
+ //ets_printf("S: %u %u\n", _sent, sent);
+ return sent;
+}
+
+
+/*
+ * Async WebSocket Client
+ */
+ const char * AWSC_PING_PAYLOAD = "ESPAsyncWebServer-PING";
+ const size_t AWSC_PING_PAYLOAD_LEN = 22;
+
+AsyncWebSocketClient::AsyncWebSocketClient(AsyncWebServerRequest *request, AsyncWebSocket *server)
+ : _controlQueue(LinkedList([](AsyncWebSocketControl *c){ delete c; }))
+ , _messageQueue(LinkedList([](AsyncWebSocketMessage *m){ delete m; }))
+ , _tempObject(NULL)
+{
+ _client = request->client();
+ _server = server;
+ _clientId = _server->_getNextId();
+ _status = WS_CONNECTED;
+ _pstate = 0;
+ _lastMessageTime = millis();
+ _keepAlivePeriod = 0;
+ _client->setRxTimeout(0);
+ _client->onError([](void *r, AsyncClient* c, int8_t error){ (void)c; ((AsyncWebSocketClient*)(r))->_onError(error); }, this);
+ _client->onAck([](void *r, AsyncClient* c, size_t len, uint32_t time){ (void)c; ((AsyncWebSocketClient*)(r))->_onAck(len, time); }, this);
+ _client->onDisconnect([](void *r, AsyncClient* c){ ((AsyncWebSocketClient*)(r))->_onDisconnect(); delete c; }, this);
+ _client->onTimeout([](void *r, AsyncClient* c, uint32_t time){ (void)c; ((AsyncWebSocketClient*)(r))->_onTimeout(time); }, this);
+ _client->onData([](void *r, AsyncClient* c, void *buf, size_t len){ (void)c; ((AsyncWebSocketClient*)(r))->_onData(buf, len); }, this);
+ _client->onPoll([](void *r, AsyncClient* c){ (void)c; ((AsyncWebSocketClient*)(r))->_onPoll(); }, this);
+ _server->_addClient(this);
+ _server->_handleEvent(this, WS_EVT_CONNECT, request, NULL, 0);
+ delete request;
+}
+
+AsyncWebSocketClient::~AsyncWebSocketClient(){
+ _messageQueue.free();
+ _controlQueue.free();
+ _server->_handleEvent(this, WS_EVT_DISCONNECT, NULL, NULL, 0);
+}
+
+void AsyncWebSocketClient::_onAck(size_t len, uint32_t time){
+ _lastMessageTime = millis();
+ if(!_controlQueue.isEmpty()){
+ auto head = _controlQueue.front();
+ if(head->finished()){
+ len -= head->len();
+ if(_status == WS_DISCONNECTING && head->opcode() == WS_DISCONNECT){
+ _controlQueue.remove(head);
+ _status = WS_DISCONNECTED;
+ _client->close(true);
+ return;
+ }
+ _controlQueue.remove(head);
+ }
+ }
+ if(len && !_messageQueue.isEmpty()){
+ _messageQueue.front()->ack(len, time);
+ }
+ _server->_cleanBuffers();
+ _runQueue();
+}
+
+void AsyncWebSocketClient::_onPoll(){
+ if(_client->canSend() && (!_controlQueue.isEmpty() || !_messageQueue.isEmpty())){
+ _runQueue();
+ } else if(_keepAlivePeriod > 0 && _controlQueue.isEmpty() && _messageQueue.isEmpty() && (millis() - _lastMessageTime) >= _keepAlivePeriod){
+ ping((uint8_t *)AWSC_PING_PAYLOAD, AWSC_PING_PAYLOAD_LEN);
+ }
+}
+
+void AsyncWebSocketClient::_runQueue(){
+ while(!_messageQueue.isEmpty() && _messageQueue.front()->finished()){
+ _messageQueue.remove(_messageQueue.front());
+ }
+
+ if(!_controlQueue.isEmpty() && (_messageQueue.isEmpty() || _messageQueue.front()->betweenFrames()) && webSocketSendFrameWindow(_client) > (size_t)(_controlQueue.front()->len() - 1)){
+ _controlQueue.front()->send(_client);
+ } else if(!_messageQueue.isEmpty() && _messageQueue.front()->betweenFrames() && webSocketSendFrameWindow(_client)){
+ _messageQueue.front()->send(_client);
+ }
+}
+
+bool AsyncWebSocketClient::queueIsFull(){
+ if((_messageQueue.length() >= WS_MAX_QUEUED_MESSAGES) || (_status != WS_CONNECTED) ) return true;
+ return false;
+}
+
+void AsyncWebSocketClient::_queueMessage(AsyncWebSocketMessage *dataMessage){
+ if(dataMessage == NULL)
+ return;
+ if(_status != WS_CONNECTED){
+ delete dataMessage;
+ return;
+ }
+ if(_messageQueue.length() >= WS_MAX_QUEUED_MESSAGES){
+ ets_printf("ERROR: Too many messages queued\n");
+ delete dataMessage;
+ } else {
+ _messageQueue.add(dataMessage);
+ }
+ if(_client->canSend())
+ _runQueue();
+}
+
+void AsyncWebSocketClient::_queueControl(AsyncWebSocketControl *controlMessage){
+ if(controlMessage == NULL)
+ return;
+ _controlQueue.add(controlMessage);
+ if(_client->canSend())
+ _runQueue();
+}
+
+void AsyncWebSocketClient::close(uint16_t code, const char * message){
+ if(_status != WS_CONNECTED)
+ return;
+ if(code){
+ uint8_t packetLen = 2;
+ if(message != NULL){
+ size_t mlen = strlen(message);
+ if(mlen > 123) mlen = 123;
+ packetLen += mlen;
+ }
+ char * buf = (char*)malloc(packetLen);
+ if(buf != NULL){
+ buf[0] = (uint8_t)(code >> 8);
+ buf[1] = (uint8_t)(code & 0xFF);
+ if(message != NULL){
+ memcpy(buf+2, message, packetLen -2);
+ }
+ _queueControl(new AsyncWebSocketControl(WS_DISCONNECT,(uint8_t*)buf,packetLen));
+ free(buf);
+ return;
+ }
+ }
+ _queueControl(new AsyncWebSocketControl(WS_DISCONNECT));
+}
+
+void AsyncWebSocketClient::ping(uint8_t *data, size_t len){
+ if(_status == WS_CONNECTED)
+ _queueControl(new AsyncWebSocketControl(WS_PING, data, len));
+}
+
+void AsyncWebSocketClient::_onError(int8_t){}
+
+void AsyncWebSocketClient::_onTimeout(uint32_t time){
+ (void)time;
+ _client->close(true);
+}
+
+void AsyncWebSocketClient::_onDisconnect(){
+ _client = NULL;
+ _server->_handleDisconnect(this);
+}
+
+void AsyncWebSocketClient::_onData(void *pbuf, size_t plen){
+ _lastMessageTime = millis();
+ uint8_t *data = (uint8_t*)pbuf;
+ while(plen > 0){
+ if(!_pstate){
+ const uint8_t *fdata = data;
+ _pinfo.index = 0;
+ _pinfo.final = (fdata[0] & 0x80) != 0;
+ _pinfo.opcode = fdata[0] & 0x0F;
+ _pinfo.masked = (fdata[1] & 0x80) != 0;
+ _pinfo.len = fdata[1] & 0x7F;
+ data += 2;
+ plen -= 2;
+ if(_pinfo.len == 126){
+ _pinfo.len = fdata[3] | (uint16_t)(fdata[2]) << 8;
+ data += 2;
+ plen -= 2;
+ } else if(_pinfo.len == 127){
+ _pinfo.len = fdata[9] | (uint16_t)(fdata[8]) << 8 | (uint32_t)(fdata[7]) << 16 | (uint32_t)(fdata[6]) << 24 | (uint64_t)(fdata[5]) << 32 | (uint64_t)(fdata[4]) << 40 | (uint64_t)(fdata[3]) << 48 | (uint64_t)(fdata[2]) << 56;
+ data += 8;
+ plen -= 8;
+ }
+
+ if(_pinfo.masked){
+ memcpy(_pinfo.mask, data, 4);
+ data += 4;
+ plen -= 4;
+ }
+ }
+
+ const size_t datalen = std::min((size_t)(_pinfo.len - _pinfo.index), plen);
+ const auto datalast = data[datalen];
+
+ if(_pinfo.masked){
+ for(size_t i=0;i_handleEvent(this, WS_EVT_DATA, (void *)&_pinfo, (uint8_t*)data, datalen);
+
+ _pinfo.index += datalen;
+ } else if((datalen + _pinfo.index) == _pinfo.len){
+ _pstate = 0;
+ if(_pinfo.opcode == WS_DISCONNECT){
+ if(datalen){
+ uint16_t reasonCode = (uint16_t)(data[0] << 8) + data[1];
+ char * reasonString = (char*)(data+2);
+ if(reasonCode > 1001){
+ _server->_handleEvent(this, WS_EVT_ERROR, (void *)&reasonCode, (uint8_t*)reasonString, strlen(reasonString));
+ }
+ }
+ if(_status == WS_DISCONNECTING){
+ _status = WS_DISCONNECTED;
+ _client->close(true);
+ } else {
+ _status = WS_DISCONNECTING;
+ _client->ackLater();
+ _queueControl(new AsyncWebSocketControl(WS_DISCONNECT, data, datalen));
+ }
+ } else if(_pinfo.opcode == WS_PING){
+ _queueControl(new AsyncWebSocketControl(WS_PONG, data, datalen));
+ } else if(_pinfo.opcode == WS_PONG){
+ if(datalen != AWSC_PING_PAYLOAD_LEN || memcmp(AWSC_PING_PAYLOAD, data, AWSC_PING_PAYLOAD_LEN) != 0)
+ _server->_handleEvent(this, WS_EVT_PONG, NULL, data, datalen);
+ } else if(_pinfo.opcode < 8){//continuation or text/binary frame
+ _server->_handleEvent(this, WS_EVT_DATA, (void *)&_pinfo, data, datalen);
+ }
+ } else {
+ //os_printf("frame error: len: %u, index: %llu, total: %llu\n", datalen, _pinfo.index, _pinfo.len);
+ //what should we do?
+ break;
+ }
+
+ // restore byte as _handleEvent may have added a null terminator i.e., data[len] = 0;
+ if (datalen > 0)
+ data[datalen] = datalast;
+
+ data += datalen;
+ plen -= datalen;
+ }
+}
+
+size_t AsyncWebSocketClient::printf(const char *format, ...) {
+ va_list arg;
+ va_start(arg, format);
+ char* temp = new char[MAX_PRINTF_LEN];
+ if(!temp){
+ va_end(arg);
+ return 0;
+ }
+ char* buffer = temp;
+ size_t len = vsnprintf(temp, MAX_PRINTF_LEN, format, arg);
+ va_end(arg);
+
+ if (len > (MAX_PRINTF_LEN - 1)) {
+ buffer = new char[len + 1];
+ if (!buffer) {
+ delete[] temp;
+ return 0;
+ }
+ va_start(arg, format);
+ vsnprintf(buffer, len + 1, format, arg);
+ va_end(arg);
+ }
+ text(buffer, len);
+ if (buffer != temp) {
+ delete[] buffer;
+ }
+ delete[] temp;
+ return len;
+}
+
+#ifndef ESP32
+size_t AsyncWebSocketClient::printf_P(PGM_P formatP, ...) {
+ va_list arg;
+ va_start(arg, formatP);
+ char* temp = new char[MAX_PRINTF_LEN];
+ if(!temp){
+ va_end(arg);
+ return 0;
+ }
+ char* buffer = temp;
+ size_t len = vsnprintf_P(temp, MAX_PRINTF_LEN, formatP, arg);
+ va_end(arg);
+
+ if (len > (MAX_PRINTF_LEN - 1)) {
+ buffer = new char[len + 1];
+ if (!buffer) {
+ delete[] temp;
+ return 0;
+ }
+ va_start(arg, formatP);
+ vsnprintf_P(buffer, len + 1, formatP, arg);
+ va_end(arg);
+ }
+ text(buffer, len);
+ if (buffer != temp) {
+ delete[] buffer;
+ }
+ delete[] temp;
+ return len;
+}
+#endif
+
+void AsyncWebSocketClient::text(const char * message, size_t len){
+ _queueMessage(new AsyncWebSocketBasicMessage(message, len));
+}
+void AsyncWebSocketClient::text(const char * message){
+ text(message, strlen(message));
+}
+void AsyncWebSocketClient::text(uint8_t * message, size_t len){
+ text((const char *)message, len);
+}
+void AsyncWebSocketClient::text(char * message){
+ text(message, strlen(message));
+}
+void AsyncWebSocketClient::text(const String &message){
+ text(message.c_str(), message.length());
+}
+void AsyncWebSocketClient::text(const __FlashStringHelper *data){
+ PGM_P p = reinterpret_cast(data);
+ size_t n = 0;
+ while (1) {
+ if (pgm_read_byte(p+n) == 0) break;
+ n += 1;
+ }
+ char * message = (char*) malloc(n+1);
+ if(message){
+ for(size_t b=0; b(data);
+ char * message = (char*) malloc(len);
+ if(message){
+ for(size_t b=0; bremoteIP();
+}
+
+uint16_t AsyncWebSocketClient::remotePort() {
+ if(!_client) {
+ return 0;
+ }
+ return _client->remotePort();
+}
+
+
+
+/*
+ * Async Web Socket - Each separate socket location
+ */
+
+AsyncWebSocket::AsyncWebSocket(const String& url)
+ :_url(url)
+ ,_clients(LinkedList([](AsyncWebSocketClient *c){ delete c; }))
+ ,_cNextId(1)
+ ,_enabled(true)
+ ,_buffers(LinkedList([](AsyncWebSocketMessageBuffer *b){ delete b; }))
+{
+ _eventHandler = NULL;
+}
+
+AsyncWebSocket::~AsyncWebSocket(){}
+
+void AsyncWebSocket::_handleEvent(AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len){
+ if(_eventHandler != NULL){
+ _eventHandler(this, client, type, arg, data, len);
+ }
+}
+
+void AsyncWebSocket::_addClient(AsyncWebSocketClient * client){
+ _clients.add(client);
+}
+
+void AsyncWebSocket::_handleDisconnect(AsyncWebSocketClient * client){
+
+ _clients.remove_first([=](AsyncWebSocketClient * c){
+ return c->id() == client->id();
+ });
+}
+
+bool AsyncWebSocket::availableForWriteAll(){
+ for(const auto& c: _clients){
+ if(c->queueIsFull()) return false;
+ }
+ return true;
+}
+
+bool AsyncWebSocket::availableForWrite(uint32_t id){
+ for(const auto& c: _clients){
+ if(c->queueIsFull() && (c->id() == id )) return false;
+ }
+ return true;
+}
+
+size_t AsyncWebSocket::count() const {
+ return _clients.count_if([](AsyncWebSocketClient * c){
+ return c->status() == WS_CONNECTED;
+ });
+}
+
+AsyncWebSocketClient * AsyncWebSocket::client(uint32_t id){
+ for(const auto &c: _clients){
+ if(c->id() == id && c->status() == WS_CONNECTED){
+ return c;
+ }
+ }
+ return nullptr;
+}
+
+
+void AsyncWebSocket::close(uint32_t id, uint16_t code, const char * message){
+ AsyncWebSocketClient * c = client(id);
+ if(c)
+ c->close(code, message);
+}
+
+void AsyncWebSocket::closeAll(uint16_t code, const char * message){
+ for(const auto& c: _clients){
+ if(c->status() == WS_CONNECTED)
+ c->close(code, message);
+ }
+}
+
+void AsyncWebSocket::cleanupClients(uint16_t maxClients)
+{
+ if (count() > maxClients){
+ _clients.front()->close();
+ }
+}
+
+void AsyncWebSocket::ping(uint32_t id, uint8_t *data, size_t len){
+ AsyncWebSocketClient * c = client(id);
+ if(c)
+ c->ping(data, len);
+}
+
+void AsyncWebSocket::pingAll(uint8_t *data, size_t len){
+ for(const auto& c: _clients){
+ if(c->status() == WS_CONNECTED)
+ c->ping(data, len);
+ }
+}
+
+void AsyncWebSocket::text(uint32_t id, const char * message, size_t len){
+ AsyncWebSocketClient * c = client(id);
+ if(c)
+ c->text(message, len);
+}
+
+void AsyncWebSocket::textAll(AsyncWebSocketMessageBuffer * buffer){
+ if (!buffer) return;
+ buffer->lock();
+ for(const auto& c: _clients){
+ if(c->status() == WS_CONNECTED){
+ c->text(buffer);
+ }
+ }
+ buffer->unlock();
+ _cleanBuffers();
+}
+
+
+void AsyncWebSocket::textAll(const char * message, size_t len){
+ AsyncWebSocketMessageBuffer * WSBuffer = makeBuffer((uint8_t *)message, len);
+ textAll(WSBuffer);
+}
+
+void AsyncWebSocket::binary(uint32_t id, const char * message, size_t len){
+ AsyncWebSocketClient * c = client(id);
+ if(c)
+ c->binary(message, len);
+}
+
+void AsyncWebSocket::binaryAll(const char * message, size_t len){
+ AsyncWebSocketMessageBuffer * buffer = makeBuffer((uint8_t *)message, len);
+ binaryAll(buffer);
+}
+
+void AsyncWebSocket::binaryAll(AsyncWebSocketMessageBuffer * buffer)
+{
+ if (!buffer) return;
+ buffer->lock();
+ for(const auto& c: _clients){
+ if(c->status() == WS_CONNECTED)
+ c->binary(buffer);
+ }
+ buffer->unlock();
+ _cleanBuffers();
+}
+
+void AsyncWebSocket::message(uint32_t id, AsyncWebSocketMessage *message){
+ AsyncWebSocketClient * c = client(id);
+ if(c)
+ c->message(message);
+}
+
+void AsyncWebSocket::messageAll(AsyncWebSocketMultiMessage *message){
+ for(const auto& c: _clients){
+ if(c->status() == WS_CONNECTED)
+ c->message(message);
+ }
+ _cleanBuffers();
+}
+
+size_t AsyncWebSocket::printf(uint32_t id, const char *format, ...){
+ AsyncWebSocketClient * c = client(id);
+ if(c){
+ va_list arg;
+ va_start(arg, format);
+ size_t len = c->printf(format, arg);
+ va_end(arg);
+ return len;
+ }
+ return 0;
+}
+
+size_t AsyncWebSocket::printfAll(const char *format, ...) {
+ va_list arg;
+ char* temp = new char[MAX_PRINTF_LEN];
+ if(!temp){
+ return 0;
+ }
+ va_start(arg, format);
+ size_t len = vsnprintf(temp, MAX_PRINTF_LEN, format, arg);
+ va_end(arg);
+ delete[] temp;
+
+ AsyncWebSocketMessageBuffer * buffer = makeBuffer(len);
+ if (!buffer) {
+ return 0;
+ }
+
+ va_start(arg, format);
+ vsnprintf( (char *)buffer->get(), len + 1, format, arg);
+ va_end(arg);
+
+ textAll(buffer);
+ return len;
+}
+
+#ifndef ESP32
+size_t AsyncWebSocket::printf_P(uint32_t id, PGM_P formatP, ...){
+ AsyncWebSocketClient * c = client(id);
+ if(c != NULL){
+ va_list arg;
+ va_start(arg, formatP);
+ size_t len = c->printf_P(formatP, arg);
+ va_end(arg);
+ return len;
+ }
+ return 0;
+}
+#endif
+
+size_t AsyncWebSocket::printfAll_P(PGM_P formatP, ...) {
+ va_list arg;
+ char* temp = new char[MAX_PRINTF_LEN];
+ if(!temp){
+ return 0;
+ }
+ va_start(arg, formatP);
+ size_t len = vsnprintf_P(temp, MAX_PRINTF_LEN, formatP, arg);
+ va_end(arg);
+ delete[] temp;
+
+ AsyncWebSocketMessageBuffer * buffer = makeBuffer(len + 1);
+ if (!buffer) {
+ return 0;
+ }
+
+ va_start(arg, formatP);
+ vsnprintf_P((char *)buffer->get(), len + 1, formatP, arg);
+ va_end(arg);
+
+ textAll(buffer);
+ return len;
+}
+
+void AsyncWebSocket::text(uint32_t id, const char * message){
+ text(id, message, strlen(message));
+}
+void AsyncWebSocket::text(uint32_t id, uint8_t * message, size_t len){
+ text(id, (const char *)message, len);
+}
+void AsyncWebSocket::text(uint32_t id, char * message){
+ text(id, message, strlen(message));
+}
+void AsyncWebSocket::text(uint32_t id, const String &message){
+ text(id, message.c_str(), message.length());
+}
+void AsyncWebSocket::text(uint32_t id, const __FlashStringHelper *message){
+ AsyncWebSocketClient * c = client(id);
+ if(c != NULL)
+ c->text(message);
+}
+void AsyncWebSocket::textAll(const char * message){
+ textAll(message, strlen(message));
+}
+void AsyncWebSocket::textAll(uint8_t * message, size_t len){
+ textAll((const char *)message, len);
+}
+void AsyncWebSocket::textAll(char * message){
+ textAll(message, strlen(message));
+}
+void AsyncWebSocket::textAll(const String &message){
+ textAll(message.c_str(), message.length());
+}
+void AsyncWebSocket::textAll(const __FlashStringHelper *message){
+ for(const auto& c: _clients){
+ if(c->status() == WS_CONNECTED)
+ c->text(message);
+ }
+}
+void AsyncWebSocket::binary(uint32_t id, const char * message){
+ binary(id, message, strlen(message));
+}
+void AsyncWebSocket::binary(uint32_t id, uint8_t * message, size_t len){
+ binary(id, (const char *)message, len);
+}
+void AsyncWebSocket::binary(uint32_t id, char * message){
+ binary(id, message, strlen(message));
+}
+void AsyncWebSocket::binary(uint32_t id, const String &message){
+ binary(id, message.c_str(), message.length());
+}
+void AsyncWebSocket::binary(uint32_t id, const __FlashStringHelper *message, size_t len){
+ AsyncWebSocketClient * c = client(id);
+ if(c != NULL)
+ c-> binary(message, len);
+}
+void AsyncWebSocket::binaryAll(const char * message){
+ binaryAll(message, strlen(message));
+}
+void AsyncWebSocket::binaryAll(uint8_t * message, size_t len){
+ binaryAll((const char *)message, len);
+}
+void AsyncWebSocket::binaryAll(char * message){
+ binaryAll(message, strlen(message));
+}
+void AsyncWebSocket::binaryAll(const String &message){
+ binaryAll(message.c_str(), message.length());
+}
+void AsyncWebSocket::binaryAll(const __FlashStringHelper *message, size_t len){
+ for(const auto& c: _clients){
+ if(c->status() == WS_CONNECTED)
+ c-> binary(message, len);
+ }
+ }
+
+const char * WS_STR_CONNECTION = "Connection";
+const char * WS_STR_UPGRADE = "Upgrade";
+const char * WS_STR_ORIGIN = "Origin";
+const char * WS_STR_VERSION = "Sec-WebSocket-Version";
+const char * WS_STR_KEY = "Sec-WebSocket-Key";
+const char * WS_STR_PROTOCOL = "Sec-WebSocket-Protocol";
+const char * WS_STR_ACCEPT = "Sec-WebSocket-Accept";
+const char * WS_STR_UUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
+
+bool AsyncWebSocket::canHandle(AsyncWebServerRequest *request){
+ if(!_enabled)
+ return false;
+
+ if(request->method() != HTTP_GET || !request->url().equals(_url) || !request->isExpectedRequestedConnType(RCT_WS))
+ return false;
+
+ request->addInterestingHeader(WS_STR_CONNECTION);
+ request->addInterestingHeader(WS_STR_UPGRADE);
+ request->addInterestingHeader(WS_STR_ORIGIN);
+ request->addInterestingHeader(WS_STR_VERSION);
+ request->addInterestingHeader(WS_STR_KEY);
+ request->addInterestingHeader(WS_STR_PROTOCOL);
+ return true;
+}
+
+void AsyncWebSocket::handleRequest(AsyncWebServerRequest *request){
+ if(!request->hasHeader(WS_STR_VERSION) || !request->hasHeader(WS_STR_KEY)){
+ request->send(400);
+ return;
+ }
+ if((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str())){
+ return request->requestAuthentication();
+ }
+ AsyncWebHeader* version = request->getHeader(WS_STR_VERSION);
+ if(version->value().toInt() != 13){
+ AsyncWebServerResponse *response = request->beginResponse(400);
+ response->addHeader(WS_STR_VERSION,"13");
+ request->send(response);
+ return;
+ }
+ AsyncWebHeader* key = request->getHeader(WS_STR_KEY);
+ AsyncWebServerResponse *response = new AsyncWebSocketResponse(key->value(), this);
+ if(request->hasHeader(WS_STR_PROTOCOL)){
+ AsyncWebHeader* protocol = request->getHeader(WS_STR_PROTOCOL);
+ //ToDo: check protocol
+ response->addHeader(WS_STR_PROTOCOL, protocol->value());
+ }
+ request->send(response);
+}
+
+AsyncWebSocketMessageBuffer * AsyncWebSocket::makeBuffer(size_t size)
+{
+ AsyncWebSocketMessageBuffer * buffer = new AsyncWebSocketMessageBuffer(size);
+ if (buffer) {
+ AsyncWebLockGuard l(_lock);
+ _buffers.add(buffer);
+ }
+ return buffer;
+}
+
+AsyncWebSocketMessageBuffer * AsyncWebSocket::makeBuffer(uint8_t * data, size_t size)
+{
+ AsyncWebSocketMessageBuffer * buffer = new AsyncWebSocketMessageBuffer(data, size);
+
+ if (buffer) {
+ AsyncWebLockGuard l(_lock);
+ _buffers.add(buffer);
+ }
+
+ return buffer;
+}
+
+void AsyncWebSocket::_cleanBuffers()
+{
+ AsyncWebLockGuard l(_lock);
+
+ for(AsyncWebSocketMessageBuffer * c: _buffers){
+ if(c && c->canDelete()){
+ _buffers.remove(c);
+ }
+ }
+}
+
+AsyncWebSocket::AsyncWebSocketClientLinkedList AsyncWebSocket::getClients() const {
+ return _clients;
+}
+
+/*
+ * Response to Web Socket request - sends the authorization and detaches the TCP Client from the web server
+ * Authentication code from https://github.com/Links2004/arduinoWebSockets/blob/master/src/WebSockets.cpp#L480
+ */
+
+AsyncWebSocketResponse::AsyncWebSocketResponse(const String& key, AsyncWebSocket *server){
+ _server = server;
+ _code = 101;
+ _sendContentLength = false;
+
+ uint8_t * hash = (uint8_t*)malloc(20);
+ if(hash == NULL){
+ _state = RESPONSE_FAILED;
+ return;
+ }
+ char * buffer = (char *) malloc(33);
+ if(buffer == NULL){
+ free(hash);
+ _state = RESPONSE_FAILED;
+ return;
+ }
+#ifdef ESP8266
+ sha1(key + WS_STR_UUID, hash);
+#else
+ (String&)key += WS_STR_UUID;
+ SHA1_CTX ctx;
+ SHA1Init(&ctx);
+ SHA1Update(&ctx, (const unsigned char*)key.c_str(), key.length());
+ SHA1Final(hash, &ctx);
+#endif
+ base64_encodestate _state;
+ base64_init_encodestate(&_state);
+ int len = base64_encode_block((const char *) hash, 20, buffer, &_state);
+ len = base64_encode_blockend((buffer + len), &_state);
+ addHeader(WS_STR_CONNECTION, WS_STR_UPGRADE);
+ addHeader(WS_STR_UPGRADE, "websocket");
+ addHeader(WS_STR_ACCEPT,buffer);
+ free(buffer);
+ free(hash);
+}
+
+void AsyncWebSocketResponse::_respond(AsyncWebServerRequest *request){
+ if(_state == RESPONSE_FAILED){
+ request->client()->close(true);
+ return;
+ }
+ String out = _assembleHead(request->version());
+ request->client()->write(out.c_str(), _headLength);
+ _state = RESPONSE_WAIT_ACK;
+}
+
+size_t AsyncWebSocketResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time){
+ (void)time;
+ if(len){
+ new AsyncWebSocketClient(request, _server);
+ }
+ return 0;
+}
diff --git a/lib/ESPAsyncWebServer/src/AsyncWebSocket.h b/lib/ESPAsyncWebServer/src/AsyncWebSocket.h
new file mode 100644
index 00000000..5b03aceb
--- /dev/null
+++ b/lib/ESPAsyncWebServer/src/AsyncWebSocket.h
@@ -0,0 +1,350 @@
+/*
+ Asynchronous WebServer library for Espressif MCUs
+
+ Copyright (c) 2016 Hristo Gochkov. All rights reserved.
+ This file is part of the esp8266 core for Arduino environment.
+
+ 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 ASYNCWEBSOCKET_H_
+#define ASYNCWEBSOCKET_H_
+
+#include
+#ifdef ESP32
+#include
+#define WS_MAX_QUEUED_MESSAGES 32
+#else
+#include
+#define WS_MAX_QUEUED_MESSAGES 8
+#endif
+#include
+
+#include "AsyncWebSynchronization.h"
+
+#ifdef ESP8266
+#include
+#ifdef CRYPTO_HASH_h // include Hash.h from espressif framework if the first include was from the crypto library
+#include <../src/Hash.h>
+#endif
+#endif
+
+#ifdef ESP32
+#define DEFAULT_MAX_WS_CLIENTS 8
+#else
+#define DEFAULT_MAX_WS_CLIENTS 4
+#endif
+
+class AsyncWebSocket;
+class AsyncWebSocketResponse;
+class AsyncWebSocketClient;
+class AsyncWebSocketControl;
+
+typedef struct {
+ /** Message type as defined by enum AwsFrameType.
+ * Note: Applications will only see WS_TEXT and WS_BINARY.
+ * All other types are handled by the library. */
+ uint8_t message_opcode;
+ /** Frame number of a fragmented message. */
+ uint32_t num;
+ /** Is this the last frame in a fragmented message ?*/
+ uint8_t final;
+ /** Is this frame masked? */
+ uint8_t masked;
+ /** Message type as defined by enum AwsFrameType.
+ * This value is the same as message_opcode for non-fragmented
+ * messages, but may also be WS_CONTINUATION in a fragmented message. */
+ uint8_t opcode;
+ /** Length of the current frame.
+ * This equals the total length of the message if num == 0 && final == true */
+ uint64_t len;
+ /** Mask key */
+ uint8_t mask[4];
+ /** Offset of the data inside the current frame. */
+ uint64_t index;
+} AwsFrameInfo;
+
+typedef enum { WS_DISCONNECTED, WS_CONNECTED, WS_DISCONNECTING } AwsClientStatus;
+typedef enum { WS_CONTINUATION, WS_TEXT, WS_BINARY, WS_DISCONNECT = 0x08, WS_PING, WS_PONG } AwsFrameType;
+typedef enum { WS_MSG_SENDING, WS_MSG_SENT, WS_MSG_ERROR } AwsMessageStatus;
+typedef enum { WS_EVT_CONNECT, WS_EVT_DISCONNECT, WS_EVT_PONG, WS_EVT_ERROR, WS_EVT_DATA } AwsEventType;
+
+class AsyncWebSocketMessageBuffer {
+ private:
+ uint8_t * _data;
+ size_t _len;
+ bool _lock;
+ uint32_t _count;
+
+ public:
+ AsyncWebSocketMessageBuffer();
+ AsyncWebSocketMessageBuffer(size_t size);
+ AsyncWebSocketMessageBuffer(uint8_t * data, size_t size);
+ AsyncWebSocketMessageBuffer(const AsyncWebSocketMessageBuffer &);
+ AsyncWebSocketMessageBuffer(AsyncWebSocketMessageBuffer &&);
+ ~AsyncWebSocketMessageBuffer();
+ void operator ++(int i) { (void)i; _count++; }
+ void operator --(int i) { (void)i; if (_count > 0) { _count--; } ; }
+ bool reserve(size_t size);
+ void lock() { _lock = true; }
+ void unlock() { _lock = false; }
+ uint8_t * get() { return _data; }
+ size_t length() { return _len; }
+ uint32_t count() { return _count; }
+ bool canDelete() { return (!_count && !_lock); }
+
+ friend AsyncWebSocket;
+
+};
+
+class AsyncWebSocketMessage {
+ protected:
+ uint8_t _opcode;
+ bool _mask;
+ AwsMessageStatus _status;
+ public:
+ AsyncWebSocketMessage():_opcode(WS_TEXT),_mask(false),_status(WS_MSG_ERROR){}
+ virtual ~AsyncWebSocketMessage(){}
+ virtual void ack(size_t len __attribute__((unused)), uint32_t time __attribute__((unused))){}
+ virtual size_t send(AsyncClient *client __attribute__((unused))){ return 0; }
+ virtual bool finished(){ return _status != WS_MSG_SENDING; }
+ virtual bool betweenFrames() const { return false; }
+};
+
+class AsyncWebSocketBasicMessage: public AsyncWebSocketMessage {
+ private:
+ size_t _len;
+ size_t _sent;
+ size_t _ack;
+ size_t _acked;
+ uint8_t * _data;
+public:
+ AsyncWebSocketBasicMessage(const char * data, size_t len, uint8_t opcode=WS_TEXT, bool mask=false);
+ AsyncWebSocketBasicMessage(uint8_t opcode=WS_TEXT, bool mask=false);
+ virtual ~AsyncWebSocketBasicMessage() override;
+ virtual bool betweenFrames() const override { return _acked == _ack; }
+ virtual void ack(size_t len, uint32_t time) override ;
+ virtual size_t send(AsyncClient *client) override ;
+};
+
+class AsyncWebSocketMultiMessage: public AsyncWebSocketMessage {
+ private:
+ uint8_t * _data;
+ size_t _len;
+ size_t _sent;
+ size_t _ack;
+ size_t _acked;
+ AsyncWebSocketMessageBuffer * _WSbuffer;
+public:
+ AsyncWebSocketMultiMessage(AsyncWebSocketMessageBuffer * buffer, uint8_t opcode=WS_TEXT, bool mask=false);
+ virtual ~AsyncWebSocketMultiMessage() override;
+ virtual bool betweenFrames() const override { return _acked == _ack; }
+ virtual void ack(size_t len, uint32_t time) override ;
+ virtual size_t send(AsyncClient *client) override ;
+};
+
+class AsyncWebSocketClient {
+ private:
+ AsyncClient *_client;
+ AsyncWebSocket *_server;
+ uint32_t _clientId;
+ AwsClientStatus _status;
+
+ LinkedList _controlQueue;
+ LinkedList _messageQueue;
+
+ uint8_t _pstate;
+ AwsFrameInfo _pinfo;
+
+ uint32_t _lastMessageTime;
+ uint32_t _keepAlivePeriod;
+
+ void _queueMessage(AsyncWebSocketMessage *dataMessage);
+ void _queueControl(AsyncWebSocketControl *controlMessage);
+ void _runQueue();
+
+ public:
+ void *_tempObject;
+
+ AsyncWebSocketClient(AsyncWebServerRequest *request, AsyncWebSocket *server);
+ ~AsyncWebSocketClient();
+
+ //client id increments for the given server
+ uint32_t id(){ return _clientId; }
+ AwsClientStatus status(){ return _status; }
+ AsyncClient* client(){ return _client; }
+ AsyncWebSocket *server(){ return _server; }
+ AwsFrameInfo const &pinfo() const { return _pinfo; }
+
+ IPAddress remoteIP();
+ uint16_t remotePort();
+
+ //control frames
+ void close(uint16_t code=0, const char * message=NULL);
+ void ping(uint8_t *data=NULL, size_t len=0);
+
+ //set auto-ping period in seconds. disabled if zero (default)
+ void keepAlivePeriod(uint16_t seconds){
+ _keepAlivePeriod = seconds * 1000;
+ }
+ uint16_t keepAlivePeriod(){
+ return (uint16_t)(_keepAlivePeriod / 1000);
+ }
+
+ //data packets
+ void message(AsyncWebSocketMessage *message){ _queueMessage(message); }
+ bool queueIsFull();
+
+ size_t printf(const char *format, ...) __attribute__ ((format (printf, 2, 3)));
+#ifndef ESP32
+ size_t printf_P(PGM_P formatP, ...) __attribute__ ((format (printf, 2, 3)));
+#endif
+ void text(const char * message, size_t len);
+ void text(const char * message);
+ void text(uint8_t * message, size_t len);
+ void text(char * message);
+ void text(const String &message);
+ void text(const __FlashStringHelper *data);
+ void text(AsyncWebSocketMessageBuffer *buffer);
+
+ void binary(const char * message, size_t len);
+ void binary(const char * message);
+ void binary(uint8_t * message, size_t len);
+ void binary(char * message);
+ void binary(const String &message);
+ void binary(const __FlashStringHelper *data, size_t len);
+ void binary(AsyncWebSocketMessageBuffer *buffer);
+
+ bool canSend() { return _messageQueue.length() < WS_MAX_QUEUED_MESSAGES; }
+
+ //system callbacks (do not call)
+ void _onAck(size_t len, uint32_t time);
+ void _onError(int8_t);
+ void _onPoll();
+ void _onTimeout(uint32_t time);
+ void _onDisconnect();
+ void _onData(void *pbuf, size_t plen);
+};
+
+typedef std::function AwsEventHandler;
+
+//WebServer Handler implementation that plays the role of a socket server
+class AsyncWebSocket: public AsyncWebHandler {
+ public:
+ typedef LinkedList AsyncWebSocketClientLinkedList;
+ private:
+ String _url;
+ AsyncWebSocketClientLinkedList _clients;
+ uint32_t _cNextId;
+ AwsEventHandler _eventHandler;
+ bool _enabled;
+ AsyncWebLock _lock;
+
+ public:
+ AsyncWebSocket(const String& url);
+ ~AsyncWebSocket();
+ const char * url() const { return _url.c_str(); }
+ void enable(bool e){ _enabled = e; }
+ bool enabled() const { return _enabled; }
+ bool availableForWriteAll();
+ bool availableForWrite(uint32_t id);
+
+ size_t count() const;
+ AsyncWebSocketClient * client(uint32_t id);
+ bool hasClient(uint32_t id){ return client(id) != NULL; }
+
+ void close(uint32_t id, uint16_t code=0, const char * message=NULL);
+ void closeAll(uint16_t code=0, const char * message=NULL);
+ void cleanupClients(uint16_t maxClients = DEFAULT_MAX_WS_CLIENTS);
+
+ void ping(uint32_t id, uint8_t *data=NULL, size_t len=0);
+ void pingAll(uint8_t *data=NULL, size_t len=0); // done
+
+ void text(uint32_t id, const char * message, size_t len);
+ void text(uint32_t id, const char * message);
+ void text(uint32_t id, uint8_t * message, size_t len);
+ void text(uint32_t id, char * message);
+ void text(uint32_t id, const String &message);
+ void text(uint32_t id, const __FlashStringHelper *message);
+
+ void textAll(const char * message, size_t len);
+ void textAll(const char * message);
+ void textAll(uint8_t * message, size_t len);
+ void textAll(char * message);
+ void textAll(const String &message);
+ void textAll(const __FlashStringHelper *message); // need to convert
+ void textAll(AsyncWebSocketMessageBuffer * buffer);
+
+ void binary(uint32_t id, const char * message, size_t len);
+ void binary(uint32_t id, const char * message);
+ void binary(uint32_t id, uint8_t * message, size_t len);
+ void binary(uint32_t id, char * message);
+ void binary(uint32_t id, const String &message);
+ void binary(uint32_t id, const __FlashStringHelper *message, size_t len);
+
+ void binaryAll(const char * message, size_t len);
+ void binaryAll(const char * message);
+ void binaryAll(uint8_t * message, size_t len);
+ void binaryAll(char * message);
+ void binaryAll(const String &message);
+ void binaryAll(const __FlashStringHelper *message, size_t len);
+ void binaryAll(AsyncWebSocketMessageBuffer * buffer);
+
+ void message(uint32_t id, AsyncWebSocketMessage *message);
+ void messageAll(AsyncWebSocketMultiMessage *message);
+
+ size_t printf(uint32_t id, const char *format, ...) __attribute__ ((format (printf, 3, 4)));
+ size_t printfAll(const char *format, ...) __attribute__ ((format (printf, 2, 3)));
+#ifndef ESP32
+ size_t printf_P(uint32_t id, PGM_P formatP, ...) __attribute__ ((format (printf, 3, 4)));
+#endif
+ size_t printfAll_P(PGM_P formatP, ...) __attribute__ ((format (printf, 2, 3)));
+
+ //event listener
+ void onEvent(AwsEventHandler handler){
+ _eventHandler = handler;
+ }
+
+ //system callbacks (do not call)
+ uint32_t _getNextId(){ return _cNextId++; }
+ void _addClient(AsyncWebSocketClient * client);
+ void _handleDisconnect(AsyncWebSocketClient * client);
+ void _handleEvent(AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len);
+ virtual bool canHandle(AsyncWebServerRequest *request) override final;
+ virtual void handleRequest(AsyncWebServerRequest *request) override final;
+
+
+ // messagebuffer functions/objects.
+ AsyncWebSocketMessageBuffer * makeBuffer(size_t size = 0);
+ AsyncWebSocketMessageBuffer * makeBuffer(uint8_t * data, size_t size);
+ LinkedList _buffers;
+ void _cleanBuffers();
+
+ AsyncWebSocketClientLinkedList getClients() const;
+};
+
+//WebServer response to authenticate the socket and detach the tcp client from the web server request
+class AsyncWebSocketResponse: public AsyncWebServerResponse {
+ private:
+ String _content;
+ AsyncWebSocket *_server;
+ public:
+ AsyncWebSocketResponse(const String& key, AsyncWebSocket *server);
+ void _respond(AsyncWebServerRequest *request);
+ size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time);
+ bool _sourceValid() const { return true; }
+};
+
+
+#endif /* ASYNCWEBSOCKET_H_ */
diff --git a/lib/ESPAsyncWebServer/src/AsyncWebSynchronization.h b/lib/ESPAsyncWebServer/src/AsyncWebSynchronization.h
new file mode 100644
index 00000000..f36c52dc
--- /dev/null
+++ b/lib/ESPAsyncWebServer/src/AsyncWebSynchronization.h
@@ -0,0 +1,87 @@
+#ifndef ASYNCWEBSYNCHRONIZATION_H_
+#define ASYNCWEBSYNCHRONIZATION_H_
+
+// Synchronisation is only available on ESP32, as the ESP8266 isn't using FreeRTOS by default
+
+#include
+
+#ifdef ESP32
+
+// This is the ESP32 version of the Sync Lock, using the FreeRTOS Semaphore
+class AsyncWebLock
+{
+private:
+ SemaphoreHandle_t _lock;
+ mutable void *_lockedBy;
+
+public:
+ AsyncWebLock() {
+ _lock = xSemaphoreCreateBinary();
+ _lockedBy = NULL;
+ xSemaphoreGive(_lock);
+ }
+
+ ~AsyncWebLock() {
+ vSemaphoreDelete(_lock);
+ }
+
+ bool lock() const {
+ extern void *pxCurrentTCB;
+ if (_lockedBy != pxCurrentTCB) {
+ xSemaphoreTake(_lock, portMAX_DELAY);
+ _lockedBy = pxCurrentTCB;
+ return true;
+ }
+ return false;
+ }
+
+ void unlock() const {
+ _lockedBy = NULL;
+ xSemaphoreGive(_lock);
+ }
+};
+
+#else
+
+// This is the 8266 version of the Sync Lock which is currently unimplemented
+class AsyncWebLock
+{
+
+public:
+ AsyncWebLock() {
+ }
+
+ ~AsyncWebLock() {
+ }
+
+ bool lock() const {
+ return false;
+ }
+
+ void unlock() const {
+ }
+};
+#endif
+
+class AsyncWebLockGuard
+{
+private:
+ const AsyncWebLock *_lock;
+
+public:
+ AsyncWebLockGuard(const AsyncWebLock &l) {
+ if (l.lock()) {
+ _lock = &l;
+ } else {
+ _lock = NULL;
+ }
+ }
+
+ ~AsyncWebLockGuard() {
+ if (_lock) {
+ _lock->unlock();
+ }
+ }
+};
+
+#endif // ASYNCWEBSYNCHRONIZATION_H_
\ No newline at end of file
diff --git a/lib/ESPAsyncWebServer/src/ESPAsyncWebServer.h b/lib/ESPAsyncWebServer/src/ESPAsyncWebServer.h
new file mode 100644
index 00000000..3b2fbd96
--- /dev/null
+++ b/lib/ESPAsyncWebServer/src/ESPAsyncWebServer.h
@@ -0,0 +1,501 @@
+/*
+ Asynchronous WebServer library for Espressif MCUs
+
+ Copyright (c) 2016 Hristo Gochkov. All rights reserved.
+ This file is part of the esp8266 core for Arduino environment.
+
+ 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 _ESPAsyncWebServer_H_
+#define _ESPAsyncWebServer_H_
+
+#include
+
+#include "Arduino.h"
+#include "FS.h"
+#include "StringArray.h"
+
+#ifdef ESP32
+#include
+#include
+#elif defined(ESP8266)
+#include
+#include
+#else
+#error Platform not supported
+#endif
+
+#ifdef ASYNCWEBSERVER_REGEX
+#define ASYNCWEBSERVER_REGEX_ATTRIBUTE
+#else
+#define ASYNCWEBSERVER_REGEX_ATTRIBUTE __attribute__((warning("ASYNCWEBSERVER_REGEX not defined")))
+#endif
+
+#define DEBUGF(...) //Serial.printf(__VA_ARGS__)
+
+class AsyncWebServer;
+class AsyncWebServerRequest;
+class AsyncWebServerResponse;
+class AsyncWebHeader;
+class AsyncWebParameter;
+class AsyncWebRewrite;
+class AsyncWebHandler;
+class AsyncStaticWebHandler;
+class AsyncCallbackWebHandler;
+class AsyncResponseStream;
+
+#ifdef ESP32
+#ifdef WEBSERVER_H
+#define HTTP_ENUM
+#endif
+#elif defined(ESP8266)
+#ifdef ESP8266WEBSERVER_H
+#define HTTP_ENUM
+#endif
+#endif
+
+#ifndef HTTP_ENUM
+typedef enum { HTTP_GET = 0b00000001,
+ HTTP_POST = 0b00000010,
+ HTTP_DELETE = 0b00000100,
+ HTTP_PUT = 0b00001000,
+ HTTP_PATCH = 0b00010000,
+ HTTP_HEAD = 0b00100000,
+ HTTP_OPTIONS = 0b01000000,
+ HTTP_ANY = 0b01111111,
+} WebRequestMethod;
+#endif
+
+//if this value is returned when asked for data, packet will not be sent and you will be asked for data again
+#define RESPONSE_TRY_AGAIN 0xFFFFFFFF
+
+typedef uint8_t WebRequestMethodComposite;
+typedef std::function ArDisconnectHandler;
+
+/*
+ * PARAMETER :: Chainable object to hold GET/POST and FILE parameters
+ * */
+
+class AsyncWebParameter {
+ private:
+ String _name;
+ String _value;
+ size_t _size;
+ bool _isForm;
+ bool _isFile;
+
+ public:
+ AsyncWebParameter(const String& name, const String& value, bool form = false, bool file = false, size_t size = 0) : _name(name), _value(value), _size(size), _isForm(form), _isFile(file) {}
+ const String& name() const { return _name; }
+ const String& value() const { return _value; }
+ size_t size() const { return _size; }
+ bool isPost() const { return _isForm; }
+ bool isFile() const { return _isFile; }
+};
+
+/*
+ * HEADER :: Chainable object to hold the headers
+ * */
+
+class AsyncWebHeader {
+ private:
+ String _name;
+ String _value;
+
+ public:
+ AsyncWebHeader(const String& name, const String& value) : _name(name), _value(value) {}
+ AsyncWebHeader(const String& data) : _name(), _value() {
+ if (!data) return;
+ int index = data.indexOf(':');
+ if (index < 0) return;
+ _name = data.substring(0, index);
+ _value = data.substring(index + 2);
+ }
+ ~AsyncWebHeader() {}
+ const String& name() const { return _name; }
+ const String& value() const { return _value; }
+ String toString() const { return String(_name + ": " + _value + "\r\n"); }
+};
+
+/*
+ * REQUEST :: Each incoming Client is wrapped inside a Request and both live together until disconnect
+ * */
+
+typedef enum { RCT_NOT_USED = -1,
+ RCT_DEFAULT = 0,
+ RCT_HTTP,
+ RCT_WS,
+ RCT_EVENT,
+ RCT_MAX } RequestedConnectionType;
+
+typedef std::function AwsResponseFiller;
+typedef std::function AwsTemplateProcessor;
+
+class AsyncWebServerRequest {
+ using File = fs::File;
+ using FS = fs::FS;
+ friend class AsyncWebServer;
+ friend class AsyncCallbackWebHandler;
+
+ private:
+ AsyncClient* _client;
+ AsyncWebServer* _server;
+ AsyncWebHandler* _handler;
+ AsyncWebServerResponse* _response;
+ StringArray _interestingHeaders;
+ ArDisconnectHandler _onDisconnectfn;
+
+ String _temp;
+ uint8_t _parseState;
+
+ uint8_t _version;
+ WebRequestMethodComposite _method;
+ String _url;
+ String _host;
+ String _contentType;
+ String _boundary;
+ String _authorization;
+ RequestedConnectionType _reqconntype;
+ void _removeNotInterestingHeaders();
+ bool _isDigest;
+ bool _isMultipart;
+ bool _isPlainPost;
+ bool _expectingContinue;
+ size_t _contentLength;
+ size_t _parsedLength;
+
+ LinkedList _headers;
+ LinkedList _params;
+ LinkedList _pathParams;
+
+ uint8_t _multiParseState;
+ uint8_t _boundaryPosition;
+ size_t _itemStartIndex;
+ size_t _itemSize;
+ String _itemName;
+ String _itemFilename;
+ String _itemType;
+ String _itemValue;
+ uint8_t* _itemBuffer;
+ size_t _itemBufferIndex;
+ bool _itemIsFile;
+
+ void _onPoll();
+ void _onAck(size_t len, uint32_t time);
+ void _onError(int8_t error);
+ void _onTimeout(uint32_t time);
+ void _onDisconnect();
+ void _onData(void* buf, size_t len);
+
+ void _addParam(AsyncWebParameter*);
+ void _addPathParam(const char* param);
+
+ bool _parseReqHead();
+ bool _parseReqHeader();
+ void _parseLine();
+ void _parsePlainPostChar(uint8_t data);
+ void _parseMultipartPostByte(uint8_t data, bool last);
+ void _addGetParams(const String& params);
+
+ void _handleUploadStart();
+ void _handleUploadByte(uint8_t data, bool last);
+ void _handleUploadEnd();
+
+ public:
+ File _tempFile;
+ void* _tempObject;
+
+ AsyncWebServerRequest(AsyncWebServer*, AsyncClient*);
+ ~AsyncWebServerRequest();
+
+ AsyncClient* client() { return _client; }
+ uint8_t version() const { return _version; }
+ WebRequestMethodComposite method() const { return _method; }
+ const String& url() const { return _url; }
+ const String& host() const { return _host; }
+ const String& contentType() const { return _contentType; }
+ size_t contentLength() const { return _contentLength; }
+ bool multipart() const { return _isMultipart; }
+ const char* methodToString() const;
+ const char* requestedConnTypeToString() const;
+ RequestedConnectionType requestedConnType() const { return _reqconntype; }
+ bool isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2 = RCT_NOT_USED, RequestedConnectionType erct3 = RCT_NOT_USED);
+ void onDisconnect(ArDisconnectHandler fn);
+
+ //hash is the string representation of:
+ // base64(user:pass) for basic or
+ // user:realm:md5(user:realm:pass) for digest
+ bool authenticate(const char* hash);
+ bool authenticate(const char* username, const char* password, const char* realm = NULL, bool passwordIsHash = false);
+ void requestAuthentication(const char* realm = NULL, bool isDigest = true);
+
+ void setHandler(AsyncWebHandler* handler) { _handler = handler; }
+ void addInterestingHeader(const String& name);
+
+ void redirect(const String& url);
+
+ void send(AsyncWebServerResponse* response);
+ void send(int code, const String& contentType = String(), const String& content = String());
+ void send(FS& fs, const String& path, const String& contentType = String(), bool download = false, AwsTemplateProcessor callback = nullptr);
+ void send(File content, const String& path, const String& contentType = String(), bool download = false, AwsTemplateProcessor callback = nullptr);
+ void send(Stream& stream, const String& contentType, size_t len, AwsTemplateProcessor callback = nullptr);
+ void send(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr);
+ void sendChunked(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr);
+ void send_P(int code, const String& contentType, const uint8_t* content, size_t len, AwsTemplateProcessor callback = nullptr);
+ void send_P(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback = nullptr);
+
+ AsyncWebServerResponse* beginResponse(int code, const String& contentType = String(), const String& content = String());
+ AsyncWebServerResponse* beginResponse(FS& fs, const String& path, const String& contentType = String(), bool download = false, AwsTemplateProcessor callback = nullptr);
+ AsyncWebServerResponse* beginResponse(File content, const String& path, const String& contentType = String(), bool download = false, AwsTemplateProcessor callback = nullptr);
+ AsyncWebServerResponse* beginResponse(Stream& stream, const String& contentType, size_t len, AwsTemplateProcessor callback = nullptr);
+ AsyncWebServerResponse* beginResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr);
+ AsyncWebServerResponse* beginChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr);
+ AsyncResponseStream* beginResponseStream(const String& contentType, size_t bufferSize = 1460);
+ AsyncWebServerResponse* beginResponse_P(int code, const String& contentType, const uint8_t* content, size_t len, AwsTemplateProcessor callback = nullptr);
+ AsyncWebServerResponse* beginResponse_P(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback = nullptr);
+
+ size_t headers() const; // get header count
+ bool hasHeader(const String& name) const; // check if header exists
+ bool hasHeader(const __FlashStringHelper* data) const; // check if header exists
+
+ AsyncWebHeader* getHeader(const String& name) const;
+ AsyncWebHeader* getHeader(const __FlashStringHelper* data) const;
+ AsyncWebHeader* getHeader(size_t num) const;
+
+ size_t params() const; // get arguments count
+ bool hasParam(const String& name, bool post = false, bool file = false) const;
+ bool hasParam(const __FlashStringHelper* data, bool post = false, bool file = false) const;
+
+ AsyncWebParameter* getParam(const String& name, bool post = false, bool file = false) const;
+ AsyncWebParameter* getParam(const __FlashStringHelper* data, bool post, bool file) const;
+ AsyncWebParameter* getParam(size_t num) const;
+
+ size_t args() const { return params(); } // get arguments count
+ const String& arg(const String& name) const; // get request argument value by name
+ const String& arg(const __FlashStringHelper* data) const; // get request argument value by F(name)
+ const String& arg(size_t i) const; // get request argument value by number
+ const String& argName(size_t i) const; // get request argument name by number
+ bool hasArg(const char* name) const; // check if argument exists
+ bool hasArg(const __FlashStringHelper* data) const; // check if F(argument) exists
+
+ const String& ASYNCWEBSERVER_REGEX_ATTRIBUTE pathArg(size_t i) const;
+
+ const String& header(const char* name) const; // get request header value by name
+ const String& header(const __FlashStringHelper* data) const; // get request header value by F(name)
+ const String& header(size_t i) const; // get request header value by number
+ const String& headerName(size_t i) const; // get request header name by number
+ String urlDecode(const String& text) const;
+};
+
+/*
+ * FILTER :: Callback to filter AsyncWebRewrite and AsyncWebHandler (done by the Server)
+ * */
+
+typedef std::function