diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8f1dca5..8640589 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -30,10 +30,10 @@ Arduino IDE version: 1.8.19 ESP8266_NODEMCU_ESP12E using ESP8266_W5500 Ethernet ESP8266 core v3.0.2 OS: Ubuntu 20.04 LTS -Linux xy-Inspiron-3593 5.13.0-39-generic #44~20.04.1-Ubuntu SMP Thu Mar 24 16:43:35 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux +Linux xy-Inspiron-3593 5.15.0-48-generic #54~20.04.1-Ubuntu SMP Thu Sep 1 16:17:26 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux Context: -I encountered a crash while trying to use the Timer Interrupt. +I encountered a crash while using this library Steps to reproduce: 1. ... diff --git a/changelog.md b/changelog.md index eb8e534..e5f7cc7 100644 --- a/changelog.md +++ b/changelog.md @@ -11,6 +11,7 @@ ## Table of contents * [Changelog](#changelog) + * [Releases v1.5.0](#releases-v150) * [Releases v1.4.1](#releases-v141) --- @@ -18,6 +19,11 @@ ## Changelog +#### Releases v1.5.0 + +1. Support using `CString` to save heap to send `very large data`. Check [request->send(200, textPlainStr, jsonChartDataCharStr); - Without using String Class - to save heap #8](https://github.com/khoih-prog/Portenta_H7_AsyncWebServer/pull/8) and [All memmove() removed - string no longer destroyed #11](https://github.com/khoih-prog/Portenta_H7_AsyncWebServer/pull/11) +2. Add multiple examples to demo the new feature + #### Releases v1.4.1 1. Initial coding to port [ESPAsyncWebServer](https://github.com/me-no-dev/ESPAsyncWebServer) to ESP8266 boards using W5x00 / ENC28J60 Ethernet. diff --git a/examples/Async_AdvancedWebServer/defines.h b/examples/Async_AdvancedWebServer/defines.h index 8ebfc9f..4137aff 100644 --- a/examples/Async_AdvancedWebServer/defines.h +++ b/examples/Async_AdvancedWebServer/defines.h @@ -30,7 +30,8 @@ #include -#define CSPIN 16 // 5 +// Using GPIO4, GPIO16, or GPIO5 +#define CSPIN 4 //16 // 5 #if USING_W5500 #include "W5500lwIP.h" diff --git a/examples/Async_AdvancedWebServer_MemoryIssues_SendArduinoString/Async_AdvancedWebServer_MemoryIssues_SendArduinoString.ino b/examples/Async_AdvancedWebServer_MemoryIssues_SendArduinoString/Async_AdvancedWebServer_MemoryIssues_SendArduinoString.ino new file mode 100644 index 0000000..6580df5 --- /dev/null +++ b/examples/Async_AdvancedWebServer_MemoryIssues_SendArduinoString/Async_AdvancedWebServer_MemoryIssues_SendArduinoString.ino @@ -0,0 +1,303 @@ +/**************************************************************************************************************************** + Async_AdvancedWebServer_MemoryIssues_SendArduinoString.ino + + For ESP8266 using W5x00/ENC8266 Ethernet + + AsyncWebServer_Ethernet is a library for the Ethernet with lwIP_5100, lwIP_5500 or lwIP_enc28j60 library + + Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) + Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_Ethernet + Licensed under GPLv3 license + + Copyright (c) 2015, Majenko Technologies + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + Neither the name of Majenko Technologies nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************************************************************/ + +#include "defines.h" + +#include + +// In bytes +//#define STRING_SIZE 40000 +#define STRING_SIZE 12000 + +AsyncWebServer server(80); + +int reqCount = 0; // number of requests received + +#define BUFFER_SIZE 512 +char temp[BUFFER_SIZE]; + +void handleRoot(AsyncWebServerRequest *request) +{ + digitalWrite(LED_BUILTIN, LED_ON); + + int sec = millis() / 1000; + int min = sec / 60; + int hr = min / 60; + int day = hr / 24; + + snprintf(temp, BUFFER_SIZE - 1, + "\ +\ +\ +AsyncWebServer-%s\ +\ +\ +\ +

AsyncWebServer_Ethernet!

\ +

running on %s

\ +

Uptime: %d d %02d:%02d:%02d

\ +\ +\ +", BOARD_NAME, SHIELD_TYPE, day, hr % 24, min % 60, sec % 60); + + request->send(200, "text/html", temp); + + digitalWrite(LED_BUILTIN, LED_OFF); +} + +void handleNotFound(AsyncWebServerRequest *request) +{ + digitalWrite(LED_BUILTIN, LED_ON); + String message = "File Not Found\n\n"; + + message += "URI: "; + message += request->url(); + message += "\nMethod: "; + message += (request->method() == HTTP_GET) ? "GET" : "POST"; + message += "\nArguments: "; + message += request->args(); + message += "\n"; + + for (uint8_t i = 0; i < request->args(); i++) + { + message += " " + request->argName(i) + ": " + request->arg(i) + "\n"; + } + + request->send(404, "text/plain", message); + digitalWrite(LED_BUILTIN, LED_OFF); +} + +void PrintHeapData(String hIn) +{ + // cores/esp8266/Esp.cpp + static uint32_t maxFreeHeap = 0xFFFFFFFF; + + // Get once at the beginning for comparison only + static uint32_t totalHeap = ESP.getFreeHeap(); + + uint32_t freeHeap = ESP.getFreeHeap(); + + // Print and update only when larger heap + if (maxFreeHeap > freeHeap) + { + maxFreeHeap = freeHeap; + + Serial.print("\nHEAP DATA - "); + Serial.print(hIn); + + Serial.print(" Free heap: "); + Serial.print(freeHeap); + Serial.print(" Used heap: "); + Serial.println(totalHeap - freeHeap); + } +} + +void PrintStringSize(String & out) +{ + static uint32_t count = 0; + + // Print only when cStr length too large and corrupting memory or every (20 * 5) s + if ( (out.length() >= STRING_SIZE) || (++count > 20) ) + { + Serial.print("\nOut String Length="); + Serial.println(out.length()); + + count = 0; + } +} + +void drawGraph(AsyncWebServerRequest *request) +{ + String out; + + out.reserve(STRING_SIZE); + char temp[70]; + + out += "\n"; + out += "\n"; + out += "\n"; + int y = rand() % 130; + + for (int x = 10; x < 1500; x += 10) + { + int y2 = rand() % 130; + sprintf(temp, "\n", x, 140 - y, x + 10, 140 - y2); + out += temp; + y = y2; + } + + out += "\n\n"; + + PrintHeapData("Pre Send"); + + PrintStringSize(out); + + request->send(200, "image/svg+xml", out); + + PrintHeapData("Post Send"); +} + +void initEthernet() +{ + SPI.begin(); + SPI.setClockDivider(SPI_CLOCK_DIV4); + SPI.setBitOrder(MSBFIRST); + SPI.setDataMode(SPI_MODE0); + +#if !USING_DHCP + eth.config(localIP, gateway, netMask, gateway); +#endif + + eth.setDefault(); + + if (!eth.begin()) + { + Serial.println("No Ethernet hardware ... Stop here"); + + while (true) + { + delay(1000); + } + } + else + { + Serial.print("Connecting to network : "); + + while (!eth.connected()) + { + Serial.print("."); + delay(1000); + } + } + + Serial.println(); + +#if USING_DHCP + Serial.print("Ethernet DHCP IP address: "); +#else + Serial.print("Ethernet Static IP address: "); +#endif + + Serial.println(eth.localIP()); +} + +void setup() +{ + pinMode(LED_BUILTIN, OUTPUT); + digitalWrite(LED_BUILTIN, LED_OFF); + + Serial.begin(115200); + while (!Serial && millis() < 5000); + + delay(200); + + Serial.print("\nStart Async_AdvancedWebServer_MemoryIssues_SendArduinoString on "); Serial.print(BOARD_NAME); + Serial.print(" with "); Serial.println(SHIELD_TYPE); + Serial.println(ASYNC_WEBSERVER_ETHERNET_VERSION); + + PrintHeapData("Start =>"); + + initEthernet(); + + /////////////////////////////////// + + server.on("/", HTTP_GET, [](AsyncWebServerRequest * request) + { + handleRoot(request); + }); + + server.on("/test.svg", HTTP_GET, [](AsyncWebServerRequest * request) + { + drawGraph(request); + }); + + server.on("/inline", [](AsyncWebServerRequest * request) + { + request->send(200, "text/plain", "This works as well"); + }); + + server.onNotFound(handleNotFound); + + server.begin(); + + Serial.print(F("HTTP EthernetWebServer is @ IP : ")); + Serial.println(eth.localIP()); + + PrintHeapData("Pre Create Arduino String"); + +} + +void heartBeatPrint() +{ + static int num = 1; + + Serial.print(F(".")); + + if (num == 80) + { + Serial.println(); + num = 1; + } + else if (num++ % 10 == 0) + { + Serial.print(F(" ")); + } +} + +void check_status() +{ + static unsigned long checkstatus_timeout = 0; + +#define STATUS_CHECK_INTERVAL 10000L + + // Send status report every STATUS_REPORT_INTERVAL (60) seconds: we don't need to send updates frequently if there is no status change. + if ((millis() > checkstatus_timeout) || (checkstatus_timeout == 0)) + { + heartBeatPrint(); + checkstatus_timeout = millis() + STATUS_CHECK_INTERVAL; + } +} + +void loop() +{ + check_status(); +} diff --git a/examples/Async_AdvancedWebServer_MemoryIssues_SendArduinoString/defines.h b/examples/Async_AdvancedWebServer_MemoryIssues_SendArduinoString/defines.h new file mode 100644 index 0000000..4137aff --- /dev/null +++ b/examples/Async_AdvancedWebServer_MemoryIssues_SendArduinoString/defines.h @@ -0,0 +1,74 @@ +/**************************************************************************************************************************** + defines.h + + For ESP8266 using W5x00/ENC8266 Ethernet + + AsyncWebServer_Ethernet is a library for the Ethernet with lwIP_5100, lwIP_5500 or lwIP_enc28j60 library + + Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) + Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_Ethernet + Licensed under GPLv3 license + ***************************************************************************************************************************************/ + +#ifndef defines_h +#define defines_h + +#if defined(ESP8266) + #define LED_ON LOW + #define LED_OFF HIGH +#else + #error Only ESP8266 +#endif + +#define _AWS_ETHERNET_LOGLEVEL_ 1 + +////////////////////////////////////////////////////////// + +#define USING_W5500 true +#define USING_W5100 false +#define USING_ENC28J60 false + +#include + +// Using GPIO4, GPIO16, or GPIO5 +#define CSPIN 4 //16 // 5 + +#if USING_W5500 + #include "W5500lwIP.h" + #define SHIELD_TYPE "ESP8266_W5500 Ethernet" + + Wiznet5500lwIP eth(CSPIN); + +#elif USING_W5100 + #include + #define SHIELD_TYPE "ESP8266_W5100 Ethernet" + + Wiznet5100lwIP eth(CSPIN); + +#elif USING_ENC28J60 + #include + #define SHIELD_TYPE "ESP8266_ENC28J60 Ethernet" + + ENC28J60lwIP eth(CSPIN); +#else + // default if none selected + #include "W5500lwIP.h" + + Wiznet5500lwIP eth(CSPIN); +#endif + +#include // WiFiClient (-> TCPClient) + +using TCPClient = WiFiClient; + +////////////////////////////////////////////////////////// + +#define USING_DHCP true + +#if !USING_DHCP + IPAddress localIP(192, 168, 2, 222); + IPAddress gateway(192, 168, 2, 1); + IPAddress netMask(255, 255, 255, 0); +#endif + +#endif //defines_h diff --git a/examples/Async_AdvancedWebServer_MemoryIssues_Send_CString/Async_AdvancedWebServer_MemoryIssues_Send_CString.ino b/examples/Async_AdvancedWebServer_MemoryIssues_Send_CString/Async_AdvancedWebServer_MemoryIssues_Send_CString.ino new file mode 100644 index 0000000..af57851 --- /dev/null +++ b/examples/Async_AdvancedWebServer_MemoryIssues_Send_CString/Async_AdvancedWebServer_MemoryIssues_Send_CString.ino @@ -0,0 +1,311 @@ +/**************************************************************************************************************************** + Async_AdvancedWebServer_MemoryIssues_Send_CString.ino + + For ESP8266 using W5x00/ENC8266 Ethernet + + AsyncWebServer_Ethernet is a library for the Ethernet with lwIP_5100, lwIP_5500 or lwIP_enc28j60 library + + Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) + Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_Ethernet + Licensed under GPLv3 license + + Copyright (c) 2015, Majenko Technologies + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + Neither the name of Majenko Technologies nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************************************************************/ + +#include "defines.h" + +#include + +char *cStr; + +// In bytes +#define CSTRING_SIZE 12000 + +AsyncWebServer server(80); + +int reqCount = 0; // number of requests received + +#define BUFFER_SIZE 768 // a little larger in case required for header shift (destructive send) +char temp[BUFFER_SIZE]; + +void handleRoot(AsyncWebServerRequest *request) +{ + digitalWrite(LED_BUILTIN, LED_ON); + + int sec = millis() / 1000; + int min = sec / 60; + int hr = min / 60; + int day = hr / 24; + + snprintf(temp, BUFFER_SIZE - 1, + "\ +\ +\ +AsyncWebServer-%s\ +\ +\ +\ +

AsyncWebServer_Ethernet!

\ +

running on %s

\ +

Uptime: %d d %02d:%02d:%02d

\ +\ +\ +", BOARD_NAME, SHIELD_TYPE, day, hr % 24, min % 60, sec % 60); + + request->send(200, "text/html", temp); + + digitalWrite(LED_BUILTIN, LED_OFF); +} + +void handleNotFound(AsyncWebServerRequest *request) +{ + digitalWrite(LED_BUILTIN, LED_ON); + String message = "File Not Found\n\n"; + + message += "URI: "; + message += request->url(); + message += "\nMethod: "; + message += (request->method() == HTTP_GET) ? "GET" : "POST"; + message += "\nArguments: "; + message += request->args(); + message += "\n"; + + for (uint8_t i = 0; i < request->args(); i++) + { + message += " " + request->argName(i) + ": " + request->arg(i) + "\n"; + } + + request->send(404, "text/plain", message); + digitalWrite(LED_BUILTIN, LED_OFF); +} + +void PrintHeapData(String hIn) +{ + // cores/esp8266/Esp.cpp + static uint32_t maxFreeHeap = 0xFFFFFFFF; + + // Get once at the beginning for comparison only + static uint32_t totalHeap = ESP.getFreeHeap(); + + uint32_t freeHeap = ESP.getFreeHeap(); + + // Print and update only when larger heap + if (maxFreeHeap > freeHeap) + { + maxFreeHeap = freeHeap; + + Serial.print("\nHEAP DATA - "); + Serial.print(hIn); + + Serial.print(" Free heap: "); + Serial.print(freeHeap); + Serial.print(" Used heap: "); + Serial.println(totalHeap - freeHeap); + } +} + +void PrintStringSize(const char* cStr) +{ + Serial.print("\nOut String Length="); + Serial.println(strlen(cStr)); +} + +void drawGraph(AsyncWebServerRequest *request) +{ + char temp[80]; + + cStr[0] = '\0'; + + strcat(cStr, "\n"); + strcat(cStr, "\n"); + strcat(cStr, "\n"); + int y = rand() % 130; + + for (int x = 10; x < 1500; x += 10) + { + int y2 = rand() % 130; + sprintf(temp, "\n", x, 140 - y, x + 10, 140 - y2); + strcat(cStr, temp); + y = y2; + } + + strcat(cStr, "\n\n"); + + PrintHeapData("Pre Send"); + + // Print only when cStr length too large and corrupting memory + if ( (strlen(cStr) >= CSTRING_SIZE)) + { + PrintStringSize(cStr); + } + + request->send(200, "image/svg+xml", cStr, false); + + PrintHeapData("Post Send"); +} + +void initEthernet() +{ + SPI.begin(); + SPI.setClockDivider(SPI_CLOCK_DIV4); + SPI.setBitOrder(MSBFIRST); + SPI.setDataMode(SPI_MODE0); + +#if !USING_DHCP + eth.config(localIP, gateway, netMask, gateway); +#endif + + eth.setDefault(); + + if (!eth.begin()) + { + Serial.println("No Ethernet hardware ... Stop here"); + + while (true) + { + delay(1000); + } + } + else + { + Serial.print("Connecting to network : "); + + while (!eth.connected()) + { + Serial.print("."); + delay(1000); + } + } + + Serial.println(); + +#if USING_DHCP + Serial.print("Ethernet DHCP IP address: "); +#else + Serial.print("Ethernet Static IP address: "); +#endif + + Serial.println(eth.localIP()); +} + +void setup() +{ + pinMode(LED_BUILTIN, OUTPUT); + digitalWrite(LED_BUILTIN, LED_OFF); + + Serial.begin(115200); + while (!Serial && millis() < 5000); + + delay(200); + + Serial.print("\nStart Async_AdvancedWebServer_MemoryIssues_Send_CString on "); Serial.print(BOARD_NAME); + Serial.print(" with "); Serial.println(SHIELD_TYPE); + Serial.println(ASYNC_WEBSERVER_ETHERNET_VERSION); + + PrintHeapData("Start =>"); + + cStr = (char *) malloc(CSTRING_SIZE); // make a little larger than required + + if (cStr == NULL) + { + Serial.println("Unable top Allocate RAM"); + + for(;;); + } + + /////////////////////////////////// + + initEthernet(); + + /////////////////////////////////// + + server.on("/", HTTP_GET, [](AsyncWebServerRequest * request) + { + handleRoot(request); + }); + + server.on("/test.svg", HTTP_GET, [](AsyncWebServerRequest * request) + { + drawGraph(request); + }); + + server.on("/inline", [](AsyncWebServerRequest * request) + { + request->send(200, "text/plain", "This works as well"); + }); + + server.onNotFound(handleNotFound); + + server.begin(); + + Serial.print(F("HTTP EthernetWebServer is @ IP : ")); + Serial.println(eth.localIP()); + + PrintHeapData("Pre Create Arduino String"); + +} + +void heartBeatPrint() +{ + static int num = 1; + + Serial.print(F(".")); + + if (num == 80) + { + //Serial.println(); + PrintStringSize(cStr); + num = 1; + } + else if (num++ % 10 == 0) + { + Serial.print(F(" ")); + } +} + +void check_status() +{ + static unsigned long checkstatus_timeout = 0; + +#define STATUS_CHECK_INTERVAL 10000L + + // Send status report every STATUS_REPORT_INTERVAL (60) seconds: we don't need to send updates frequently if there is no status change. + if ((millis() > checkstatus_timeout) || (checkstatus_timeout == 0)) + { + heartBeatPrint(); + checkstatus_timeout = millis() + STATUS_CHECK_INTERVAL; + } +} + +void loop() +{ + check_status(); +} diff --git a/examples/Async_AdvancedWebServer_MemoryIssues_Send_CString/defines.h b/examples/Async_AdvancedWebServer_MemoryIssues_Send_CString/defines.h new file mode 100644 index 0000000..5fe5d52 --- /dev/null +++ b/examples/Async_AdvancedWebServer_MemoryIssues_Send_CString/defines.h @@ -0,0 +1,74 @@ +/**************************************************************************************************************************** + defines.h + + For ESP8266 using W5x00/ENC8266 Ethernet + + AsyncWebServer_Ethernet is a library for the Ethernet with lwIP_5100, lwIP_5500 or lwIP_enc28j60 library + + Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) + Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_Ethernet + Licensed under GPLv3 license + ***************************************************************************************************************************************/ + +#ifndef defines_h +#define defines_h + +#if defined(ESP8266) + #define LED_ON LOW + #define LED_OFF HIGH +#else + #error Only ESP8266 +#endif + +#define _AWS_ETHERNET_LOGLEVEL_ 1 + +////////////////////////////////////////////////////////// + +#define USING_W5500 false +#define USING_W5100 false +#define USING_ENC28J60 true + +#include + +// Using GPIO4, GPIO16, or GPIO5 +#define CSPIN 4 //16 // 5 + +#if USING_W5500 + #include "W5500lwIP.h" + #define SHIELD_TYPE "ESP8266_W5500 Ethernet" + + Wiznet5500lwIP eth(CSPIN); + +#elif USING_W5100 + #include + #define SHIELD_TYPE "ESP8266_W5100 Ethernet" + + Wiznet5100lwIP eth(CSPIN); + +#elif USING_ENC28J60 + #include + #define SHIELD_TYPE "ESP8266_ENC28J60 Ethernet" + + ENC28J60lwIP eth(CSPIN); +#else + // default if none selected + #include "W5500lwIP.h" + + Wiznet5500lwIP eth(CSPIN); +#endif + +#include // WiFiClient (-> TCPClient) + +using TCPClient = WiFiClient; + +////////////////////////////////////////////////////////// + +#define USING_DHCP true + +#if !USING_DHCP + IPAddress localIP(192, 168, 2, 222); + IPAddress gateway(192, 168, 2, 1); + IPAddress netMask(255, 255, 255, 0); +#endif + +#endif //defines_h diff --git a/library.json b/library.json index 18c64d3..4aabda2 100644 --- a/library.json +++ b/library.json @@ -1,7 +1,7 @@ { "name":"AsyncWebServer_Ethernet", - "version": "1.4.1", - "description":"Asynchronous HTTP and WebSocket Server Library for ESP8266 using W5x00 or ENC28J60 Ethernet. This is Asynchronous HTTP and WebSocket Server Library for ESP8266 using W5x00 or ENC28J60 Ethernet with lwIP_5100, lwIP_5500 or lwIP_enc28j60 library", + "version": "1.5.0", + "description":"Asynchronous HTTP and WebSocket Server Library for ESP8266 using W5x00 or ENC28J60 Ethernet. This is Asynchronous HTTP and WebSocket Server Library for ESP8266 using W5x00 or ENC28J60 Ethernet with lwIP_5100, lwIP_5500 or lwIP_enc28j60 library. Now supporting using CString to save heap to send very large data", "keywords":"http, async, async-webserver, websocket, webserver, esp8266, w5x00, enc28j60", "authors": [ diff --git a/library.properties b/library.properties index 0f48505..0a8e377 100644 --- a/library.properties +++ b/library.properties @@ -1,12 +1,12 @@ name=AsyncWebServer_Ethernet -version=1.4.1 +version=1.5.0 author=Hristo Gochkov,Khoi Hoang maintainer=Khoi Hoang sentence=Asynchronous HTTP and WebSocket Server Library for ESP8266 using W5x00 or ENC28J60 Ethernet -paragraph=This is Asynchronous HTTP and WebSocket Server Library for ESP8266 using W5x00 or ENC28J60 Ethernet with lwIP_5100, lwIP_5500 or lwIP_enc28j60 library +paragraph=This is Asynchronous HTTP and WebSocket Server Library for ESP8266 using W5x00 or ENC28J60 Ethernet with lwIP_5100, lwIP_5500 or lwIP_enc28j60 library. Now supporting using CString to save heap to send very large data category=Communication url=https://github.com/khoih-prog/AsyncWebServer_Ethernet license=GPLv3 architectures=esp8266 -depends=ESP AsyncTCP +depends=ESPAsyncTCP includes=AsyncWebServer_Ethernet.h diff --git a/pics/Async_AdvancedWebServer_MemoryIssues_Send_CString_ENC28J60.png b/pics/Async_AdvancedWebServer_MemoryIssues_Send_CString_ENC28J60.png new file mode 100644 index 0000000..696b65b Binary files /dev/null and b/pics/Async_AdvancedWebServer_MemoryIssues_Send_CString_ENC28J60.png differ diff --git a/pics/Async_AdvancedWebServer_MemoryIssues_Send_CString_W5500.png b/pics/Async_AdvancedWebServer_MemoryIssues_Send_CString_W5500.png new file mode 100644 index 0000000..2963031 Binary files /dev/null and b/pics/Async_AdvancedWebServer_MemoryIssues_Send_CString_W5500.png differ diff --git a/platformio/platformio.ini b/platformio/platformio.ini index 79ac76f..e8768d9 100644 --- a/platformio/platformio.ini +++ b/platformio/platformio.ini @@ -34,16 +34,12 @@ upload_speed = 921600 ;monitor_port = COM11 ; Checks for the compatibility with frameworks and dev/platforms +; Adjust as necessary lib_compat_mode = strict +lib_ldf_mode = chain+ +;lib_ldf_mode = deep+ lib_deps = -; PlatformIO 4.x -; AsyncTCP@>=1.1.1 -; ESPAsyncTCP@>=1.2.2 - -; PlatformIO 5.x -; me-no-dev/AsyncTCP@>=1.1.1 - me-no-dev/ESPAsyncTCP@>=1.2.2 build_flags = ; set your debug output (default=Serial) @@ -54,6 +50,13 @@ build_flags = [env:ESP8266] platform = espressif8266 framework = arduino + +; PlatformIO 4.x +; ESPAsyncTCP@>=1.2.2 + +; PlatformIO 5.x + me-no-dev/ESPAsyncTCP@>=1.2.2 + ; ============================================================ ; Board configuration ; choose your board by uncommenting one of the following lines @@ -99,6 +102,13 @@ board = nodemcuv2 [env:ESP32] platform = espressif32 framework = arduino + +; PlatformIO 4.x +; AsyncTCP@>=1.1.1 + +; PlatformIO 5.x +; me-no-dev/AsyncTCP@>=1.1.1 + ; ============================================================ ; Board configuration ; choose your board by uncommenting one of the following lines diff --git a/src/AsyncEventSource_Ethernet.cpp b/src/AsyncEventSource_Ethernet.cpp index 23aece3..a6e7fe1 100644 --- a/src/AsyncEventSource_Ethernet.cpp +++ b/src/AsyncEventSource_Ethernet.cpp @@ -12,14 +12,16 @@ as published bythe Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program 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 General Public License for more details. - You should have received a copy of the GNU General Public License along with this program. If not, see . + You should have received a copy of the GNU General Public License along with this program. + If not, see . - Version: 1.4.1 + Version: 1.5.0 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.4.1 K Hoang 18/03/2022 Initial coding for ESP8266 using W5x00/ENC8266 Ethernet. Bump up version to v1.4.1 to sync with AsyncWebServer_STM32 v1.4.1 + 1.5.0 K Hoang 05/10/2022 Option to use non-destroyed cString instead of String to save Heap *****************************************************************************************************************************/ #define _AWS_ETHERNET_LOGLEVEL_ 1 @@ -29,6 +31,8 @@ #include "Arduino.h" #include "AsyncEventSource_Ethernet.h" +///////////////////////////////////////////////////////// + static String generateEventMessage(const char *message, const char *event, uint32_t id, uint32_t reconnect) { String ev = ""; @@ -143,6 +147,9 @@ static String generateEventMessage(const char *message, const char *event, uint3 return ev; } +///////////////////////////////////////////////////////// +///////////////////////////////////////////////////////// + // Message AsyncEventSourceMessage::AsyncEventSourceMessage(const char * data, size_t len) @@ -161,15 +168,19 @@ AsyncEventSourceMessage::AsyncEventSourceMessage(const char * data, size_t len) } } +///////////////////////////////////////////////////////// + AsyncEventSourceMessage::~AsyncEventSourceMessage() { if (_data != NULL) free(_data); } +///////////////////////////////////////////////////////// + size_t AsyncEventSourceMessage::ack(size_t len, uint32_t time) { - (void)time; + AWS_ETHERNET_UNUSED(time); // If the whole message is now acked... if (_acked + len > _len) @@ -187,6 +198,8 @@ size_t AsyncEventSourceMessage::ack(size_t len, uint32_t time) return 0; } +///////////////////////////////////////////////////////// + size_t AsyncEventSourceMessage::send(AsyncClient *client) { const size_t len = _len - _sent; @@ -206,6 +219,9 @@ size_t AsyncEventSourceMessage::send(AsyncClient *client) return sent; } +///////////////////////////////////////////////////////// +///////////////////////////////////////////////////////// + // Client AsyncEventSourceClient::AsyncEventSourceClient(AsyncWebServerRequest *request, AsyncEventSource *server) @@ -225,13 +241,13 @@ AsyncEventSourceClient::AsyncEventSourceClient(AsyncWebServerRequest *request, A _client->onError(NULL, NULL); _client->onAck([](void *r, AsyncClient * c, size_t len, uint32_t time) { - (void)c; + AWS_ETHERNET_UNUSED(c); ((AsyncEventSourceClient*)(r))->_onAck(len, time); }, this); _client->onPoll([](void *r, AsyncClient * c) { - (void)c; + AWS_ETHERNET_UNUSED(c); ((AsyncEventSourceClient*)(r))->_onPoll(); }, this); @@ -253,12 +269,16 @@ AsyncEventSourceClient::AsyncEventSourceClient(AsyncWebServerRequest *request, A delete request; } +///////////////////////////////////////////////////////// + AsyncEventSourceClient::~AsyncEventSourceClient() { _messageQueue.free(); close(); } +///////////////////////////////////////////////////////// + void AsyncEventSourceClient::_queueMessage(AsyncEventSourceMessage *dataMessage) { if (dataMessage == NULL) @@ -284,6 +304,8 @@ void AsyncEventSourceClient::_queueMessage(AsyncEventSourceMessage *dataMessage) _runQueue(); } +///////////////////////////////////////////////////////// + void AsyncEventSourceClient::_onAck(size_t len, uint32_t time) { while (len && !_messageQueue.isEmpty()) @@ -297,6 +319,8 @@ void AsyncEventSourceClient::_onAck(size_t len, uint32_t time) _runQueue(); } +///////////////////////////////////////////////////////// + void AsyncEventSourceClient::_onPoll() { if (!_messageQueue.isEmpty()) @@ -305,36 +329,48 @@ void AsyncEventSourceClient::_onPoll() } } +///////////////////////////////////////////////////////// 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() { +///////////////////////////////////////////////////////// + +void AsyncEventSourceClient::_runQueue() +{ while (!_messageQueue.isEmpty() && _messageQueue.front()->finished()) { _messageQueue.remove(_messageQueue.front()); @@ -347,6 +383,8 @@ void AsyncEventSourceClient::_runQueue() { } } +///////////////////////////////////////////////////////// +///////////////////////////////////////////////////////// // Handler @@ -359,42 +397,39 @@ AsyncEventSource::AsyncEventSource(const String& url) , _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) @@ -404,6 +439,8 @@ void AsyncEventSource::close() } } +///////////////////////////////////////////////////////// + // pmb fix size_t AsyncEventSource::avgPacketsWaiting() const { @@ -425,6 +462,8 @@ size_t AsyncEventSource::avgPacketsWaiting() const 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); @@ -438,6 +477,8 @@ void AsyncEventSource::send(const char *message, const char *event, uint32_t id, } } +///////////////////////////////////////////////////////// + size_t AsyncEventSource::count() const { return _clients.count_if([](AsyncEventSourceClient * c) @@ -446,6 +487,8 @@ size_t AsyncEventSource::count() const }); } +///////////////////////////////////////////////////////// + bool AsyncEventSource::canHandle(AsyncWebServerRequest *request) { if (request->method() != HTTP_GET || !request->url().equals(_url)) @@ -458,6 +501,8 @@ bool AsyncEventSource::canHandle(AsyncWebServerRequest *request) return true; } +///////////////////////////////////////////////////////// + void AsyncEventSource::handleRequest(AsyncWebServerRequest *request) { if ((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str())) @@ -466,6 +511,9 @@ void AsyncEventSource::handleRequest(AsyncWebServerRequest *request) request->send(new AsyncEventSourceResponse(this)); } +///////////////////////////////////////////////////////// +///////////////////////////////////////////////////////// + // Response AsyncEventSourceResponse::AsyncEventSourceResponse(AsyncEventSource *server) @@ -478,6 +526,8 @@ AsyncEventSourceResponse::AsyncEventSourceResponse(AsyncEventSource *server) addHeader("Connection", "keep-alive"); } +///////////////////////////////////////////////////////// + void AsyncEventSourceResponse::_respond(AsyncWebServerRequest *request) { String out = _assembleHead(request->version()); @@ -485,6 +535,8 @@ void AsyncEventSourceResponse::_respond(AsyncWebServerRequest *request) _state = RESPONSE_WAIT_ACK; } +///////////////////////////////////////////////////////// + size_t AsyncEventSourceResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time __attribute__((unused))) { if (len) @@ -495,4 +547,5 @@ size_t AsyncEventSourceResponse::_ack(AsyncWebServerRequest *request, size_t len return 0; } +///////////////////////////////////////////////////////// diff --git a/src/AsyncEventSource_Ethernet.h b/src/AsyncEventSource_Ethernet.h index bf32ffd..21139ae 100644 --- a/src/AsyncEventSource_Ethernet.h +++ b/src/AsyncEventSource_Ethernet.h @@ -12,14 +12,16 @@ as published bythe Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program 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 General Public License for more details. - You should have received a copy of the GNU General Public License along with this program. If not, see . + You should have received a copy of the GNU General Public License along with this program. + If not, see . - Version: 1.4.1 + Version: 1.5.0 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.4.1 K Hoang 18/03/2022 Initial coding for ESP8266 using W5x00/ENC8266 Ethernet. Bump up version to v1.4.1 to sync with AsyncWebServer_STM32 v1.4.1 + 1.5.0 K Hoang 05/10/2022 Option to use non-destroyed cString instead of String to save Heap *****************************************************************************************************************************/ #pragma once @@ -39,17 +41,23 @@ // Ethernet #include +///////////////////////////////////////////////////////// + #define SSE_MAX_QUEUED_MESSAGES 32 //#define SSE_MAX_QUEUED_MESSAGES 8 #define DEFAULT_MAX_SSE_CLIENTS 8 //#define DEFAULT_MAX_SSE_CLIENTS 4 +///////////////////////////////////////////////////////// + class AsyncEventSource; class AsyncEventSourceResponse; class AsyncEventSourceClient; typedef std::function ArEventHandlerFunction; +///////////////////////////////////////////////////////// + class AsyncEventSourceMessage { private: @@ -65,17 +73,24 @@ class AsyncEventSourceMessage size_t ack(size_t len, uint32_t time __attribute__((unused))); size_t send(AsyncClient *client); - bool finished() + ///////////////////////////////////////////////// + + inline bool finished() { return _acked == _len; } - bool sent() + ///////////////////////////////////////////////// + + inline bool sent() { return _sent == _len; } }; +///////////////////////////////////////////////// +///////////////////////////////////////////////// + class AsyncEventSourceClient { private: @@ -91,30 +106,42 @@ class AsyncEventSourceClient AsyncEventSourceClient(AsyncWebServerRequest *request, AsyncEventSource *server); ~AsyncEventSourceClient(); - AsyncClient* client() + ///////////////////////////////////////////////// + + inline 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 + ///////////////////////////////////////////////// + + inline bool connected() const { return (_client != NULL) && _client->connected(); } - uint32_t lastId() const + ///////////////////////////////////////////////// + + inline uint32_t lastId() const { return _lastId; } - size_t packetsWaiting() const + ///////////////////////////////////////////////// + + inline size_t packetsWaiting() const { return _messageQueue.length(); } + ///////////////////////////////////////////////// + //system callbacks (do not call) void _onAck(size_t len, uint32_t time); void _onPoll(); @@ -122,6 +149,9 @@ class AsyncEventSourceClient void _onDisconnect(); }; +///////////////////////////////////////////////// +///////////////////////////////////////////////// + class AsyncEventSource: public AsyncWebHandler { private: @@ -133,11 +163,15 @@ class AsyncEventSource: public AsyncWebHandler AsyncEventSource(const String& url); ~AsyncEventSource(); - const char * url() const + ///////////////////////////////////////////////// + + inline 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); @@ -151,6 +185,9 @@ class AsyncEventSource: public AsyncWebHandler virtual void handleRequest(AsyncWebServerRequest *request) override final; }; +///////////////////////////////////////////////// +///////////////////////////////////////////////// + class AsyncEventSourceResponse: public AsyncWebServerResponse { private: @@ -162,7 +199,9 @@ class AsyncEventSourceResponse: public AsyncWebServerResponse void _respond(AsyncWebServerRequest *request); size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time); - bool _sourceValid() const + ///////////////////////////////////////////////// + + inline bool _sourceValid() const { return true; } diff --git a/src/AsyncJson_Ethernet.h b/src/AsyncJson_Ethernet.h index 2c3aa65..495a1a2 100644 --- a/src/AsyncJson_Ethernet.h +++ b/src/AsyncJson_Ethernet.h @@ -12,14 +12,16 @@ as published bythe Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program 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 General Public License for more details. - You should have received a copy of the GNU General Public License along with this program. If not, see . + You should have received a copy of the GNU General Public License along with this program. + If not, see . - Version: 1.4.1 + Version: 1.5.0 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.4.1 K Hoang 18/03/2022 Initial coding for ESP8266 using W5x00/ENC8266 Ethernet. Bump up version to v1.4.1 to sync with AsyncWebServer_STM32 v1.4.1 + 1.5.0 K Hoang 05/10/2022 Option to use non-destroyed cString instead of String to save Heap *****************************************************************************************************************************/ /* Async Response to use with ArduinoJson and AsyncWebServer @@ -60,19 +62,25 @@ #ifndef ASYNC_JSON_ETHERNET_H_ #define ASYNC_JSON_ETHERNET_H_ +///////////////////////////////////////////////////////// + #include #include #include +///////////////////////////////////////////////////////// + #if ARDUINOJSON_VERSION_MAJOR == 5 -#define ARDUINOJSON_5_COMPATIBILITY + #define ARDUINOJSON_5_COMPATIBILITY #else -#define DYNAMIC_JSON_DOCUMENT_SIZE 1024 + #define DYNAMIC_JSON_DOCUMENT_SIZE 1024 #endif constexpr const char* JSON_MIMETYPE = "application/json"; +///////////////////////////////////////////////// + /* Json Response * */ @@ -91,6 +99,8 @@ class ChunkPrint : public Print virtual ~ChunkPrint() {} + ///////////////////////////////////////////////// + size_t write(uint8_t c) { if (_to_skip > 0) @@ -110,12 +120,16 @@ class ChunkPrint : public Print return 0; } - size_t write(const uint8_t *buffer, size_t size) + ///////////////////////////////////////////////// + + inline size_t write(const uint8_t *buffer, size_t size) { return this->Print::write(buffer, size); } }; +///////////////////////////////////////////////// + class AsyncJsonResponse: public AsyncAbstractResponse { protected: @@ -131,6 +145,8 @@ class AsyncJsonResponse: public AsyncAbstractResponse public: + ///////////////////////////////////////////////// + #ifdef ARDUINOJSON_5_COMPATIBILITY AsyncJsonResponse(bool isArray = false): _isValid {false} { @@ -155,17 +171,25 @@ class AsyncJsonResponse: public AsyncAbstractResponse } #endif + ///////////////////////////////////////////////// + ~AsyncJsonResponse() {} + + ///////////////////////////////////////////////// - JsonVariant & getRoot() + inline JsonVariant & getRoot() { return _root; } + + ///////////////////////////////////////////////// - bool _sourceValid() const + inline bool _sourceValid() const { return _isValid; } + + ///////////////////////////////////////////////// size_t setLength() { @@ -184,11 +208,15 @@ class AsyncJsonResponse: public AsyncAbstractResponse return _contentLength; } - size_t getSize() + ///////////////////////////////////////////////// + + inline size_t getSize() { return _jsonBuffer.size(); } + ///////////////////////////////////////////////// + size_t _fillBuffer(uint8_t *data, size_t len) { ChunkPrint dest(data, _sentLength, len); @@ -200,8 +228,14 @@ class AsyncJsonResponse: public AsyncAbstractResponse #endif return len; } + + ///////////////////////////////////////////////// + }; +///////////////////////////////////////////////// +///////////////////////////////////////////////// + class PrettyAsyncJsonResponse: public AsyncJsonResponse { public: @@ -211,6 +245,8 @@ class PrettyAsyncJsonResponse: public AsyncJsonResponse PrettyAsyncJsonResponse (bool isArray = false, size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE) : AsyncJsonResponse {isArray, maxJsonBufferSize} {} #endif + ///////////////////////////////////////////////// + size_t setLength () { #ifdef ARDUINOJSON_5_COMPATIBILITY @@ -226,6 +262,8 @@ class PrettyAsyncJsonResponse: public AsyncJsonResponse return _contentLength; } + + ///////////////////////////////////////////////// size_t _fillBuffer (uint8_t *data, size_t len) { @@ -241,8 +279,13 @@ class PrettyAsyncJsonResponse: public AsyncJsonResponse } }; +///////////////////////////////////////////////// + typedef std::function ArJsonRequestHandlerFunction; +///////////////////////////////////////////////// +///////////////////////////////////////////////// + class AsyncCallbackJsonWebHandler: public AsyncWebHandler { private: @@ -259,6 +302,8 @@ class AsyncCallbackJsonWebHandler: public AsyncWebHandler size_t _maxContentLength; public: + + ///////////////////////////////////////////////// #ifdef ARDUINOJSON_5_COMPATIBILITY AsyncCallbackJsonWebHandler(const String& uri, ArJsonRequestHandlerFunction onRequest) @@ -268,21 +313,29 @@ class AsyncCallbackJsonWebHandler: public AsyncWebHandler : _uri(uri), _method(HTTP_POST | HTTP_PUT | HTTP_PATCH), _onRequest(onRequest), maxJsonBufferSize(maxJsonBufferSize), _maxContentLength(16384) {} #endif - void setMethod(WebRequestMethodComposite method) + ///////////////////////////////////////////////// + + inline void setMethod(WebRequestMethodComposite method) { _method = method; } + + ///////////////////////////////////////////////// - void setMaxContentLength(int maxContentLength) + inline void setMaxContentLength(int maxContentLength) { _maxContentLength = maxContentLength; } + + ///////////////////////////////////////////////// - void onRequest(ArJsonRequestHandlerFunction fn) + inline void onRequest(ArJsonRequestHandlerFunction fn) { _onRequest = fn; } + ///////////////////////////////////////////////// + virtual bool canHandle(AsyncWebServerRequest *request) override final { if (!_onRequest) @@ -302,6 +355,8 @@ class AsyncCallbackJsonWebHandler: public AsyncWebHandler return true; } + ///////////////////////////////////////////////// + virtual void handleRequest(AsyncWebServerRequest *request) override final { if (_onRequest) @@ -337,10 +392,14 @@ class AsyncCallbackJsonWebHandler: public AsyncWebHandler 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 { @@ -359,6 +418,8 @@ class AsyncCallbackJsonWebHandler: public AsyncWebHandler } } } + + ///////////////////////////////////////////////// virtual bool isRequestHandlerTrivial() override final { diff --git a/src/AsyncWebAuthentication_Ethernet.cpp b/src/AsyncWebAuthentication_Ethernet.cpp index 16bec23..b6184c7 100644 --- a/src/AsyncWebAuthentication_Ethernet.cpp +++ b/src/AsyncWebAuthentication_Ethernet.cpp @@ -12,14 +12,16 @@ as published bythe Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program 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 General Public License for more details. - You should have received a copy of the GNU General Public License along with this program. If not, see . + You should have received a copy of the GNU General Public License along with this program. + If not, see . - Version: 1.4.1 + Version: 1.5.0 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.4.1 K Hoang 18/03/2022 Initial coding for ESP8266 using W5x00/ENC8266 Ethernet. Bump up version to v1.4.1 to sync with AsyncWebServer_STM32 v1.4.1 + 1.5.0 K Hoang 05/10/2022 Option to use non-destroyed cString instead of String to save Heap *****************************************************************************************************************************/ #define _AWS_ETHERNET_LOGLEVEL_ 1 @@ -34,6 +36,8 @@ #include "Crypto/bearssl_hash.h" #include "Crypto/Hash.h" +///////////////////////////////////////////////// + // Basic Auth hash = base64("username:password") bool checkBasicAuthentication(const char * hash, const char * username, const char * password) @@ -41,6 +45,7 @@ bool checkBasicAuthentication(const char * hash, const char * username, const ch if (username == NULL || password == NULL || hash == NULL) { LOGDEBUG("checkBasicAuthentication: Fail: NULL username/password/hash"); + return false; } @@ -70,6 +75,7 @@ bool checkBasicAuthentication(const char * hash, const char * username, const ch LOGDEBUG("checkBasicAuthentication: NULL encoded"); delete[] toencode; + return false; } @@ -81,6 +87,7 @@ bool checkBasicAuthentication(const char * hash, const char * username, const ch delete[] toencode; delete[] encoded; + return true; } @@ -88,9 +95,12 @@ bool checkBasicAuthentication(const char * hash, const char * username, const ch delete[] toencode; delete[] encoded; + return false; } +///////////////////////////////////////////////// + static bool getMD5(uint8_t * data, uint16_t len, char * output) { //33 bytes or more @@ -127,6 +137,8 @@ static bool getMD5(uint8_t * data, uint16_t len, char * output) return true; } +///////////////////////////////////////////////// + static String genRandomMD5() { // For Ethernet @@ -145,6 +157,8 @@ static String genRandomMD5() return res; } +///////////////////////////////////////////////// + static String stringMD5(const String& in) { char * out = (char*) malloc(33); @@ -160,6 +174,8 @@ static String stringMD5(const String& in) return res; } +///////////////////////////////////////////////// + String generateDigestHash(const char * username, const char * password, const char * realm) { if (username == NULL || password == NULL || realm == NULL) @@ -189,6 +205,8 @@ String generateDigestHash(const char * username, const char * password, const ch return res; } +///////////////////////////////////////////////// + String requestDigestAuthentication(const char * realm) { String header = "realm=\""; @@ -209,8 +227,10 @@ String requestDigestAuthentication(const char * realm) return header; } +///////////////////////////////////////////////// + bool checkDigestAuthentication(const char * header, const char * method, const char * username, const char * password, - const char * realm, bool passwordIsHash, const char * nonce, const char * opaque, const char * uri) + const char * realm, bool passwordIsHash, const char * nonce, const char * opaque, const char * uri) { if (username == NULL || password == NULL || header == NULL || method == NULL) { diff --git a/src/AsyncWebAuthentication_Ethernet.h b/src/AsyncWebAuthentication_Ethernet.h index 5ccbe09..b0006b0 100644 --- a/src/AsyncWebAuthentication_Ethernet.h +++ b/src/AsyncWebAuthentication_Ethernet.h @@ -12,14 +12,16 @@ as published bythe Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program 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 General Public License for more details. - You should have received a copy of the GNU General Public License along with this program. If not, see . + You should have received a copy of the GNU General Public License along with this program. + If not, see . - Version: 1.4.1 + Version: 1.5.0 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.4.1 K Hoang 18/03/2022 Initial coding for ESP8266 using W5x00/ENC8266 Ethernet. Bump up version to v1.4.1 to sync with AsyncWebServer_STM32 v1.4.1 + 1.5.0 K Hoang 05/10/2022 Option to use non-destroyed cString instead of String to save Heap *****************************************************************************************************************************/ #pragma once @@ -30,11 +32,18 @@ #include "Arduino.h" #include "AsyncWebServer_Ethernet_Debug.h" +///////////////////////////////////////////////// + bool checkBasicAuthentication(const char * header, const char * username, const char * password); + String requestDigestAuthentication(const char * realm); -bool checkDigestAuthentication(const char * header, const char * method, const char * username, const char * password, const char * realm, bool passwordIsHash, const char * nonce, const char * opaque, const char * uri); + +bool checkDigestAuthentication(const char * header, const char * method, const char * username, const char * password, + const char * realm, bool passwordIsHash, const char * nonce, const char * opaque, const char * uri); //for storing hashed versions on the device that can be authenticated against String generateDigestHash(const char * username, const char * password, const char * realm); +///////////////////////////////////////////////// + #endif // ASYNCWEB_AUTHENTICATION_ETHERNET_H_ diff --git a/src/AsyncWebHandlerImpl_Ethernet.h b/src/AsyncWebHandlerImpl_Ethernet.h index 91d47f5..7b21a03 100644 --- a/src/AsyncWebHandlerImpl_Ethernet.h +++ b/src/AsyncWebHandlerImpl_Ethernet.h @@ -12,14 +12,16 @@ as published bythe Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program 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 General Public License for more details. - You should have received a copy of the GNU General Public License along with this program. If not, see . + You should have received a copy of the GNU General Public License along with this program. + If not, see . - Version: 1.4.1 + Version: 1.5.0 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.4.1 K Hoang 18/03/2022 Initial coding for ESP8266 using W5x00/ENC8266 Ethernet. Bump up version to v1.4.1 to sync with AsyncWebServer_STM32 v1.4.1 + 1.5.0 K Hoang 05/10/2022 Option to use non-destroyed cString instead of String to save Heap *****************************************************************************************************************************/ #pragma once @@ -36,6 +38,8 @@ #include "stddef.h" #include +///////////////////////////////////////////////// + class AsyncStaticWebHandler: public AsyncWebHandler { private: @@ -63,6 +67,8 @@ class AsyncStaticWebHandler: public AsyncWebHandler AsyncStaticWebHandler& setLastModified(time_t last_modified); AsyncStaticWebHandler& setLastModified(); //sets to current time. Make sure sntp is runing and time is updated + ///////////////////////////////////////////////// + AsyncStaticWebHandler& setTemplateProcessor(AwsTemplateProcessor newCallback) { _callback = newCallback; @@ -84,31 +90,44 @@ class AsyncCallbackWebHandler: public AsyncWebHandler public: AsyncCallbackWebHandler() : _uri(), _method(HTTP_ANY), _onRequest(NULL), _onUpload(NULL), _onBody(NULL), _isRegex(false) {} - void setUri(const String& uri) + ///////////////////////////////////////////////// + + inline void setUri(const String& uri) { _uri = uri; _isRegex = uri.startsWith("^") && uri.endsWith("$"); } - void setMethod(WebRequestMethodComposite method) + ///////////////////////////////////////////////// + + inline void setMethod(WebRequestMethodComposite method) { _method = method; } - void onRequest(ArRequestHandlerFunction fn) + + ///////////////////////////////////////////////// + + inline void onRequest(ArRequestHandlerFunction fn) { _onRequest = fn; } - void onUpload(ArUploadHandlerFunction fn) + ///////////////////////////////////////////////// + + inline void onUpload(ArUploadHandlerFunction fn) { _onUpload = fn; } - void onBody(ArBodyHandlerFunction fn) + ///////////////////////////////////////////////// + + inline void onBody(ArBodyHandlerFunction fn) { _onBody = fn; } + ///////////////////////////////////////////////// + virtual bool canHandle(AsyncWebServerRequest *request) override final { if (!_onRequest) @@ -155,6 +174,8 @@ class AsyncCallbackWebHandler: public AsyncWebHandler return true; } + ///////////////////////////////////////////////// + virtual void handleRequest(AsyncWebServerRequest *request) override final { if (_onRequest) @@ -163,12 +184,16 @@ class AsyncCallbackWebHandler: public AsyncWebHandler request->send(500); } + ///////////////////////////////////////////////// + virtual void handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) override final { if (_onBody) _onBody(request, data, len, index, total); } + ///////////////////////////////////////////////// + virtual bool isRequestHandlerTrivial() override final { return _onRequest ? false : true; diff --git a/src/AsyncWebHandlers_Ethernet.cpp b/src/AsyncWebHandlers_Ethernet.cpp index 547fa4c..2a0b032 100644 --- a/src/AsyncWebHandlers_Ethernet.cpp +++ b/src/AsyncWebHandlers_Ethernet.cpp @@ -12,23 +12,29 @@ as published bythe Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program 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 General Public License for more details. - You should have received a copy of the GNU General Public License along with this program. If not, see . + You should have received a copy of the GNU General Public License along with this program. + If not, see . - Version: 1.4.1 + Version: 1.5.0 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.4.1 K Hoang 18/03/2022 Initial coding for ESP8266 using W5x00/ENC8266 Ethernet. Bump up version to v1.4.1 to sync with AsyncWebServer_STM32 v1.4.1 + 1.5.0 K Hoang 05/10/2022 Option to use non-destroyed cString instead of String to save Heap *****************************************************************************************************************************/ -#define _AWS_ETHERNET_LOGLEVEL_ 1 +#if !defined(_AWS_ETHERNET_LOGLEVEL_) + #define _AWS_ETHERNET_LOGLEVEL_ 1 +#endif #include "AsyncWebServer_Ethernet_Debug.h" #include "AsyncWebServer_Ethernet.hpp" #include "AsyncWebHandlerImpl_Ethernet.h" +///////////////////////////////////////////////// + AsyncStaticWebHandler::AsyncStaticWebHandler(const char* uri, /*FS& fs,*/ const char* path, const char* cache_control) : _uri(uri), _path(path), _cache_control(cache_control), _last_modified(""), _callback(nullptr) { @@ -56,12 +62,16 @@ AsyncStaticWebHandler::AsyncStaticWebHandler(const char* uri, /*FS& fs,*/ const _gzipStats = 0xF8; } +///////////////////////////////////////////////// + AsyncStaticWebHandler& AsyncStaticWebHandler::setIsDir(bool isDir) { _isDir = isDir; return *this; } +///////////////////////////////////////////////// + AsyncStaticWebHandler& AsyncStaticWebHandler::setCacheControl(const char* cache_control) { _cache_control = String(cache_control); @@ -69,6 +79,8 @@ AsyncStaticWebHandler& AsyncStaticWebHandler::setCacheControl(const char* cache_ return *this; } +///////////////////////////////////////////////// + AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(const char* last_modified) { _last_modified = String(last_modified); @@ -76,6 +88,8 @@ AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(const char* last_m return *this; } +///////////////////////////////////////////////// + AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(struct tm* last_modified) { char result[30]; @@ -85,6 +99,8 @@ AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(struct tm* last_mo return setLastModified((const char *)result); } +///////////////////////////////////////////////// + // For Ethernet AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(time_t last_modified) { @@ -101,6 +117,8 @@ AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified() return setLastModified(last_modified); } +///////////////////////////////////////////////// + bool AsyncStaticWebHandler::canHandle(AsyncWebServerRequest *request) { if (request->method() != HTTP_GET @@ -114,9 +132,13 @@ bool AsyncStaticWebHandler::canHandle(AsyncWebServerRequest *request) return false; } +///////////////////////////////////////////////// + // For Ethernet #define FILE_IS_REAL(f) (f == true) +///////////////////////////////////////////////// + uint8_t AsyncStaticWebHandler::_countBits(const uint8_t value) const { uint8_t w = value; diff --git a/src/AsyncWebRequest_Ethernet.cpp b/src/AsyncWebRequest_Ethernet.cpp index 09fb00e..a446975 100644 --- a/src/AsyncWebRequest_Ethernet.cpp +++ b/src/AsyncWebRequest_Ethernet.cpp @@ -12,17 +12,21 @@ as published bythe Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program 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 General Public License for more details. - You should have received a copy of the GNU General Public License along with this program. If not, see . + You should have received a copy of the GNU General Public License along with this program. + If not, see . - Version: 1.4.1 + Version: 1.5.0 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.4.1 K Hoang 18/03/2022 Initial coding for ESP8266 using W5x00/ENC8266 Ethernet. Bump up version to v1.4.1 to sync with AsyncWebServer_STM32 v1.4.1 + 1.5.0 K Hoang 05/10/2022 Option to use non-destroyed cString instead of String to save Heap *****************************************************************************************************************************/ -#define _AWS_ETHERNET_LOGLEVEL_ 1 +#if !defined(_AWS_ETHERNET_LOGLEVEL_) + #define _AWS_ETHERNET_LOGLEVEL_ 1 +#endif #include "AsyncWebServer_Ethernet_Debug.h" @@ -30,12 +34,16 @@ #include "AsyncWebResponseImpl_Ethernet.h" #include "AsyncWebAuthentication_Ethernet.h" +///////////////////////////////////////////////////////// + //static const String SharedEmptyString = String(); #define __is_param_char(c) ((c) && ((c) != '{') && ((c) != '[') && ((c) != '&') && ((c) != '=')) enum { PARSE_REQ_START, PARSE_REQ_HEADERS, PARSE_REQ_BODY, PARSE_REQ_END, PARSE_REQ_FAIL }; +///////////////////////////////////////////////////////// + AsyncWebServerRequest::AsyncWebServerRequest(AsyncWebServer* s, AsyncClient* c) : _client(c), _server(s), _handler(NULL), _response(NULL), _temp(), _parseState(0) , _version(0), _method(HTTP_ANY), _url(), _host(), _contentType(), _boundary() @@ -58,14 +66,14 @@ AsyncWebServerRequest::AsyncWebServerRequest(AsyncWebServer* s, AsyncClient* c) { c->onError([](void *r, AsyncClient * c, int8_t error) { - (void)c; + AWS_ETHERNET_UNUSED(c); AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onError(error); }, this); c->onAck([](void *r, AsyncClient * c, size_t len, uint32_t time) { - (void)c; + AWS_ETHERNET_UNUSED(c); AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onAck(len, time); }, this); @@ -79,26 +87,28 @@ AsyncWebServerRequest::AsyncWebServerRequest(AsyncWebServer* s, AsyncClient* c) c->onTimeout([](void *r, AsyncClient * c, uint32_t time) { - (void)c; + AWS_ETHERNET_UNUSED(c); AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onTimeout(time); }, this); c->onData([](void *r, AsyncClient * c, void *buf, size_t len) { - (void)c; + AWS_ETHERNET_UNUSED(c); AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onData(buf, len); }, this); c->onPoll([](void *r, AsyncClient * c) { - (void)c; + AWS_ETHERNET_UNUSED(c); AsyncWebServerRequest *req = ( AsyncWebServerRequest*)r; req->_onPoll(); }, this); } +///////////////////////////////////////////////////////// + AsyncWebServerRequest::~AsyncWebServerRequest() { _headers.free(); @@ -119,6 +129,8 @@ AsyncWebServerRequest::~AsyncWebServerRequest() } } +///////////////////////////////////////////////////////// + void AsyncWebServerRequest::_onData(void *buf, size_t len) { size_t i = 0; @@ -246,6 +258,8 @@ void AsyncWebServerRequest::_onData(void *buf, size_t len) } } +///////////////////////////////////////////////////////// + void AsyncWebServerRequest::_removeNotInterestingHeaders() { if (_interestingHeaders.containsIgnoreCase("ANY")) @@ -260,6 +274,8 @@ void AsyncWebServerRequest::_removeNotInterestingHeaders() } } +///////////////////////////////////////////////////////// + void AsyncWebServerRequest::_onPoll() { LOGDEBUG("onPoll"); @@ -270,6 +286,8 @@ void AsyncWebServerRequest::_onPoll() } } +///////////////////////////////////////////////////////// + void AsyncWebServerRequest::_onAck(size_t len, uint32_t time) { LOGDEBUG3("onAck: len =", len, ", time =", time); @@ -289,25 +307,33 @@ void AsyncWebServerRequest::_onAck(size_t len, uint32_t time) } } +///////////////////////////////////////////////////////// + void AsyncWebServerRequest::_onError(int8_t error) { - (void)error; + AWS_ETHERNET_UNUSED(error); } +///////////////////////////////////////////////////////// + void AsyncWebServerRequest::_onTimeout(uint32_t time) { - (void)time; + AWS_ETHERNET_UNUSED(time); LOGDEBUG3("TIMEOUT: time =", time, ", state =", _client->stateToString()); _client->close(); } +///////////////////////////////////////////////////////// + void AsyncWebServerRequest::onDisconnect (ArDisconnectHandler fn) { _onDisconnectfn = fn; } +///////////////////////////////////////////////////////// + void AsyncWebServerRequest::_onDisconnect() { LOGDEBUG("_onDisconnect"); @@ -320,16 +346,22 @@ void AsyncWebServerRequest::_onDisconnect() _server->_handleDisconnect(this); } +///////////////////////////////////////////////////////// + void AsyncWebServerRequest::_addParam(AsyncWebParameter *p) { _params.add(p); } +///////////////////////////////////////////////////////// + void AsyncWebServerRequest::_addPathParam(const char *p) { _pathParams.add(new String(p)); } +///////////////////////////////////////////////////////// + void AsyncWebServerRequest::_addGetParams(const String& params) { size_t start = 0; @@ -353,6 +385,8 @@ void AsyncWebServerRequest::_addGetParams(const String& params) } } +///////////////////////////////////////////////////////// + bool AsyncWebServerRequest::_parseReqHead() { // Split the head into method, url and version @@ -411,6 +445,8 @@ bool AsyncWebServerRequest::_parseReqHead() return true; } +///////////////////////////////////////////////////////// + bool strContains(String src, String find, bool mindcase = true) { int pos = 0, i = 0; @@ -442,6 +478,8 @@ bool strContains(String src, String find, bool mindcase = true) return false; } +///////////////////////////////////////////////////////// + bool AsyncWebServerRequest::_parseReqHeader() { int index = _temp.indexOf(':'); @@ -511,6 +549,8 @@ bool AsyncWebServerRequest::_parseReqHeader() return true; } +///////////////////////////////////////////////////////// + void AsyncWebServerRequest::_parsePlainPostChar(uint8_t data) { if (data && (char)data != '&') @@ -532,6 +572,8 @@ void AsyncWebServerRequest::_parsePlainPostChar(uint8_t data) } } +///////////////////////////////////////////////////////// + void AsyncWebServerRequest::_handleUploadByte(uint8_t data, bool last) { _itemBuffer[_itemBufferIndex++] = data; @@ -546,6 +588,8 @@ void AsyncWebServerRequest::_handleUploadByte(uint8_t data, bool last) } } +///////////////////////////////////////////////////////// + enum { EXPECT_BOUNDARY, @@ -561,6 +605,7 @@ enum PARSE_ERROR }; +///////////////////////////////////////////////////////// void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last) { @@ -845,6 +890,8 @@ void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last) } } +///////////////////////////////////////////////////////// + void AsyncWebServerRequest::_parseLine() { if (_parseState == PARSE_REQ_START) @@ -899,12 +946,15 @@ void AsyncWebServerRequest::_parseLine() } } +///////////////////////////////////////////////////////// size_t AsyncWebServerRequest::headers() const { return _headers.length(); } +///////////////////////////////////////////////////////// + bool AsyncWebServerRequest::hasHeader(const String& name) const { for (const auto& h : _headers) @@ -918,6 +968,8 @@ bool AsyncWebServerRequest::hasHeader(const String& name) const return false; } +///////////////////////////////////////////////////////// + AsyncWebHeader* AsyncWebServerRequest::getHeader(const String& name) const { for (const auto& h : _headers) @@ -931,6 +983,8 @@ AsyncWebHeader* AsyncWebServerRequest::getHeader(const String& name) const return nullptr; } +///////////////////////////////////////////////////////// + AsyncWebHeader* AsyncWebServerRequest::getHeader(size_t num) const { auto header = _headers.nth(num); @@ -938,11 +992,14 @@ AsyncWebHeader* AsyncWebServerRequest::getHeader(size_t num) const return (header ? *header : nullptr); } +///////////////////////////////////////////////////////// + size_t AsyncWebServerRequest::params() const { return _params.length(); } +///////////////////////////////////////////////////////// bool AsyncWebServerRequest::hasParam(const String& name, bool post, bool file) const { @@ -957,6 +1014,8 @@ bool AsyncWebServerRequest::hasParam(const String& name, bool post, bool file) c return false; } +///////////////////////////////////////////////////////// + AsyncWebParameter* AsyncWebServerRequest::getParam(const String& name, bool post, bool file) const { for (const auto& p : _params) @@ -970,6 +1029,8 @@ AsyncWebParameter* AsyncWebServerRequest::getParam(const String& name, bool post return nullptr; } +///////////////////////////////////////////////////////// + AsyncWebParameter* AsyncWebServerRequest::getParam(size_t num) const { auto param = _params.nth(num); @@ -977,12 +1038,16 @@ AsyncWebParameter* AsyncWebServerRequest::getParam(size_t num) const return (param ? *param : nullptr); } +///////////////////////////////////////////////////////// + void AsyncWebServerRequest::addInterestingHeader(const String& name) { if (!_interestingHeaders.containsIgnoreCase(name)) _interestingHeaders.add(name); } +///////////////////////////////////////////////////////// + void AsyncWebServerRequest::send(AsyncWebServerResponse *response) { _response = response; @@ -1008,32 +1073,65 @@ void AsyncWebServerRequest::send(AsyncWebServerResponse *response) } } -AsyncWebServerResponse * AsyncWebServerRequest::beginResponse_P(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback) +///////////////////////////////////////////////////////// + +AsyncWebServerResponse * AsyncWebServerRequest::beginResponse_P(int code, const String& contentType, const uint8_t * content, size_t len, + AwsTemplateProcessor callback) { return new AsyncProgmemResponse(code, contentType, content, len, callback); } -AsyncWebServerResponse * AsyncWebServerRequest::beginResponse_P(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback) +///////////////////////////////////////////////////////// + +AsyncWebServerResponse * AsyncWebServerRequest::beginResponse_P(int code, const String& contentType, PGM_P content, + AwsTemplateProcessor callback) { return beginResponse_P(code, contentType, (const uint8_t *)content, strlen_P(content), callback); } +///////////////////////////////////////////////////////// + +//RSMOD/////////////////////////////////////////////// + +AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(int code, const String& contentType, const char * content) +{ + return new AsyncBasicResponse(code, contentType, content); +} +///////////////////////////////////////////////// + AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(int code, const String& contentType, const String& content) { return new AsyncBasicResponse(code, contentType, content); } -AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback) +///////////////////////////////////////////////// +// KH add for favicon +AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(int code, const String& contentType, const uint8_t * content, size_t len, + AwsTemplateProcessor callback) +{ + return new AsyncProgmemResponse(code, contentType, content, len, callback); +} + +///////////////////////////////////////////////// + +AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(Stream &stream, const String& contentType, size_t len, + AwsTemplateProcessor callback) { return new AsyncStreamResponse(stream, contentType, len, callback); } -AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback) +///////////////////////////////////////////////////////// + +AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(const String& contentType, size_t len, AwsResponseFiller callback, + AwsTemplateProcessor templateCallback) { return new AsyncCallbackResponse(contentType, len, callback, templateCallback); } -AsyncWebServerResponse * AsyncWebServerRequest::beginChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback) +///////////////////////////////////////////////////////// + +AsyncWebServerResponse * AsyncWebServerRequest::beginChunkedResponse(const String& contentType, AwsResponseFiller callback, + AwsTemplateProcessor templateCallback) { if (_version) return new AsyncChunkedResponse(contentType, callback, templateCallback); @@ -1041,31 +1139,60 @@ AsyncWebServerResponse * AsyncWebServerRequest::beginChunkedResponse(const Strin return new AsyncCallbackResponse(contentType, 0, callback, templateCallback); } +///////////////////////////////////////////////////////// + AsyncResponseStream * AsyncWebServerRequest::beginResponseStream(const String& contentType, size_t bufferSize) { return new AsyncResponseStream(contentType, bufferSize); } +//RSMOD/////////////////////////////////////////////// + +void AsyncWebServerRequest::send(int code, const String& contentType, const char *content, bool nonCopyingSend) +{ + if (nonCopyingSend) + { + send(beginResponse(code, contentType, String(content))); // for backwards compatibility + } + else + { + send(beginResponse(code, contentType, content)); + } +} + +///////////////////////////////////////////////// + void AsyncWebServerRequest::send(int code, const String& contentType, const String& content) { send(beginResponse(code, contentType, content)); } -void AsyncWebServerRequest::send(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback) +///////////////////////////////////////////////////////// + +void AsyncWebServerRequest::send(Stream &stream, const String& contentType, size_t len, + AwsTemplateProcessor callback) { send(beginResponse(stream, contentType, len, callback)); } -void AsyncWebServerRequest::send(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback) +///////////////////////////////////////////////////////// + +void AsyncWebServerRequest::send(const String& contentType, size_t len, AwsResponseFiller callback, + AwsTemplateProcessor templateCallback) { send(beginResponse(contentType, len, callback, templateCallback)); } -void AsyncWebServerRequest::sendChunked(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback) +///////////////////////////////////////////////////////// + +void AsyncWebServerRequest::sendChunked(const String& contentType, AwsResponseFiller callback, + AwsTemplateProcessor templateCallback) { send(beginChunkedResponse(contentType, callback, templateCallback)); } +///////////////////////////////////////////////////////// + void AsyncWebServerRequest::redirect(const String& url) { AsyncWebServerResponse * response = beginResponse(302); @@ -1073,6 +1200,8 @@ void AsyncWebServerRequest::redirect(const String& url) send(response); } +///////////////////////////////////////////////////////// + bool AsyncWebServerRequest::authenticate(const char * username, const char * password, const char * realm, bool passwordIsHash) { LOGDEBUG1("AsyncWebServerRequest::authenticate: auth-len =", _authorization.length()); @@ -1104,6 +1233,8 @@ bool AsyncWebServerRequest::authenticate(const char * username, const char * pas return false; } +///////////////////////////////////////////////////////// + bool AsyncWebServerRequest::authenticate(const char * hash) { if (!_authorization.length() || hash == NULL) @@ -1133,6 +1264,8 @@ bool AsyncWebServerRequest::authenticate(const char * hash) return (_authorization.equals(hash)); } +///////////////////////////////////////////////////////// + void AsyncWebServerRequest::requestAuthentication(const char * realm, bool isDigest) { AsyncWebServerResponse * r = beginResponse(401); @@ -1158,6 +1291,8 @@ void AsyncWebServerRequest::requestAuthentication(const char * realm, bool isDig send(r); } +///////////////////////////////////////////////////////// + bool AsyncWebServerRequest::hasArg(const char* name) const { for (const auto& arg : _params) @@ -1171,6 +1306,8 @@ bool AsyncWebServerRequest::hasArg(const char* name) const return false; } +///////////////////////////////////////////////////////// + const String& AsyncWebServerRequest::arg(const String& name) const { for (const auto& arg : _params) @@ -1184,16 +1321,22 @@ const String& AsyncWebServerRequest::arg(const String& name) const return SharedEmptyString; } +///////////////////////////////////////////////////////// + const String& AsyncWebServerRequest::arg(size_t i) const { return getParam(i)->value(); } +///////////////////////////////////////////////////////// + const String& AsyncWebServerRequest::argName(size_t i) const { return getParam(i)->name(); } +///////////////////////////////////////////////////////// + const String& AsyncWebServerRequest::pathArg(size_t i) const { auto param = _pathParams.nth(i); @@ -1201,6 +1344,8 @@ const String& AsyncWebServerRequest::pathArg(size_t i) const return (param ? **param : SharedEmptyString); } +///////////////////////////////////////////////////////// + const String& AsyncWebServerRequest::header(const char* name) const { AsyncWebHeader* h = getHeader(String(name)); @@ -1208,6 +1353,8 @@ const String& AsyncWebServerRequest::header(const char* name) const return (h ? h->value() : SharedEmptyString); } +///////////////////////////////////////////////////////// + const String& AsyncWebServerRequest::header(size_t i) const { AsyncWebHeader* h = getHeader(i); @@ -1215,6 +1362,8 @@ const String& AsyncWebServerRequest::header(size_t i) const return (h ? h->value() : SharedEmptyString); } +///////////////////////////////////////////////////////// + const String& AsyncWebServerRequest::headerName(size_t i) const { AsyncWebHeader* h = getHeader(i); @@ -1222,6 +1371,8 @@ const String& AsyncWebServerRequest::headerName(size_t i) const return (h ? h->name() : SharedEmptyString); } +///////////////////////////////////////////////////////// + String AsyncWebServerRequest::urlDecode(const String& text) const { char temp[] = "0x00"; @@ -1256,6 +1407,7 @@ String AsyncWebServerRequest::urlDecode(const String& text) const return decoded; } +///////////////////////////////////////////////////////// const char * AsyncWebServerRequest::methodToString() const { @@ -1279,6 +1431,8 @@ const char * AsyncWebServerRequest::methodToString() const return "UNKNOWN"; } +///////////////////////////////////////////////////////// + const char *AsyncWebServerRequest::requestedConnTypeToString() const { switch (_reqconntype) @@ -1298,7 +1452,10 @@ const char *AsyncWebServerRequest::requestedConnTypeToString() const } } -bool AsyncWebServerRequest::isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2, RequestedConnectionType erct3) +///////////////////////////////////////////////////////// + +bool AsyncWebServerRequest::isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2, + RequestedConnectionType erct3) { bool res = false; diff --git a/src/AsyncWebResponseImpl_Ethernet.h b/src/AsyncWebResponseImpl_Ethernet.h index a7ebe01..14254a5 100644 --- a/src/AsyncWebResponseImpl_Ethernet.h +++ b/src/AsyncWebResponseImpl_Ethernet.h @@ -12,14 +12,16 @@ as published bythe Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program 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 General Public License for more details. - You should have received a copy of the GNU General Public License along with this program. If not, see . + You should have received a copy of the GNU General Public License along with this program. + If not, see . - Version: 1.4.1 + Version: 1.5.0 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.4.1 K Hoang 18/03/2022 Initial coding for ESP8266 using W5x00/ENC8266 Ethernet. Bump up version to v1.4.1 to sync with AsyncWebServer_STM32 v1.4.1 + 1.5.0 K Hoang 05/10/2022 Option to use non-destroyed cString instead of String to save Heap *****************************************************************************************************************************/ #pragma once @@ -34,24 +36,36 @@ #endif #include + // It is possible to restore these defines, but one can use _min and _max instead. Or std::min, std::max. +///////////////////////////////////////////////// class AsyncBasicResponse: public AsyncWebServerResponse { private: String _content; + + char *_contentCstr; // RSMOD + String _partialHeader; public: AsyncBasicResponse(int code, const String& contentType = String(), const String& content = String()); + + AsyncBasicResponse(int code, const String& contentType, const char *content = nullptr); // RSMOD + void _respond(AsyncWebServerRequest *request); size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time); - bool _sourceValid() const + ///////////////////////////////////////////////// + + inline bool _sourceValid() const { return true; } + + ///////////////////////////////////////////////// }; class AsyncAbstractResponse: public AsyncWebServerResponse @@ -74,23 +88,33 @@ class AsyncAbstractResponse: public AsyncWebServerResponse void _respond(AsyncWebServerRequest *request); size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time); - bool _sourceValid() const + ///////////////////////////////////////////////// + + inline bool _sourceValid() const { return false; } - virtual size_t _fillBuffer(uint8_t *buf __attribute__((unused)), size_t maxLen __attribute__((unused))) + ///////////////////////////////////////////////// + + virtual size_t _fillBuffer(uint8_t *buf __attribute__((unused)), size_t maxLen __attribute__((unused))) { return 0; } + + ///////////////////////////////////////////////// }; +///////////////////////////////////////////////// + #ifndef TEMPLATE_PLACEHOLDER #define TEMPLATE_PLACEHOLDER '%' #endif #define TEMPLATE_PARAM_NAME_LENGTH 32 +///////////////////////////////////////////////// + class AsyncStreamResponse: public AsyncAbstractResponse { private: @@ -99,14 +123,20 @@ class AsyncStreamResponse: public AsyncAbstractResponse public: AsyncStreamResponse(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback = nullptr); - bool _sourceValid() const + ///////////////////////////////////////////////// + + inline bool _sourceValid() const { return !!(_content); } + ///////////////////////////////////////////////// + virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override; }; +///////////////////////////////////////////////// + class AsyncCallbackResponse: public AsyncAbstractResponse { private: @@ -116,14 +146,20 @@ class AsyncCallbackResponse: public AsyncAbstractResponse public: AsyncCallbackResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); - bool _sourceValid() const + ///////////////////////////////////////////////// + + inline bool _sourceValid() const { return !!(_content); } + ///////////////////////////////////////////////// + virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override; }; +///////////////////////////////////////////////// + class AsyncChunkedResponse: public AsyncAbstractResponse { private: @@ -133,16 +169,24 @@ class AsyncChunkedResponse: public AsyncAbstractResponse public: AsyncChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); - bool _sourceValid() const + ///////////////////////////////////////////////// + + inline bool _sourceValid() const { return !!(_content); } + ///////////////////////////////////////////////// + virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override; }; +///////////////////////////////////////////////// + class cbuf; +///////////////////////////////////////////////// + class AsyncProgmemResponse: public AsyncAbstractResponse { private: @@ -151,10 +195,21 @@ class AsyncProgmemResponse: public AsyncAbstractResponse public: AsyncProgmemResponse(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback=nullptr); - bool _sourceValid() const { return true; } + + ///////////////////////////////////////////////// + + inline bool _sourceValid() const + { + return (_state < RESPONSE_END); + } + + ///////////////////////////////////////////////// + virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override; }; +///////////////////////////////////////////////// + class AsyncResponseStream: public AsyncAbstractResponse, public Print { private: @@ -164,15 +219,21 @@ class AsyncResponseStream: public AsyncAbstractResponse, public Print AsyncResponseStream(const String& contentType, size_t bufferSize); ~AsyncResponseStream(); - bool _sourceValid() const + ///////////////////////////////////////////////// + + inline bool _sourceValid() const { return (_state < RESPONSE_END); } + ///////////////////////////////////////////////// + virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override; size_t write(const uint8_t *data, size_t len); size_t write(uint8_t data); using Print::write; }; +///////////////////////////////////////////////// + #endif /* ASYNCWEBSERVERRESPONSEIMPL_ETHERNET_H_ */ diff --git a/src/AsyncWebResponses_Ethernet.cpp b/src/AsyncWebResponses_Ethernet.cpp index c88fa0f..ad7486c 100644 --- a/src/AsyncWebResponses_Ethernet.cpp +++ b/src/AsyncWebResponses_Ethernet.cpp @@ -12,17 +12,21 @@ as published bythe Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program 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 General Public License for more details. - You should have received a copy of the GNU General Public License along with this program. If not, see . + You should have received a copy of the GNU General Public License along with this program. + If not, see . - Version: 1.4.1 + Version: 1.5.0 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.4.1 K Hoang 18/03/2022 Initial coding for ESP8266 using W5x00/ENC8266 Ethernet. Bump up version to v1.4.1 to sync with AsyncWebServer_STM32 v1.4.1 + 1.5.0 K Hoang 05/10/2022 Option to use non-destroyed cString instead of String to save Heap *****************************************************************************************************************************/ -#define _AWS_ETHERNET_LOGLEVEL_ 1 +#if !defined(_AWS_ETHERNET_LOGLEVEL_) + #define _AWS_ETHERNET_LOGLEVEL_ 1 +#endif #include "AsyncWebServer_Ethernet_Debug.h" @@ -208,12 +212,44 @@ void AsyncWebServerResponse::_respond(AsyncWebServerRequest *request) size_t AsyncWebServerResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time) { - (void)request; - (void)len; - (void)time; + AWS_ETHERNET_UNUSED(request); + AWS_ETHERNET_UNUSED(len); + AWS_ETHERNET_UNUSED(time); + return 0; } +//////////////////////////////////////////////// +//////////////////////////////////////////////// + +//RSMOD/////////////////////////////////////////////// + +/* + String/Code Response + * */ +AsyncBasicResponse::AsyncBasicResponse(int code, const String& contentType, const char *content) +{ + _code = code; + _content = String(""); + _contentCstr = (char *)content; // RSMOD + _contentType = contentType; + _partialHeader = String(); + + int iLen; + + if ((iLen = strlen(_contentCstr))) + { + _contentLength = iLen; + + if (!_contentType.length()) + _contentType = "text/plain"; + } + + addHeader("Connection", "close"); +} + +///////////////////////////////////////////////// + /* String/Code Response * */ @@ -221,11 +257,16 @@ AsyncBasicResponse::AsyncBasicResponse(int code, const String& contentType, cons { _code = code; _content = content; + + _contentCstr = nullptr; // RSMOD + _contentType = contentType; + _partialHeader = String(); if (_content.length()) { _contentLength = _content.length(); + if (!_contentType.length()) _contentType = "text/plain"; } @@ -233,6 +274,24 @@ AsyncBasicResponse::AsyncBasicResponse(int code, const String& contentType, cons addHeader("Connection", "close"); } +///////////////////////////////////////////////// + +// KH add for favicon +#if 0 +AsyncBasicResponse::AsyncBasicResponse(int code, const String& contentType, const uint8_t * content, size_t len) +{ + _code = code; + _content = content; + _contentType = contentType; + + _contentLength = len; + + addHeader("Connection", "close"); +} +#endif + +///////////////////////////////////////////////// + void AsyncBasicResponse::_respond(AsyncWebServerRequest *request) { _state = RESPONSE_HEADERS; @@ -240,68 +299,215 @@ void AsyncBasicResponse::_respond(AsyncWebServerRequest *request) size_t outLen = out.length(); size_t space = request->client()->space(); + LOGDEBUG3("AsyncBasicResponse::_respond : Pre_respond, _contentLength =", _contentLength, ", out =", out ); + LOGDEBUG3("outLen =", outLen, ", _contentCstr =", _contentCstr); + if (!_contentLength && space >= outLen) { + LOGDEBUG("Step 1"); + _writtenLength += request->client()->write(out.c_str(), outLen); _state = RESPONSE_WAIT_ACK; } else if (_contentLength && space >= outLen + _contentLength) { + LOGDEBUG("Step 2"); + + if (_contentCstr) + { + _content = String(_contentCstr); // short _contentCstr - so just send as Arduino String - not much of a penalty - fall into below + } + out += _content; outLen += _contentLength; _writtenLength += request->client()->write(out.c_str(), outLen); + _state = RESPONSE_WAIT_ACK; } else if (space && space < outLen) { String partial = out.substring(0, space); - _content = out.substring(space) + _content; - _contentLength += outLen - space; + + LOGDEBUG("Step 3"); + + if (_contentCstr) + { + _partialHeader = out.substring(space); + } + else + { + _content = out.substring(space) + _content; + _contentLength += outLen - space; + } + + LOGDEBUG1("partial =", partial); + _writtenLength += request->client()->write(partial.c_str(), partial.length()); + _state = RESPONSE_CONTENT; } else if (space > outLen && space < (outLen + _contentLength)) { size_t shift = space - outLen; + + LOGDEBUG("Step 4"); + outLen += shift; _sentLength += shift; - out += _content.substring(0, shift); - _content = _content.substring(shift); + + if (_contentCstr) + { + char *s = (char *)malloc(shift + 1); + + if (s != nullptr) + { + strncpy(s, _contentCstr, shift); + s[shift] = '\0'; + out += String(s); + _contentCstr += shift; + + free(s); + } + else + { + LOGERROR("AsyncBasicResponse::_respond: Out of heap"); + + return; + } + } + else + { + out += _content.substring(0, shift); + _content = _content.substring(shift); + } + + LOGDEBUG1("out =", out); + _writtenLength += request->client()->write(out.c_str(), outLen); _state = RESPONSE_CONTENT; } else { - _content = out + _content; - _contentLength += outLen; + LOGDEBUG("Step 5"); + + if (_contentCstr) + { + _partialHeader = out; + } + else + { + _content = out + _content; + _contentLength += outLen; + } + _state = RESPONSE_CONTENT; } + + LOGDEBUG3("AsyncBasicResponse::_respond : Post_respond, _contentLength =", _contentLength, ", out =", out ); + LOGDEBUG3("outLen =", outLen, ", _contentCstr =", _contentCstr); } +///////////////////////////////////////////////// + size_t AsyncBasicResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time) { - (void)time; + AWS_ETHERNET_UNUSED(time); + + LOGDEBUG1("AsyncBasicResponse::_ack : Pre_ack, _contentLength =", _contentLength); + _ackedLength += len; if (_state == RESPONSE_CONTENT) { + String out; size_t available = _contentLength - _sentLength; size_t space = request->client()->space(); + if (_partialHeader.length() > 0) + { + if (_partialHeader.length() > space) + { + // Header longer than space - send a piece of it, and make the _partialHeader = to what remains + String _subHeader; + String tmpString; + + _subHeader = _partialHeader.substring(0, space); + tmpString = _partialHeader.substring(space); + _partialHeader = tmpString; + + _writtenLength += request->client()->write(_subHeader.c_str(), space); + + return (_partialHeader.length()); + } + else + { + // _partialHeader is <= space length - therefore send the whole thing, and make the remaining length = to the _contrentLength + _writtenLength += request->client()->write(_partialHeader.c_str(), _partialHeader.length()); + + _partialHeader = String(); + + return (_contentLength); + } + } + + // if we are here - there is no _partialHJeader to send + + LOGDEBUG3("AsyncBasicResponse::_ack : available =", available, ", space =", space ); + //we can fit in this packet if (space > available) { - _writtenLength += request->client()->write(_content.c_str(), available); - _content = String(); + LOGDEBUG1("AsyncBasicResponse::_ack : Pre_ack, _contentLength =", _contentLength); + + if (_contentCstr) + { + LOGDEBUG1("In space>available : output =", _contentCstr); + + _writtenLength += request->client()->write(_contentCstr, available); + //_contentCstr[0] = '\0'; + } + else + { + _writtenLength += request->client()->write(_content.c_str(), available); + _content = String(); + } + _state = RESPONSE_WAIT_ACK; return available; } //send some data, the rest on ack - String out = _content.substring(0, space); - _content = _content.substring(space); + if (_contentCstr) + { + char *s = (char *)malloc(space + 1); + + if (s != nullptr) + { + strncpy(s, _contentCstr, space); + s[space] = '\0'; + out = String(s); + _contentCstr += space; + + free(s); + } + else + { + LOGERROR("AsyncBasicResponse::_ack: Out of heap"); + + return 0; + } + } + else + { + out = _content.substring(0, space); + _content = _content.substring(space); + } + _sentLength += space; + + LOGDEBUG1("In space>available : output =", out); + _writtenLength += request->client()->write(out.c_str(), space); return space; @@ -311,13 +517,15 @@ size_t AsyncBasicResponse::_ack(AsyncWebServerRequest *request, size_t len, uint if (_ackedLength >= _writtenLength) { _state = RESPONSE_END; - request->client()->close(true); /* Might it be required? */ } } + LOGDEBUG3("AsyncBasicResponse::_ack : Post_ack, _contentLength =", _contentLength, ", _contentCstr =", _contentCstr); + return 0; } +///////////////////////////////////////////////////////////////////////////////////////// /* Abstract Response @@ -344,7 +552,7 @@ void AsyncAbstractResponse::_respond(AsyncWebServerRequest *request) size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time) { - (void)time; + AWS_ETHERNET_UNUSED(time); if (!_sourceValid()) { diff --git a/src/AsyncWebServer_Ethernet.cpp b/src/AsyncWebServer_Ethernet.cpp index 211f1a0..db1078e 100644 --- a/src/AsyncWebServer_Ethernet.cpp +++ b/src/AsyncWebServer_Ethernet.cpp @@ -12,23 +12,31 @@ as published bythe Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program 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 General Public License for more details. - You should have received a copy of the GNU General Public License along with this program. If not, see . + You should have received a copy of the GNU General Public License along with this program. + If not, see . - Version: 1.4.1 + Version: 1.5.0 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.4.1 K Hoang 18/03/2022 Initial coding for ESP8266 using W5x00/ENC8266 Ethernet. Bump up version to v1.4.1 to sync with AsyncWebServer_STM32 v1.4.1 + 1.5.0 K Hoang 05/10/2022 Option to use non-destroyed cString instead of String to save Heap *****************************************************************************************************************************/ -#define _AWS_ETHERNET_LOGLEVEL_ 1 +#if !defined(_AWS_ETHERNET_LOGLEVEL_) + #define _AWS_ETHERNET_LOGLEVEL_ 1 +#endif + +///////////////////////////////////////////////// #include "AsyncWebServer_Ethernet_Debug.h" #include "AsyncWebServer_Ethernet.hpp" #include "AsyncWebHandlerImpl_Ethernet.h" +///////////////////////////////////////////////// + AsyncWebServer::AsyncWebServer(uint16_t port) : _server(port), _rewrites(LinkedList([](AsyncWebRewrite * r) { @@ -49,7 +57,11 @@ AsyncWebServer::AsyncWebServer(uint16_t port) if (c == NULL) return; - c->setRxTimeout(3); + // KH set no RxTimeout for slower Firefox / network + //c->setRxTimeout(3); + c->setRxTimeout(0); + ////// + AsyncWebServerRequest *r = new AsyncWebServerRequest((AsyncWebServer*)s, c); if (r == NULL) @@ -61,6 +73,8 @@ AsyncWebServer::AsyncWebServer(uint16_t port) }, this); } +///////////////////////////////////////////////// + AsyncWebServer::~AsyncWebServer() { reset(); @@ -70,6 +84,8 @@ AsyncWebServer::~AsyncWebServer() delete _catchAllHandler; } +///////////////////////////////////////////////// + AsyncWebRewrite& AsyncWebServer::addRewrite(AsyncWebRewrite* rewrite) { _rewrites.add(rewrite); @@ -77,55 +93,75 @@ AsyncWebRewrite& AsyncWebServer::addRewrite(AsyncWebRewrite* rewrite) return *rewrite; } +///////////////////////////////////////////////// + bool AsyncWebServer::removeRewrite(AsyncWebRewrite *rewrite) { return _rewrites.remove(rewrite); } +///////////////////////////////////////////////// + AsyncWebRewrite& AsyncWebServer::rewrite(const char* from, const char* to) { return addRewrite(new AsyncWebRewrite(from, to)); } +///////////////////////////////////////////////// + AsyncWebHandler& AsyncWebServer::addHandler(AsyncWebHandler* handler) { _handlers.add(handler); return *handler; } +///////////////////////////////////////////////// + bool AsyncWebServer::removeHandler(AsyncWebHandler *handler) { return _handlers.remove(handler); } +///////////////////////////////////////////////// + void AsyncWebServer::begin() { _server.setNoDelay(true); _server.begin(); } +///////////////////////////////////////////////// + void AsyncWebServer::end() { _server.end(); } +///////////////////////////////////////////////// + #if ASYNC_TCP_SSL_ENABLED void AsyncWebServer::onSslFileRequest(AcSSlFileHandler cb, void* arg) { _server.onSslFileRequest(cb, arg); } +///////////////////////////////////////////////// + void AsyncWebServer::beginSecure(const char *cert, const char *key, const char *password) { _server.beginSecure(cert, key, password); } #endif +///////////////////////////////////////////////// + void AsyncWebServer::_handleDisconnect(AsyncWebServerRequest *request) { delete request; } +///////////////////////////////////////////////// + void AsyncWebServer::_rewriteRequest(AsyncWebServerRequest *request) { for (const auto& r : _rewrites) @@ -138,6 +174,8 @@ void AsyncWebServer::_rewriteRequest(AsyncWebServerRequest *request) } } +///////////////////////////////////////////////// + void AsyncWebServer::_attachHandler(AsyncWebServerRequest *request) { for (const auto& h : _handlers) @@ -153,6 +191,7 @@ void AsyncWebServer::_attachHandler(AsyncWebServerRequest *request) request->setHandler(_catchAllHandler); } +///////////////////////////////////////////////// AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload, ArBodyHandlerFunction onBody) @@ -169,7 +208,10 @@ AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodCom return *handler; } -AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload) +///////////////////////////////////////////////// + +AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, + ArUploadHandlerFunction onUpload) { AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler(); handler->setUri(uri); @@ -181,6 +223,8 @@ AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodCom return *handler; } +///////////////////////////////////////////////// + AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest) { AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler(); @@ -192,6 +236,8 @@ AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodCom return *handler; } +///////////////////////////////////////////////// + AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, ArRequestHandlerFunction onRequest) { AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler(); @@ -202,16 +248,22 @@ AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, ArRequestHandlerFun return *handler; } +///////////////////////////////////////////////// + void AsyncWebServer::onNotFound(ArRequestHandlerFunction fn) { _catchAllHandler->onRequest(fn); } +///////////////////////////////////////////////// + void AsyncWebServer::onRequestBody(ArBodyHandlerFunction fn) { _catchAllHandler->onBody(fn); } +///////////////////////////////////////////////// + void AsyncWebServer::reset() { _rewrites.free(); @@ -224,3 +276,6 @@ void AsyncWebServer::reset() _catchAllHandler->onBody(NULL); } } + +///////////////////////////////////////////////// + diff --git a/src/AsyncWebServer_Ethernet.h b/src/AsyncWebServer_Ethernet.h index d5b7468..1b366fc 100644 --- a/src/AsyncWebServer_Ethernet.h +++ b/src/AsyncWebServer_Ethernet.h @@ -12,23 +12,29 @@ as published bythe Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program 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 General Public License for more details. - You should have received a copy of the GNU General Public License along with this program. If not, see . + You should have received a copy of the GNU General Public License along with this program. + If not, see . - Version: 1.4.1 + Version: 1.5.0 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.4.1 K Hoang 18/03/2022 Initial coding for ESP8266 using W5x00/ENC8266 Ethernet. Bump up version to v1.4.1 to sync with AsyncWebServer_STM32 v1.4.1 + 1.5.0 K Hoang 05/10/2022 Option to use non-destroyed cString instead of String to save Heap *****************************************************************************************************************************/ #ifndef _ASYNC_WEBSERVER_ETHERNET_H_ #define _ASYNC_WEBSERVER_ETHERNET_H_ +///////////////////////////////////////////////////////// + #if defined(ESP8266) #include #endif +///////////////////////////////////////////////////////// + #include "AsyncWebServer_Ethernet.hpp" #include "AsyncWebResponseImpl_Ethernet.h" diff --git a/src/AsyncWebServer_Ethernet.hpp b/src/AsyncWebServer_Ethernet.hpp index 05b20e5..ae287c5 100644 --- a/src/AsyncWebServer_Ethernet.hpp +++ b/src/AsyncWebServer_Ethernet.hpp @@ -12,14 +12,16 @@ as published bythe Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program 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 General Public License for more details. - You should have received a copy of the GNU General Public License along with this program. If not, see . + You should have received a copy of the GNU General Public License along with this program. + If not, see . - Version: 1.4.1 + Version: 1.5.0 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.4.1 K Hoang 18/03/2022 Initial coding for ESP8266 using W5x00/ENC8266 Ethernet. Bump up version to v1.4.1 to sync with AsyncWebServer_STM32 v1.4.1 + 1.5.0 K Hoang 05/10/2022 Option to use non-destroyed cString instead of String to save Heap *****************************************************************************************************************************/ #ifndef _ASYNC_WEBSERVER_ETHERNET_HPP_ @@ -29,13 +31,17 @@ #error This code is curretly intended to run only on the ESP8266 platform #endif -#define ASYNC_WEBSERVER_ETHERNET_VERSION "AsyncWebServer_Ethernet v1.4.1" +///////////////////////////////////////////////////////// + +#define ASYNC_WEBSERVER_ETHERNET_VERSION "AsyncWebServer_Ethernet v1.5.0" #define ASYNC_WEBSERVER_ETHERNET_VERSION_MAJOR 1 -#define ASYNC_WEBSERVER_ETHERNET_VERSION_MINOR 4 -#define ASYNC_WEBSERVER_ETHERNET_VERSION_PATCH 1 +#define ASYNC_WEBSERVER_ETHERNET_VERSION_MINOR 5 +#define ASYNC_WEBSERVER_ETHERNET_VERSION_PATCH 0 + +#define ASYNC_WEBSERVER_ETHERNET_VERSION_INT 1005000 -#define ASYNC_WEBSERVER_ETHERNET_VERSION_INT 1004001 +///////////////////////////////////////////////////////// #ifndef BOARD_NAME #if defined(ARDUINO_BOARD) @@ -45,10 +51,14 @@ #endif #endif +///////////////////////////////////////////////////////// + #ifndef AWS_ETHERNET_UNUSED #define AWS_ETHERNET_UNUSED(x) (void)(x) #endif +///////////////////////////////////////////////////////// + #include "Arduino.h" #include @@ -59,6 +69,8 @@ #include "AsyncWebServer_Ethernet_Debug.h" #include "StringArray_Ethernet.h" +///////////////////////////////////////////////////////// + #ifdef ASYNCWEBSERVER_REGEX #warning Using ASYNCWEBSERVER_REGEX #define ASYNCWEBSERVER_REGEX_ATTRIBUTE @@ -81,6 +93,8 @@ class AsyncStaticWebHandler; class AsyncCallbackWebHandler; class AsyncResponseStream; +///////////////////////////////////////////////////////// + #ifndef WEBSERVER_H typedef enum { @@ -99,7 +113,10 @@ class AsyncResponseStream; #define RESPONSE_TRY_AGAIN 0xFFFFFFFF typedef uint8_t WebRequestMethodComposite; -typedef std::function ArDisconnectHandler; +typedef std::function ArDisconnectHandler; + +///////////////////////////////////////////////////////// +///////////////////////////////////////////////////////// /* PARAMETER :: Chainable object to hold GET/POST and FILE parameters @@ -118,32 +135,47 @@ class AsyncWebParameter 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 + ///////////////////////////////////////////////// + + inline const String& name() const { return _name; } - const String& value() const + ///////////////////////////////////////////////// + + inline const String& value() const { return _value; } - size_t size() const + ///////////////////////////////////////////////// + + inline size_t size() const { return _size; } - bool isPost() const + ///////////////////////////////////////////////// + + inline bool isPost() const { return _isForm; } - bool isFile() const + ///////////////////////////////////////////////// + + inline bool isFile() const { return _isFile; } + + ///////////////////////////////////////////////// }; +///////////////////////////////////////////////////////// +///////////////////////////////////////////////////////// + /* HEADER :: Chainable object to hold the headers * */ @@ -157,38 +189,53 @@ class AsyncWebHeader public: AsyncWebHeader(const String& name, const String& value): _name(name), _value(value) {} - AsyncWebHeader(const String& data): _name(), _value() + ///////////////////////////////////////////////// + + AsyncWebHeader(const String& data): _name(), _value() { - if (!data) + if (!data) return; - + int index = data.indexOf(':'); - - if (index < 0) + + if (index < 0) return; - + _name = data.substring(0, index); _value = data.substring(index + 2); } + ///////////////////////////////////////////////// + ~AsyncWebHeader() {} - const String& name() const + ///////////////////////////////////////////////// + + inline const String& name() const { return _name; } - const String& value() const + ///////////////////////////////////////////////// + + inline const String& value() const { return _value; } - String toString() const + ///////////////////////////////////////////////// + + inline String toString() const { return String(_name + ": " + _value + "\r\n"); } + + ///////////////////////////////////////////////// }; +///////////////////////////////////////////////////////// +///////////////////////////////////////////////////////// + /* REQUEST :: Each incoming Client is wrapped inside a Request and both live together until disconnect * */ @@ -198,6 +245,9 @@ typedef enum { RCT_NOT_USED = -1, RCT_DEFAULT = 0, RCT_HTTP, RCT_WS, RCT_EVENT, typedef std::function AwsResponseFiller; typedef std::function AwsTemplateProcessor; +///////////////////////////////////////////////////////// +///////////////////////////////////////////////////////// + class AsyncWebServerRequest { friend class AsyncWebServer; @@ -277,55 +327,78 @@ class AsyncWebServerRequest AsyncWebServerRequest(AsyncWebServer*, AsyncClient*); ~AsyncWebServerRequest(); - AsyncClient* client() + ///////////////////////////////////////////////// + + inline AsyncClient* client() { return _client; } - uint8_t version() const + ///////////////////////////////////////////////// + + inline uint8_t version() const { return _version; } - WebRequestMethodComposite method() const + ///////////////////////////////////////////////// + + inline WebRequestMethodComposite method() const { return _method; } - const String& url() const + ///////////////////////////////////////////////// + + inline const String& url() const { return _url; } - const String& host() const + ///////////////////////////////////////////////// + + inline const String& host() const { return _host; } - const String& contentType() const + ///////////////////////////////////////////////// + + inline const String& contentType() const { return _contentType; } - size_t contentLength() const + ///////////////////////////////////////////////// + + inline size_t contentLength() const { return _contentLength; } - bool multipart() const + ///////////////////////////////////////////////// + + inline bool multipart() const { return _isMultipart; } + ///////////////////////////////////////////////// + const char * methodToString() const; const char * requestedConnTypeToString() const; - RequestedConnectionType requestedConnType() const + ///////////////////////////////////////////////// + + inline RequestedConnectionType requestedConnType() const { return _reqconntype; } - bool isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2 = RCT_NOT_USED, RequestedConnectionType erct3 = RCT_NOT_USED); + ///////////////////////////////////////////////// + + bool isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2 = RCT_NOT_USED, + RequestedConnectionType erct3 = RCT_NOT_USED); void onDisconnect (ArDisconnectHandler fn); //hash is the string representation of: @@ -335,32 +408,46 @@ class AsyncWebServerRequest 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) + ///////////////////////////////////////////////// + + inline 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(int code, const String& contentType, const char *content, bool nonCopyingSend = true); // RSMOD 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); AsyncWebServerResponse *beginResponse(int code, const String& contentType = String(), const String& content = String()); + AsyncWebServerResponse *beginResponse(int code, const String& contentType, const char * content = nullptr); // RSMOD + + // KH add + AsyncWebServerResponse *beginResponse(int code, const String& contentType, const uint8_t * content, size_t len, + 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 *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); + 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); AsyncWebServerResponse *beginChunkedResponse(const String& contentType, AwsResponseFiller callback, - AwsTemplateProcessor templateCallback = nullptr); + AwsTemplateProcessor templateCallback = nullptr); + AsyncResponseStream *beginResponseStream(const String& contentType, size_t bufferSize = 1460); size_t headers() const; // get header count @@ -375,11 +462,15 @@ class AsyncWebServerRequest AsyncWebParameter* getParam(const String& name, bool post = false, bool file = false) const; AsyncWebParameter* getParam(size_t num) const; - size_t args() const + ///////////////////////////////////////////////// + + inline 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(size_t i) const; // get request argument value by number const String& argName(size_t i) const; // get request argument name by number @@ -393,6 +484,9 @@ class AsyncWebServerRequest String urlDecode(const String& text) const; }; +///////////////////////////////////////////////////////// +///////////////////////////////////////////////////////// + /* FILTER :: Callback to filter AsyncWebRewrite and AsyncWebHandler (done by the Server) * */ @@ -403,6 +497,9 @@ bool ON_STA_FILTER(AsyncWebServerRequest *request); bool ON_AP_FILTER(AsyncWebServerRequest *request); +///////////////////////////////////////////////////////// +///////////////////////////////////////////////////////// + /* REWRITE :: One instance can be handle any Request (done by the Server) * */ @@ -416,51 +513,71 @@ class AsyncWebRewrite ArRequestFilterFunction _filter; public: - AsyncWebRewrite(const char* from, const char* to): _from(from), _toUrl(to), _params(String()), _filter(NULL) + + ///////////////////////////////////////////////// + + AsyncWebRewrite(const char* from, const char* to): _from(from), _toUrl(to), _params(String()), _filter(NULL) { int index = _toUrl.indexOf('?'); - - if (index > 0) + + if (index > 0) { _params = _toUrl.substring(index + 1); _toUrl = _toUrl.substring(0, index); } } + ///////////////////////////////////////////////// + virtual ~AsyncWebRewrite() {} - AsyncWebRewrite& setFilter(ArRequestFilterFunction fn) + ///////////////////////////////////////////////// + + inline AsyncWebRewrite& setFilter(ArRequestFilterFunction fn) { _filter = fn; return *this; } - bool filter(AsyncWebServerRequest *request) const + ///////////////////////////////////////////////// + + inline bool filter(AsyncWebServerRequest *request) const { return _filter == NULL || _filter(request); } - const String& from(void) const + ///////////////////////////////////////////////// + + inline const String& from() const { return _from; } - const String& toUrl(void) const + ///////////////////////////////////////////////// + + inline const String& toUrl() const { return _toUrl; } - const String& params(void) const + ///////////////////////////////////////////////// + + inline const String& params() const { return _params; } - virtual bool match(AsyncWebServerRequest *request) + ///////////////////////////////////////////////// + + virtual bool match(AsyncWebServerRequest *request) { return from() == request->url() && filter(request); } }; +///////////////////////////////////////////////////////// +///////////////////////////////////////////////////////// + /* HANDLER :: One instance can be attached to any Request (done by the Server) * */ @@ -475,41 +592,61 @@ class AsyncWebHandler public: AsyncWebHandler(): _username(""), _password("") {} - AsyncWebHandler& setFilter(ArRequestFilterFunction fn) + ///////////////////////////////////////////////// + + inline AsyncWebHandler& setFilter(ArRequestFilterFunction fn) { _filter = fn; return *this; } - AsyncWebHandler& setAuthentication(const char *username, const char *password) + ///////////////////////////////////////////////// + + inline AsyncWebHandler& setAuthentication(const char *username, const char *password) { _username = String(username); _password = String(password); return *this; }; - bool filter(AsyncWebServerRequest *request) + ///////////////////////////////////////////////// + + inline bool filter(AsyncWebServerRequest *request) { return _filter == NULL || _filter(request); } + ///////////////////////////////////////////////// + virtual ~AsyncWebHandler() {} - virtual bool canHandle(AsyncWebServerRequest *request __attribute__((unused))) + ///////////////////////////////////////////////// + + virtual bool canHandle(AsyncWebServerRequest *request __attribute__((unused))) { return false; } + ///////////////////////////////////////////////// + virtual void handleRequest(AsyncWebServerRequest *request __attribute__((unused))) {} - virtual void handleUpload(AsyncWebServerRequest *request __attribute__((unused)), const String& filename __attribute__((unused)), size_t index __attribute__((unused)), uint8_t *data __attribute__((unused)), size_t len __attribute__((unused)), bool final __attribute__((unused))) {} - virtual void handleBody(AsyncWebServerRequest *request __attribute__((unused)), uint8_t *data __attribute__((unused)), size_t len __attribute__((unused)), size_t index __attribute__((unused)), size_t total __attribute__((unused))) {} + virtual void handleUpload(AsyncWebServerRequest *request __attribute__((unused)), const String& filename __attribute__((unused)), + size_t index __attribute__((unused)), uint8_t *data __attribute__((unused)), size_t len __attribute__((unused)), + bool final __attribute__((unused))) {} + virtual void handleBody(AsyncWebServerRequest *request __attribute__((unused)), uint8_t *data __attribute__((unused)), + size_t len __attribute__((unused)), size_t index __attribute__((unused)), size_t total __attribute__((unused))) {} - virtual bool isRequestHandlerTrivial() + ///////////////////////////////////////////////// + + virtual bool isRequestHandlerTrivial() { return true; } }; +///////////////////////////////////////////////////////// +///////////////////////////////////////////////////////// + /* RESPONSE :: One instance is created for each Request (attached by the Handler) * */ @@ -519,6 +656,8 @@ typedef enum RESPONSE_SETUP, RESPONSE_HEADERS, RESPONSE_CONTENT, RESPONSE_WAIT_ACK, RESPONSE_END, RESPONSE_FAILED } WebResponseState; +///////////////////////////////////////////////////////// + class AsyncWebServerResponse { protected: @@ -551,6 +690,9 @@ class AsyncWebServerResponse virtual size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time); }; +///////////////////////////////////////////////////////// +///////////////////////////////////////////////////////// + /* SERVER :: One instance * */ @@ -559,6 +701,8 @@ typedef std::function ArRequestHandlerFunc typedef std::function ArUploadHandlerFunction; typedef std::function ArBodyHandlerFunction; +///////////////////////////////////////////////////////// + class AsyncWebServer { protected: @@ -588,8 +732,10 @@ class AsyncWebServer AsyncCallbackWebHandler& on(const char* uri, ArRequestHandlerFunction onRequest); AsyncCallbackWebHandler& on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest); - AsyncCallbackWebHandler& on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload); - AsyncCallbackWebHandler& on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload, ArBodyHandlerFunction onBody); + AsyncCallbackWebHandler& on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, + ArUploadHandlerFunction onUpload); + AsyncCallbackWebHandler& on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, + ArUploadHandlerFunction onUpload, ArBodyHandlerFunction onBody); void onNotFound(ArRequestHandlerFunction fn); //called when handler is not assigned void onRequestBody(ArBodyHandlerFunction fn); //handle posts with plain body content (JSON often transmitted this way as a request) @@ -601,46 +747,64 @@ class AsyncWebServer void _rewriteRequest(AsyncWebServerRequest *request); }; -class DefaultHeaders +///////////////////////////////////////////////////////// + +class DefaultHeaders { using headers_t = LinkedList; headers_t _headers; + ///////////////////////////////////////////////// + DefaultHeaders() - : _headers(headers_t([](AsyncWebHeader * h) + : _headers(headers_t([](AsyncWebHeader * h) { delete h; })) {} + ///////////////////////////////////////////////// + public: using ConstIterator = headers_t::ConstIterator; - void addHeader(const String& name, const String& value) + ///////////////////////////////////////////////// + + void addHeader(const String& name, const String& value) { _headers.add(new AsyncWebHeader(name, value)); } - ConstIterator begin() const + ///////////////////////////////////////////////// + + inline ConstIterator begin() const { return _headers.begin(); } - ConstIterator end() const + ///////////////////////////////////////////////// + + inline ConstIterator end() const { return _headers.end(); } + ///////////////////////////////////////////////// + DefaultHeaders(DefaultHeaders const &) = delete; DefaultHeaders &operator=(DefaultHeaders const &) = delete; - static DefaultHeaders &Instance() + ///////////////////////////////////////////////// + + static DefaultHeaders &Instance() { static DefaultHeaders instance; return instance; } }; +///////////////////////////////////////////////////////// + #include "AsyncWebResponseImpl_Ethernet.h" #include "AsyncWebHandlerImpl_Ethernet.h" #include "AsyncWebSocket_Ethernet.h" diff --git a/src/AsyncWebServer_Ethernet_Debug.h b/src/AsyncWebServer_Ethernet_Debug.h index ec8ce32..3b6ab7f 100644 --- a/src/AsyncWebServer_Ethernet_Debug.h +++ b/src/AsyncWebServer_Ethernet_Debug.h @@ -12,14 +12,16 @@ as published bythe Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program 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 General Public License for more details. - You should have received a copy of the GNU General Public License along with this program. If not, see . + You should have received a copy of the GNU General Public License along with this program. + If not, see . - Version: 1.4.1 + Version: 1.5.0 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.4.1 K Hoang 18/03/2022 Initial coding for ESP8266 using W5x00/ENC8266 Ethernet. Bump up version to v1.4.1 to sync with AsyncWebServer_STM32 v1.4.1 + 1.5.0 K Hoang 05/10/2022 Option to use non-destroyed cString instead of String to save Heap *****************************************************************************************************************************/ #pragma once diff --git a/src/AsyncWebServer_Ethernet_cbuf.hpp b/src/AsyncWebServer_Ethernet_cbuf.hpp index ab64862..74e71af 100644 --- a/src/AsyncWebServer_Ethernet_cbuf.hpp +++ b/src/AsyncWebServer_Ethernet_cbuf.hpp @@ -18,14 +18,16 @@ as published bythe Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program 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 General Public License for more details. - You should have received a copy of the GNU General Public License along with this program. If not, see . + You should have received a copy of the GNU General Public License along with this program. + If not, see . - Version: 1.4.1 + Version: 1.5.0 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.4.1 K Hoang 18/03/2022 Initial coding for ESP8266 using W5x00/ENC8266 Ethernet. Bump up version to v1.4.1 to sync with AsyncWebServer_STM32 v1.4.1 + 1.5.0 K Hoang 05/10/2022 Option to use non-destroyed cString instead of String to save Heap *****************************************************************************************************************************/ #pragma once @@ -37,6 +39,8 @@ #include #include +///////////////////////////////////////////////// + class cbuf { public: @@ -49,16 +53,22 @@ class cbuf size_t size(); size_t room() const; + + ///////////////////////////////////////////////// inline bool empty() const { return _begin == _end; } + + ///////////////////////////////////////////////// inline bool full() const { return wrap_if_bufend(_end + 1) == _begin; } + + ///////////////////////////////////////////////// int peek(); size_t peek(char *dst, size_t size); @@ -75,17 +85,21 @@ class cbuf cbuf *next; private: + + ///////////////////////////////////////////////// + inline char* wrap_if_bufend(char* ptr) const { return (ptr == _bufend) ? _buf : ptr; } + + ///////////////////////////////////////////////// size_t _size; char* _buf; const char* _bufend; char* _begin; char* _end; - }; #endif // _ASYNC_ETHERNET_CBUF_HPP_ diff --git a/src/AsyncWebServer_Ethernet_cbuf_Impl.h b/src/AsyncWebServer_Ethernet_cbuf_Impl.h index 39d5337..a96827d 100644 --- a/src/AsyncWebServer_Ethernet_cbuf_Impl.h +++ b/src/AsyncWebServer_Ethernet_cbuf_Impl.h @@ -18,14 +18,16 @@ as published bythe Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program 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 General Public License for more details. - You should have received a copy of the GNU General Public License along with this program. If not, see . + You should have received a copy of the GNU General Public License along with this program. + If not, see . - Version: 1.4.1 + Version: 1.5.0 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.4.1 K Hoang 18/03/2022 Initial coding for ESP8266 using W5x00/ENC8266 Ethernet. Bump up version to v1.4.1 to sync with AsyncWebServer_STM32 v1.4.1 + 1.5.0 K Hoang 05/10/2022 Option to use non-destroyed cString instead of String to save Heap *****************************************************************************************************************************/ #pragma once @@ -35,23 +37,31 @@ #include "AsyncWebServer_Ethernet_cbuf.hpp" +///////////////////////////////////////////////// + cbuf::cbuf(size_t size) : next(NULL), _size(size), _buf(new char[size]), _bufend(_buf + size), _begin(_buf), _end(_begin) { } +///////////////////////////////////////////////// + cbuf::~cbuf() { - delete[] _buf; + if (_buf != nullptr) + delete[] _buf; } +///////////////////////////////////////////////// + size_t cbuf::resizeAdd(size_t addSize) { return resize(_size + addSize); } +///////////////////////////////////////////////// + size_t cbuf::resize(size_t newSize) { - size_t bytes_available = available(); // not lose any data @@ -87,6 +97,8 @@ size_t cbuf::resize(size_t newSize) return _size; } +///////////////////////////////////////////////// + size_t cbuf::available() const { if (_end >= _begin) @@ -97,11 +109,15 @@ size_t cbuf::available() const return _size - (_begin - _end); } +///////////////////////////////////////////////// + size_t cbuf::size() { return _size; } +///////////////////////////////////////////////// + size_t cbuf::room() const { if (_end >= _begin) @@ -112,6 +128,8 @@ size_t cbuf::room() const return _begin - _end - 1; } +///////////////////////////////////////////////// + int cbuf::peek() { if (empty()) @@ -120,6 +138,8 @@ int cbuf::peek() return static_cast(*_begin); } +///////////////////////////////////////////////// + size_t cbuf::peek(char *dst, size_t size) { size_t bytes_available = available(); @@ -141,6 +161,8 @@ size_t cbuf::peek(char *dst, size_t size) return size_read; } +///////////////////////////////////////////////// + int cbuf::read() { if (empty()) @@ -152,6 +174,8 @@ int cbuf::read() return static_cast(result); } +///////////////////////////////////////////////// + size_t cbuf::read(char* dst, size_t size) { size_t bytes_available = available(); @@ -174,6 +198,8 @@ size_t cbuf::read(char* dst, size_t size) return size_read; } +///////////////////////////////////////////////// + size_t cbuf::write(char c) { if (full()) @@ -185,6 +211,8 @@ size_t cbuf::write(char c) return 1; } +///////////////////////////////////////////////// + size_t cbuf::write(const char* src, size_t size) { size_t bytes_available = room(); @@ -207,12 +235,16 @@ size_t cbuf::write(const char* src, size_t size) return size_written; } +///////////////////////////////////////////////// + void cbuf::flush() { _begin = _buf; _end = _buf; } +///////////////////////////////////////////////// + size_t cbuf::remove(size_t size) { size_t bytes_available = available(); @@ -220,6 +252,7 @@ size_t cbuf::remove(size_t size) if (size >= bytes_available) { flush(); + return 0; } @@ -237,4 +270,6 @@ size_t cbuf::remove(size_t size) return available(); } +///////////////////////////////////////////////// + #endif // _ASYNC_ETHERNET_CBUF_IMPL_H_ diff --git a/src/AsyncWebSocket_Ethernet.cpp b/src/AsyncWebSocket_Ethernet.cpp index 526891e..967c9ef 100644 --- a/src/AsyncWebSocket_Ethernet.cpp +++ b/src/AsyncWebSocket_Ethernet.cpp @@ -12,19 +12,23 @@ as published bythe Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program 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 General Public License for more details. - You should have received a copy of the GNU General Public License along with this program. If not, see . + You should have received a copy of the GNU General Public License along with this program. + If not, see . - Version: 1.4.1 + Version: 1.5.0 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.4.1 K Hoang 18/03/2022 Initial coding for ESP8266 using W5x00/ENC8266 Ethernet. Bump up version to v1.4.1 to sync with AsyncWebServer_STM32 v1.4.1 + 1.5.0 K Hoang 05/10/2022 Option to use non-destroyed cString instead of String to save Heap *****************************************************************************************************************************/ #include "Arduino.h" -#define _AWS_ETHERNET_LOGLEVEL_ 1 +#if !defined(_AWS_ETHERNET_LOGLEVEL_) + #define _AWS_ETHERNET_LOGLEVEL_ 1 +#endif #include "AsyncWebServer_Ethernet_Debug.h" @@ -36,8 +40,12 @@ #include "Crypto/sha1.h" #include "Crypto/Hash.h" +///////////////////////////////////////////////// + #define MAX_PRINTF_LEN 64 +///////////////////////////////////////////////// + char *ltrim(char *s) { while (isspace(*s)) @@ -46,6 +54,8 @@ char *ltrim(char *s) return s; } +///////////////////////////////////////////////// + char *rtrim(char *s) { char* back = s + strlen(s); @@ -57,11 +67,15 @@ char *rtrim(char *s) return s; } +///////////////////////////////////////////////// + char *trim(char *s) { return rtrim(ltrim(s)); } +///////////////////////////////////////////////// + size_t b64_encoded_size(size_t inlen) { size_t ret; @@ -76,6 +90,8 @@ size_t b64_encoded_size(size_t inlen) return ret; } +///////////////////////////////////////////////// + char * b64_encode(const unsigned char *in, size_t len, char * out) { const char b64chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; @@ -121,6 +137,8 @@ char * b64_encode(const unsigned char *in, size_t len, char * out) return out; } +///////////////////////////////////////////////// + size_t webSocketSendFrameWindow(AsyncClient *client) { if (!client->canSend()) @@ -134,6 +152,8 @@ size_t webSocketSendFrameWindow(AsyncClient *client) return (space - 8); } +///////////////////////////////////////////////// + size_t webSocketSendFrame(AsyncClient *client, bool final, uint8_t opcode, bool mask, uint8_t *data, size_t len) { if (!client->canSend()) @@ -233,19 +253,22 @@ size_t webSocketSendFrame(AsyncClient *client, bool final, uint8_t opcode, bool 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) { @@ -263,6 +286,7 @@ AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(uint8_t * data, size_t } } +///////////////////////////////////////////////// AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(size_t size) : _data(nullptr), _len(size), _lock(false), _count(0) @@ -273,9 +297,10 @@ AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(size_t size) { _data[_len] = 0; } - } +///////////////////////////////////////////////// + AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(const AsyncWebSocketMessageBuffer & copy) : _data(nullptr), _len(0), _lock(false), _count(0) { @@ -294,9 +319,10 @@ AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(const AsyncWebSocketMes memcpy(_data, copy._data, _len); _data[_len] = 0; } - } +///////////////////////////////////////////////// + AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(AsyncWebSocketMessageBuffer && copy) : _data(nullptr), _len(0), _lock(false), _count(0) { @@ -309,9 +335,10 @@ AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(AsyncWebSocketMessageBu _data = copy._data; copy._data = nullptr; } - } +///////////////////////////////////////////////// + AsyncWebSocketMessageBuffer::~AsyncWebSocketMessageBuffer() { if (_data) @@ -320,6 +347,8 @@ AsyncWebSocketMessageBuffer::~AsyncWebSocketMessageBuffer() } } +///////////////////////////////////////////////// + bool AsyncWebSocketMessageBuffer::reserve(size_t size) { _len = size; @@ -342,9 +371,11 @@ bool AsyncWebSocketMessageBuffer::reserve(size_t size) { return false; } - } +///////////////////////////////////////////////// +///////////////////////////////////////////////// + /* Control Frame */ @@ -359,6 +390,9 @@ class AsyncWebSocketControl 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) { @@ -381,27 +415,37 @@ class AsyncWebSocketControl _data = NULL; } + ///////////////////////////////////////////////// + virtual ~AsyncWebSocketControl() { if (_data != NULL) free(_data); } + ///////////////////////////////////////////////// + virtual bool finished() const { return _finished; } - uint8_t opcode() + ///////////////////////////////////////////////// + + inline uint8_t opcode() { return _opcode; } - uint8_t len() + ///////////////////////////////////////////////// + + inline uint8_t len() { return _len + 2; } + ///////////////////////////////////////////////// + size_t send(AsyncClient *client) { _finished = true; @@ -409,6 +453,9 @@ class AsyncWebSocketControl } }; +///////////////////////////////////////////////// +///////////////////////////////////////////////// + /* Basic Buffered Message */ @@ -432,6 +479,8 @@ AsyncWebSocketBasicMessage::AsyncWebSocketBasicMessage(const char * data, size_t } } +///////////////////////////////////////////////// + AsyncWebSocketBasicMessage::AsyncWebSocketBasicMessage(uint8_t opcode, bool mask) : _len(0), _sent(0), _ack(0), _acked(0), _data(NULL) { @@ -439,6 +488,7 @@ AsyncWebSocketBasicMessage::AsyncWebSocketBasicMessage(uint8_t opcode, bool mask _mask = mask; } +///////////////////////////////////////////////// AsyncWebSocketBasicMessage::~AsyncWebSocketBasicMessage() { @@ -446,9 +496,12 @@ AsyncWebSocketBasicMessage::~AsyncWebSocketBasicMessage() free(_data); } +///////////////////////////////////////////////// + void AsyncWebSocketBasicMessage::ack(size_t len, uint32_t time) { - (void)time; + AWS_ETHERNET_UNUSED(time); + _acked += len; if (_sent == _len && _acked == _ack) @@ -457,6 +510,8 @@ void AsyncWebSocketBasicMessage::ack(size_t len, uint32_t time) } } +///////////////////////////////////////////////// + size_t AsyncWebSocketBasicMessage::send(AsyncClient *client) { if (_status != WS_MSG_SENDING) @@ -509,6 +564,8 @@ size_t AsyncWebSocketBasicMessage::send(AsyncClient *client) return sent; } +///////////////////////////////////////////////// + bool AsyncWebSocketBasicMessage::reserve(size_t size) { if (size) @@ -520,6 +577,7 @@ bool AsyncWebSocketBasicMessage::reserve(size_t size) memset(_data, 0, size); _len = size; _status = WS_MSG_SENDING; + return true; } } @@ -527,6 +585,9 @@ bool AsyncWebSocketBasicMessage::reserve(size_t size) return false; } +///////////////////////////////////////////////// +///////////////////////////////////////////////// + /* AsyncWebSocketMultiMessage Message */ @@ -551,9 +612,9 @@ AsyncWebSocketMultiMessage::AsyncWebSocketMultiMessage(AsyncWebSocketMessageBuff { _status = WS_MSG_ERROR; } - } +///////////////////////////////////////////////// AsyncWebSocketMultiMessage::~AsyncWebSocketMultiMessage() { @@ -563,9 +624,12 @@ AsyncWebSocketMultiMessage::~AsyncWebSocketMultiMessage() } } +///////////////////////////////////////////////// + void AsyncWebSocketMultiMessage::ack(size_t len, uint32_t time) { - (void)time; + AWS_ETHERNET_UNUSED(time); + _acked += len; if (_sent >= _len && _acked >= _ack) @@ -576,6 +640,8 @@ void AsyncWebSocketMultiMessage::ack(size_t len, uint32_t time) LOGDEBUG1("ACK:", _len); } +///////////////////////////////////////////////// + size_t AsyncWebSocketMultiMessage::send(AsyncClient *client) { if (_status != WS_MSG_SENDING) @@ -589,6 +655,7 @@ size_t AsyncWebSocketMultiMessage::send(AsyncClient *client) if (_sent == _len) { _status = WS_MSG_SENT; + return 0; } @@ -631,6 +698,8 @@ size_t AsyncWebSocketMultiMessage::send(AsyncClient *client) return sent; } +///////////////////////////////////////////////// +///////////////////////////////////////////////// /* Async WebSocket Client @@ -638,6 +707,8 @@ size_t AsyncWebSocketMultiMessage::send(AsyncClient *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) { @@ -659,13 +730,15 @@ AsyncWebSocketClient::AsyncWebSocketClient(AsyncWebServerRequest *request, Async _client->onError([](void *r, AsyncClient * c, int8_t error) { - (void)c; + AWS_ETHERNET_UNUSED(c); + ((AsyncWebSocketClient*)(r))->_onError(error); }, this); _client->onAck([](void *r, AsyncClient * c, size_t len, uint32_t time) { - (void)c; + AWS_ETHERNET_UNUSED(c); + ((AsyncWebSocketClient*)(r))->_onAck(len, time); }, this); @@ -677,19 +750,22 @@ AsyncWebSocketClient::AsyncWebSocketClient(AsyncWebServerRequest *request, Async _client->onTimeout([](void *r, AsyncClient * c, uint32_t time) { - (void)c; + AWS_ETHERNET_UNUSED(c); + ((AsyncWebSocketClient*)(r))->_onTimeout(time); }, this); _client->onData([](void *r, AsyncClient * c, void *buf, size_t len) { - (void)c; + AWS_ETHERNET_UNUSED(c); + ((AsyncWebSocketClient*)(r))->_onData(buf, len); }, this); _client->onPoll([](void *r, AsyncClient * c) { - (void)c; + AWS_ETHERNET_UNUSED(c); + ((AsyncWebSocketClient*)(r))->_onPoll(); }, this); @@ -699,6 +775,8 @@ AsyncWebSocketClient::AsyncWebSocketClient(AsyncWebServerRequest *request, Async delete request; } +///////////////////////////////////////////////// + AsyncWebSocketClient::~AsyncWebSocketClient() { _messageQueue.free(); @@ -706,6 +784,8 @@ AsyncWebSocketClient::~AsyncWebSocketClient() _server->_handleEvent(this, WS_EVT_DISCONNECT, NULL, NULL, 0); } +///////////////////////////////////////////////// + void AsyncWebSocketClient::_onAck(size_t len, uint32_t time) { _lastMessageTime = millis(); @@ -739,6 +819,8 @@ void AsyncWebSocketClient::_onAck(size_t len, uint32_t time) _runQueue(); } +///////////////////////////////////////////////// + void AsyncWebSocketClient::_onPoll() { if (_client->canSend() && (!_controlQueue.isEmpty() || !_messageQueue.isEmpty())) { @@ -750,6 +832,8 @@ void AsyncWebSocketClient::_onPoll() } } +///////////////////////////////////////////////// + void AsyncWebSocketClient::_runQueue() { while (!_messageQueue.isEmpty() && _messageQueue.front()->finished()) @@ -768,6 +852,8 @@ void AsyncWebSocketClient::_runQueue() } } +///////////////////////////////////////////////// + bool AsyncWebSocketClient::queueIsFull() { if ((_messageQueue.length() >= WS_MAX_QUEUED_MESSAGES) || (_status != WS_CONNECTED) ) @@ -776,6 +862,8 @@ bool AsyncWebSocketClient::queueIsFull() return false; } +///////////////////////////////////////////////// + void AsyncWebSocketClient::_queueMessage(AsyncWebSocketMessage *dataMessage) { if (dataMessage == NULL) @@ -801,6 +889,8 @@ void AsyncWebSocketClient::_queueMessage(AsyncWebSocketMessage *dataMessage) _runQueue(); } +///////////////////////////////////////////////// + void AsyncWebSocketClient::_queueControl(AsyncWebSocketControl *controlMessage) { if (controlMessage == NULL) @@ -812,6 +902,8 @@ void AsyncWebSocketClient::_queueControl(AsyncWebSocketControl *controlMessage) _runQueue(); } +///////////////////////////////////////////////// + void AsyncWebSocketClient::close(uint16_t code, const char * message) { if (_status != WS_CONNECTED) @@ -853,26 +945,37 @@ void AsyncWebSocketClient::close(uint16_t code, const char * message) _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; + AWS_ETHERNET_UNUSED(time); + _client->close(true); } +///////////////////////////////////////////////// + void AsyncWebSocketClient::_onDisconnect() { _client = NULL; _server->_handleDisconnect(this); } +///////////////////////////////////////////////// + void AsyncWebSocketClient::_onData(void *pbuf, size_t plen) { _lastMessageTime = millis(); @@ -897,9 +1000,11 @@ void AsyncWebSocketClient::_onData(void *pbuf, size_t plen) data += 2; plen -= 2; } - else if (_pinfo.len == 127) { + 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; + | (uint64_t)(fdata[5]) << 32 | (uint64_t)(fdata[4]) << 40 | (uint64_t)(fdata[3]) << 48 + | (uint64_t)(fdata[2]) << 56; data += 8; plen -= 8; } @@ -927,7 +1032,8 @@ void AsyncWebSocketClient::_onData(void *pbuf, size_t plen) if (_pinfo.index == 0) { - if (_pinfo.opcode) { + if (_pinfo.opcode) + { _pinfo.message_opcode = _pinfo.opcode; _pinfo.num = 0; } @@ -999,6 +1105,8 @@ void AsyncWebSocketClient::_onData(void *pbuf, size_t plen) } } +///////////////////////////////////////////////// + size_t AsyncWebSocketClient::printf(const char *format, ...) { va_list arg; @@ -1008,6 +1116,7 @@ size_t AsyncWebSocketClient::printf(const char *format, ...) if (!temp) { va_end(arg); + return 0; } @@ -1022,6 +1131,7 @@ size_t AsyncWebSocketClient::printf(const char *format, ...) if (!buffer) { delete[] temp; + return 0; } @@ -1038,69 +1148,96 @@ size_t AsyncWebSocketClient::printf(const char *format, ...) } delete[] temp; + return len; } +///////////////////////////////////////////////// + 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(AsyncWebSocketMessageBuffer * buffer) { _queueMessage(new AsyncWebSocketMultiMessage(buffer)); } +///////////////////////////////////////////////// + void AsyncWebSocketClient::binary(const char * message, size_t len) { _queueMessage(new AsyncWebSocketBasicMessage(message, len, WS_BINARY)); } +///////////////////////////////////////////////// + void AsyncWebSocketClient::binary(const char * message) { binary(message, strlen(message)); } +///////////////////////////////////////////////// + void AsyncWebSocketClient::binary(uint8_t * message, size_t len) { binary((const char *)message, len); } +///////////////////////////////////////////////// + void AsyncWebSocketClient::binary(char * message) { binary(message, strlen(message)); } +///////////////////////////////////////////////// + void AsyncWebSocketClient::binary(const String & message) { binary(message.c_str(), message.length()); } +///////////////////////////////////////////////// + void AsyncWebSocketClient::binary(AsyncWebSocketMessageBuffer * buffer) { _queueMessage(new AsyncWebSocketMultiMessage(buffer, WS_BINARY)); } +///////////////////////////////////////////////// + IPAddress AsyncWebSocketClient::remoteIP() { if (!_client) @@ -1112,6 +1249,8 @@ IPAddress AsyncWebSocketClient::remoteIP() return _client->remoteIP(); } +///////////////////////////////////////////////// + uint16_t AsyncWebSocketClient::remotePort() { if (!_client) @@ -1122,6 +1261,9 @@ uint16_t AsyncWebSocketClient::remotePort() return _client->remotePort(); } +///////////////////////////////////////////////// +///////////////////////////////////////////////// + /* Async Web Socket - Each separate socket location */ @@ -1140,8 +1282,12 @@ AsyncWebSocket::AsyncWebSocket(const String & url) _eventHandler = NULL; } +///////////////////////////////////////////////// + AsyncWebSocket::~AsyncWebSocket() {} +///////////////////////////////////////////////// + void AsyncWebSocket::_handleEvent(AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len) { if (_eventHandler != NULL) @@ -1150,20 +1296,25 @@ void AsyncWebSocket::_handleEvent(AsyncWebSocketClient * client, AwsEventType ty } } +///////////////////////////////////////////////// + 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) @@ -1175,6 +1326,8 @@ bool AsyncWebSocket::availableForWriteAll() return true; } +///////////////////////////////////////////////// + bool AsyncWebSocket::availableForWrite(uint32_t id) { for (const auto& c : _clients) @@ -1186,6 +1339,8 @@ bool AsyncWebSocket::availableForWrite(uint32_t id) return true; } +///////////////////////////////////////////////// + size_t AsyncWebSocket::count() const { return _clients.count_if([](AsyncWebSocketClient * c) @@ -1194,6 +1349,8 @@ size_t AsyncWebSocket::count() const }); } +///////////////////////////////////////////////// + AsyncWebSocketClient * AsyncWebSocket::client(uint32_t id) { for (const auto &c : _clients) @@ -1207,6 +1364,7 @@ AsyncWebSocketClient * AsyncWebSocket::client(uint32_t id) return nullptr; } +///////////////////////////////////////////////// void AsyncWebSocket::close(uint32_t id, uint16_t code, const char * message) { @@ -1216,6 +1374,8 @@ void AsyncWebSocket::close(uint32_t id, uint16_t code, const char * message) c->close(code, message); } +///////////////////////////////////////////////// + void AsyncWebSocket::closeAll(uint16_t code, const char * message) { for (const auto& c : _clients) @@ -1225,6 +1385,8 @@ void AsyncWebSocket::closeAll(uint16_t code, const char * message) } } +///////////////////////////////////////////////// + void AsyncWebSocket::cleanupClients(uint16_t maxClients) { if (count() > maxClients) @@ -1233,6 +1395,8 @@ void AsyncWebSocket::cleanupClients(uint16_t maxClients) } } +///////////////////////////////////////////////// + void AsyncWebSocket::ping(uint32_t id, uint8_t *data, size_t len) { AsyncWebSocketClient * c = client(id); @@ -1241,6 +1405,8 @@ void AsyncWebSocket::ping(uint32_t id, uint8_t *data, size_t len) c->ping(data, len); } +///////////////////////////////////////////////// + void AsyncWebSocket::pingAll(uint8_t *data, size_t len) { for (const auto& c : _clients) @@ -1250,6 +1416,8 @@ void AsyncWebSocket::pingAll(uint8_t *data, size_t len) } } +///////////////////////////////////////////////// + void AsyncWebSocket::text(uint32_t id, const char * message, size_t len) { AsyncWebSocketClient * c = client(id); @@ -1258,6 +1426,8 @@ void AsyncWebSocket::text(uint32_t id, const char * message, size_t len) c->text(message, len); } +///////////////////////////////////////////////// + void AsyncWebSocket::textAll(AsyncWebSocketMessageBuffer * buffer) { if (!buffer) @@ -1277,6 +1447,7 @@ void AsyncWebSocket::textAll(AsyncWebSocketMessageBuffer * buffer) _cleanBuffers(); } +///////////////////////////////////////////////// void AsyncWebSocket::textAll(const char * message, size_t len) { @@ -1284,6 +1455,8 @@ void AsyncWebSocket::textAll(const char * message, size_t len) textAll(WSBuffer); } +///////////////////////////////////////////////// + void AsyncWebSocket::binary(uint32_t id, const char * message, size_t len) { AsyncWebSocketClient * c = client(id); @@ -1292,12 +1465,16 @@ void AsyncWebSocket::binary(uint32_t id, const char * message, size_t len) 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) @@ -1315,6 +1492,8 @@ void AsyncWebSocket::binaryAll(AsyncWebSocketMessageBuffer * buffer) _cleanBuffers(); } +///////////////////////////////////////////////// + void AsyncWebSocket::message(uint32_t id, AsyncWebSocketMessage * message) { AsyncWebSocketClient * c = client(id); @@ -1323,6 +1502,8 @@ void AsyncWebSocket::message(uint32_t id, AsyncWebSocketMessage * message) c->message(message); } +///////////////////////////////////////////////// + void AsyncWebSocket::messageAll(AsyncWebSocketMultiMessage * message) { for (const auto& c : _clients) @@ -1334,6 +1515,8 @@ void AsyncWebSocket::messageAll(AsyncWebSocketMultiMessage * message) _cleanBuffers(); } +///////////////////////////////////////////////// + size_t AsyncWebSocket::printf(uint32_t id, const char *format, ...) { AsyncWebSocketClient * c = client(id); @@ -1344,12 +1527,15 @@ size_t AsyncWebSocket::printf(uint32_t id, const char *format, ...) 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; @@ -1377,97 +1563,134 @@ size_t AsyncWebSocket::printfAll(const char *format, ...) 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::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::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::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()); } +///////////////////////////////////////////////// + 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"; +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) { @@ -1487,6 +1710,8 @@ bool AsyncWebSocket::canHandle(AsyncWebServerRequest * request) return true; } +///////////////////////////////////////////////// + void AsyncWebSocket::handleRequest(AsyncWebServerRequest * request) { if (!request->hasHeader(WS_STR_VERSION) || !request->hasHeader(WS_STR_KEY)) @@ -1496,7 +1721,8 @@ void AsyncWebSocket::handleRequest(AsyncWebServerRequest * request) return; } - if ((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str())) { + if ((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str())) + { return request->requestAuthentication(); } @@ -1524,6 +1750,8 @@ void AsyncWebSocket::handleRequest(AsyncWebServerRequest * request) request->send(response); } +///////////////////////////////////////////////// + AsyncWebSocketMessageBuffer * AsyncWebSocket::makeBuffer(size_t size) { AsyncWebSocketMessageBuffer * buffer = new AsyncWebSocketMessageBuffer(size); @@ -1537,6 +1765,8 @@ AsyncWebSocketMessageBuffer * AsyncWebSocket::makeBuffer(size_t size) return buffer; } +///////////////////////////////////////////////// + AsyncWebSocketMessageBuffer * AsyncWebSocket::makeBuffer(uint8_t * data, size_t size) { AsyncWebSocketMessageBuffer * buffer = new AsyncWebSocketMessageBuffer(data, size); @@ -1550,6 +1780,8 @@ AsyncWebSocketMessageBuffer * AsyncWebSocket::makeBuffer(uint8_t * data, size_t return buffer; } +///////////////////////////////////////////////// + void AsyncWebSocket::_cleanBuffers() { AsyncWebLockGuard l(_lock); @@ -1563,28 +1795,21 @@ void AsyncWebSocket::_cleanBuffers() } } +///////////////////////////////////////////////// + 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 */ -/*static */ -/*void acceptKey(char * skey, char * ckey) { - char sha1HashBin[22] = { 0 }; - //String key = base64_encode(sha1HashBin, 20); - __disable_irq(); - char buf[256]; - sprintf(buf, "%s258EAFA5-E914-47DA-95CA-C5AB0DC85B11", skey); - SHA1(sha1HashBin, (const char *)buf, strlen(buf)); - b64_encode((const unsigned char *)sha1HashBin, 20, buf); - sprintf(ckey, "%s", trim(buf)); - __enable_irq(); -} -*/ + AsyncWebSocketResponse::AsyncWebSocketResponse(const String & key, AsyncWebSocket * server) { _server = server; @@ -1596,6 +1821,7 @@ AsyncWebSocketResponse::AsyncWebSocketResponse(const String & key, AsyncWebSocke if (hash == NULL) { _state = RESPONSE_FAILED; + return; } @@ -1605,6 +1831,7 @@ AsyncWebSocketResponse::AsyncWebSocketResponse(const String & key, AsyncWebSocke { free(hash); _state = RESPONSE_FAILED; + return; } @@ -1630,22 +1857,28 @@ AsyncWebSocketResponse::AsyncWebSocketResponse(const String & key, AsyncWebSocke 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; + AWS_ETHERNET_UNUSED(time); if (len) { diff --git a/src/AsyncWebSocket_Ethernet.h b/src/AsyncWebSocket_Ethernet.h index 27940ef..78c6592 100644 --- a/src/AsyncWebSocket_Ethernet.h +++ b/src/AsyncWebSocket_Ethernet.h @@ -12,14 +12,16 @@ as published bythe Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program 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 General Public License for more details. - You should have received a copy of the GNU General Public License along with this program. If not, see . + You should have received a copy of the GNU General Public License along with this program. + If not, see . - Version: 1.4.1 + Version: 1.5.0 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.4.1 K Hoang 18/03/2022 Initial coding for ESP8266 using W5x00/ENC8266 Ethernet. Bump up version to v1.4.1 to sync with AsyncWebServer_STM32 v1.4.1 + 1.5.0 K Hoang 05/10/2022 Option to use non-destroyed cString instead of String to save Heap *****************************************************************************************************************************/ #pragma once @@ -42,12 +44,14 @@ #include "AsyncWebSynchronization_Ethernet.h" +///////////////////////////////////////////////// + class AsyncWebSocket; class AsyncWebSocketResponse; class AsyncWebSocketClient; class AsyncWebSocketControl; - +///////////////////////////////////////////////// typedef struct { @@ -74,6 +78,8 @@ typedef struct uint64_t index; } AwsFrameInfo; +///////////////////////////////////////////////// + typedef enum { WS_DISCONNECTED, @@ -107,6 +113,8 @@ typedef enum WS_EVT_DATA } AwsEventType; +///////////////////////////////////////////////// + class AsyncWebSocketMessageBuffer { private: @@ -123,15 +131,20 @@ class AsyncWebSocketMessageBuffer AsyncWebSocketMessageBuffer(AsyncWebSocketMessageBuffer &&); ~AsyncWebSocketMessageBuffer(); + ///////////////////////////////////////////////// + void operator ++(int i) { - (void)i; + AWS_ETHERNET_UNUSED(i); + _count++; } + ///////////////////////////////////////////////// + void operator --(int i) { - (void)i; + AWS_ETHERNET_UNUSED(i); if (_count > 0) { @@ -139,42 +152,60 @@ class AsyncWebSocketMessageBuffer } ; } + ///////////////////////////////////////////////// + bool reserve(size_t size); - void lock() + ///////////////////////////////////////////////// + + inline void lock() { _lock = true; } - void unlock() + ///////////////////////////////////////////////// + + inline void unlock() { _lock = false; } - uint8_t * get() + ///////////////////////////////////////////////// + + inline uint8_t * get() { return _data; } - size_t length() + ///////////////////////////////////////////////// + + inline size_t length() { return _len; } - uint32_t count() + ///////////////////////////////////////////////// + + inline uint32_t count() { return _count; } - bool canDelete() + ///////////////////////////////////////////////// + + inline bool canDelete() { return (!_count && !_lock); } + ///////////////////////////////////////////////// + friend AsyncWebSocket; }; +///////////////////////////////////////////////// + class AsyncWebSocketMessage { protected: @@ -187,22 +218,30 @@ class AsyncWebSocketMessage virtual ~AsyncWebSocketMessage() {} virtual void ack(size_t len __attribute__((unused)), uint32_t time __attribute__((unused))) {} - virtual size_t send(AsyncClient *client __attribute__((unused))) + ///////////////////////////////////////////////// + + virtual size_t send(AsyncClient *client __attribute__((unused))) { return 0; } - virtual bool finished() + ///////////////////////////////////////////////// + + virtual bool finished() { return _status != WS_MSG_SENDING; } - virtual bool betweenFrames() const + ///////////////////////////////////////////////// + + virtual bool betweenFrames() const { return false; } }; +///////////////////////////////////////////////// + class AsyncWebSocketBasicMessage: public AsyncWebSocketMessage { private: @@ -217,17 +256,23 @@ class AsyncWebSocketBasicMessage: public AsyncWebSocketMessage AsyncWebSocketBasicMessage(uint8_t opcode = WS_TEXT, bool mask = false); virtual ~AsyncWebSocketBasicMessage() override; - virtual bool betweenFrames() const 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; virtual bool reserve(size_t size); }; +///////////////////////////////////////////////// + class AsyncWebSocketMultiMessage: public AsyncWebSocketMessage { private: @@ -242,15 +287,21 @@ class AsyncWebSocketMultiMessage: public AsyncWebSocketMessage AsyncWebSocketMultiMessage(AsyncWebSocketMessageBuffer * buffer, uint8_t opcode = WS_TEXT, bool mask = false); virtual ~AsyncWebSocketMultiMessage() override; - virtual bool betweenFrames() const 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: @@ -278,32 +329,44 @@ class AsyncWebSocketClient AsyncWebSocketClient(AsyncWebServerRequest *request, AsyncWebSocket *server); ~AsyncWebSocketClient(); + ////////////////////////////////////////////////// + //client id increments for the given server - uint32_t id() + inline uint32_t id() { return _clientId; } - AwsClientStatus status() + ///////////////////////////////////////////////// + + inline AwsClientStatus status() { return _status; } - AsyncClient* client() + ///////////////////////////////////////////////// + + inline AsyncClient* client() { return _client; } - AsyncWebSocket *server() + ///////////////////////////////////////////////// + + inline AsyncWebSocket *server() { return _server; } - AwsFrameInfo const &pinfo() const + ///////////////////////////////////////////////// + + inline AwsFrameInfo const &pinfo() const { return _pinfo; } + ///////////////////////////////////////////////// + IPAddress remoteIP(); uint16_t remotePort(); @@ -311,23 +374,31 @@ class AsyncWebSocketClient 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) + inline void keepAlivePeriod(uint16_t seconds) { _keepAlivePeriod = seconds * 1000; } - uint16_t keepAlivePeriod() + ///////////////////////////////////////////////// + + inline uint16_t keepAlivePeriod() { return (uint16_t)(_keepAlivePeriod / 1000); } + ///////////////////////////////////////////////// + //data packets - void message(AsyncWebSocketMessage *message) + inline void message(AsyncWebSocketMessage *message) { _queueMessage(message); } + ///////////////////////////////////////////////// + bool queueIsFull(); size_t printf(const char *format, ...) __attribute__ ((format (printf, 2, 3))); @@ -346,11 +417,15 @@ class AsyncWebSocketClient void binary(const String &message); void binary(AsyncWebSocketMessageBuffer *buffer); - bool canSend() + ///////////////////////////////////////////////// + + inline 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); @@ -360,8 +435,12 @@ class AsyncWebSocketClient 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 { @@ -380,32 +459,44 @@ class AsyncWebSocket: public AsyncWebHandler AsyncWebSocket(const String& url); ~AsyncWebSocket(); - const char * url() const + ///////////////////////////////////////////////// + + inline const char * url() const { return _url.c_str(); } - void enable(bool e) + ///////////////////////////////////////////////// + + inline void enable(bool e) { _enabled = e; } - bool enabled() const + ///////////////////////////////////////////////// + + inline 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) + ///////////////////////////////////////////////// + + inline 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); @@ -445,25 +536,30 @@ class AsyncWebSocket: public AsyncWebHandler 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))); + ///////////////////////////////////////////////// + //event listener - void onEvent(AwsEventHandler handler) + inline void onEvent(AwsEventHandler handler) { _eventHandler = handler; } + ///////////////////////////////////////////////// + //system callbacks (do not call) - uint32_t _getNextId() + inline 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); @@ -473,6 +569,8 @@ class AsyncWebSocket: public AsyncWebHandler AsyncWebSocketClientLinkedList getClients() const; }; +///////////////////////////////////////////////// + //WebServer response to authenticate the socket and detach the tcp client from the web server request class AsyncWebSocketResponse: public AsyncWebServerResponse { @@ -485,11 +583,14 @@ class AsyncWebSocketResponse: public AsyncWebServerResponse void _respond(AsyncWebServerRequest *request); size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time); - bool _sourceValid() const + ///////////////////////////////////////////////// + + inline bool _sourceValid() const { return true; } }; +///////////////////////////////////////////////// #endif /* ASYNCWEBSOCKET_ETHERNET_H_ */ diff --git a/src/AsyncWebSynchronization_Ethernet.h b/src/AsyncWebSynchronization_Ethernet.h index feff0be..54dc57e 100644 --- a/src/AsyncWebSynchronization_Ethernet.h +++ b/src/AsyncWebSynchronization_Ethernet.h @@ -12,14 +12,16 @@ as published bythe Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program 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 General Public License for more details. - You should have received a copy of the GNU General Public License along with this program. If not, see . + You should have received a copy of the GNU General Public License along with this program. + If not, see . - Version: 1.4.1 + Version: 1.5.0 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.4.1 K Hoang 18/03/2022 Initial coding for ESP8266 using W5x00/ENC8266 Ethernet. Bump up version to v1.4.1 to sync with AsyncWebServer_STM32 v1.4.1 + 1.5.0 K Hoang 05/10/2022 Option to use non-destroyed cString instead of String to save Heap *****************************************************************************************************************************/ #pragma once @@ -31,44 +33,59 @@ #include +///////////////////////////////////////////////// + // This is the Ethernet version of the Sync Lock which is currently unimplemented class AsyncWebLock { -public: - AsyncWebLock() { - } + public: + AsyncWebLock() {} + + ~AsyncWebLock() {} - ~AsyncWebLock() { - } + ///////////////////////////////////////////////// + + inline bool lock() const + { + return false; + } - bool lock() const { - return false; - } + ///////////////////////////////////////////////// - void unlock() const { - } + inline void unlock() const {} }; class AsyncWebLockGuard { -private: - const AsyncWebLock *_lock; - -public: - AsyncWebLockGuard(const AsyncWebLock &l) { - if (l.lock()) { - _lock = &l; - } else { - _lock = NULL; + private: + const AsyncWebLock *_lock; + + public: + + ///////////////////////////////////////////////// + + AsyncWebLockGuard(const AsyncWebLock &l) + { + if (l.lock()) + { + _lock = &l; + } + else + { + _lock = NULL; + } } - } - ~AsyncWebLockGuard() { - if (_lock) { - _lock->unlock(); + ///////////////////////////////////////////////// + + ~AsyncWebLockGuard() + { + if (_lock) + { + _lock->unlock(); + } } - } }; #endif // ASYNCWEBSYNCHRONIZATION_ETHERNET_H_ diff --git a/src/StringArray_Ethernet.h b/src/StringArray_Ethernet.h index 79dc80e..d14d9a8 100644 --- a/src/StringArray_Ethernet.h +++ b/src/StringArray_Ethernet.h @@ -12,14 +12,16 @@ as published bythe Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program 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 General Public License for more details. - You should have received a copy of the GNU General Public License along with this program. If not, see . + You should have received a copy of the GNU General Public License along with this program. + If not, see . - Version: 1.4.1 + Version: 1.5.0 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.4.1 K Hoang 18/03/2022 Initial coding for ESP8266 using W5x00/ENC8266 Ethernet. Bump up version to v1.4.1 to sync with AsyncWebServer_STM32 v1.4.1 + 1.5.0 K Hoang 05/10/2022 Option to use non-destroyed cString instead of String to save Heap *****************************************************************************************************************************/ #pragma once @@ -30,6 +32,8 @@ #include "stddef.h" #include "WString.h" +///////////////////////////////////////////////// + template class LinkedListNode { @@ -40,17 +44,24 @@ class LinkedListNode LinkedListNode(const T val): _value(val), next(nullptr) {} ~LinkedListNode() {} - const T& value() const + ///////////////////////////////////////////////// + + inline const T& value() const { return _value; }; - - T& value() + + ///////////////////////////////////////////////// + + inline T& value() { return _value; } }; +///////////////////////////////////////////////// +///////////////////////////////////////////////// + template class Item = LinkedListNode> class LinkedList { @@ -71,23 +82,31 @@ class LinkedList Iterator(ItemType* current = nullptr) : _node(current) {} Iterator(const Iterator& i) : _node(i._node) {} - Iterator& operator ++() + ///////////////////////////////////////////////// + + inline Iterator& operator ++() { _node = _node->next; return *this; } - - bool operator != (const Iterator& i) const + + ///////////////////////////////////////////////// + + inline bool operator != (const Iterator& i) const { return _node != i._node; } - - const T& operator * () const + + ///////////////////////////////////////////////// + + inline const T& operator * () const { return _node->value(); } - - const T* operator -> () const + + ///////////////////////////////////////////////// + + inline const T* operator -> () const { return &_node->value(); } @@ -96,118 +115,139 @@ class LinkedList public: typedef const Iterator ConstIterator; - ConstIterator begin() const + ///////////////////////////////////////////////// + + inline ConstIterator begin() const { return ConstIterator(_root); } - - ConstIterator end() const + + ///////////////////////////////////////////////// + + inline ConstIterator end() const { return ConstIterator(nullptr); } + ///////////////////////////////////////////////// + LinkedList(OnRemove onRemove) : _root(nullptr), _onRemove(onRemove) {} ~LinkedList() {} - - void add(const T& t) + + ///////////////////////////////////////////////// + + void add(const T& t) { auto it = new ItemType(t); - - if (!_root) + + if (!_root) { _root = it; - } - else + } + else { auto i = _root; - - while (i->next) + + while (i->next) i = i->next; - + i->next = it; } } - - T& front() const + + ///////////////////////////////////////////////// + + inline T& front() const { return _root->value(); } - bool isEmpty() const + ///////////////////////////////////////////////// + + inline bool isEmpty() const { return _root == nullptr; } - - size_t length() const + + ///////////////////////////////////////////////// + + size_t length() const { size_t i = 0; auto it = _root; - - while (it) + + while (it) { i++; it = it->next; } - + return i; } - - size_t count_if(Predicate predicate) const + + ///////////////////////////////////////////////// + + size_t count_if(Predicate predicate) const { size_t i = 0; auto it = _root; - - while (it) + + while (it) { - if (!predicate) + if (!predicate) { i++; } - else if (predicate(it->value())) + else if (predicate(it->value())) { i++; } - + it = it->next; } + return i; } - - const T* nth(size_t N) const + + ///////////////////////////////////////////////// + + const T* nth(size_t N) const { size_t i = 0; auto it = _root; - while (it) + while (it) { if (i++ == N) return &(it->value()); - + it = it->next; } - + return nullptr; } - - bool remove(const T& t) + + ///////////////////////////////////////////////// + + bool remove(const T& t) { auto it = _root; auto pit = _root; - - while (it) + + while (it) { - if (it->value() == t) + if (it->value() == t) { - if (it == _root) + if (it == _root) { _root = _root->next; - } - else + } + else { pit->next = it->next; } - if (_onRemove) + if (_onRemove) { _onRemove(it->value()); } @@ -215,67 +255,73 @@ class LinkedList delete it; return true; } - + pit = it; it = it->next; } - + return false; } - - bool remove_first(Predicate predicate) + + ///////////////////////////////////////////////// + + bool remove_first(Predicate predicate) { auto it = _root; auto pit = _root; - - while (it) + + while (it) { - if (predicate(it->value())) + if (predicate(it->value())) { - if (it == _root) + if (it == _root) { _root = _root->next; - } - else + } + else { pit->next = it->next; } - - if (_onRemove) + + if (_onRemove) { _onRemove(it->value()); } - + delete it; return true; } - + pit = it; it = it->next; } - + return false; } - void free() + ///////////////////////////////////////////////// + + void free() { - while (_root != nullptr) + while (_root != nullptr) { auto it = _root; _root = _root->next; - - if (_onRemove) + + if (_onRemove) { _onRemove(it->value()); } - + delete it; } - + _root = nullptr; } }; +///////////////////////////////////////////////// +///////////////////////////////////////////////// class StringArray : public LinkedList { @@ -283,16 +329,18 @@ class StringArray : public LinkedList StringArray() : LinkedList(nullptr) {} - bool containsIgnoreCase(const String& str) + ///////////////////////////////////////////////// + + bool containsIgnoreCase(const String& str) { - for (const auto& s : *this) + for (const auto& s : *this) { - if (str.equalsIgnoreCase(s)) + if (str.equalsIgnoreCase(s)) { return true; } } - + return false; } }; diff --git a/src/libb64/cdecode.c b/src/libb64/cdecode.c index 9a1c8c2..9f2a1c6 100644 --- a/src/libb64/cdecode.c +++ b/src/libb64/cdecode.c @@ -17,14 +17,8 @@ as published bythe Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program 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 General Public License for more details. - You should have received a copy of the GNU General Public License along with this program. If not, see . - - Version: 1.4.1 - - Version Modified By Date Comments - ------- ----------- ---------- ----------- - 1.4.1 K Hoang 18/03/2022 Initial coding for ESP8266 using W5x00/ENC8266 Ethernet. - Bump up version to v1.4.1 to sync with AsyncWebServer_STM32 v1.4.1 + You should have received a copy of the GNU General Public License along with this program. + If not, see *****************************************************************************************************************************/ #include "cdecode.h" diff --git a/src/libb64/cdecode.h b/src/libb64/cdecode.h index 6da8e7c..91fdceb 100644 --- a/src/libb64/cdecode.h +++ b/src/libb64/cdecode.h @@ -17,14 +17,8 @@ as published bythe Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program 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 General Public License for more details. - You should have received a copy of the GNU General Public License along with this program. If not, see . - - Version: 1.4.1 - - Version Modified By Date Comments - ------- ----------- ---------- ----------- - 1.4.1 K Hoang 18/03/2022 Initial coding for ESP8266 using W5x00/ENC8266 Ethernet. - Bump up version to v1.4.1 to sync with AsyncWebServer_STM32 v1.4.1 + You should have received a copy of the GNU General Public License along with this program. + If not, see *****************************************************************************************************************************/ #pragma once diff --git a/src/libb64/cencode.c b/src/libb64/cencode.c index a46744b..7405704 100644 --- a/src/libb64/cencode.c +++ b/src/libb64/cencode.c @@ -17,14 +17,8 @@ as published bythe Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program 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 General Public License for more details. - You should have received a copy of the GNU General Public License along with this program. If not, see . - - Version: 1.4.1 - - Version Modified By Date Comments - ------- ----------- ---------- ----------- - 1.4.1 K Hoang 18/03/2022 Initial coding for ESP8266 using W5x00/ENC8266 Ethernet. - Bump up version to v1.4.1 to sync with AsyncWebServer_STM32 v1.4.1 + You should have received a copy of the GNU General Public License along with this program. + If not, see *****************************************************************************************************************************/ #include "cencode.h" diff --git a/src/libb64/cencode.h b/src/libb64/cencode.h index b05fed6..8972390 100644 --- a/src/libb64/cencode.h +++ b/src/libb64/cencode.h @@ -17,14 +17,8 @@ as published bythe Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program 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 General Public License for more details. - You should have received a copy of the GNU General Public License along with this program. If not, see . - - Version: 1.4.1 - - Version Modified By Date Comments - ------- ----------- ---------- ----------- - 1.4.1 K Hoang 18/03/2022 Initial coding for ESP8266 using W5x00/ENC8266 Ethernet. - Bump up version to v1.4.1 to sync with AsyncWebServer_STM32 v1.4.1 + You should have received a copy of the GNU General Public License along with this program. + If not, see *****************************************************************************************************************************/ #pragma once