diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f64f5e2..26e1f07 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -15,7 +15,7 @@ If you don't find anything, please [open a new issue](https://github.com/khoih-p Please ensure to specify the following: * Arduino IDE version (e.g. 1.8.19) or Platform.io version -* `ArduinoCore-mbed` Core Version (e.g. `ArduinoCore-mbed` mbed_portenta core v2.6.1) +* `ArduinoCore-mbed` Core Version (e.g. `ArduinoCore-mbed` mbed_portenta core v3.3.0) * `Portenta_H7` Board type (e.g. Portenta_H7 Rev2 ABX00042, etc.) * Contextual information (e.g. what you were trying to achieve) * Simplest possible steps to reproduce @@ -28,13 +28,13 @@ Please ensure to specify the following: ``` Arduino IDE version: 1.8.19 -`ArduinoCore-mbed` mbed_portenta core v2.6.1 +`ArduinoCore-mbed` mbed_portenta core v3.3.0 Portenta_H7 Rev2 ABX00042 OS: Ubuntu 20.04 LTS -Linux xy-Inspiron-3593 5.4.0-94-generic #106-Ubuntu SMP Thu Jan 6 23:58:14 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux +Linux xy-Inspiron-3593 5.15.0-46-generic #49~20.04.1-Ubuntu SMP Thu Aug 4 19:15:44 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux Context: -I encountered a crash while using Ethernet Async_AdvancedWebServer. +I encountered a crash while using this library Steps to reproduce: 1. ... diff --git a/Packages_Patches/arduino/hardware/mbed_portenta/3.3.0/portenta_post_install.sh b/Packages_Patches/arduino/hardware/mbed_portenta/3.3.0/portenta_post_install.sh new file mode 100644 index 0000000..7fe71d6 --- /dev/null +++ b/Packages_Patches/arduino/hardware/mbed_portenta/3.3.0/portenta_post_install.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash + +portenta_h7_rules () { + echo "" + echo "# Portenta H7 bootloader mode UDEV rules" + echo "" +cat < /etc/udev/rules.d/49-portenta_h7.rules + +# reload udev rules +echo "Reload rules..." +udevadm trigger +udevadm control --reload-rules diff --git a/README.md b/README.md index 571fda8..7382fb6 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,8 @@ [![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](#Contributing) [![GitHub issues](https://img.shields.io/github/issues/khoih-prog/Portenta_H7_AsyncWebServer.svg)](http://github.com/khoih-prog/Portenta_H7_AsyncWebServer/issues) -Buy Me A Coffee +Donate to my libraries using BuyMeACoffee + --- @@ -166,8 +167,8 @@ to apply the better and faster **asynchronous** feature of the **powerful** [ESP ## Prerequisites 1. [`Arduino IDE 1.8.19+` for Arduino](https://github.com/arduino/Arduino). [![GitHub release](https://img.shields.io/github/release/arduino/Arduino.svg)](https://github.com/arduino/Arduino/releases/latest) - 2. [`ArduinoCore-mbed mbed_portenta core 2.7.2+`](https://github.com/arduino/ArduinoCore-mbed) for Arduino **Portenta_H7** boards, such as **Portenta_H7 Rev2 ABX00042, etc.**. [![GitHub release](https://img.shields.io/github/release/arduino/ArduinoCore-mbed.svg)](https://github.com/arduino/ArduinoCore-mbed/releases/latest). To be used with [**Portenta_H7_AsyncTCP releases v1.3.0+**](https://github.com/khoih-prog/Portenta_H7_AsyncTCP/releases/tag/v1.3.0) - 3. [`Portenta_H7_AsyncTCP library v1.3.0+`](https://github.com/khoih-prog/Portenta_H7_AsyncTCP) for Portenta_H7 using `Vision-shield Ethernet` or `Murata WiFi`. [To install. check [![arduino-library-badge](https://www.ardu-badge.com/badge/Portenta_H7_AsyncTCP.svg?)](https://www.ardu-badge.com/Portenta_H7_AsyncTCP) + 2. [`ArduinoCore-mbed mbed_portenta core 3.3.0+`](https://github.com/arduino/ArduinoCore-mbed) for Arduino **Portenta_H7** boards, such as **Portenta_H7 Rev2 ABX00042, etc.**. [![GitHub release](https://img.shields.io/github/release/arduino/ArduinoCore-mbed.svg)](https://github.com/arduino/ArduinoCore-mbed/releases/latest). + 3. [`Portenta_H7_AsyncTCP library v1.4.0+`](https://github.com/khoih-prog/Portenta_H7_AsyncTCP) for Portenta_H7 using `Vision-shield Ethernet` or `Murata WiFi`. [![GitHub release](https://img.shields.io/github/release/khoih-prog/Portenta_H7_AsyncTCP.svg)](https://github.com/khoih-prog/Portenta_H7_AsyncTCP/releases/latest) --- @@ -180,15 +181,15 @@ The best and easiest way is to use `Arduino Library Manager`. Search for `Porten ### Manual Install 1. Navigate to [Portenta_H7_AsyncWebServer](https://github.com/khoih-prog/Portenta_H7_AsyncWebServer) page. -2. Download the latest release `Portenta_H7_AsyncWebServer-master.zip`. -3. Extract the zip file to `Portenta_H7_AsyncWebServer-master` directory -4. Copy the whole `Portenta_H7_AsyncWebServer-master` folder to Arduino libraries' directory such as `~/Arduino/libraries/`. +2. Download the latest release `Portenta_H7_AsyncWebServer-main.zip`. +3. Extract the zip file to `Portenta_H7_AsyncWebServer-main` directory +4. Copy the whole `Portenta_H7_AsyncWebServer-main` folder to Arduino libraries' directory such as `~/Arduino/libraries/`. ### VS Code & PlatformIO: 1. Install [VS Code](https://code.visualstudio.com/) 2. Install [PlatformIO](https://platformio.org/platformio-ide) -3. Install [**Portenta_H7_AsyncWebServer** library](https://platformio.org/lib/show/12920/Portenta_H7_AsyncWebServer) by using [Library Manager](https://platformio.org/lib/show/12920/Portenta_H7_AsyncWebServer/installation). Search for **Portenta_H7_AsyncWebServer** in [Platform.io Author's Libraries](https://platformio.org/lib/search?query=author:%22Khoi%20Hoang%22) +3. Install [**Portenta_H7_AsyncWebServer** library](https://registry.platformio.org/libraries/khoih-prog/Portenta_H7_AsyncWebServer) by using [Library Manager](https://registry.platformio.org/libraries/khoih-prog/Portenta_H7_AsyncWebServer/installation). Search for **Portenta_H7_AsyncWebServer** in [Platform.io Author's Libraries](https://platformio.org/lib/search?query=author:%22Khoi%20Hoang%22) 4. Use included [platformio.ini](platformio/platformio.ini) file from examples to ensure that all dependent libraries will installed automatically. Please visit documentation for the other options and examples at [Project Configuration File](https://docs.platformio.org/page/projectconf.html) --- @@ -198,12 +199,12 @@ The best and easiest way is to use `Arduino Library Manager`. Search for `Porten #### 1. For Portenta_H7 boards using Arduino IDE in Linux - **To be able to upload firmware to Portenta_H7 using Arduino IDE in Linux (Ubuntu, etc.)**, you have to copy the file [portenta_post_install.sh](Packages_Patches/arduino/hardware/mbed_portenta/2.6.1/portenta_post_install.sh) into mbed_portenta directory (~/.arduino15/packages/arduino/hardware/mbed_portenta/2.6.1/portenta_post_install.sh). + **To be able to upload firmware to Portenta_H7 using Arduino IDE in Linux (Ubuntu, etc.)**, you have to copy the file [portenta_post_install.sh](Packages_Patches/arduino/hardware/mbed_portenta/3.3.0/portenta_post_install.sh) into mbed_portenta directory (~/.arduino15/packages/arduino/hardware/mbed_portenta/3.3.0/portenta_post_install.sh). Then run the following command using `sudo` ``` -$ cd ~/.arduino15/packages/arduino/hardware/mbed_portenta/2.6.1 +$ cd ~/.arduino15/packages/arduino/hardware/mbed_portenta/3.3.0 $ chmod 755 portenta_post_install.sh $ sudo ./portenta_post_install.sh ``` @@ -216,9 +217,9 @@ This will create the file `/etc/udev/rules.d/49-portenta_h7.rules` as follows: SUBSYSTEMS=="usb", ATTRS{idVendor}=="2341", ATTRS{idProduct}=="035b", GROUP="plugdev", MODE="0666" ``` -Supposing the ArduinoCore-mbed core version is 2.6.1. Now only one file must be copied into the directory: +Supposing the ArduinoCore-mbed core version is 3.3.0. Now only one file must be copied into the directory: -- `~/.arduino15/packages/arduino/hardware/mbed_portenta/2.6.1/portenta_post_install.sh` +- `~/.arduino15/packages/arduino/hardware/mbed_portenta/3.3.0/portenta_post_install.sh` Whenever a new version is installed, remember to copy this files into the new version directory. For example, new version is x.yy.zz @@ -230,7 +231,7 @@ This file must be copied into the directory: #### 2. To fix compile error relating to dns_gethostbyname and LwIP stack -#### Notes: Only for cores v2.5.2-. This fix is not necessary for core v2.6.1+ +#### Notes: Only for cores v2.5.2-. This fix is not necessary for core v3.3.0+ **To be able to compile, run on Portenta_H7 boards**, you have to copy the whole [mbed_portenta Packages_Patches](Packages_Patches/arduino/hardware/mbed_portenta/2.5.2) directory into Arduino mbed_portenta directory (~/.arduino15/packages/arduino/hardware/mbed_portenta/2.5.2). @@ -1484,248 +1485,7 @@ build_flags = ### Example [Async_AdvancedWebServer](examples/Ethernet/Async_AdvancedWebServer) -```cpp -#if !( defined(ARDUINO_PORTENTA_H7_M7) || defined(ARDUINO_PORTENTA_H7_M4) ) - #error For Portenta_H7 only -#endif - -#define _PORTENTA_H7_AWS_LOGLEVEL_ 1 - -#define USE_ETHERNET_PORTENTA_H7 true - -#include -#include -#warning Using Portenta_Ethernet lib for Portenta_H7. - -#include - -// Enter a MAC address and IP address for your controller below. -#define NUMBER_OF_MAC 20 - -byte mac[][NUMBER_OF_MAC] = -{ - { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x01 }, - { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x02 }, - { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x03 }, - { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x04 }, - { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x05 }, - { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x06 }, - { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x07 }, - { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x08 }, - { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x09 }, - { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x0A }, - { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x0B }, - { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x0C }, - { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x0D }, - { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x0E }, - { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x0F }, - { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x10 }, - { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x11 }, - { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x12 }, - { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x13 }, - { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x14 }, -}; -// Select the IP address according to your local network -IPAddress ip(192, 168, 2, 232); - -AsyncWebServer server(80); - -int reqCount = 0; // number of requests received - -#define LED_OFF HIGH -#define LED_ON LOW - - -#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_Portenta_H7!

\ -

running on %s

\ -

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

\ -\ -\ -", BOARD_NAME, BOARD_NAME, 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 drawGraph(AsyncWebServerRequest *request) -{ - String out; - - out.reserve(3000); - char temp[70]; - - out += "\n"; - out += "\n"; - out += "\n"; - int y = rand() % 130; - - for (int x = 10; x < 300; x += 10) - { - int y2 = rand() % 130; - sprintf(temp, "\n", x, 140 - y, x + 10, 140 - y2); - out += temp; - y = y2; - } - out += "\n\n"; - - request->send(200, "image/svg+xml", out); -} - - -void setup() -{ - pinMode(LED_BUILTIN, OUTPUT); - digitalWrite(LED_BUILTIN, LED_OFF); - - Serial.begin(115200); - while (!Serial); - - delay(200); - - Serial.print("\nStart Async_AdvancedWebServer on "); Serial.print(BOARD_NAME); - Serial.print(" with "); Serial.println(SHIELD_TYPE); - Serial.println(PORTENTA_H7_ASYNC_TCP_VERSION); - Serial.println(PORTENTA_H7_ASYNC_WEBSERVER_VERSION); - - /////////////////////////////////// - - // start the ethernet connection and the server - // Use random mac - uint16_t index = millis() % NUMBER_OF_MAC; - - // Use Static IP - //Ethernet.begin(mac[index], ip); - // Use DHCP dynamic IP and random mac - Ethernet.begin(mac[index]); - - if (Ethernet.hardwareStatus() == EthernetNoHardware) - { - Serial.println("No Ethernet found. Stay here forever"); - - while (true) - { - delay(1); // do nothing, no point running without Ethernet hardware - } - } - - if (Ethernet.linkStatus() == LinkOFF) - { - Serial.println("Not connected Ethernet cable"); - } - - Serial.print(F("Using mac index = ")); - Serial.println(index); - - Serial.print(F("Connected! IP address: ")); - Serial.println(Ethernet.localIP()); - - /////////////////////////////////// - - - 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(Ethernet.localIP()); -} - -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(); -} -``` +https://github.com/khoih-prog/Portenta_H7_AsyncWebServer/blob/f934df6aecbf814047affe39d3d0e3f23fc1fccf/examples/WiFi/Async_AdvancedWebServer/Async_AdvancedWebServer.ino#L41-L268 You can access the Async Advanced WebServer @ the server IP @@ -1745,8 +1505,8 @@ Following is debug terminal output when running example [MQTT_ThingStream](examp ``` Start MQTT_ThingStream on PORTENTA_H7_M7 with Ethernet using Portenta_Ethernet Library -Portenta_H7_AsyncTCP v1.3.0 -Portenta_H7_AsyncWebServer v1.2.1 +Portenta_H7_AsyncTCP v1.4.0 +Portenta_H7_AsyncWebServer v1.3.0 Using mac index = 17 Connected! IP address: 192.168.2.87 *************************************** @@ -1770,8 +1530,8 @@ Following is debug terminal output when running example [WebClient](examples/Eth ``` Start WebClientRepeating on PORTENTA_H7_M7 with Ethernet using Portenta_Ethernet Library -Portenta_H7_AsyncTCP v1.3.0 -Portenta_H7_AsyncWebServer v1.2.1 +Portenta_H7_AsyncTCP v1.4.0 +Portenta_H7_AsyncWebServer v1.3.0 Using mac index = 16 Connected! IP address: 192.168.2.87 @@ -1838,8 +1598,8 @@ Following is debug terminal output when running example [MQTTClient_Auth](exampl ``` Start MQTTClient_Auth on PORTENTA_H7_M7 with Ethernet using Portenta_Ethernet Library -Portenta_H7_AsyncTCP v1.3.0 -Portenta_H7_AsyncWebServer v1.2.1 +Portenta_H7_AsyncTCP v1.4.0 +Portenta_H7_AsyncWebServer v1.3.0 Using mac index = 9 Connected! IP address: 192.168.2.87 Attempting MQTT connection to broker.emqx.io...connected @@ -1859,8 +1619,8 @@ Following is debug terminal output when running example [MQTTClient_Basic](examp ``` Start MQTTClient_Basic on PORTENTA_H7_M7 with Ethernet using Portenta_Ethernet Library -Portenta_H7_AsyncTCP v1.3.0 -Portenta_H7_AsyncWebServer v1.2.1 +Portenta_H7_AsyncTCP v1.4.0 +Portenta_H7_AsyncWebServer v1.3.0 Using mac index = 8 Connected! IP address: 192.168.2.87 Attempting MQTT connection to broker.emqx.io...connected @@ -1878,8 +1638,8 @@ Following is debug terminal output when running example [Async_HTTPBasicAuth](ex ``` Start Async_HTTPBasicAuth on PORTENTA_H7_M7 with Ethernet using Portenta_Ethernet Library -Portenta_H7_AsyncTCP v1.3.0 -Portenta_H7_AsyncWebServer v1.2.1 +Portenta_H7_AsyncTCP v1.4.0 +Portenta_H7_AsyncWebServer v1.3.0 Using mac index = 16 Connected! IP address: 192.168.2.87 Async_HttpBasicAuth started @ IP : 192.168.2.87 @@ -1908,8 +1668,8 @@ Following are debug terminal output and screen shots when running example [Async ``` Start Async_AdvancedWebServer on PORTENTA_H7_M7 with Ethernet using Portenta_Ethernet Library -Portenta_H7_AsyncTCP v1.3.0 -Portenta_H7_AsyncWebServer v1.2.1 +Portenta_H7_AsyncTCP v1.4.0 +Portenta_H7_AsyncWebServer v1.3.0 Using mac index = 4 Connected! IP address: 192.168.2.87 HTTP EthernetWebServer is @ IP : 192.168.2.87 @@ -1921,6 +1681,12 @@ You can access the Async Advanced WebServers at the displayed server IP, e.g. `1

+#### Using Firefox Browser + +

+ +

+ --- #### 7. Async_AdvancedWebServer on PORTENTA_H7_M7 using WiFi @@ -1929,8 +1695,8 @@ Following is the debug terminal and screen shot when running example [Async_Adva ``` Start Async_AdvancedWebServer on PORTENTA_H7_M7 with Portenta_H7 WiFi -Portenta_H7_AsyncTCP v1.3.0 -Portenta_H7_AsyncWebServer v1.2.1 +Portenta_H7_AsyncTCP v1.4.0 +Portenta_H7_AsyncWebServer v1.3.0 Connecting to SSID: HueNet1 SSID: HueNet1 Local IP Address: 192.168.2.94 @@ -1985,6 +1751,7 @@ Submit issues to: [Portenta_H7_AsyncWebServer issues](https://github.com/khoih-p 1. Add support to Portenta_H7 using `Vision-shield Ethernet` 2. Add Table of Contents + 3. Fix issue with slow browsers or network. Check [Target stops responding after variable time when using Firefox on Windows 10 #3](https://github.com/khoih-prog/AsyncWebServer_RP2040W/issues/3) --- --- diff --git a/changelog.md b/changelog.md index efa5f23..deb712a 100644 --- a/changelog.md +++ b/changelog.md @@ -11,6 +11,7 @@ ## Table of Contents * [Changelog](#changelog) + * [Releases v1.3.0](#Releases-v130) * [Releases v1.2.1](#Releases-v121) * [Releases v1.2.0](#Releases-v120) * [Releases v1.1.1](#Releases-v111) @@ -22,6 +23,10 @@ ## Changelog +### Releases v1.3.0 + +1. Fix issue with slow browsers or network. Check [Target stops responding after variable time when using Firefox on Windows 10 #3](https://github.com/khoih-prog/AsyncWebServer_RP2040W/issues/3) + ### Releases v1.2.1 1. Fix authenticate issue caused by libb64 diff --git a/library.json b/library.json index 6934770..6e8b0c8 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "Portenta_H7_AsyncWebServer", - "version": "1.2.1", + "version": "1.3.0", "keywords": "http, async, websocket, webserver, async-webserver, async-tcp, async-udp, async-websocket, async-http, ssl, tls, mbed, mbed-portenta, portenta-h7, portentah7, portenta-h7-m7, portenta-h7-m4, portentah7-m7, portentah7-m4, stm32h7", "description": "Asynchronous WebServer Library for STM32H7-based Portenta_H7 using mbed_portenta core. This library, which is relied on Portenta_H7_AsyncTCP, is part of a series of advanced Async libraries, such as AsyncTCP, AsyncUDP, AsyncWebSockets, AsyncHTTPRequest, AsyncHTTPSRequest, etc.", "authors": @@ -34,7 +34,7 @@ { "owner": "khoih-prog", "name": "Portenta_H7_AsyncTCP", - "version": "^1.3.0" + "version": "^1.4.0" } ], "license": "LGPL-3.0", diff --git a/library.properties b/library.properties index 1967a60..9692c65 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=Portenta_H7_AsyncWebServer -version=1.2.1 +version=1.3.0 author=Hristo Gochkov,Khoi Hoang maintainer=Khoi Hoang sentence=Asynchronous WebServer Library for STM32H7-based Portenta_H7 using mbed_portenta core. diff --git a/pics/Async_AdvancedWebServer_Firefox.png b/pics/Async_AdvancedWebServer_Firefox.png new file mode 100644 index 0000000..fab3f56 Binary files /dev/null and b/pics/Async_AdvancedWebServer_Firefox.png differ diff --git a/platformio/platformio.ini b/platformio/platformio.ini index d44d2a3..a4c6fb0 100644 --- a/platformio/platformio.ini +++ b/platformio/platformio.ini @@ -38,13 +38,16 @@ 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 -; Portenta_H7_AsyncTCP@>=1.3.0 +; Portenta_H7_AsyncTCP@>=1.4.0 ; PlatformIO 5.x - khoih-prog/Portenta_H7_AsyncTCP@>=1.3.0 + khoih-prog/Portenta_H7_AsyncTCP@>=1.4.0 build_flags = diff --git a/src/Portenta_H7_AsyncEventSource.cpp b/src/Portenta_H7_AsyncEventSource.cpp index b4b8c5f..41004c3 100644 --- a/src/Portenta_H7_AsyncEventSource.cpp +++ b/src/Portenta_H7_AsyncEventSource.cpp @@ -9,7 +9,7 @@ Built by Khoi Hoang https://github.com/khoih-prog/Portenta_H7_AsyncWebServer Licensed under GPLv3 license - Version: 1.2.1 + Version: 1.3.0 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -18,6 +18,7 @@ 1.1.1 K Hoang 12/10/2021 Update `platform.ini` and `library.json` 1.2.0 K Hoang 07/12/2021 Fix crashing issue 1.2.1 K Hoang 12/01/2022 Fix authenticate issue caused by libb64 + 1.3.0 K Hoang 26/09/2022 Fix issue with slow browsers or network *****************************************************************************************************************************/ #if !defined(_PORTENTA_H7_AWS_LOGLEVEL_) @@ -29,6 +30,8 @@ #include "Arduino.h" #include "Portenta_H7_AsyncEventSource.h" +///////////////////////////////////////////////// + static String generateEventMessage(const char *message, const char *event, uint32_t id, uint32_t reconnect) { String ev = ""; @@ -143,6 +146,8 @@ static String generateEventMessage(const char *message, const char *event, uint3 return ev; } +///////////////////////////////////////////////// + // Message AsyncEventSourceMessage::AsyncEventSourceMessage(const char * data, size_t len) @@ -161,12 +166,16 @@ 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) { PORTENTA_H7_AWS_UNUSED(time); @@ -187,6 +196,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 +217,8 @@ size_t AsyncEventSourceMessage::send(AsyncClient *client) return sent; } +///////////////////////////////////////////////// + // Client AsyncEventSourceClient::AsyncEventSourceClient(AsyncWebServerRequest *request, AsyncEventSource *server) @@ -253,12 +266,16 @@ AsyncEventSourceClient::AsyncEventSourceClient(AsyncWebServerRequest *request, A delete request; } +///////////////////////////////////////////////// + AsyncEventSourceClient::~AsyncEventSourceClient() { _messageQueue.free(); close(); } +///////////////////////////////////////////////// + void AsyncEventSourceClient::_queueMessage(AsyncEventSourceMessage *dataMessage) { if (dataMessage == NULL) @@ -285,6 +302,8 @@ void AsyncEventSourceClient::_queueMessage(AsyncEventSourceMessage *dataMessage) _runQueue(); } +///////////////////////////////////////////////// + void AsyncEventSourceClient::_onAck(size_t len, uint32_t time) { AWS_LOGDEBUG("AsyncEventSourceClient::_onAck"); @@ -300,6 +319,8 @@ void AsyncEventSourceClient::_onAck(size_t len, uint32_t time) _runQueue(); } +///////////////////////////////////////////////// + void AsyncEventSourceClient::_onPoll() { AWS_LOGDEBUG("AsyncEventSourceClient::_onPoll"); @@ -310,6 +331,7 @@ void AsyncEventSourceClient::_onPoll() } } +///////////////////////////////////////////////// void AsyncEventSourceClient::_onTimeout(uint32_t time __attribute__((unused))) { @@ -318,6 +340,8 @@ void AsyncEventSourceClient::_onTimeout(uint32_t time __attribute__((unused))) _client->close(true); } +///////////////////////////////////////////////// + void AsyncEventSourceClient::_onDisconnect() { AWS_LOGDEBUG("AsyncEventSourceClient::_onDisconnect"); @@ -326,6 +350,8 @@ void AsyncEventSourceClient::_onDisconnect() _server->_handleDisconnect(this); } +///////////////////////////////////////////////// + void AsyncEventSourceClient::close() { AWS_LOGDEBUG("AsyncEventSourceClient::close"); @@ -334,6 +360,8 @@ void AsyncEventSourceClient::close() _client->close(); } +///////////////////////////////////////////////// + void AsyncEventSourceClient::write(const char * message, size_t len) { AWS_LOGDEBUG3("AsyncEventSourceClient::write: message =", message, ", len =", len); @@ -341,6 +369,8 @@ 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) { AWS_LOGDEBUG5("AsyncEventSourceClient::send: message =", message, ", event =", event, ", id =", id); @@ -349,6 +379,8 @@ void AsyncEventSourceClient::send(const char *message, const char *event, uint32 _queueMessage(new AsyncEventSourceMessage(ev.c_str(), ev.length())); } +///////////////////////////////////////////////// + void AsyncEventSourceClient::_runQueue() { while (!_messageQueue.isEmpty() && _messageQueue.front()->finished()) @@ -363,6 +395,7 @@ void AsyncEventSourceClient::_runQueue() } } +///////////////////////////////////////////////// // Handler @@ -375,11 +408,15 @@ AsyncEventSource::AsyncEventSource(const String& url) , _connectcb(NULL) {} +///////////////////////////////////////////////// + AsyncEventSource::~AsyncEventSource() { close(); } +///////////////////////////////////////////////// + void AsyncEventSource::onConnect(ArEventHandlerFunction cb) { AWS_LOGDEBUG("AsyncEventSource::onConnect"); @@ -387,6 +424,8 @@ void AsyncEventSource::onConnect(ArEventHandlerFunction cb) _connectcb = cb; } +///////////////////////////////////////////////// + void AsyncEventSource::_addClient(AsyncEventSourceClient * client) { /*char * temp = (char *)malloc(2054); @@ -410,12 +449,16 @@ void AsyncEventSource::_addClient(AsyncEventSourceClient * client) _connectcb(client); } +///////////////////////////////////////////////// + void AsyncEventSource::_handleDisconnect(AsyncEventSourceClient * client) { AWS_LOGDEBUG("AsyncEventSource::_handleDisconnect"); _clients.remove(client); } +///////////////////////////////////////////////// + void AsyncEventSource::close() { AWS_LOGDEBUG("AsyncEventSource::close"); @@ -427,6 +470,8 @@ void AsyncEventSource::close() } } +///////////////////////////////////////////////// + // pmb fix size_t AsyncEventSource::avgPacketsWaiting() const { @@ -449,6 +494,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); @@ -462,6 +509,8 @@ void AsyncEventSource::send(const char *message, const char *event, uint32_t id, } } +///////////////////////////////////////////////// + size_t AsyncEventSource::count() const { return _clients.count_if([](AsyncEventSourceClient * c) @@ -470,6 +519,8 @@ size_t AsyncEventSource::count() const }); } +///////////////////////////////////////////////// + bool AsyncEventSource::canHandle(AsyncWebServerRequest *request) { if (request->method() != HTTP_GET || !request->url().equals(_url)) @@ -482,6 +533,8 @@ bool AsyncEventSource::canHandle(AsyncWebServerRequest *request) return true; } +///////////////////////////////////////////////// + void AsyncEventSource::handleRequest(AsyncWebServerRequest *request) { if ((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str())) @@ -490,6 +543,8 @@ void AsyncEventSource::handleRequest(AsyncWebServerRequest *request) request->send(new AsyncEventSourceResponse(this)); } +///////////////////////////////////////////////// + // Response AsyncEventSourceResponse::AsyncEventSourceResponse(AsyncEventSource *server) @@ -502,6 +557,8 @@ AsyncEventSourceResponse::AsyncEventSourceResponse(AsyncEventSource *server) addHeader("Connection", "keep-alive"); } +///////////////////////////////////////////////// + void AsyncEventSourceResponse::_respond(AsyncWebServerRequest *request) { String out = _assembleHead(request->version()); @@ -509,6 +566,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) @@ -519,3 +578,5 @@ size_t AsyncEventSourceResponse::_ack(AsyncWebServerRequest *request, size_t len return 0; } +///////////////////////////////////////////////// + diff --git a/src/Portenta_H7_AsyncEventSource.h b/src/Portenta_H7_AsyncEventSource.h index fde583b..9f51041 100644 --- a/src/Portenta_H7_AsyncEventSource.h +++ b/src/Portenta_H7_AsyncEventSource.h @@ -9,7 +9,7 @@ Built by Khoi Hoang https://github.com/khoih-prog/Portenta_H7_AsyncWebServer Licensed under GPLv3 license - Version: 1.2.1 + Version: 1.3.0 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -18,6 +18,7 @@ 1.1.1 K Hoang 12/10/2021 Update `platform.ini` and `library.json` 1.2.0 K Hoang 07/12/2021 Fix crashing issue 1.2.1 K Hoang 12/01/2022 Fix authenticate issue caused by libb64 + 1.3.0 K Hoang 26/09/2022 Fix issue with slow browsers or network *****************************************************************************************************************************/ #pragma once @@ -40,11 +41,15 @@ #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: @@ -71,6 +76,8 @@ class AsyncEventSourceMessage } }; +///////////////////////////////////////////////// + class AsyncEventSourceClient { private: @@ -117,6 +124,8 @@ class AsyncEventSourceClient void _onDisconnect(); }; +///////////////////////////////////////////////// + class AsyncEventSource: public AsyncWebHandler { private: @@ -146,6 +155,8 @@ class AsyncEventSource: public AsyncWebHandler virtual void handleRequest(AsyncWebServerRequest *request) override final; }; +///////////////////////////////////////////////// + class AsyncEventSourceResponse: public AsyncWebServerResponse { private: @@ -163,4 +174,6 @@ class AsyncEventSourceResponse: public AsyncWebServerResponse } }; +///////////////////////////////////////////////// + #endif /* PORTENTA_H7_ASYNCEVENTSOURCE_H_ */ diff --git a/src/Portenta_H7_AsyncJson.h b/src/Portenta_H7_AsyncJson.h index eed5621..83229e4 100644 --- a/src/Portenta_H7_AsyncJson.h +++ b/src/Portenta_H7_AsyncJson.h @@ -9,7 +9,7 @@ Built by Khoi Hoang https://github.com/khoih-prog/Portenta_H7_AsyncWebServer Licensed under GPLv3 license - Version: 1.2.1 + Version: 1.3.0 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -18,6 +18,7 @@ 1.1.1 K Hoang 12/10/2021 Update `platform.ini` and `library.json` 1.2.0 K Hoang 07/12/2021 Fix crashing issue 1.2.1 K Hoang 12/01/2022 Fix authenticate issue caused by libb64 + 1.3.0 K Hoang 26/09/2022 Fix issue with slow browsers or network *****************************************************************************************************************************/ /* Async Response to use with ArduinoJson and AsyncWebServer @@ -62,14 +63,18 @@ #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 * */ @@ -113,6 +118,8 @@ class ChunkPrint : public Print } }; +///////////////////////////////////////////////// + class AsyncJsonResponse: public AsyncAbstractResponse { protected: @@ -199,6 +206,8 @@ class AsyncJsonResponse: public AsyncAbstractResponse } }; +///////////////////////////////////////////////// + class PrettyAsyncJsonResponse: public AsyncJsonResponse { public: @@ -238,6 +247,8 @@ class PrettyAsyncJsonResponse: public AsyncJsonResponse } }; +///////////////////////////////////////////////// + typedef std::function ArJsonRequestHandlerFunction; class AsyncCallbackJsonWebHandler: public AsyncWebHandler @@ -265,21 +276,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) { _method = method; } + + ///////////////////////////////////////////////// void setMaxContentLength(int maxContentLength) { _maxContentLength = maxContentLength; } + + ///////////////////////////////////////////////// void onRequest(ArJsonRequestHandlerFunction fn) { _onRequest = fn; } + ///////////////////////////////////////////////// + virtual bool canHandle(AsyncWebServerRequest *request) override final { if (!_onRequest) @@ -299,6 +318,8 @@ class AsyncCallbackJsonWebHandler: public AsyncWebHandler return true; } + ///////////////////////////////////////////////// + virtual void handleRequest(AsyncWebServerRequest *request) override final { if (_onRequest) @@ -334,10 +355,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 { @@ -356,11 +381,18 @@ class AsyncCallbackJsonWebHandler: public AsyncWebHandler } } } + + ///////////////////////////////////////////////// virtual bool isRequestHandlerTrivial() override final { return _onRequest ? false : true; } + + ///////////////////////////////////////////////// + }; +///////////////////////////////////////////////// + #endif // PORTENTA_H7_ASYNC_JSON_H_ diff --git a/src/Portenta_H7_AsyncWebAuthentication.cpp b/src/Portenta_H7_AsyncWebAuthentication.cpp index 5628c31..ab7b2ab 100644 --- a/src/Portenta_H7_AsyncWebAuthentication.cpp +++ b/src/Portenta_H7_AsyncWebAuthentication.cpp @@ -9,7 +9,7 @@ Built by Khoi Hoang https://github.com/khoih-prog/Portenta_H7_AsyncWebServer Licensed under GPLv3 license - Version: 1.2.1 + Version: 1.3.0 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -18,6 +18,7 @@ 1.1.1 K Hoang 12/10/2021 Update `platform.ini` and `library.json` 1.2.0 K Hoang 07/12/2021 Fix crashing issue 1.2.1 K Hoang 12/01/2022 Fix authenticate issue caused by libb64 + 1.3.0 K Hoang 26/09/2022 Fix issue with slow browsers or network *****************************************************************************************************************************/ #if !defined(_PORTENTA_H7_AWS_LOGLEVEL_) @@ -34,6 +35,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 +44,7 @@ bool checkBasicAuthentication(const char * hash, const char * username, const ch if (username == NULL || password == NULL || hash == NULL) { AWS_LOGDEBUG("checkBasicAuthentication: Fail: NULL username/password/hash"); + return false; } @@ -70,6 +74,7 @@ bool checkBasicAuthentication(const char * hash, const char * username, const ch AWS_LOGDEBUG("checkBasicAuthentication: NULL encoded"); delete[] toencode; + return false; } @@ -81,6 +86,7 @@ bool checkBasicAuthentication(const char * hash, const char * username, const ch delete[] toencode; delete[] encoded; + return true; } @@ -88,9 +94,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,9 +136,10 @@ static bool getMD5(uint8_t * data, uint16_t len, char * output) return true; } +///////////////////////////////////////////////// + static String genRandomMD5() { - // For STM32 uint32_t r = rand(); char * out = (char*) malloc(33); @@ -145,6 +155,8 @@ static String genRandomMD5() return res; } +///////////////////////////////////////////////// + static String stringMD5(const String& in) { char * out = (char*) malloc(33); @@ -160,6 +172,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 +203,8 @@ String generateDigestHash(const char * username, const char * password, const ch return res; } +///////////////////////////////////////////////// + String requestDigestAuthentication(const char * realm) { String header = "realm=\""; @@ -209,8 +225,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) { @@ -351,3 +369,6 @@ bool checkDigestAuthentication(const char * header, const char * method, const c return false; } + +///////////////////////////////////////////////// + diff --git a/src/Portenta_H7_AsyncWebAuthentication.h b/src/Portenta_H7_AsyncWebAuthentication.h index 3c9766e..b8e7956 100644 --- a/src/Portenta_H7_AsyncWebAuthentication.h +++ b/src/Portenta_H7_AsyncWebAuthentication.h @@ -9,7 +9,7 @@ Built by Khoi Hoang https://github.com/khoih-prog/Portenta_H7_AsyncWebServer Licensed under GPLv3 license - Version: 1.2.1 + Version: 1.3.0 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -18,6 +18,7 @@ 1.1.1 K Hoang 12/10/2021 Update `platform.ini` and `library.json` 1.2.0 K Hoang 07/12/2021 Fix crashing issue 1.2.1 K Hoang 12/01/2022 Fix authenticate issue caused by libb64 + 1.3.0 K Hoang 26/09/2022 Fix issue with slow browsers or network *****************************************************************************************************************************/ #pragma once @@ -28,9 +29,12 @@ #include "Arduino.h" #include "Portenta_H7_AsyncWebServer_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); diff --git a/src/Portenta_H7_AsyncWebHandlerImpl.h b/src/Portenta_H7_AsyncWebHandlerImpl.h index b3ca023..d8dc1fd 100644 --- a/src/Portenta_H7_AsyncWebHandlerImpl.h +++ b/src/Portenta_H7_AsyncWebHandlerImpl.h @@ -9,7 +9,7 @@ Built by Khoi Hoang https://github.com/khoih-prog/Portenta_H7_AsyncWebServer Licensed under GPLv3 license - Version: 1.2.1 + Version: 1.3.0 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -18,6 +18,7 @@ 1.1.1 K Hoang 12/10/2021 Update `platform.ini` and `library.json` 1.2.0 K Hoang 07/12/2021 Fix crashing issue 1.2.1 K Hoang 12/01/2022 Fix authenticate issue caused by libb64 + 1.3.0 K Hoang 26/09/2022 Fix issue with slow browsers or network *****************************************************************************************************************************/ #pragma once @@ -34,6 +35,8 @@ #include "stddef.h" #include +///////////////////////////////////////////////// + class AsyncStaticWebHandler: public AsyncWebHandler { private: @@ -61,13 +64,18 @@ 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; + return *this; } }; +///////////////////////////////////////////////// + class AsyncCallbackWebHandler: public AsyncWebHandler { private: @@ -82,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) { _uri = uri; _isRegex = uri.startsWith("^") && uri.endsWith("$"); } + ///////////////////////////////////////////////// + void setMethod(WebRequestMethodComposite method) { _method = method; } + + ///////////////////////////////////////////////// + void onRequest(ArRequestHandlerFunction fn) { _onRequest = fn; } + ///////////////////////////////////////////////// + void onUpload(ArUploadHandlerFunction fn) { _onUpload = fn; } + ///////////////////////////////////////////////// + void onBody(ArBodyHandlerFunction fn) { _onBody = fn; } + ///////////////////////////////////////////////// + virtual bool canHandle(AsyncWebServerRequest *request) override final { if (!_onRequest) @@ -153,6 +174,8 @@ class AsyncCallbackWebHandler: public AsyncWebHandler return true; } + ///////////////////////////////////////////////// + virtual void handleRequest(AsyncWebServerRequest *request) override final { if (_onRequest) @@ -161,16 +184,22 @@ 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; } }; +///////////////////////////////////////////////// + #endif /* PORTENTA_H7_ASYNCWEBSERVERHANDLERIMPL_H_ */ diff --git a/src/Portenta_H7_AsyncWebHandlers.cpp b/src/Portenta_H7_AsyncWebHandlers.cpp index 87a0387..90fe060 100644 --- a/src/Portenta_H7_AsyncWebHandlers.cpp +++ b/src/Portenta_H7_AsyncWebHandlers.cpp @@ -9,7 +9,7 @@ Built by Khoi Hoang https://github.com/khoih-prog/Portenta_H7_AsyncWebServer Licensed under GPLv3 license - Version: 1.2.1 + Version: 1.3.0 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -18,6 +18,7 @@ 1.1.1 K Hoang 12/10/2021 Update `platform.ini` and `library.json` 1.2.0 K Hoang 07/12/2021 Fix crashing issue 1.2.1 K Hoang 12/01/2022 Fix authenticate issue caused by libb64 + 1.3.0 K Hoang 26/09/2022 Fix issue with slow browsers or network *****************************************************************************************************************************/ #if !defined(_PORTENTA_H7_AWS_LOGLEVEL_) @@ -29,6 +30,8 @@ #include "Portenta_H7_AsyncWebServer.h" #include "Portenta_H7_AsyncWebHandlerImpl.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 +59,17 @@ 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 +77,8 @@ AsyncStaticWebHandler& AsyncStaticWebHandler::setCacheControl(const char* cache_ return *this; } +///////////////////////////////////////////////// + AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(const char* last_modified) { _last_modified = String(last_modified); @@ -76,6 +86,8 @@ AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(const char* last_m return *this; } +///////////////////////////////////////////////// + AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(struct tm* last_modified) { char result[30]; @@ -85,12 +97,15 @@ AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(struct tm* last_mo return setLastModified((const char *)result); } -// For STM32 +///////////////////////////////////////////////// + AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(time_t last_modified) { return setLastModified((struct tm *)gmtime(&last_modified)); } +///////////////////////////////////////////////// + AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified() { time_t last_modified; @@ -101,6 +116,8 @@ AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified() return setLastModified(last_modified); } +///////////////////////////////////////////////// + bool AsyncStaticWebHandler::canHandle(AsyncWebServerRequest *request) { if (request->method() != HTTP_GET @@ -114,9 +131,12 @@ bool AsyncStaticWebHandler::canHandle(AsyncWebServerRequest *request) return false; } -// For STM32 +///////////////////////////////////////////////// + #define FILE_IS_REAL(f) (f == true) +///////////////////////////////////////////////// + uint8_t AsyncStaticWebHandler::_countBits(const uint8_t value) const { uint8_t w = value; @@ -127,3 +147,6 @@ uint8_t AsyncStaticWebHandler::_countBits(const uint8_t value) const return n; } + +///////////////////////////////////////////////// + diff --git a/src/Portenta_H7_AsyncWebRequest.cpp b/src/Portenta_H7_AsyncWebRequest.cpp index 6f5b7a7..0149d35 100644 --- a/src/Portenta_H7_AsyncWebRequest.cpp +++ b/src/Portenta_H7_AsyncWebRequest.cpp @@ -9,7 +9,7 @@ Built by Khoi Hoang https://github.com/khoih-prog/Portenta_H7_AsyncWebServer Licensed under GPLv3 license - Version: 1.2.1 + Version: 1.3.0 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -18,6 +18,7 @@ 1.1.1 K Hoang 12/10/2021 Update `platform.ini` and `library.json` 1.2.0 K Hoang 07/12/2021 Fix crashing issue 1.2.1 K Hoang 12/01/2022 Fix authenticate issue caused by libb64 + 1.3.0 K Hoang 26/09/2022 Fix issue with slow browsers or network *****************************************************************************************************************************/ #if !defined(_PORTENTA_H7_AWS_LOGLEVEL_) @@ -32,10 +33,14 @@ //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() @@ -59,6 +64,7 @@ AsyncWebServerRequest::AsyncWebServerRequest(AsyncWebServer* s, AsyncClient* c) c->onError([](void *r, AsyncClient * c, int8_t error) { PORTENTA_H7_AWS_UNUSED(c); + AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onError(error); }, this); @@ -66,6 +72,7 @@ AsyncWebServerRequest::AsyncWebServerRequest(AsyncWebServer* s, AsyncClient* c) c->onAck([](void *r, AsyncClient * c, size_t len, uint32_t time) { PORTENTA_H7_AWS_UNUSED(c); + AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onAck(len, time); }, this); @@ -80,6 +87,7 @@ AsyncWebServerRequest::AsyncWebServerRequest(AsyncWebServer* s, AsyncClient* c) c->onTimeout([](void *r, AsyncClient * c, uint32_t time) { PORTENTA_H7_AWS_UNUSED(c); + AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onTimeout(time); }, this); @@ -87,6 +95,7 @@ AsyncWebServerRequest::AsyncWebServerRequest(AsyncWebServer* s, AsyncClient* c) c->onData([](void *r, AsyncClient * c, void *buf, size_t len) { PORTENTA_H7_AWS_UNUSED(c); + AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onData(buf, len); }, this); @@ -94,11 +103,14 @@ AsyncWebServerRequest::AsyncWebServerRequest(AsyncWebServer* s, AsyncClient* c) c->onPoll([](void *r, AsyncClient * c) { PORTENTA_H7_AWS_UNUSED(c); + AsyncWebServerRequest *req = ( AsyncWebServerRequest*)r; req->_onPoll(); }, this); } +///////////////////////////////////////////////// + AsyncWebServerRequest::~AsyncWebServerRequest() { _headers.free(); @@ -119,6 +131,8 @@ AsyncWebServerRequest::~AsyncWebServerRequest() } } +///////////////////////////////////////////////// + void AsyncWebServerRequest::_onData(void *buf, size_t len) { size_t i = 0; @@ -143,6 +157,7 @@ void AsyncWebServerRequest::_onData(void *buf, size_t len) // No new line, just add the buffer in _temp char ch = str[len - 1]; str[len - 1] = 0; + _temp.reserve(_temp.length() + len); _temp.concat(str); _temp.concat(ch); @@ -151,6 +166,7 @@ void AsyncWebServerRequest::_onData(void *buf, size_t len) { // Found new line - extract it and parse str[i] = 0; // Terminate the string at the end of the line. + _temp.concat(str); _temp.trim(); _parseLine(); @@ -246,9 +262,12 @@ void AsyncWebServerRequest::_onData(void *buf, size_t len) } // KH, Important for Portenta Murata WiFi, or system will hang - delay(0); + //delay(0); + yield(); } +///////////////////////////////////////////////// + void AsyncWebServerRequest::_removeNotInterestingHeaders() { if (_interestingHeaders.containsIgnoreCase("ANY")) @@ -263,6 +282,8 @@ void AsyncWebServerRequest::_removeNotInterestingHeaders() } } +///////////////////////////////////////////////// + void AsyncWebServerRequest::_onPoll() { if (_response != NULL && _client != NULL && _client->canSend() && !_response->_finished()) @@ -271,9 +292,12 @@ void AsyncWebServerRequest::_onPoll() } // KH, Important for Portenta Murata WiFi, or system will hang - delay(0); + //delay(0); + yield(); } +///////////////////////////////////////////////// + void AsyncWebServerRequest::_onAck(size_t len, uint32_t time) { AWS_LOGDEBUG3("onAck: len =", len, ", time =", time); @@ -293,31 +317,41 @@ void AsyncWebServerRequest::_onAck(size_t len, uint32_t time) } // KH, Important for Portenta Murata WiFi, or system will hang - delay(0); + //delay(0); + yield(); } +///////////////////////////////////////////////// + void AsyncWebServerRequest::_onError(int8_t error) { PORTENTA_H7_AWS_UNUSED(error); } +///////////////////////////////////////////////// + void AsyncWebServerRequest::_onTimeout(uint32_t time) { PORTENTA_H7_AWS_UNUSED(time); - AWS_LOGDEBUG3("TIMEOUT: time =", time, ", state =", _client->stateToString()); + AWS_LOGINFO3("TIMEOUT: time =", time, ", state =", _client->stateToString()); _client->close(); // KH, Important for Portenta Murata WiFi, or system will hang - delay(0); + //delay(0); + yield(); } +///////////////////////////////////////////////// + void AsyncWebServerRequest::onDisconnect (ArDisconnectHandler fn) { _onDisconnectfn = fn; } +///////////////////////////////////////////////// + void AsyncWebServerRequest::_onDisconnect() { if (_onDisconnectfn) @@ -328,19 +362,26 @@ void AsyncWebServerRequest::_onDisconnect() _server->_handleDisconnect(this); // KH, Important for Portenta Murata WiFi, or system will hang - delay(0); + //delay(0); + yield(); } +///////////////////////////////////////////////// + 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; @@ -364,6 +405,8 @@ void AsyncWebServerRequest::_addGetParams(const String& params) } } +///////////////////////////////////////////////// + bool AsyncWebServerRequest::_parseReqHead() { // Split the head into method, url and version @@ -422,6 +465,8 @@ bool AsyncWebServerRequest::_parseReqHead() return true; } +///////////////////////////////////////////////// + bool strContains(String src, String find, bool mindcase = true) { int pos = 0, i = 0; @@ -453,6 +498,8 @@ bool strContains(String src, String find, bool mindcase = true) return false; } +///////////////////////////////////////////////// + bool AsyncWebServerRequest::_parseReqHeader() { int index = _temp.indexOf(':'); @@ -522,6 +569,8 @@ bool AsyncWebServerRequest::_parseReqHeader() return true; } +///////////////////////////////////////////////// + void AsyncWebServerRequest::_parsePlainPostChar(uint8_t data) { if (data && (char)data != '&') @@ -543,6 +592,8 @@ void AsyncWebServerRequest::_parsePlainPostChar(uint8_t data) } } +///////////////////////////////////////////////// + void AsyncWebServerRequest::_handleUploadByte(uint8_t data, bool last) { _itemBuffer[_itemBufferIndex++] = data; @@ -557,6 +608,8 @@ void AsyncWebServerRequest::_handleUploadByte(uint8_t data, bool last) } } +///////////////////////////////////////////////// + enum { EXPECT_BOUNDARY, @@ -572,6 +625,7 @@ enum PARSE_ERROR }; +///////////////////////////////////////////////// void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last) { @@ -602,16 +656,19 @@ void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last) if (_parsedLength < 2 && data != '-') { _multiParseState = PARSE_ERROR; + return; } else if (_parsedLength - 2 < _boundary.length() && _boundary.c_str()[_parsedLength - 2] != data) { _multiParseState = PARSE_ERROR; + return; } else if (_parsedLength - 2 == _boundary.length() && data != '\r') { _multiParseState = PARSE_ERROR; + return; } else if (_parsedLength - 3 == _boundary.length()) @@ -619,6 +676,7 @@ void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last) if (data != '\n') { _multiParseState = PARSE_ERROR; + return; } @@ -696,6 +754,7 @@ void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last) if (_itemBuffer == NULL) { _multiParseState = PARSE_ERROR; + return; } @@ -803,6 +862,7 @@ void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last) { AWS_LOGDEBUG1("ERROR: The parser got to the end of the POST but is expecting more bytes =", (_contentLength - _parsedLength - 4)); AWS_LOGDEBUG("Drop an issue so we can have more info on the matter!"); + _contentLength = _parsedLength + 4;//lets close the request gracefully } @@ -857,6 +917,8 @@ void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last) } } +///////////////////////////////////////////////// + void AsyncWebServerRequest::_parseLine() { if (_parseState == PARSE_REQ_START) @@ -911,12 +973,15 @@ void AsyncWebServerRequest::_parseLine() } } +///////////////////////////////////////////////// size_t AsyncWebServerRequest::headers() const { return _headers.length(); } +///////////////////////////////////////////////// + bool AsyncWebServerRequest::hasHeader(const String& name) const { for (const auto& h : _headers) @@ -930,6 +995,8 @@ bool AsyncWebServerRequest::hasHeader(const String& name) const return false; } +///////////////////////////////////////////////// + AsyncWebHeader* AsyncWebServerRequest::getHeader(const String& name) const { for (const auto& h : _headers) @@ -943,6 +1010,8 @@ AsyncWebHeader* AsyncWebServerRequest::getHeader(const String& name) const return nullptr; } +///////////////////////////////////////////////// + AsyncWebHeader* AsyncWebServerRequest::getHeader(size_t num) const { auto header = _headers.nth(num); @@ -950,11 +1019,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 { @@ -969,6 +1041,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) @@ -982,6 +1056,8 @@ AsyncWebParameter* AsyncWebServerRequest::getParam(const String& name, bool post return nullptr; } +///////////////////////////////////////////////// + AsyncWebParameter* AsyncWebServerRequest::getParam(size_t num) const { auto param = _params.nth(num); @@ -989,12 +1065,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; @@ -1021,21 +1101,29 @@ void AsyncWebServerRequest::send(AsyncWebServerResponse *response) } } +///////////////////////////////////////////////// + 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) { return new AsyncStreamResponse(stream, contentType, len, callback); } +///////////////////////////////////////////////// + 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) { if (_version) @@ -1044,31 +1132,43 @@ 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); } +///////////////////////////////////////////////// + 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) { send(beginResponse(stream, contentType, len, callback)); } +///////////////////////////////////////////////// + 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) { send(beginChunkedResponse(contentType, callback, templateCallback)); } +///////////////////////////////////////////////// + void AsyncWebServerRequest::redirect(const String& url) { AsyncWebServerResponse * response = beginResponse(302); @@ -1076,6 +1176,8 @@ void AsyncWebServerRequest::redirect(const String& url) send(response); } +///////////////////////////////////////////////// + bool AsyncWebServerRequest::authenticate(const char * username, const char * password, const char * realm, bool passwordIsHash) { AWS_LOGDEBUG1("AsyncWebServerRequest::authenticate: auth-len =", _authorization.length()); @@ -1107,6 +1209,8 @@ bool AsyncWebServerRequest::authenticate(const char * username, const char * pas return false; } +///////////////////////////////////////////////// + bool AsyncWebServerRequest::authenticate(const char * hash) { if (!_authorization.length() || hash == NULL) @@ -1136,6 +1240,8 @@ bool AsyncWebServerRequest::authenticate(const char * hash) return (_authorization.equals(hash)); } +///////////////////////////////////////////////// + void AsyncWebServerRequest::requestAuthentication(const char * realm, bool isDigest) { AsyncWebServerResponse * r = beginResponse(401); @@ -1161,6 +1267,8 @@ void AsyncWebServerRequest::requestAuthentication(const char * realm, bool isDig send(r); } +///////////////////////////////////////////////// + bool AsyncWebServerRequest::hasArg(const char* name) const { for (const auto& arg : _params) @@ -1174,6 +1282,8 @@ bool AsyncWebServerRequest::hasArg(const char* name) const return false; } +///////////////////////////////////////////////// + const String& AsyncWebServerRequest::arg(const String& name) const { for (const auto& arg : _params) @@ -1187,16 +1297,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); @@ -1204,6 +1320,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)); @@ -1211,6 +1329,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); @@ -1218,6 +1338,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); @@ -1225,6 +1347,8 @@ const String& AsyncWebServerRequest::headerName(size_t i) const return (h ? h->name() : SharedEmptyString); } +///////////////////////////////////////////////// + String AsyncWebServerRequest::urlDecode(const String& text) const { char temp[] = "0x00"; @@ -1259,6 +1383,7 @@ String AsyncWebServerRequest::urlDecode(const String& text) const return decoded; } +///////////////////////////////////////////////// const char * AsyncWebServerRequest::methodToString() const { @@ -1282,6 +1407,8 @@ const char * AsyncWebServerRequest::methodToString() const return "UNKNOWN"; } +///////////////////////////////////////////////// + const char *AsyncWebServerRequest::requestedConnTypeToString() const { switch (_reqconntype) @@ -1301,6 +1428,8 @@ const char *AsyncWebServerRequest::requestedConnTypeToString() const } } +///////////////////////////////////////////////// + bool AsyncWebServerRequest::isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2, RequestedConnectionType erct3) { bool res = false; @@ -1316,3 +1445,6 @@ bool AsyncWebServerRequest::isExpectedRequestedConnType(RequestedConnectionType return res; } + +///////////////////////////////////////////////// + diff --git a/src/Portenta_H7_AsyncWebResponseImpl.h b/src/Portenta_H7_AsyncWebResponseImpl.h index 32dd2b9..6e8e0e1 100644 --- a/src/Portenta_H7_AsyncWebResponseImpl.h +++ b/src/Portenta_H7_AsyncWebResponseImpl.h @@ -9,7 +9,7 @@ Built by Khoi Hoang https://github.com/khoih-prog/Portenta_H7_AsyncWebServer Licensed under GPLv3 license - Version: 1.2.1 + Version: 1.3.0 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -18,6 +18,7 @@ 1.1.1 K Hoang 12/10/2021 Update `platform.ini` and `library.json` 1.2.0 K Hoang 07/12/2021 Fix crashing issue 1.2.1 K Hoang 12/01/2022 Fix authenticate issue caused by libb64 + 1.3.0 K Hoang 26/09/2022 Fix issue with slow browsers or network *****************************************************************************************************************************/ #pragma once @@ -25,6 +26,8 @@ #ifndef PORTENTA_H7_ASYNCWEBSERVERRESPONSEIMPL_H_ #define PORTENTA_H7_ASYNCWEBSERVERRESPONSEIMPL_H_ +///////////////////////////////////////////////// + #ifdef Arduino_h // arduino is not compatible with std::vector #undef min @@ -34,6 +37,8 @@ #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: @@ -51,6 +56,8 @@ class AsyncBasicResponse: public AsyncWebServerResponse } }; +///////////////////////////////////////////////// + class AsyncAbstractResponse: public AsyncWebServerResponse { private: @@ -82,12 +89,16 @@ class AsyncAbstractResponse: public AsyncWebServerResponse } }; +///////////////////////////////////////////////// + #ifndef TEMPLATE_PLACEHOLDER #define TEMPLATE_PLACEHOLDER '%' #endif #define TEMPLATE_PARAM_NAME_LENGTH 32 +///////////////////////////////////////////////// + class AsyncStreamResponse: public AsyncAbstractResponse { private: @@ -104,6 +115,8 @@ class AsyncStreamResponse: public AsyncAbstractResponse virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override; }; +///////////////////////////////////////////////// + class AsyncCallbackResponse: public AsyncAbstractResponse { private: @@ -121,6 +134,8 @@ class AsyncCallbackResponse: public AsyncAbstractResponse virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override; }; +///////////////////////////////////////////////// + class AsyncChunkedResponse: public AsyncAbstractResponse { private: @@ -138,8 +153,12 @@ class AsyncChunkedResponse: public AsyncAbstractResponse virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override; }; +///////////////////////////////////////////////// + class cbuf; +///////////////////////////////////////////////// + class AsyncResponseStream: public AsyncAbstractResponse, public Print { private: @@ -160,4 +179,6 @@ class AsyncResponseStream: public AsyncAbstractResponse, public Print using Print::write; }; +///////////////////////////////////////////////// + #endif /* PORTENTA_H7_ASYNCWEBSERVERRESPONSEIMPL_H_ */ diff --git a/src/Portenta_H7_AsyncWebResponses.cpp b/src/Portenta_H7_AsyncWebResponses.cpp index f57d2f6..1a88d29 100644 --- a/src/Portenta_H7_AsyncWebResponses.cpp +++ b/src/Portenta_H7_AsyncWebResponses.cpp @@ -9,7 +9,7 @@ Built by Khoi Hoang https://github.com/khoih-prog/Portenta_H7_AsyncWebServer Licensed under GPLv3 license - Version: 1.2.1 + Version: 1.3.0 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -18,6 +18,7 @@ 1.1.1 K Hoang 12/10/2021 Update `platform.ini` and `library.json` 1.2.0 K Hoang 07/12/2021 Fix crashing issue 1.2.1 K Hoang 12/01/2022 Fix authenticate issue caused by libb64 + 1.3.0 K Hoang 26/09/2022 Fix issue with slow browsers or network *****************************************************************************************************************************/ #if !defined(_PORTENTA_H7_AWS_LOGLEVEL_) @@ -30,7 +31,8 @@ #include "Portenta_H7_AsyncWebResponseImpl.h" #include "cbuf.h" -// Since ESP8266 does not link memchr by default, here's its implementation. +///////////////////////////////////////////////// + void* memchr(void* ptr, int ch, size_t count) { unsigned char* p = static_cast(ptr); @@ -44,6 +46,8 @@ void* memchr(void* ptr, int ch, size_t count) return nullptr; } +///////////////////////////////////////////////// + /* Abstract Response * */ @@ -95,6 +99,8 @@ const char* AsyncWebServerResponse::_responseCodeToString(int code) } } +///////////////////////////////////////////////// + AsyncWebServerResponse::AsyncWebServerResponse() : _code(0) , _headers(LinkedList([](AsyncWebHeader * h) @@ -110,34 +116,46 @@ AsyncWebServerResponse::AsyncWebServerResponse() } } +///////////////////////////////////////////////// + AsyncWebServerResponse::~AsyncWebServerResponse() { _headers.free(); } +///////////////////////////////////////////////// + void AsyncWebServerResponse::setCode(int code) { if (_state == RESPONSE_SETUP) _code = code; } +///////////////////////////////////////////////// + void AsyncWebServerResponse::setContentLength(size_t len) { if (_state == RESPONSE_SETUP) _contentLength = len; } +///////////////////////////////////////////////// + void AsyncWebServerResponse::setContentType(const String& type) { if (_state == RESPONSE_SETUP) _contentType = type; } +///////////////////////////////////////////////// + void AsyncWebServerResponse::addHeader(const String& name, const String& value) { _headers.add(new AsyncWebHeader(name, value)); } +///////////////////////////////////////////////// + String AsyncWebServerResponse::_assembleHead(uint8_t version) { if (version) @@ -181,26 +199,36 @@ String AsyncWebServerResponse::_assembleHead(uint8_t version) return out; } +///////////////////////////////////////////////// + bool AsyncWebServerResponse::_started() const { return _state > RESPONSE_SETUP; } +///////////////////////////////////////////////// + bool AsyncWebServerResponse::_finished() const { return _state > RESPONSE_WAIT_ACK; } +///////////////////////////////////////////////// + bool AsyncWebServerResponse::_failed() const { return _state == RESPONSE_FAILED; } +///////////////////////////////////////////////// + bool AsyncWebServerResponse::_sourceValid() const { return false; } +///////////////////////////////////////////////// + void AsyncWebServerResponse::_respond(AsyncWebServerRequest *request) { _state = RESPONSE_END; @@ -208,14 +236,19 @@ void AsyncWebServerResponse::_respond(AsyncWebServerRequest *request) request->client()->close(); } +///////////////////////////////////////////////// + size_t AsyncWebServerResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time) { PORTENTA_H7_AWS_UNUSED(request); PORTENTA_H7_AWS_UNUSED(len); PORTENTA_H7_AWS_UNUSED(time); + return 0; } +///////////////////////////////////////////////// + /* String/Code Response * */ @@ -236,6 +269,8 @@ AsyncBasicResponse::AsyncBasicResponse(int code, const String& contentType, cons addHeader("Connection", "close"); } +///////////////////////////////////////////////// + void AsyncBasicResponse::_respond(AsyncWebServerRequest *request) { _state = RESPONSE_HEADERS; @@ -281,9 +316,12 @@ void AsyncBasicResponse::_respond(AsyncWebServerRequest *request) } } +///////////////////////////////////////////////// + size_t AsyncBasicResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time) { PORTENTA_H7_AWS_UNUSED(time); + _ackedLength += len; if (_state == RESPONSE_CONTENT) @@ -320,6 +358,7 @@ size_t AsyncBasicResponse::_ack(AsyncWebServerRequest *request, size_t len, uint return 0; } +///////////////////////////////////////////////// /* Abstract Response @@ -336,6 +375,8 @@ AsyncAbstractResponse::AsyncAbstractResponse(AwsTemplateProcessor callback): _ca } } +///////////////////////////////////////////////// + void AsyncAbstractResponse::_respond(AsyncWebServerRequest *request) { addHeader("Connection", "close"); @@ -344,6 +385,8 @@ void AsyncAbstractResponse::_respond(AsyncWebServerRequest *request) _ack(request, 0, 0); } +///////////////////////////////////////////////// + size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time) { PORTENTA_H7_AWS_UNUSED(time); @@ -494,6 +537,8 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest *request, size_t len, u return 0; } +///////////////////////////////////////////////// + size_t AsyncAbstractResponse::_readDataFromCacheOrContent(uint8_t* data, const size_t len) { // If we have something in cache, copy it to buffer @@ -512,6 +557,8 @@ size_t AsyncAbstractResponse::_readDataFromCacheOrContent(uint8_t* data, const s return readFromCache + readFromContent; } +///////////////////////////////////////////////// + size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size_t len) { if (!_callback) @@ -643,6 +690,8 @@ size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size return len; } +///////////////////////////////////////////////// + /* Stream Response * */ @@ -655,6 +704,8 @@ AsyncStreamResponse::AsyncStreamResponse(Stream &stream, const String& contentTy _contentType = contentType; } +///////////////////////////////////////////////// + size_t AsyncStreamResponse::_fillBuffer(uint8_t *data, size_t len) { size_t available = _content->available(); @@ -667,6 +718,8 @@ size_t AsyncStreamResponse::_fillBuffer(uint8_t *data, size_t len) return outLen; } +///////////////////////////////////////////////// + /* Callback Response * */ @@ -685,6 +738,8 @@ AsyncCallbackResponse::AsyncCallbackResponse(const String& contentType, size_t l _filledLength = 0; } +///////////////////////////////////////////////// + size_t AsyncCallbackResponse::_fillBuffer(uint8_t *data, size_t len) { size_t ret = _content(data, len, _filledLength); @@ -697,6 +752,8 @@ size_t AsyncCallbackResponse::_fillBuffer(uint8_t *data, size_t len) return ret; } +///////////////////////////////////////////////// + /* Chunked Response * */ @@ -712,6 +769,8 @@ AsyncChunkedResponse::AsyncChunkedResponse(const String& contentType, AwsRespons _filledLength = 0; } +///////////////////////////////////////////////// + size_t AsyncChunkedResponse::_fillBuffer(uint8_t *data, size_t len) { size_t ret = _content(data, len, _filledLength); @@ -724,6 +783,8 @@ size_t AsyncChunkedResponse::_fillBuffer(uint8_t *data, size_t len) return ret; } +///////////////////////////////////////////////// + /* Response Stream (You can print/write/printf to it, up to the contentLen bytes) * */ @@ -736,16 +797,22 @@ AsyncResponseStream::AsyncResponseStream(const String& contentType, size_t buffe _content = new cbuf(bufferSize); } +///////////////////////////////////////////////// + AsyncResponseStream::~AsyncResponseStream() { delete _content; } +///////////////////////////////////////////////// + size_t AsyncResponseStream::_fillBuffer(uint8_t *buf, size_t maxLen) { return _content->read((char*)buf, maxLen); } +///////////////////////////////////////////////// + size_t AsyncResponseStream::write(const uint8_t *data, size_t len) { if (_started()) @@ -763,7 +830,12 @@ size_t AsyncResponseStream::write(const uint8_t *data, size_t len) return written; } +///////////////////////////////////////////////// + size_t AsyncResponseStream::write(uint8_t data) { return write(&data, 1); } + +///////////////////////////////////////////////// + diff --git a/src/Portenta_H7_AsyncWebServer.cpp b/src/Portenta_H7_AsyncWebServer.cpp index 95d9225..d245068 100644 --- a/src/Portenta_H7_AsyncWebServer.cpp +++ b/src/Portenta_H7_AsyncWebServer.cpp @@ -9,7 +9,7 @@ Built by Khoi Hoang https://github.com/khoih-prog/Portenta_H7_AsyncWebServer Licensed under GPLv3 license - Version: 1.2.1 + Version: 1.3.0 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -18,6 +18,7 @@ 1.1.1 K Hoang 12/10/2021 Update `platform.ini` and `library.json` 1.2.0 K Hoang 07/12/2021 Fix crashing issue 1.2.1 K Hoang 12/01/2022 Fix authenticate issue caused by libb64 + 1.3.0 K Hoang 26/09/2022 Fix issue with slow browsers or network *****************************************************************************************************************************/ #if !defined(_PORTENTA_H7_AWS_LOGLEVEL_) @@ -29,6 +30,8 @@ #include "Portenta_H7_AsyncWebServer.h" #include "Portenta_H7_AsyncWebHandlerImpl.h" +///////////////////////////////////////////////// + AsyncWebServer::AsyncWebServer(uint16_t port) : _server(port), _rewrites(LinkedList([](AsyncWebRewrite * r) { @@ -49,7 +52,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 +68,8 @@ AsyncWebServer::AsyncWebServer(uint16_t port) }, this); } +///////////////////////////////////////////////// + AsyncWebServer::~AsyncWebServer() { reset(); @@ -70,6 +79,8 @@ AsyncWebServer::~AsyncWebServer() delete _catchAllHandler; } +///////////////////////////////////////////////// + AsyncWebRewrite& AsyncWebServer::addRewrite(AsyncWebRewrite* rewrite) { _rewrites.add(rewrite); @@ -77,55 +88,77 @@ 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 +171,8 @@ void AsyncWebServer::_rewriteRequest(AsyncWebServerRequest *request) } } +///////////////////////////////////////////////// + void AsyncWebServer::_attachHandler(AsyncWebServerRequest *request) { for (const auto& h : _handlers) @@ -145,17 +180,17 @@ void AsyncWebServer::_attachHandler(AsyncWebServerRequest *request) if (h->filter(request) && h->canHandle(request)) { request->setHandler(h); + return; } } request->addInterestingHeader("ANY"); - // KH, fix crashing issue from v1.2.0, for Arduino-mbed core v2.6.1 - //request->setHandler(_catchAllHandler); request->setHandler(NULL); } +///////////////////////////////////////////////// AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload, ArBodyHandlerFunction onBody) @@ -172,6 +207,8 @@ AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodCom return *handler; } +///////////////////////////////////////////////// + AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload) { AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler(); @@ -184,6 +221,8 @@ AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodCom return *handler; } +///////////////////////////////////////////////// + AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest) { AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler(); @@ -195,6 +234,8 @@ AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodCom return *handler; } +///////////////////////////////////////////////// + AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, ArRequestHandlerFunction onRequest) { AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler(); @@ -205,16 +246,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(); @@ -228,4 +275,6 @@ void AsyncWebServer::reset() } } +///////////////////////////////////////////////// + diff --git a/src/Portenta_H7_AsyncWebServer.h b/src/Portenta_H7_AsyncWebServer.h index d1fd22a..c4fc2c4 100644 --- a/src/Portenta_H7_AsyncWebServer.h +++ b/src/Portenta_H7_AsyncWebServer.h @@ -9,7 +9,7 @@ Built by Khoi Hoang https://github.com/khoih-prog/Portenta_H7_AsyncWebServer Licensed under GPLv3 license - Version: 1.2.1 + Version: 1.3.0 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -18,6 +18,7 @@ 1.1.1 K Hoang 12/10/2021 Update `platform.ini` and `library.json` 1.2.0 K Hoang 07/12/2021 Fix crashing issue 1.2.1 K Hoang 12/01/2022 Fix authenticate issue caused by libb64 + 1.3.0 K Hoang 26/09/2022 Fix issue with slow browsers or network *****************************************************************************************************************************/ #ifndef _PORTENTA_H7_ASYNC_WEBSERVER_H_ @@ -32,13 +33,13 @@ #endif -#define PORTENTA_H7_ASYNC_WEBSERVER_VERSION "Portenta_H7_AsyncWebServer v1.2.1" +#define PORTENTA_H7_ASYNC_WEBSERVER_VERSION "Portenta_H7_AsyncWebServer v1.3.0" #define PORTENTA_H7_ASYNC_WEBSERVER_VERSION_MAJOR 1 -#define PORTENTA_H7_ASYNC_WEBSERVER_VERSION_MINOR 2 -#define PORTENTA_H7_ASYNC_WEBSERVER_VERSION_PATCH 1 +#define PORTENTA_H7_ASYNC_WEBSERVER_VERSION_MINOR 3 +#define PORTENTA_H7_ASYNC_WEBSERVER_VERSION_PATCH 0 -#define PORTENTA_H7_ASYNC_WEBSERVER_VERSION_INT 1002001 +#define PORTENTA_H7_ASYNC_WEBSERVER_VERSION_INT 1003000 #ifndef PORTENTA_H7_AWS_UNUSED #define PORTENTA_H7_AWS_UNUSED(x) (void)(x) diff --git a/src/Portenta_H7_AsyncWebServer_Debug.h b/src/Portenta_H7_AsyncWebServer_Debug.h index da7031f..74a125c 100644 --- a/src/Portenta_H7_AsyncWebServer_Debug.h +++ b/src/Portenta_H7_AsyncWebServer_Debug.h @@ -9,7 +9,7 @@ Built by Khoi Hoang https://github.com/khoih-prog/Portenta_H7_AsyncWebServer Licensed under GPLv3 license - Version: 1.2.1 + Version: 1.3.0 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -18,6 +18,7 @@ 1.1.1 K Hoang 12/10/2021 Update `platform.ini` and `library.json` 1.2.0 K Hoang 07/12/2021 Fix crashing issue 1.2.1 K Hoang 12/01/2022 Fix authenticate issue caused by libb64 + 1.3.0 K Hoang 26/09/2022 Fix issue with slow browsers or network *****************************************************************************************************************************/ #pragma once @@ -25,12 +26,16 @@ #ifndef PORTENTA_H7_ASYNC_WEBSERVER_DEBUG_H #define PORTENTA_H7_ASYNC_WEBSERVER_DEBUG_H +///////////////////////////////////////////////// + #ifdef PORTENTA_H7_ASYNCWEBSERVER_DEBUG_PORT #define DBG_PORT_AWS PORTENTA_H7_ASYNCWEBSERVER_DEBUG_PORT #else #define DBG_PORT_AWS Serial #endif +///////////////////////////////////////////////// + // Change _PORTENTA_H7_AWS_LOGLEVEL_ to set tracing and logging verbosity // 0: DISABLED: no logging // 1: ERROR: errors @@ -58,18 +63,24 @@ #define AWS_LOGERROR2(x,y,z) if(_PORTENTA_H7_AWS_LOGLEVEL_>0) { AWS_PRINT_MARK; AWS_PRINT(x); AWS_PRINT_SP; AWS_PRINT(y); AWS_PRINT_SP; AWS_PRINTLN(z); } #define AWS_LOGERROR3(x,y,z,w) if(_PORTENTA_H7_AWS_LOGLEVEL_>0) { AWS_PRINT_MARK; AWS_PRINT(x); AWS_PRINT_SP; AWS_PRINT(y); AWS_PRINT_SP; AWS_PRINT(z); AWS_PRINT_SP; AWS_PRINTLN(w); } +///////////////////////////////////////////////// + #define AWS_LOGWARN(x) if(_PORTENTA_H7_AWS_LOGLEVEL_>1) { AWS_PRINT_MARK; AWS_PRINTLN(x); } #define AWS_LOGWARN0(x) if(_PORTENTA_H7_AWS_LOGLEVEL_>1) { AWS_PRINT(x); } #define AWS_LOGWARN1(x,y) if(_PORTENTA_H7_AWS_LOGLEVEL_>1) { AWS_PRINT_MARK; AWS_PRINT(x); AWS_PRINT_SP; AWS_PRINTLN(y); } #define AWS_LOGWARN2(x,y,z) if(_PORTENTA_H7_AWS_LOGLEVEL_>1) { AWS_PRINT_MARK; AWS_PRINT(x); AWS_PRINT_SP; AWS_PRINT(y); AWS_PRINT_SP; AWS_PRINTLN(z); } #define AWS_LOGWARN3(x,y,z,w) if(_PORTENTA_H7_AWS_LOGLEVEL_>1) { AWS_PRINT_MARK; AWS_PRINT(x); AWS_PRINT_SP; AWS_PRINT(y); AWS_PRINT_SP; AWS_PRINT(z); AWS_PRINT_SP; AWS_PRINTLN(w); } +///////////////////////////////////////////////// + #define AWS_LOGINFO(x) if(_PORTENTA_H7_AWS_LOGLEVEL_>2) { AWS_PRINT_MARK; AWS_PRINTLN(x); } #define AWS_LOGINFO0(x) if(_PORTENTA_H7_AWS_LOGLEVEL_>2) { AWS_PRINT(x); } #define AWS_LOGINFO1(x,y) if(_PORTENTA_H7_AWS_LOGLEVEL_>2) { AWS_PRINT_MARK; AWS_PRINT(x); AWS_PRINT_SP; AWS_PRINTLN(y); } #define AWS_LOGINFO2(x,y,z) if(_PORTENTA_H7_AWS_LOGLEVEL_>2) { AWS_PRINT_MARK; AWS_PRINT(x); AWS_PRINT_SP; AWS_PRINT(y); AWS_PRINT_SP; AWS_PRINTLN(z); } #define AWS_LOGINFO3(x,y,z,w) if(_PORTENTA_H7_AWS_LOGLEVEL_>2) { AWS_PRINT_MARK; AWS_PRINT(x); AWS_PRINT_SP; AWS_PRINT(y); AWS_PRINT_SP; AWS_PRINT(z); AWS_PRINT_SP; AWS_PRINTLN(w); } +///////////////////////////////////////////////// + #define AWS_LOGDEBUG(x) if(_PORTENTA_H7_AWS_LOGLEVEL_>3) { AWS_PRINT_MARK; AWS_PRINTLN(x); } #define AWS_LOGDEBUG0(x) if(_PORTENTA_H7_AWS_LOGLEVEL_>3) { AWS_PRINT(x); } #define AWS_LOGDEBUG1(x,y) if(_PORTENTA_H7_AWS_LOGLEVEL_>3) { AWS_PRINT_MARK; AWS_PRINT(x); AWS_PRINT_SP; AWS_PRINTLN(y); } @@ -77,4 +88,6 @@ #define AWS_LOGDEBUG3(x,y,z,w) if(_PORTENTA_H7_AWS_LOGLEVEL_>3) { AWS_PRINT_MARK; AWS_PRINT(x); AWS_PRINT_SP; AWS_PRINT(y); AWS_PRINT_SP; AWS_PRINT(z); AWS_PRINT_SP; AWS_PRINTLN(w); } #define AWS_LOGDEBUG5(x,y,z,w,xx,yy) if(_PORTENTA_H7_AWS_LOGLEVEL_>3) { AWS_PRINT_MARK; AWS_PRINT(x); AWS_PRINT_SP; AWS_PRINT(y); AWS_PRINT_SP; AWS_PRINT(z); AWS_PRINT_SP; AWS_PRINT(w); AWS_PRINT_SP; AWS_PRINT(xx); AWS_PRINT_SP; AWS_PRINTLN(yy);} +///////////////////////////////////////////////// + #endif //PORTENTA_H7_ASYNC_WEBSERVER_DEBUG_H diff --git a/src/Portenta_H7_AsyncWebSocket.cpp b/src/Portenta_H7_AsyncWebSocket.cpp index 18a86df..2f3943f 100644 --- a/src/Portenta_H7_AsyncWebSocket.cpp +++ b/src/Portenta_H7_AsyncWebSocket.cpp @@ -9,7 +9,7 @@ Built by Khoi Hoang https://github.com/khoih-prog/Portenta_H7_AsyncWebServer Licensed under GPLv3 license - Version: 1.2.1 + Version: 1.3.0 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -18,6 +18,7 @@ 1.1.1 K Hoang 12/10/2021 Update `platform.ini` and `library.json` 1.2.0 K Hoang 07/12/2021 Fix crashing issue 1.2.1 K Hoang 12/01/2022 Fix authenticate issue caused by libb64 + 1.3.0 K Hoang 26/09/2022 Fix issue with slow browsers or network *****************************************************************************************************************************/ #include "Arduino.h" @@ -38,6 +39,8 @@ #define MAX_PRINTF_LEN 64 +///////////////////////////////////////////////// + size_t webSocketSendFrameWindow(AsyncClient *client) { if (!client->canSend()) @@ -51,6 +54,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()) @@ -118,6 +123,7 @@ size_t webSocketSendFrame(AsyncClient *client, bool final, uint8_t opcode, bool AWS_LOGDEBUG1("Error adding header, bytes =", headLen); free(buf); + return 0; } @@ -144,25 +150,26 @@ size_t webSocketSendFrame(AsyncClient *client, bool final, uint8_t opcode, bool if (!client->send()) { AWS_LOGDEBUG1("Error sending frame: bytes =", headLen + len); + return 0; } return len; } +///////////////////////////////////////////////// /* AsyncWebSocketMessageBuffer */ - - AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer() : _data(nullptr), _len(0), _lock(false), _count(0) { - } +///////////////////////////////////////////////// + AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(uint8_t * data, size_t size) : _data(nullptr), _len(size), _lock(false), _count(0) { @@ -180,6 +187,7 @@ AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(uint8_t * data, size_t } } +///////////////////////////////////////////////// AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(size_t size) : _data(nullptr), _len(size), _lock(false), _count(0) @@ -190,9 +198,10 @@ AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(size_t size) { _data[_len] = 0; } - } +///////////////////////////////////////////////// + AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(const AsyncWebSocketMessageBuffer & copy) : _data(nullptr), _len(0), _lock(false), _count(0) { @@ -211,9 +220,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) { @@ -226,9 +236,10 @@ AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(AsyncWebSocketMessageBu _data = copy._data; copy._data = nullptr; } - } +///////////////////////////////////////////////// + AsyncWebSocketMessageBuffer::~AsyncWebSocketMessageBuffer() { if (_data) @@ -237,6 +248,8 @@ AsyncWebSocketMessageBuffer::~AsyncWebSocketMessageBuffer() } } +///////////////////////////////////////////////// + bool AsyncWebSocketMessageBuffer::reserve(size_t size) { _len = size; @@ -259,9 +272,10 @@ bool AsyncWebSocketMessageBuffer::reserve(size_t size) { return false; } - } +///////////////////////////////////////////////// + /* Control Frame */ @@ -326,6 +340,8 @@ class AsyncWebSocketControl } }; +///////////////////////////////////////////////// + /* Basic Buffered Message */ @@ -349,6 +365,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) { @@ -356,6 +374,7 @@ AsyncWebSocketBasicMessage::AsyncWebSocketBasicMessage(uint8_t opcode, bool mask _mask = mask; } +///////////////////////////////////////////////// AsyncWebSocketBasicMessage::~AsyncWebSocketBasicMessage() { @@ -363,6 +382,8 @@ AsyncWebSocketBasicMessage::~AsyncWebSocketBasicMessage() free(_data); } +///////////////////////////////////////////////// + void AsyncWebSocketBasicMessage::ack(size_t len, uint32_t time) { PORTENTA_H7_AWS_UNUSED(time); @@ -374,6 +395,8 @@ void AsyncWebSocketBasicMessage::ack(size_t len, uint32_t time) } } +///////////////////////////////////////////////// + size_t AsyncWebSocketBasicMessage::send(AsyncClient *client) { if (_status != WS_MSG_SENDING) @@ -426,6 +449,8 @@ size_t AsyncWebSocketBasicMessage::send(AsyncClient *client) return sent; } +///////////////////////////////////////////////// + bool AsyncWebSocketBasicMessage::reserve(size_t size) { if (size) @@ -444,6 +469,8 @@ bool AsyncWebSocketBasicMessage::reserve(size_t size) return false; } +///////////////////////////////////////////////// + /* AsyncWebSocketMultiMessage Message */ @@ -471,6 +498,7 @@ AsyncWebSocketMultiMessage::AsyncWebSocketMultiMessage(AsyncWebSocketMessageBuff } +///////////////////////////////////////////////// AsyncWebSocketMultiMessage::~AsyncWebSocketMultiMessage() { @@ -480,6 +508,8 @@ AsyncWebSocketMultiMessage::~AsyncWebSocketMultiMessage() } } +///////////////////////////////////////////////// + void AsyncWebSocketMultiMessage::ack(size_t len, uint32_t time) { PORTENTA_H7_AWS_UNUSED(time); @@ -493,6 +523,8 @@ void AsyncWebSocketMultiMessage::ack(size_t len, uint32_t time) AWS_LOGDEBUG1("ACK:", _len); } +///////////////////////////////////////////////// + size_t AsyncWebSocketMultiMessage::send(AsyncClient *client) { if (_status != WS_MSG_SENDING) @@ -506,6 +538,7 @@ size_t AsyncWebSocketMultiMessage::send(AsyncClient *client) if (_sent == _len) { _status = WS_MSG_SENT; + return 0; } @@ -548,6 +581,7 @@ size_t AsyncWebSocketMultiMessage::send(AsyncClient *client) return sent; } +///////////////////////////////////////////////// /* Async WebSocket Client @@ -577,12 +611,14 @@ AsyncWebSocketClient::AsyncWebSocketClient(AsyncWebServerRequest *request, Async _client->onError([](void *r, AsyncClient * c, int8_t error) { PORTENTA_H7_AWS_UNUSED(c); + ((AsyncWebSocketClient*)(r))->_onError(error); }, this); _client->onAck([](void *r, AsyncClient * c, size_t len, uint32_t time) { PORTENTA_H7_AWS_UNUSED(c); + ((AsyncWebSocketClient*)(r))->_onAck(len, time); }, this); @@ -595,18 +631,21 @@ AsyncWebSocketClient::AsyncWebSocketClient(AsyncWebServerRequest *request, Async _client->onTimeout([](void *r, AsyncClient * c, uint32_t time) { PORTENTA_H7_AWS_UNUSED(c); + ((AsyncWebSocketClient*)(r))->_onTimeout(time); }, this); _client->onData([](void *r, AsyncClient * c, void *buf, size_t len) { PORTENTA_H7_AWS_UNUSED(c); + ((AsyncWebSocketClient*)(r))->_onData(buf, len); }, this); _client->onPoll([](void *r, AsyncClient * c) { PORTENTA_H7_AWS_UNUSED(c); + ((AsyncWebSocketClient*)(r))->_onPoll(); }, this); @@ -616,6 +655,8 @@ AsyncWebSocketClient::AsyncWebSocketClient(AsyncWebServerRequest *request, Async delete request; } +///////////////////////////////////////////////// + AsyncWebSocketClient::~AsyncWebSocketClient() { _messageQueue.free(); @@ -623,6 +664,8 @@ AsyncWebSocketClient::~AsyncWebSocketClient() _server->_handleEvent(this, WS_EVT_DISCONNECT, NULL, NULL, 0); } +///////////////////////////////////////////////// + void AsyncWebSocketClient::_onAck(size_t len, uint32_t time) { _lastMessageTime = millis(); @@ -656,6 +699,8 @@ void AsyncWebSocketClient::_onAck(size_t len, uint32_t time) _runQueue(); } +///////////////////////////////////////////////// + void AsyncWebSocketClient::_onPoll() { if (_client->canSend() && (!_controlQueue.isEmpty() || !_messageQueue.isEmpty())) { @@ -667,6 +712,8 @@ void AsyncWebSocketClient::_onPoll() } } +///////////////////////////////////////////////// + void AsyncWebSocketClient::_runQueue() { while (!_messageQueue.isEmpty() && _messageQueue.front()->finished()) @@ -685,6 +732,8 @@ void AsyncWebSocketClient::_runQueue() } } +///////////////////////////////////////////////// + bool AsyncWebSocketClient::queueIsFull() { if ((_messageQueue.length() >= WS_MAX_QUEUED_MESSAGES) || (_status != WS_CONNECTED) ) @@ -693,6 +742,8 @@ bool AsyncWebSocketClient::queueIsFull() return false; } +///////////////////////////////////////////////// + void AsyncWebSocketClient::_queueMessage(AsyncWebSocketMessage *dataMessage) { if (dataMessage == NULL) @@ -718,6 +769,8 @@ void AsyncWebSocketClient::_queueMessage(AsyncWebSocketMessage *dataMessage) _runQueue(); } +///////////////////////////////////////////////// + void AsyncWebSocketClient::_queueControl(AsyncWebSocketControl *controlMessage) { if (controlMessage == NULL) @@ -729,6 +782,8 @@ void AsyncWebSocketClient::_queueControl(AsyncWebSocketControl *controlMessage) _runQueue(); } +///////////////////////////////////////////////// + void AsyncWebSocketClient::close(uint16_t code, const char * message) { if (_status != WS_CONNECTED) @@ -770,26 +825,36 @@ 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) { PORTENTA_H7_AWS_UNUSED(time); _client->close(true); } +///////////////////////////////////////////////// + void AsyncWebSocketClient::_onDisconnect() { _client = NULL; _server->_handleDisconnect(this); } +///////////////////////////////////////////////// + void AsyncWebSocketClient::_onData(void *pbuf, size_t plen) { _lastMessageTime = millis(); @@ -918,6 +983,8 @@ void AsyncWebSocketClient::_onData(void *pbuf, size_t plen) } } +///////////////////////////////////////////////// + size_t AsyncWebSocketClient::printf(const char *format, ...) { va_list arg; @@ -927,6 +994,7 @@ size_t AsyncWebSocketClient::printf(const char *format, ...) if (!temp) { va_end(arg); + return 0; } @@ -941,6 +1009,7 @@ size_t AsyncWebSocketClient::printf(const char *format, ...) if (!buffer) { delete[] temp; + return 0; } @@ -961,77 +1030,104 @@ size_t AsyncWebSocketClient::printf(const char *format, ...) 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) { - //return IPAddress(0U); return IPAddress(0, 0, 0, 0); } return _client->remoteIP(); } +///////////////////////////////////////////////// + uint16_t AsyncWebSocketClient::remotePort() { if (!_client) @@ -1042,6 +1138,8 @@ uint16_t AsyncWebSocketClient::remotePort() return _client->remotePort(); } +///////////////////////////////////////////////// + /* Async Web Socket - Each separate socket location */ @@ -1060,8 +1158,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) @@ -1070,11 +1172,15 @@ void AsyncWebSocket::_handleEvent(AsyncWebSocketClient * client, AwsEventType ty } } +///////////////////////////////////////////////// + void AsyncWebSocket::_addClient(AsyncWebSocketClient * client) { _clients.add(client); } +///////////////////////////////////////////////// + void AsyncWebSocket::_handleDisconnect(AsyncWebSocketClient * client) { @@ -1084,6 +1190,8 @@ void AsyncWebSocket::_handleDisconnect(AsyncWebSocketClient * client) }); } +///////////////////////////////////////////////// + bool AsyncWebSocket::availableForWriteAll() { for (const auto& c : _clients) @@ -1095,6 +1203,8 @@ bool AsyncWebSocket::availableForWriteAll() return true; } +///////////////////////////////////////////////// + bool AsyncWebSocket::availableForWrite(uint32_t id) { for (const auto& c : _clients) @@ -1106,6 +1216,8 @@ bool AsyncWebSocket::availableForWrite(uint32_t id) return true; } +///////////////////////////////////////////////// + size_t AsyncWebSocket::count() const { return _clients.count_if([](AsyncWebSocketClient * c) @@ -1114,6 +1226,8 @@ size_t AsyncWebSocket::count() const }); } +///////////////////////////////////////////////// + AsyncWebSocketClient * AsyncWebSocket::client(uint32_t id) { for (const auto &c : _clients) @@ -1127,6 +1241,7 @@ AsyncWebSocketClient * AsyncWebSocket::client(uint32_t id) return nullptr; } +///////////////////////////////////////////////// void AsyncWebSocket::close(uint32_t id, uint16_t code, const char * message) { @@ -1136,6 +1251,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) @@ -1145,6 +1262,8 @@ void AsyncWebSocket::closeAll(uint16_t code, const char * message) } } +///////////////////////////////////////////////// + void AsyncWebSocket::cleanupClients(uint16_t maxClients) { if (count() > maxClients) @@ -1153,6 +1272,8 @@ void AsyncWebSocket::cleanupClients(uint16_t maxClients) } } +///////////////////////////////////////////////// + void AsyncWebSocket::ping(uint32_t id, uint8_t *data, size_t len) { AsyncWebSocketClient * c = client(id); @@ -1161,6 +1282,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) @@ -1170,6 +1293,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); @@ -1178,6 +1303,8 @@ void AsyncWebSocket::text(uint32_t id, const char * message, size_t len) c->text(message, len); } +///////////////////////////////////////////////// + void AsyncWebSocket::textAll(AsyncWebSocketMessageBuffer * buffer) { if (!buffer) @@ -1197,6 +1324,7 @@ void AsyncWebSocket::textAll(AsyncWebSocketMessageBuffer * buffer) _cleanBuffers(); } +///////////////////////////////////////////////// void AsyncWebSocket::textAll(const char * message, size_t len) { @@ -1204,6 +1332,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); @@ -1212,12 +1342,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) @@ -1235,6 +1369,8 @@ void AsyncWebSocket::binaryAll(AsyncWebSocketMessageBuffer * buffer) _cleanBuffers(); } +///////////////////////////////////////////////// + void AsyncWebSocket::message(uint32_t id, AsyncWebSocketMessage * message) { AsyncWebSocketClient * c = client(id); @@ -1243,6 +1379,8 @@ void AsyncWebSocket::message(uint32_t id, AsyncWebSocketMessage * message) c->message(message); } +///////////////////////////////////////////////// + void AsyncWebSocket::messageAll(AsyncWebSocketMultiMessage * message) { for (const auto& c : _clients) @@ -1254,6 +1392,8 @@ void AsyncWebSocket::messageAll(AsyncWebSocketMultiMessage * message) _cleanBuffers(); } +///////////////////////////////////////////////// + size_t AsyncWebSocket::printf(uint32_t id, const char *format, ...) { AsyncWebSocketClient * c = client(id); @@ -1270,6 +1410,8 @@ size_t AsyncWebSocket::printf(uint32_t id, const char *format, ...) return 0; } +///////////////////////////////////////////////// + size_t AsyncWebSocket::printfAll(const char *format, ...) { va_list arg; @@ -1297,64 +1439,89 @@ 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()); @@ -1365,21 +1532,29 @@ 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"; @@ -1389,6 +1564,8 @@ const char * WS_STR_PROTOCOL = "Sec-WebSocket-Protocol"; const char * WS_STR_ACCEPT = "Sec-WebSocket-Accept"; const char * WS_STR_UUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; +///////////////////////////////////////////////// + bool AsyncWebSocket::canHandle(AsyncWebServerRequest * request) { if (!_enabled) @@ -1407,6 +1584,8 @@ bool AsyncWebSocket::canHandle(AsyncWebServerRequest * request) return true; } +///////////////////////////////////////////////// + void AsyncWebSocket::handleRequest(AsyncWebServerRequest * request) { if (!request->hasHeader(WS_STR_VERSION) || !request->hasHeader(WS_STR_KEY)) @@ -1445,6 +1624,8 @@ void AsyncWebSocket::handleRequest(AsyncWebServerRequest * request) request->send(response); } +///////////////////////////////////////////////// + AsyncWebSocketMessageBuffer * AsyncWebSocket::makeBuffer(size_t size) { AsyncWebSocketMessageBuffer * buffer = new AsyncWebSocketMessageBuffer(size); @@ -1458,6 +1639,8 @@ AsyncWebSocketMessageBuffer * AsyncWebSocket::makeBuffer(size_t size) return buffer; } +///////////////////////////////////////////////// + AsyncWebSocketMessageBuffer * AsyncWebSocket::makeBuffer(uint8_t * data, size_t size) { AsyncWebSocketMessageBuffer * buffer = new AsyncWebSocketMessageBuffer(data, size); @@ -1471,6 +1654,8 @@ AsyncWebSocketMessageBuffer * AsyncWebSocket::makeBuffer(uint8_t * data, size_t return buffer; } +///////////////////////////////////////////////// + void AsyncWebSocket::_cleanBuffers() { AsyncWebLockGuard l(_lock); @@ -1484,11 +1669,15 @@ 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 @@ -1505,6 +1694,7 @@ AsyncWebSocketResponse::AsyncWebSocketResponse(const String & key, AsyncWebSocke if (hash == NULL) { _state = RESPONSE_FAILED; + return; } @@ -1514,6 +1704,7 @@ AsyncWebSocketResponse::AsyncWebSocketResponse(const String & key, AsyncWebSocke { free(hash); _state = RESPONSE_FAILED; + return; } @@ -1540,11 +1731,14 @@ AsyncWebSocketResponse::AsyncWebSocketResponse(const String & key, AsyncWebSocke free(hash); } +///////////////////////////////////////////////// + void AsyncWebSocketResponse::_respond(AsyncWebServerRequest * request) { if (_state == RESPONSE_FAILED) { request->client()->close(true); + return; } @@ -1553,6 +1747,8 @@ void AsyncWebSocketResponse::_respond(AsyncWebServerRequest * request) _state = RESPONSE_WAIT_ACK; } +///////////////////////////////////////////////// + size_t AsyncWebSocketResponse::_ack(AsyncWebServerRequest * request, size_t len, uint32_t time) { PORTENTA_H7_AWS_UNUSED(time); @@ -1564,3 +1760,6 @@ size_t AsyncWebSocketResponse::_ack(AsyncWebServerRequest * request, size_t len, return 0; } + +///////////////////////////////////////////////// + diff --git a/src/Portenta_H7_AsyncWebSocket.h b/src/Portenta_H7_AsyncWebSocket.h index fa3faf4..8b39445 100644 --- a/src/Portenta_H7_AsyncWebSocket.h +++ b/src/Portenta_H7_AsyncWebSocket.h @@ -9,7 +9,7 @@ Built by Khoi Hoang https://github.com/khoih-prog/Portenta_H7_AsyncWebServer Licensed under GPLv3 license - Version: 1.2.1 + Version: 1.3.0 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -18,6 +18,7 @@ 1.1.1 K Hoang 12/10/2021 Update `platform.ini` and `library.json` 1.2.0 K Hoang 07/12/2021 Fix crashing issue 1.2.1 K Hoang 12/01/2022 Fix authenticate issue caused by libb64 + 1.3.0 K Hoang 26/09/2022 Fix issue with slow browsers or network *****************************************************************************************************************************/ #pragma once @@ -38,11 +39,15 @@ #include "Portenta_H7_AsyncWebSynchronization.h" +///////////////////////////////////////////////// + class AsyncWebSocket; class AsyncWebSocketResponse; class AsyncWebSocketClient; class AsyncWebSocketControl; +///////////////////////////////////////////////// + typedef struct { /** Message type as defined by enum AwsFrameType. @@ -68,6 +73,8 @@ typedef struct uint64_t index; } AwsFrameInfo; +///////////////////////////////////////////////// + typedef enum { WS_DISCONNECTED, @@ -75,6 +82,8 @@ typedef enum WS_DISCONNECTING } AwsClientStatus; +///////////////////////////////////////////////// + typedef enum { WS_CONTINUATION, @@ -85,6 +94,8 @@ typedef enum WS_PONG } AwsFrameType; +///////////////////////////////////////////////// + typedef enum { WS_MSG_SENDING, @@ -92,6 +103,8 @@ typedef enum WS_MSG_ERROR } AwsMessageStatus; +///////////////////////////////////////////////// + typedef enum { WS_EVT_CONNECT, @@ -101,6 +114,8 @@ typedef enum WS_EVT_DATA } AwsEventType; +///////////////////////////////////////////////// + class AsyncWebSocketMessageBuffer { private: @@ -116,12 +131,16 @@ class AsyncWebSocketMessageBuffer AsyncWebSocketMessageBuffer(const AsyncWebSocketMessageBuffer &); AsyncWebSocketMessageBuffer(AsyncWebSocketMessageBuffer &&); ~AsyncWebSocketMessageBuffer(); + + ///////////////////////////////////////////////// void operator ++(int i) { PORTENTA_H7_AWS_UNUSED(i); _count++; } + + ///////////////////////////////////////////////// void operator --(int i) { @@ -132,43 +151,61 @@ class AsyncWebSocketMessageBuffer _count--; } ; } + + ///////////////////////////////////////////////// bool reserve(size_t size); + + ///////////////////////////////////////////////// void lock() { _lock = true; } + + ///////////////////////////////////////////////// void unlock() { _lock = false; } + + ///////////////////////////////////////////////// uint8_t * get() { return _data; } + + ///////////////////////////////////////////////// size_t length() { return _len; } + + ///////////////////////////////////////////////// uint32_t count() { return _count; } + + ///////////////////////////////////////////////// bool canDelete() { return (!_count && !_lock); } + ///////////////////////////////////////////////// + friend AsyncWebSocket; }; +///////////////////////////////////////////////// + class AsyncWebSocketMessage { protected: @@ -180,16 +217,22 @@ class AsyncWebSocketMessage AsyncWebSocketMessage(): _opcode(WS_TEXT), _mask(false), _status(WS_MSG_ERROR) {} virtual ~AsyncWebSocketMessage() {} virtual void ack(size_t len __attribute__((unused)), uint32_t time __attribute__((unused))) {} + + ///////////////////////////////////////////////// virtual size_t send(AsyncClient *client __attribute__((unused))) { return 0; } + + ///////////////////////////////////////////////// virtual bool finished() { return _status != WS_MSG_SENDING; } + + ///////////////////////////////////////////////// virtual bool betweenFrames() const { @@ -197,6 +240,8 @@ class AsyncWebSocketMessage } }; +///////////////////////////////////////////////// + class AsyncWebSocketBasicMessage: public AsyncWebSocketMessage { private: @@ -210,11 +255,15 @@ class AsyncWebSocketBasicMessage: public AsyncWebSocketMessage AsyncWebSocketBasicMessage(const char * data, size_t len, uint8_t opcode = WS_TEXT, bool mask = false); AsyncWebSocketBasicMessage(uint8_t opcode = WS_TEXT, bool mask = false); virtual ~AsyncWebSocketBasicMessage() override; + + ///////////////////////////////////////////////// virtual bool betweenFrames() const override { return _acked == _ack; } + + ///////////////////////////////////////////////// virtual void ack(size_t len, uint32_t time) override; virtual size_t send(AsyncClient *client) override; @@ -222,6 +271,8 @@ class AsyncWebSocketBasicMessage: public AsyncWebSocketMessage virtual bool reserve(size_t size); }; +///////////////////////////////////////////////// + class AsyncWebSocketMultiMessage: public AsyncWebSocketMessage { private: @@ -235,16 +286,22 @@ class AsyncWebSocketMultiMessage: public AsyncWebSocketMessage public: AsyncWebSocketMultiMessage(AsyncWebSocketMessageBuffer * buffer, uint8_t opcode = WS_TEXT, bool mask = false); virtual ~AsyncWebSocketMultiMessage() override; + + ///////////////////////////////////////////////// virtual bool betweenFrames() const override { return _acked == _ack; } + + ///////////////////////////////////////////////// virtual void ack(size_t len, uint32_t time) override ; virtual size_t send(AsyncClient *client) override ; }; +///////////////////////////////////////////////// + class AsyncWebSocketClient { private: @@ -272,11 +329,15 @@ class AsyncWebSocketClient AsyncWebSocketClient(AsyncWebServerRequest *request, AsyncWebSocket *server); ~AsyncWebSocketClient(); + ///////////////////////////////////////////////// + //client id increments for the given server uint32_t id() { return _clientId; } + + ///////////////////////////////////////////////// AwsClientStatus status() { @@ -287,17 +348,23 @@ class AsyncWebSocketClient { return _client; } + + ///////////////////////////////////////////////// AsyncWebSocket *server() { return _server; } + + ///////////////////////////////////////////////// AwsFrameInfo const &pinfo() const { return _pinfo; } + ///////////////////////////////////////////////// + IPAddress remoteIP(); uint16_t remotePort(); @@ -305,22 +372,30 @@ 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) { _keepAlivePeriod = seconds * 1000; } + + ///////////////////////////////////////////////// uint16_t keepAlivePeriod() { return (uint16_t)(_keepAlivePeriod / 1000); } + ///////////////////////////////////////////////// + //data packets void message(AsyncWebSocketMessage *message) { _queueMessage(message); } + + ///////////////////////////////////////////////// bool queueIsFull(); @@ -340,11 +415,15 @@ class AsyncWebSocketClient void binary(const String &message); void binary(AsyncWebSocketMessageBuffer *buffer); + ///////////////////////////////////////////////// + bool canSend() { return _messageQueue.length() < WS_MAX_QUEUED_MESSAGES; } + ///////////////////////////////////////////////// + //system callbacks (do not call) void _onAck(size_t len, uint32_t time); void _onError(int8_t); @@ -354,8 +433,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 { @@ -373,33 +456,45 @@ class AsyncWebSocket: public AsyncWebHandler public: AsyncWebSocket(const String& url); ~AsyncWebSocket(); + + ///////////////////////////////////////////////// const char * url() const { return _url.c_str(); } + + ///////////////////////////////////////////////// void enable(bool e) { _enabled = e; } + + ///////////////////////////////////////////////// bool enabled() const { return _enabled; } + + ///////////////////////////////////////////////// bool availableForWriteAll(); bool availableForWrite(uint32_t id); size_t count() const; AsyncWebSocketClient * client(uint32_t id); + + ///////////////////////////////////////////////// bool hasClient(uint32_t id) { return client(id) != NULL; } + ///////////////////////////////////////////////// + void close(uint32_t id, uint16_t code = 0, const char * message = NULL); void closeAll(uint16_t code = 0, const char * message = NULL); void cleanupClients(uint16_t maxClients = DEFAULT_MAX_WS_CLIENTS); @@ -439,17 +534,23 @@ 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) { _eventHandler = handler; } + ///////////////////////////////////////////////// + //system callbacks (do not call) uint32_t _getNextId() { return _cNextId++; } + + ///////////////////////////////////////////////// void _addClient(AsyncWebSocketClient * client); void _handleDisconnect(AsyncWebSocketClient * client); @@ -457,7 +558,6 @@ class AsyncWebSocket: public AsyncWebHandler 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); @@ -467,6 +567,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 { @@ -478,6 +580,8 @@ class AsyncWebSocketResponse: public AsyncWebServerResponse AsyncWebSocketResponse(const String& key, AsyncWebSocket *server); void _respond(AsyncWebServerRequest *request); size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time); + + ///////////////////////////////////////////////// bool _sourceValid() const { @@ -485,5 +589,6 @@ class AsyncWebSocketResponse: public AsyncWebServerResponse } }; +///////////////////////////////////////////////// #endif /* PORTENTA_H7_ASYNCWEBSOCKET_H_ */ diff --git a/src/Portenta_H7_AsyncWebSynchronization.h b/src/Portenta_H7_AsyncWebSynchronization.h index aee893e..7370073 100644 --- a/src/Portenta_H7_AsyncWebSynchronization.h +++ b/src/Portenta_H7_AsyncWebSynchronization.h @@ -9,7 +9,7 @@ Built by Khoi Hoang https://github.com/khoih-prog/Portenta_H7_AsyncWebServer Licensed under GPLv3 license - Version: 1.2.1 + Version: 1.3.0 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -18,6 +18,7 @@ 1.1.1 K Hoang 12/10/2021 Update `platform.ini` and `library.json` 1.2.0 K Hoang 07/12/2021 Fix crashing issue 1.2.1 K Hoang 12/01/2022 Fix authenticate issue caused by libb64 + 1.3.0 K Hoang 26/09/2022 Fix issue with slow browsers or network *****************************************************************************************************************************/ #pragma once @@ -27,6 +28,8 @@ #include +///////////////////////////////////////////////// + // This is the STM32 version of the Sync Lock which is currently unimplemented class AsyncWebLock { @@ -44,6 +47,8 @@ class AsyncWebLock void unlock() const {} }; +///////////////////////////////////////////////// + class AsyncWebLockGuard { private: @@ -71,4 +76,6 @@ class AsyncWebLockGuard } }; +///////////////////////////////////////////////// + #endif // PORTENTA_H7_ASYNCWEBSYNCHRONIZATION_H_ diff --git a/src/Portenta_H7_StringArray.h b/src/Portenta_H7_StringArray.h index 1ac971e..77c5b87 100644 --- a/src/Portenta_H7_StringArray.h +++ b/src/Portenta_H7_StringArray.h @@ -9,7 +9,7 @@ Built by Khoi Hoang https://github.com/khoih-prog/Portenta_H7_AsyncWebServer Licensed under GPLv3 license - Version: 1.2.1 + Version: 1.3.0 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -18,6 +18,7 @@ 1.1.1 K Hoang 12/10/2021 Update `platform.ini` and `library.json` 1.2.0 K Hoang 07/12/2021 Fix crashing issue 1.2.1 K Hoang 12/01/2022 Fix authenticate issue caused by libb64 + 1.3.0 K Hoang 26/09/2022 Fix issue with slow browsers or network *****************************************************************************************************************************/ #pragma once @@ -28,6 +29,8 @@ #include "stddef.h" #include "WString.h" +///////////////////////////////////////////////// + template class LinkedListNode { @@ -38,17 +41,23 @@ class LinkedListNode LinkedListNode(const T val): _value(val), next(nullptr) {} ~LinkedListNode() {} + ///////////////////////////////////////////////// + const T& value() const { return _value; }; + ///////////////////////////////////////////////// + T& value() { return _value; } }; +///////////////////////////////////////////////// + template class Item = LinkedListNode> class LinkedList { @@ -69,22 +78,30 @@ class LinkedList Iterator(ItemType* current = nullptr) : _node(current) {} Iterator(const Iterator& i) : _node(i._node) {} + ///////////////////////////////////////////////// + Iterator& operator ++() { _node = _node->next; return *this; } + ///////////////////////////////////////////////// + bool operator != (const Iterator& i) const { return _node != i._node; } + ///////////////////////////////////////////////// + const T& operator * () const { return _node->value(); } + ///////////////////////////////////////////////// + const T* operator -> () const { return &_node->value(); @@ -94,19 +111,27 @@ class LinkedList public: typedef const Iterator ConstIterator; + ///////////////////////////////////////////////// + ConstIterator begin() const { return ConstIterator(_root); } + ///////////////////////////////////////////////// + ConstIterator end() const { return ConstIterator(nullptr); } + ///////////////////////////////////////////////// + LinkedList(OnRemove onRemove) : _root(nullptr), _onRemove(onRemove) {} ~LinkedList() {} + ///////////////////////////////////////////////// + void add(const T& t) { auto it = new ItemType(t); @@ -126,16 +151,22 @@ class LinkedList } } + ///////////////////////////////////////////////// + T& front() const { return _root->value(); } + ///////////////////////////////////////////////// + bool isEmpty() const { return _root == nullptr; } + ///////////////////////////////////////////////// + size_t length() const { size_t i = 0; @@ -150,6 +181,8 @@ class LinkedList return i; } + ///////////////////////////////////////////////// + size_t count_if(Predicate predicate) const { size_t i = 0; @@ -172,6 +205,8 @@ class LinkedList return i; } + ///////////////////////////////////////////////// + const T* nth(size_t N) const { size_t i = 0; @@ -188,6 +223,8 @@ class LinkedList return nullptr; } + ///////////////////////////////////////////////// + bool remove(const T& t) { auto it = _root; @@ -212,6 +249,7 @@ class LinkedList } delete it; + return true; } @@ -222,6 +260,8 @@ class LinkedList return false; } + ///////////////////////////////////////////////// + bool remove_first(Predicate predicate) { auto it = _root; @@ -246,6 +286,7 @@ class LinkedList } delete it; + return true; } @@ -256,6 +297,8 @@ class LinkedList return false; } + ///////////////////////////////////////////////// + void free() { while (_root != nullptr) @@ -275,6 +318,7 @@ class LinkedList } }; +///////////////////////////////////////////////// class StringArray : public LinkedList { @@ -282,6 +326,8 @@ class StringArray : public LinkedList StringArray() : LinkedList(nullptr) {} + ///////////////////////////////////////////////// + bool containsIgnoreCase(const String& str) { for (const auto& s : *this) @@ -296,4 +342,6 @@ class StringArray : public LinkedList } }; +///////////////////////////////////////////////// + #endif /* PORTENTA_H7_STRINGARRAY_H_ */ diff --git a/src/libb64/cdecode.c b/src/libb64/cdecode.c index 7adf7e8..fd567f1 100644 --- a/src/libb64/cdecode.c +++ b/src/libb64/cdecode.c @@ -12,7 +12,7 @@ Built by Khoi Hoang https://github.com/khoih-prog/Portenta_H7_AsyncWebServer Licensed under GPLv3 license - Version: 1.2.1 + Version: 1.3.0 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -21,6 +21,7 @@ 1.1.1 K Hoang 12/10/2021 Update `platform.ini` and `library.json` 1.2.0 K Hoang 07/12/2021 Fix crashing issue 1.2.1 K Hoang 12/01/2022 Fix authenticate issue caused by libb64 + 1.3.0 K Hoang 26/09/2022 Fix issue with slow browsers or network *****************************************************************************************************************************/ #include "cdecode.h" diff --git a/src/libb64/cdecode.h b/src/libb64/cdecode.h index 80e911e..9cc60e5 100644 --- a/src/libb64/cdecode.h +++ b/src/libb64/cdecode.h @@ -12,7 +12,7 @@ Built by Khoi Hoang https://github.com/khoih-prog/Portenta_H7_AsyncWebServer Licensed under GPLv3 license - Version: 1.2.1 + Version: 1.3.0 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -21,6 +21,7 @@ 1.1.1 K Hoang 12/10/2021 Update `platform.ini` and `library.json` 1.2.0 K Hoang 07/12/2021 Fix crashing issue 1.2.1 K Hoang 12/01/2022 Fix authenticate issue caused by libb64 + 1.3.0 K Hoang 26/09/2022 Fix issue with slow browsers or network *****************************************************************************************************************************/ #pragma once diff --git a/src/libb64/cencode.c b/src/libb64/cencode.c index cba5665..bf0fcb0 100644 --- a/src/libb64/cencode.c +++ b/src/libb64/cencode.c @@ -12,7 +12,7 @@ Built by Khoi Hoang https://github.com/khoih-prog/Portenta_H7_AsyncWebServer Licensed under GPLv3 license - Version: 1.2.1 + Version: 1.3.0 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -21,6 +21,7 @@ 1.1.1 K Hoang 12/10/2021 Update `platform.ini` and `library.json` 1.2.0 K Hoang 07/12/2021 Fix crashing issue 1.2.1 K Hoang 12/01/2022 Fix authenticate issue caused by libb64 + 1.3.0 K Hoang 26/09/2022 Fix issue with slow browsers or network *****************************************************************************************************************************/ #include "cencode.h" diff --git a/src/libb64/cencode.h b/src/libb64/cencode.h index c9d4f27..288e1d0 100644 --- a/src/libb64/cencode.h +++ b/src/libb64/cencode.h @@ -12,7 +12,7 @@ Built by Khoi Hoang https://github.com/khoih-prog/Portenta_H7_AsyncWebServer Licensed under GPLv3 license - Version: 1.2.1 + Version: 1.3.0 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -21,6 +21,7 @@ 1.1.1 K Hoang 12/10/2021 Update `platform.ini` and `library.json` 1.2.0 K Hoang 07/12/2021 Fix crashing issue 1.2.1 K Hoang 12/01/2022 Fix authenticate issue caused by libb64 + 1.3.0 K Hoang 26/09/2022 Fix issue with slow browsers or network *****************************************************************************************************************************/ #pragma once