From c9be0bdaabf1ff14f8415d5605c0e85d2f496f1a Mon Sep 17 00:00:00 2001 From: Khoi Hoang <57012152+khoih-prog@users.noreply.github.com> Date: Thu, 25 Feb 2021 16:51:32 -0500 Subject: [PATCH] Releases v1.1.3 ### Releases v1.1.3 1. Fix non-persistent Connection header bug. Check [**'Connection' header expects 'disconnect' instead 'close' ? #13**](https://github.com/khoih-prog/AsyncHTTPRequest_Generic/issues/13) 2. Add ESP32-S2 support 3. Tested with [**Latest ESP32 Core 1.0.5**](https://github.com/espressif/arduino-esp32) for ESP32-based boards. --- CONTRIBUTING.md | 6 +- Images/esp32_s2_Core_Unzipped.png | Bin 0 -> 45169 bytes Images/esp32_s2_Toolchain.png | Bin 0 -> 22374 bytes Images/esp32_s2_esptool.png | Bin 0 -> 46738 bytes Images/esp32_s2_tools.png | Bin 0 -> 41911 bytes README.md | 257 ++++++- esp32s2_WebServer_Patch/WebServer.cpp | 705 ++++++++++++++++++ esp32s2_WebServer_Patch/WebServer.h | 211 ++++++ .../AsyncCustomHeader_STM32.ino | 7 +- examples/AsyncCustomHeader_STM32/defines.h | 3 +- .../AsyncDweetGet_STM32.ino | 7 +- examples/AsyncDweetGet_STM32/defines.h | 3 +- .../AsyncDweetPost_STM32.ino | 7 +- examples/AsyncDweetPost_STM32/defines.h | 3 +- .../AsyncHTTPMultiRequests_ESP.ino | 5 +- .../AsyncHTTPRequest_ESP.ino | 5 +- .../AsyncHTTPRequest_ESP_WiFiManager.ino | 24 +- .../AsyncHTTPRequest_STM32.ino | 7 +- examples/AsyncHTTPRequest_STM32/defines.h | 3 +- .../AsyncSimpleGET_STM32.ino | 7 +- examples/AsyncSimpleGET_STM32/defines.h | 3 +- .../AsyncWebClientRepeating_STM32.ino | 7 +- .../AsyncWebClientRepeating_STM32/defines.h | 3 +- library.json | 14 +- library.properties | 8 +- platformio/platformio.ini | 6 +- src/AsyncHTTPRequest_Debug_Generic.h | 63 +- src/AsyncHTTPRequest_Generic.h | 5 +- src/AsyncHTTPRequest_Impl_Generic.h | 7 +- src/utility/xbuf.h | 3 +- src/utility/xbuf_Impl.h | 3 +- src_cpp/AsyncHTTPRequest_Debug_Generic.h | 63 +- src_cpp/AsyncHTTPRequest_Generic.cpp | 99 ++- src_cpp/AsyncHTTPRequest_Generic.h | 10 +- src_cpp/utility/xbuf.cpp | 3 +- src_cpp/utility/xbuf.h | 3 +- src_h/AsyncHTTPRequest_Debug_Generic.h | 63 +- src_h/AsyncHTTPRequest_Generic.h | 5 +- src_h/AsyncHTTPRequest_Impl_Generic.h | 7 +- src_h/utility/xbuf.h | 3 +- src_h/utility/xbuf_Impl.h | 3 +- 41 files changed, 1469 insertions(+), 172 deletions(-) create mode 100644 Images/esp32_s2_Core_Unzipped.png create mode 100644 Images/esp32_s2_Toolchain.png create mode 100644 Images/esp32_s2_esptool.png create mode 100644 Images/esp32_s2_tools.png create mode 100644 esp32s2_WebServer_Patch/WebServer.cpp create mode 100644 esp32s2_WebServer_Patch/WebServer.h diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a9dbe41..84c4df1 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.13) or Platform.io version -* `ESP8266`,`ESP32` or `STM32` Core Version (e.g. ESP8266 core v2.7.4, ESP32 v1.0.4 or STM32 v1.9.0) +* `ESP8266`,`ESP32` or `STM32` Core Version (e.g. ESP8266 core v2.7.4, ESP32 v1.0.5 or STM32 v1.9.0) * Contextual information (e.g. what you were trying to achieve) * Simplest possible steps to reproduce * Anything that might be relevant in your opinion, such as: @@ -27,9 +27,9 @@ Please ensure to specify the following: ``` Arduino IDE version: 1.8.13 -ESP32 Core Version 1.0.4 +ESP32 Core Version 1.0.5 OS: Ubuntu 20.04 LTS -Linux xy-Inspiron-3593 5.4.0-65-generic #73-Ubuntu SMP Mon Jan 18 17:25:17 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux +Linux xy-Inspiron-3593 5.4.0-66-generic #74-Ubuntu SMP Wed Jan 27 22:54:38 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux Context: I encountered an endless loop while trying to connect to Local WiFi. diff --git a/Images/esp32_s2_Core_Unzipped.png b/Images/esp32_s2_Core_Unzipped.png new file mode 100644 index 0000000000000000000000000000000000000000..76286a20ed5d904cfefaa4e97ff5d6294cfdd67d GIT binary patch literal 45169 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 literal 0 HcmV?d00001 diff --git a/Images/esp32_s2_esptool.png b/Images/esp32_s2_esptool.png new file mode 100644 index 0000000000000000000000000000000000000000..a9075ebb5c6dd4d03bdac630bb24cfe07ace01b0 GIT binary patch literal 46738 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 literal 0 HcmV?d00001 diff --git a/Images/esp32_s2_tools.png b/Images/esp32_s2_tools.png new file mode 100644 index 0000000000000000000000000000000000000000..91339754495ca28c1bb53c024df92502a7a6ab3a GIT binary patch literal 41911 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 literal 0 HcmV?d00001 diff --git a/README.md b/README.md index fbe2ae7..537dd67 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,9 @@ * [Features](#features) * [Supports](#supports) * [Principles of operation](#principles-of-operation) + * [Currently supported Boards](#currently-supported-boards) * [Changelog](#changelog) + * [Releases v1.1.3](#releases-v113) * [Releases v1.1.2](#releases-v112) * [Releases v1.1.1](#releases-v111) * [Releases v1.1.0](#releases-v110) @@ -38,7 +40,22 @@ * [For core version v1.8.9-](#for-core-version-v189-) * [5. For Adafruit SAMD boards](#5-for-adafruit-samd-boards) * [6. For Seeeduino SAMD boards](#6-for-seeeduino-samd-boards) - * [7. For STM32 boards](#7-for-stm32-boards) + * [7. For STM32 boards](#7-for-stm32-boards) +* [HOWTO Install esp32-s2 core for ESP32-S2 (Saola, AI-Thinker ESP-12K) boards into Arduino IDE)](#howto-install-esp32-s2-core-for-esp32-s2-saola-ai-thinker-esp-12k-boards-into-arduino-ide) + * [1. Save the original esp32 core](#1-save-the-original-esp32-core) + * [2. Download esp32-s2 core](#2-download-esp32-s2-core) + * [2.1 Download zip](#21-download-zip) + * [2.2 Unzip](#22-unzip) + * [2.3 Update esp32-s2 core directories](#23-update-esp32-s2-core-directories) + * [3. Download tools](#3-download-tools) + * [3.1 Download Toolchain for Xtensa (ESP32-S2) based on GCC](#31-download-toolchain-for-xtensa-esp32-s2-based-on-gcc) + * [3.2 Download esptool](#32-download-esptool) + * [3.3 Unzip](#33-unzip) + * [4. Update tools](#4-update-tools) + * [4.1 Update Toolchain](#41-update-toolchain) + * [4.2 Update esptool](#42-update-esptool) + * [5. esp32-s2 WebServer Library Patch](#5-esp32-s2-webserver-library-patch) +* [Note for Platform IO using ESP32 LittleFS](#note-for-platform-io-using-esp32-littlefs) * [HOWTO Fix `Multiple Definitions` Linker Error](#howto-fix-multiple-definitions-linker-error) * [Note for Platform IO using ESP32 LittleFS](#note-for-platform-io-using-esp32-littlefs) * [HOWTO Use analogRead() with ESP32 running WiFi and/or BlueTooth (BT/BLE)](#howto-use-analogread-with-esp32-running-wifi-andor-bluetooth-btble) @@ -79,15 +96,15 @@ ## Why do we need this Async [AsyncHTTPRequest_Generic library](https://github.com/khoih-prog/AsyncHTTPRequest_Generic) -#### Features +### Features -1. Asynchronous HTTP Request library for ESP8266, ESP32 using built-in WiFi, and STM32 boards using built-in LAN8742A Ethernet. +1. Asynchronous HTTP Request library for ESP8266, including ESP32-S2 (ESP32-S2 Saola, AI-Thinker ESP-12K, etc.) using built-in WiFi, and STM32 boards using built-in LAN8742A Ethernet. 2. Providing a subset of HTTP. 3. Relying on on **[`ESPAsyncTCP`](https://github.com/me-no-dev/ESPAsyncTCP) for ESP8266, [`AsyncTCP`](https://github.com/me-no-dev/AsyncTCP) for ESP32** using built-in WiFi 4. Relying on **[`STM32duino LwIP`](https://github.com/stm32duino/LwIP)/[`STM32duino STM32Ethernet`](https://github.com/stm32duino/STM32Ethernet)/[`STM32AsyncTCP`](https://github.com/philbowles/STM32AsyncTCP) for STM32 using built-in LAN8742A Ethernet.** 5. Methods similar in format and usage to XmlHTTPrequest in Javascript. -#### Supports +### Supports 1. **GET, POST, PUT, PATCH, DELETE and HEAD** 2. Request and response headers @@ -96,7 +113,7 @@ 5. Optional onData callback. 6. Optional onReadyStatechange callback. -#### Principles of operation +### Principles of operation This library adds a simple HTTP layer on top of the ESPAsyncTCP/AsyncTCP/STM32 AsyncTCP library to **facilitate REST communication from a Client to a Server**. The paradigm is similar to the XMLHttpRequest in Javascript, employing the notion of a ready-state progression through the transaction request. @@ -110,12 +127,37 @@ Request and response headers are handled in the typical fashion. Chunked responses are recognized and handled transparently. +This library is based on, modified from: + +1. [Bob Lemaire's asyncHTTPrequest Library](https://github.com/boblemaire/asyncHTTPrequest) + +--- + +### Currently Supported Boards + +#### 1. ESP32 including ESP32-S2 (ESP32-S2 Saola, AI-Thinker ESP-12K, etc.) + +#### 2. ESP8266 + +#### 3. STM32F/L/H/G/WB/MP1 with built-in LAN8742A Ethernet. + +1. Nucleo-144 (F429ZI, F746ZG, F756ZG, F767ZI) +2. Discovery STM32F746G-DISCOVERY +3. Any STM32 boards with enough flash/memory and already configured to run LAN8742A Ethernet. + + --- --- ## Changelog +### Releases v1.1.3 + +1. Fix non-persistent Connection header bug. Check [**'Connection' header expects 'disconnect' instead 'close' ? #13**](https://github.com/khoih-prog/AsyncHTTPRequest_Generic/issues/13) +2. Add ESP32-S2 support +3. Tested with [**Latest ESP32 Core 1.0.5**](https://github.com/espressif/arduino-esp32) for ESP32-based boards. + ### Releases v1.1.2 1. Rename _lock and _unlock to avoid conflict with [**ESP32/ESP8266 AsyncWebServer**](https://github.com/me-no-dev/ESPAsyncWebServer) library. Check [**compatibility with ESPAsyncWebServer #11**](https://github.com/khoih-prog/AsyncHTTPRequest_Generic/issues/11) @@ -147,24 +189,6 @@ Chunked responses are recognized and handled transparently. 1. Initial coding to add support to **STM32F/L/H/G/WB/MP1** using built-in LAN8742A Ethernet (Nucleo-144, DISCOVERY, etc). 2. Add examples using STM32 boards. -#### Currently Supported Boards - -#### 1. ESP32 and ESP8266 - -#### 2. STM32F/L/H/G/WB/MP1 with built-in LAN8742A Ethernet. - -1. Nucleo-144 (F429ZI, F746ZG, F756ZG, F767ZI) -2. Discovery STM32F746G-DISCOVERY -3. Any STM32 boards with enough flash/memory and already configured to run LAN8742A Ethernet. - ---- - -#### AsyncHTTPRequest_Generic for ESP32, ESP8266 using built-in WiFi and STM32 boards using built-in LAN8742A Ethernet - -This library is based on, modified from: - -1. [Bob Lemaire's asyncHTTPrequest Library](https://github.com/boblemaire/asyncHTTPrequest) - --- --- @@ -172,15 +196,17 @@ This library is based on, modified from: 1. [`Arduino IDE 1.8.13+` for Arduino](https://www.arduino.cc/en/Main/Software) 2. [`ESP8266 Core 2.7.4+`](https://github.com/esp8266/Arduino) for ESP8266-based boards. [![Latest release](https://img.shields.io/github/release/esp8266/Arduino.svg)](https://github.com/esp8266/Arduino/releases/latest/) - 3. [`ESP32 Core 1.0.4+`](https://github.com/espressif/arduino-esp32) for ESP32-based boards. [Latest stable release ![Release Version](https://img.shields.io/github/release/espressif/arduino-esp32.svg?style=plastic) - 4. [`Arduino Core for STM32 1.9.0+`](https://github.com/stm32duino/Arduino_Core_STM32) for for STM32 using built-in Ethernet LAN8742A. [![GitHub release](https://img.shields.io/github/release/stm32duino/Arduino_Core_STM32.svg)](https://github.com/stm32duino/Arduino_Core_STM32/releases/latest) - 5. [`ESPAsyncTCP v1.2.2+`](https://github.com/me-no-dev/ESPAsyncTCP) for ESP8266. - 6. [`AsyncTCP v1.1.1+`](https://github.com/me-no-dev/AsyncTCP) for ESP32. - 7. [`STM32Ethernet library v1.2.0+`](https://github.com/stm32duino/STM32Ethernet) for STM32 using built-in Ethernet LAN8742A on (Nucleo-144, Discovery). [![GitHub release](https://img.shields.io/github/release/stm32duino/STM32Ethernet.svg)](https://github.com/stm32duino/STM32Ethernet/releases/latest) - 8. [`LwIP library v2.1.2+`](https://github.com/stm32duino/LwIP) for STM32 using built-in Ethernet LAN8742A on (Nucleo-144, Discovery). [![GitHub release](https://img.shields.io/github/release/stm32duino/LwIP.svg)](https://github.com/stm32duino/LwIP/releases/latest) - 9. [`STM32AsyncTCP library v1.0.0+`](https://github.com/philbowles/STM32AsyncTCP) for STM32 using built-in Ethernet LAN8742A on (Nucleo-144, Discovery). -10. [`ESPAsync_WiFiManager library v1.4.3+`](https://github.com/khoih-prog/ESPAsync_WiFiManager) for ESP32/ESP8266 using some examples. [![GitHub release](https://img.shields.io/github/release/khoih-prog/ESPAsync_WiFiManager.svg)](https://github.com/khoih-prog/ESPAsync_WiFiManager/releases) - + 3. [`ESP32 Core 1.0.5+`](https://github.com/espressif/arduino-esp32) for ESP32-based boards. [Latest stable release ![Release Version](https://img.shields.io/github/release/espressif/arduino-esp32.svg?style=plastic) + 4. [`ESP32S2 Core 1.0.4+`](https://github.com/espressif/arduino-esp32/tree/esp32s2) for ESP32-S2-based boards. + 5. [`Arduino Core for STM32 1.9.0+`](https://github.com/stm32duino/Arduino_Core_STM32) for for STM32 using built-in Ethernet LAN8742A. [![GitHub release](https://img.shields.io/github/release/stm32duino/Arduino_Core_STM32.svg)](https://github.com/stm32duino/Arduino_Core_STM32/releases/latest) + 6. [`ESPAsyncTCP v1.2.2+`](https://github.com/me-no-dev/ESPAsyncTCP) for ESP8266. + 7. [`AsyncTCP v1.1.1+`](https://github.com/me-no-dev/AsyncTCP) for ESP32. + 8. [`STM32Ethernet library v1.2.0+`](https://github.com/stm32duino/STM32Ethernet) for STM32 using built-in Ethernet LAN8742A on (Nucleo-144, Discovery). [![GitHub release](https://img.shields.io/github/release/stm32duino/STM32Ethernet.svg)](https://github.com/stm32duino/STM32Ethernet/releases/latest) + 9. [`LwIP library v2.1.2+`](https://github.com/stm32duino/LwIP) for STM32 using built-in Ethernet LAN8742A on (Nucleo-144, Discovery). [![GitHub release](https://img.shields.io/github/release/stm32duino/LwIP.svg)](https://github.com/stm32duino/LwIP/releases/latest) +10. [`STM32AsyncTCP library v1.0.0+`](https://github.com/philbowles/STM32AsyncTCP) for STM32 using built-in Ethernet LAN8742A on (Nucleo-144, Discovery). +11. [`ESPAsync_WiFiManager library v1.6.0+`](https://github.com/khoih-prog/ESPAsync_WiFiManager) for ESP32/ESP8266 using some examples. [![GitHub release](https://img.shields.io/github/release/khoih-prog/ESPAsync_WiFiManager.svg)](https://github.com/khoih-prog/ESPAsync_WiFiManager/releases) +12. [`LittleFS_esp32 v1.0.5+`](https://github.com/lorol/LITTLEFS) for ESP32-based boards using LittleFS. To install, check [![arduino-library-badge](https://www.ardu-badge.com/badge/LittleFS_esp32.svg?)](https://www.ardu-badge.com/LittleFS_esp32). + --- ## Installation @@ -346,6 +372,136 @@ theses files must be copied into the corresponding directory: --- --- +## HOWTO Install esp32-s2 core for ESP32-S2 (Saola, AI-Thinker ESP-12K) boards into Arduino IDE + + +These are instructions demonstrating the steps to install esp32-s2 core on Ubuntu machines. For Windows or other OS'es, just follow the the similar principles and steps. + +Assuming you already installed Arduino IDE ESP32 core and the installed directory is + +`/home/your_account/.arduino15/packages/esp32` + + +### 1. Save the original esp32 core + +First, copy the whole original esp32 core to another safe place. Then delete all the sub-directories of + +`/home/your_account/.arduino15/packages/esp32/hardware/esp32/1.0.4` + +--- + +### 2. Download esp32-s2 core + +#### 2.1 Download zip + +Download [**esp32-s2 core**](https://github.com/espressif/arduino-esp32/tree/esp32s2) in the `zip` format: + +`arduino-esp32-esp32s2.zip` + +#### 2.2 Unzip + +

+ +

+ +#### 2.3 Update esp32-s2 core directories + +Copy all subdirectories of esp32-s2 core into `/home/your_account/.arduino15/packages/esp32/hardware/esp32/1.0.4` + +--- + +### 3 Download tools + + +#### 3.1 Download Toolchain for Xtensa (ESP32-S2) based on GCC + +Download [**esp32-s2 Toolchain**](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/api-guides/tools/idf-tools.html#xtensa-esp32s2-elf) corresponding to your environment (linux-amd64, win64, etc.). + +For example `xtensa-esp32s2-elf-gcc8_4_0-esp-2020r3-linux-amd64.tar.gz`, then un-archive. + + +

+ +

+ +#### 3.2 Download esptool + + +Download [esptool](https://github.com/espressif/esptool/releases) int the `zip` format: + +`esptool-3.0.zip` + +#### 3.3 Unzip + +

+ +

+ +--- + +### 4. Update tools + +#### 4.1 Update Toolchain + +Copy whole `xtensa-esp32s2-elf` directory into `/home/your_account/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools` + + +#### 4.2 Update esptool + +Rename `esptool-3.0` directory to `esptool` + + +Copy whole `esptool` directory into `/home/your_account/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools` + + +

+ +

+ + +### 5. esp32-s2 WebServer Library Patch + +If you haven't installed a new version with [WebServer.handleClient delay PR #4350](https://github.com/espressif/arduino-esp32/pull/4350) or haven't applied the above mentioned PR, you have to use the following patch. + + +**To be able to run Config Portal on ESP32-S2 boards**, you have to copy the files in [esp32-s2 WebServer Patch](esp32s2_WebServer_Patch/) directory into esp32-s2 WebServer library directory (~/.arduino15/packages/esp32/hardware/esp32/1.0.4/libraries/WebServer). + +Supposing the esp32-s2 version is 1.0.4, these files `WebServer.h/cpp` must be copied into the directory to replace: + +- `~/.arduino15/packages/esp32/hardware/esp32/1.0.4/libraries/WebServer/src/WebServer.h` +- `~/.arduino15/packages/esp32/hardware/esp32/1.0.4/libraries/WebServer/src/WebServer.cpp` + + +--- + +That's it. You're now ready to compile and test for ESP32-S2 now + +--- +--- + +### Note for Platform IO using ESP32 LittleFS + +In Platform IO, to fix the error when using [`LittleFS_esp32 v1.0`](https://github.com/lorol/LITTLEFS) for ESP32-based boards with ESP32 core v1.0.4- (ESP-IDF v3.2-), uncomment the following line + +from + +``` +//#define CONFIG_LITTLEFS_FOR_IDF_3_2 /* For old IDF - like in release 1.0.4 */ +``` + +to + +``` +#define CONFIG_LITTLEFS_FOR_IDF_3_2 /* For old IDF - like in release 1.0.4 */ +``` + +It's advisable to use the latest [`LittleFS_esp32 v1.0.5+`](https://github.com/lorol/LITTLEFS) to avoid the issue. + +Thanks to [Roshan](https://github.com/solroshan) to report the issue in [Error esp_littlefs.c 'utime_p'](https://github.com/khoih-prog/ESPAsync_WiFiManager/issues/28) + +--- +--- + ### HOWTO Fix `Multiple Definitions` Linker Error The current library implementation, using xyz-Impl.h instead of standard xyz.cpp, possibly creates certain `Multiple Definitions` Linker error in certain use cases. Although it's simple to just modify several lines of code, either in the library or in the application, the library is adding a separate source directory, named src_cpp, besides the standard src directory. @@ -444,8 +600,8 @@ Please take a look at other examples, as well. ```cpp #include "defines.h" -// 600s = 10 minutes to not flooding, 10s in testing -#define HTTP_REQUEST_INTERVAL_MS 10000 //600000 +// 600s = 10 minutes to not flooding, 60s in testing +#define HTTP_REQUEST_INTERVAL_MS 60000 //600000 #include // https://github.com/khoih-prog/AsyncHTTPRequest_Generic @@ -540,6 +696,17 @@ void loop(void) ```cpp +/* + Currently support + 1) STM32 boards with built-in Ethernet (to use USE_BUILTIN_ETHERNET = true) such as : + - Nucleo-144 (F429ZI, F767ZI) + - Discovery (STM32F746G-DISCOVERY) + - STM32 boards (STM32F/L/H/G/WB/MP1) with 32K+ Flash, with Built-in Ethernet, + - See How To Use Built-in Ethernet at (https://github.com/khoih-prog/EthernetWebServer_STM32/issues/1) + 2) STM32F/L/H/G/WB/MP1 boards (with 32+K Flash) running ENC28J60 shields (to use USE_BUILTIN_ETHERNET = false) + 3) STM32F/L/H/G/WB/MP1 boards (with 32+K Flash) running W5x00 shields +*/ + #ifndef defines_h #define defines_h @@ -653,7 +820,7 @@ IPAddress ip(192, 168, 2, 232); ``` Start AsyncHTTPRequest_STM32 on NUCLEO_F767ZI -AsyncHTTPRequest_Generic v1.1.2 +AsyncHTTPRequest_Generic v1.1.3 AsyncHTTPRequest @ IP : 192.168.2.72 ************************************** @@ -698,7 +865,7 @@ week_number: 37 ``` Starting AsyncHTTPRequest_ESP_WiFiManager using LittleFS on ESP8266_NODEMCU -AsyncHTTPRequest_Generic v1.1.2 +AsyncHTTPRequest_Generic v1.1.3 Stored: SSID = HueNet1, Pass = 12345678 Got stored Credentials. Timeout 120s ConnectMultiWiFi in setup @@ -731,7 +898,7 @@ HHHHHH ``` Starting AsyncHTTPRequest_ESP_WiFiManager using SPIFFS on ESP32_DEV -AsyncHTTPRequest_Generic v1.1.2 +AsyncHTTPRequest_Generic v1.1.3 Stored: SSID = HueNet1, Pass = 12345678 Got stored Credentials. Timeout 120s ConnectMultiWiFi in setup @@ -782,7 +949,7 @@ HHHHHHHHH HHHHHHHHHH HHHHHHHHHH ``` Starting AsyncHTTPRequest_ESP using ESP8266_NODEMCU -AsyncHTTPRequest_Generic v1.1.2 +AsyncHTTPRequest_Generic v1.1.3 Connecting to WiFi SSID: HueNet1 ........... HTTP WebServer is @ IP : 192.168.2.81 @@ -814,7 +981,7 @@ HHHHHHHHH HHHHHHHHHH HHHHHHHHHH H ``` Start AsyncWebClientRepeating_STM32 on NUCLEO_F767ZI -AsyncHTTPRequest_Generic v1.1.2 +AsyncHTTPRequest_Generic v1.1.3 AsyncHTTPRequest @ IP : 192.168.2.72 ************************************** @@ -913,6 +1080,12 @@ Submit issues to: [AsyncHTTPRequest_Generic issues](https://github.com/khoih-pro ## Releases +### Releases v1.1.3 + +1. Fix non-persistent Connection header bug. Check [**'Connection' header expects 'disconnect' instead 'close' ? #13**](https://github.com/khoih-prog/AsyncHTTPRequest_Generic/issues/13) +2. Add ESP32-S2 support +3. Tested with [**Latest ESP32 Core 1.0.5**](https://github.com/espressif/arduino-esp32) for ESP32-based boards. + ### Releases v1.1.2 1. Rename _lock and _unlock to avoid conflict with [**ESP32/ESP8266 AsyncWebServer**](https://github.com/me-no-dev/ESPAsyncWebServer) library. Check [**compatibility with ESPAsyncWebServer #11**](https://github.com/khoih-prog/AsyncHTTPRequest_Generic/issues/11) @@ -950,16 +1123,20 @@ This library is based on, modified, bug-fixed and improved from: 2. Thanks to [Daniel Brunner](https://github.com/0xFEEDC0DE64) to report and make PR in [Fixed linker errors when included in multiple .cpp files](https://github.com/khoih-prog/AsyncHTTPRequest_Generic/pull/1) leading to v1.0.1. See [**HOWTO Fix `Multiple Definitions` Linker Error**](https://github.com/khoih-prog/AsyncHTTPRequest_Generic#HOWTO-Fix-Multiple-Definitions-Linker-Error) -3. Thanks to [gleniat](https://github.com/gleniat) to make enhancement request in[Add support for sending PUT, PATCH, DELETE request](https://github.com/khoih-prog/AsyncHTTPRequest_Generic/issues/5) leading to v1.1.0. +3. Thanks to [gleniat](https://github.com/gleniat) to make enhancement request in [Add support for sending PUT, PATCH, DELETE request](https://github.com/khoih-prog/AsyncHTTPRequest_Generic/issues/5) leading to v1.1.0. 4. Thanks to [BadDwarf](https://github.com/baddwarf) to report [**compatibility with ESPAsyncWebServer #11**](https://github.com/khoih-prog/AsyncHTTPRequest_Generic/issues/11) leading to the enhancement in v1.1.2. +5. Thanks to [spdi](https://github.com/spdi) to report [**'Connection' header expects 'disconnect' instead 'close' ? #13**](https://github.com/khoih-prog/AsyncHTTPRequest_Generic/issues/13) leading to new release v1.1.3 to fix bug. + + +
boblemaire
⭐️ Bob Lemaire

0xFEEDC0DE64
Daniel Brunner

gleniat
gleniat

baddwarf
BadDwarf

spdi
spdi

diff --git a/esp32s2_WebServer_Patch/WebServer.cpp b/esp32s2_WebServer_Patch/WebServer.cpp new file mode 100644 index 0000000..5d8f313 --- /dev/null +++ b/esp32s2_WebServer_Patch/WebServer.cpp @@ -0,0 +1,705 @@ +/* + WebServer.cpp - Dead simple web-server. + Supports only one simultaneous client, knows how to handle GET and POST. + + Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling) +*/ + +// KH, Using "WebServer.handleClient delay" (https://github.com/espressif/arduino-esp32/pull/4350) + +#include +#include +#include +#include "WiFiServer.h" +#include "WiFiClient.h" +#include "WebServer.h" +#include "FS.h" +#include "detail/RequestHandlersImpl.h" +#include "mbedtls/md5.h" + + +static const char AUTHORIZATION_HEADER[] = "Authorization"; +static const char qop_auth[] = "qop=\"auth\""; +static const char WWW_Authenticate[] = "WWW-Authenticate"; +static const char Content_Length[] = "Content-Length"; + + +WebServer::WebServer(IPAddress addr, int port) +: _corsEnabled(false) +, _server(addr, port) +, _currentMethod(HTTP_ANY) +, _currentVersion(0) +, _currentStatus(HC_NONE) +, _statusChange(0) +, _nullDelay(true) +, _currentHandler(nullptr) +, _firstHandler(nullptr) +, _lastHandler(nullptr) +, _currentArgCount(0) +, _currentArgs(nullptr) +, _postArgsLen(0) +, _postArgs(nullptr) +, _headerKeysCount(0) +, _currentHeaders(nullptr) +, _contentLength(0) +, _chunked(false) +{ +} + +WebServer::WebServer(int port) +: _corsEnabled(false) +, _server(port) +, _currentMethod(HTTP_ANY) +, _currentVersion(0) +, _currentStatus(HC_NONE) +, _statusChange(0) +, _nullDelay(true) +, _currentHandler(nullptr) +, _firstHandler(nullptr) +, _lastHandler(nullptr) +, _currentArgCount(0) +, _currentArgs(nullptr) +, _postArgsLen(0) +, _postArgs(nullptr) +, _headerKeysCount(0) +, _currentHeaders(nullptr) +, _contentLength(0) +, _chunked(false) +{ +} + +WebServer::~WebServer() { + _server.close(); + if (_currentHeaders) + delete[]_currentHeaders; + RequestHandler* handler = _firstHandler; + while (handler) { + RequestHandler* next = handler->next(); + delete handler; + handler = next; + } +} + +void WebServer::begin() { + close(); + _server.begin(); + _server.setNoDelay(true); +} + +void WebServer::begin(uint16_t port) { + close(); + _server.begin(port); + _server.setNoDelay(true); +} + +String WebServer::_extractParam(String& authReq,const String& param,const char delimit){ + int _begin = authReq.indexOf(param); + if (_begin == -1) + return ""; + return authReq.substring(_begin+param.length(),authReq.indexOf(delimit,_begin+param.length())); +} + +static String md5str(String &in){ + char out[33] = {0}; + mbedtls_md5_context _ctx; + uint8_t i; + uint8_t * _buf = (uint8_t*)malloc(16); + if(_buf == NULL) + return String(out); + memset(_buf, 0x00, 16); + mbedtls_md5_init(&_ctx); + mbedtls_md5_starts(&_ctx); + mbedtls_md5_update(&_ctx, (const uint8_t *)in.c_str(), in.length()); + mbedtls_md5_finish(&_ctx, _buf); + for(i = 0; i < 16; i++) { + sprintf(out + (i * 2), "%02x", _buf[i]); + } + out[32] = 0; + free(_buf); + return String(out); +} + +bool WebServer::authenticate(const char * username, const char * password){ + if(hasHeader(FPSTR(AUTHORIZATION_HEADER))) { + String authReq = header(FPSTR(AUTHORIZATION_HEADER)); + if(authReq.startsWith(F("Basic"))){ + authReq = authReq.substring(6); + authReq.trim(); + char toencodeLen = strlen(username)+strlen(password)+1; + char *toencode = new char[toencodeLen + 1]; + if(toencode == NULL){ + authReq = ""; + return false; + } + char *encoded = new char[base64_encode_expected_len(toencodeLen)+1]; + if(encoded == NULL){ + authReq = ""; + delete[] toencode; + return false; + } + sprintf(toencode, "%s:%s", username, password); + if(base64_encode_chars(toencode, toencodeLen, encoded) > 0 && authReq.equalsConstantTime(encoded)) { + authReq = ""; + delete[] toencode; + delete[] encoded; + return true; + } + delete[] toencode; + delete[] encoded; + } else if(authReq.startsWith(F("Digest"))) { + authReq = authReq.substring(7); + log_v("%s", authReq.c_str()); + String _username = _extractParam(authReq,F("username=\""),'\"'); + if(!_username.length() || _username != String(username)) { + authReq = ""; + return false; + } + // extracting required parameters for RFC 2069 simpler Digest + String _realm = _extractParam(authReq, F("realm=\""),'\"'); + String _nonce = _extractParam(authReq, F("nonce=\""),'\"'); + String _uri = _extractParam(authReq, F("uri=\""),'\"'); + String _response = _extractParam(authReq, F("response=\""),'\"'); + String _opaque = _extractParam(authReq, F("opaque=\""),'\"'); + + if((!_realm.length()) || (!_nonce.length()) || (!_uri.length()) || (!_response.length()) || (!_opaque.length())) { + authReq = ""; + return false; + } + if((_opaque != _sopaque) || (_nonce != _snonce) || (_realm != _srealm)) { + authReq = ""; + return false; + } + // parameters for the RFC 2617 newer Digest + String _nc,_cnonce; + if(authReq.indexOf(FPSTR(qop_auth)) != -1) { + _nc = _extractParam(authReq, F("nc="), ','); + _cnonce = _extractParam(authReq, F("cnonce=\""),'\"'); + } + String _H1 = md5str(String(username) + ':' + _realm + ':' + String(password)); + log_v("Hash of user:realm:pass=%s", _H1.c_str()); + String _H2 = ""; + if(_currentMethod == HTTP_GET){ + _H2 = md5str(String(F("GET:")) + _uri); + }else if(_currentMethod == HTTP_POST){ + _H2 = md5str(String(F("POST:")) + _uri); + }else if(_currentMethod == HTTP_PUT){ + _H2 = md5str(String(F("PUT:")) + _uri); + }else if(_currentMethod == HTTP_DELETE){ + _H2 = md5str(String(F("DELETE:")) + _uri); + }else{ + _H2 = md5str(String(F("GET:")) + _uri); + } + log_v("Hash of GET:uri=%s", _H2.c_str()); + String _responsecheck = ""; + if(authReq.indexOf(FPSTR(qop_auth)) != -1) { + _responsecheck = md5str(_H1 + ':' + _nonce + ':' + _nc + ':' + _cnonce + F(":auth:") + _H2); + } else { + _responsecheck = md5str(_H1 + ':' + _nonce + ':' + _H2); + } + log_v("The Proper response=%s", _responsecheck.c_str()); + if(_response == _responsecheck){ + authReq = ""; + return true; + } + } + authReq = ""; + } + return false; +} + +String WebServer::_getRandomHexString() { + char buffer[33]; // buffer to hold 32 Hex Digit + /0 + int i; + for(i = 0; i < 4; i++) { + sprintf (buffer + (i*8), "%08x", esp_random()); + } + return String(buffer); +} + +void WebServer::requestAuthentication(HTTPAuthMethod mode, const char* realm, const String& authFailMsg) { + if(realm == NULL) { + _srealm = String(F("Login Required")); + } else { + _srealm = String(realm); + } + if(mode == BASIC_AUTH) { + sendHeader(String(FPSTR(WWW_Authenticate)), String(F("Basic realm=\"")) + _srealm + String(F("\""))); + } else { + _snonce=_getRandomHexString(); + _sopaque=_getRandomHexString(); + sendHeader(String(FPSTR(WWW_Authenticate)), String(F("Digest realm=\"")) +_srealm + String(F("\", qop=\"auth\", nonce=\"")) + _snonce + String(F("\", opaque=\"")) + _sopaque + String(F("\""))); + } + using namespace mime; + send(401, String(FPSTR(mimeTable[html].mimeType)), authFailMsg); +} + +void WebServer::on(const Uri &uri, WebServer::THandlerFunction handler) { + on(uri, HTTP_ANY, handler); +} + +void WebServer::on(const Uri &uri, HTTPMethod method, WebServer::THandlerFunction fn) { + on(uri, method, fn, _fileUploadHandler); +} + +void WebServer::on(const Uri &uri, HTTPMethod method, WebServer::THandlerFunction fn, WebServer::THandlerFunction ufn) { + _addRequestHandler(new FunctionRequestHandler(fn, ufn, uri, method)); +} + +void WebServer::addHandler(RequestHandler* handler) { + _addRequestHandler(handler); +} + +void WebServer::_addRequestHandler(RequestHandler* handler) { + if (!_lastHandler) { + _firstHandler = handler; + _lastHandler = handler; + } + else { + _lastHandler->next(handler); + _lastHandler = handler; + } +} + +void WebServer::serveStatic(const char* uri, FS& fs, const char* path, const char* cache_header) { + _addRequestHandler(new StaticRequestHandler(fs, path, uri, cache_header)); +} + +void WebServer::handleClient() { + if (_currentStatus == HC_NONE) { + WiFiClient client = _server.available(); + if (!client) { + if (_nullDelay) { + delay(1); + } + return; + } + + log_v("New client"); + + _currentClient = client; + _currentStatus = HC_WAIT_READ; + _statusChange = millis(); + } + + bool keepCurrentClient = false; + bool callYield = false; + + if (_currentClient.connected()) { + switch (_currentStatus) { + case HC_NONE: + // No-op to avoid C++ compiler warning + break; + case HC_WAIT_READ: + // Wait for data from client to become available + if (_currentClient.available()) { + if (_parseRequest(_currentClient)) { + // because HTTP_MAX_SEND_WAIT is expressed in milliseconds, + // it must be divided by 1000 + _currentClient.setTimeout(HTTP_MAX_SEND_WAIT / 1000); + _contentLength = CONTENT_LENGTH_NOT_SET; + _handleRequest(); + +// Fix for issue with Chrome based browsers: https://github.com/espressif/arduino-esp32/issues/3652 +// if (_currentClient.connected()) { +// _currentStatus = HC_WAIT_CLOSE; +// _statusChange = millis(); +// keepCurrentClient = true; +// } + } + } else { // !_currentClient.available() + if (millis() - _statusChange <= HTTP_MAX_DATA_WAIT) { + keepCurrentClient = true; + } + callYield = true; + } + break; + case HC_WAIT_CLOSE: + // Wait for client to close the connection + if (millis() - _statusChange <= HTTP_MAX_CLOSE_WAIT) { + keepCurrentClient = true; + callYield = true; + } + } + } + + if (!keepCurrentClient) { + _currentClient = WiFiClient(); + _currentStatus = HC_NONE; + _currentUpload.reset(); + } + + if (callYield) { + yield(); + } +} + +void WebServer::close() { + _server.close(); + _currentStatus = HC_NONE; + if(!_headerKeysCount) + collectHeaders(0, 0); +} + +void WebServer::stop() { + close(); +} + +void WebServer::sendHeader(const String& name, const String& value, bool first) { + String headerLine = name; + headerLine += F(": "); + headerLine += value; + headerLine += "\r\n"; + + if (first) { + _responseHeaders = headerLine + _responseHeaders; + } + else { + _responseHeaders += headerLine; + } +} + +void WebServer::setContentLength(const size_t contentLength) { + _contentLength = contentLength; +} + +void WebServer::enableDelay(boolean value) { + _nullDelay = value; +} + +void WebServer::enableCORS(boolean value) { + _corsEnabled = value; +} + +void WebServer::enableCrossOrigin(boolean value) { + enableCORS(value); +} + +void WebServer::_prepareHeader(String& response, int code, const char* content_type, size_t contentLength) { + response = String(F("HTTP/1.")) + String(_currentVersion) + ' '; + response += String(code); + response += ' '; + response += _responseCodeToString(code); + response += "\r\n"; + + using namespace mime; + if (!content_type) + content_type = mimeTable[html].mimeType; + + sendHeader(String(F("Content-Type")), String(FPSTR(content_type)), true); + if (_contentLength == CONTENT_LENGTH_NOT_SET) { + sendHeader(String(FPSTR(Content_Length)), String(contentLength)); + } else if (_contentLength != CONTENT_LENGTH_UNKNOWN) { + sendHeader(String(FPSTR(Content_Length)), String(_contentLength)); + } else if(_contentLength == CONTENT_LENGTH_UNKNOWN && _currentVersion){ //HTTP/1.1 or above client + //let's do chunked + _chunked = true; + sendHeader(String(F("Accept-Ranges")),String(F("none"))); + sendHeader(String(F("Transfer-Encoding")),String(F("chunked"))); + } + if (_corsEnabled) { + sendHeader(String(FPSTR("Access-Control-Allow-Origin")), String("*")); + } + sendHeader(String(F("Connection")), String(F("close"))); + + response += _responseHeaders; + response += "\r\n"; + _responseHeaders = ""; +} + +void WebServer::send(int code, const char* content_type, const String& content) { + String header; + // Can we asume the following? + //if(code == 200 && content.length() == 0 && _contentLength == CONTENT_LENGTH_NOT_SET) + // _contentLength = CONTENT_LENGTH_UNKNOWN; + _prepareHeader(header, code, content_type, content.length()); + _currentClientWrite(header.c_str(), header.length()); + if(content.length()) + sendContent(content); +} + +void WebServer::send_P(int code, PGM_P content_type, PGM_P content) { + size_t contentLength = 0; + + if (content != NULL) { + contentLength = strlen_P(content); + } + + String header; + char type[64]; + memccpy_P((void*)type, (PGM_VOID_P)content_type, 0, sizeof(type)); + _prepareHeader(header, code, (const char* )type, contentLength); + _currentClientWrite(header.c_str(), header.length()); + sendContent_P(content); +} + +void WebServer::send_P(int code, PGM_P content_type, PGM_P content, size_t contentLength) { + String header; + char type[64]; + memccpy_P((void*)type, (PGM_VOID_P)content_type, 0, sizeof(type)); + _prepareHeader(header, code, (const char* )type, contentLength); + sendContent(header); + sendContent_P(content, contentLength); +} + +void WebServer::send(int code, char* content_type, const String& content) { + send(code, (const char*)content_type, content); +} + +void WebServer::send(int code, const String& content_type, const String& content) { + send(code, (const char*)content_type.c_str(), content); +} + +void WebServer::sendContent(const String& content) { + sendContent(content.c_str(), content.length()); +} + +void WebServer::sendContent(const char* content, size_t contentLength) { + const char * footer = "\r\n"; + if(_chunked) { + char * chunkSize = (char *)malloc(11); + if(chunkSize){ + sprintf(chunkSize, "%x%s", contentLength, footer); + _currentClientWrite(chunkSize, strlen(chunkSize)); + free(chunkSize); + } + } + _currentClientWrite(content, contentLength); + if(_chunked){ + _currentClient.write(footer, 2); + if (contentLength == 0) { + _chunked = false; + } + } +} + +void WebServer::sendContent_P(PGM_P content) { + sendContent_P(content, strlen_P(content)); +} + +void WebServer::sendContent_P(PGM_P content, size_t size) { + const char * footer = "\r\n"; + if(_chunked) { + char * chunkSize = (char *)malloc(11); + if(chunkSize){ + sprintf(chunkSize, "%x%s", size, footer); + _currentClientWrite(chunkSize, strlen(chunkSize)); + free(chunkSize); + } + } + _currentClientWrite_P(content, size); + if(_chunked){ + _currentClient.write(footer, 2); + if (size == 0) { + _chunked = false; + } + } +} + + +void WebServer::_streamFileCore(const size_t fileSize, const String & fileName, const String & contentType) +{ + using namespace mime; + setContentLength(fileSize); + if (fileName.endsWith(String(FPSTR(mimeTable[gz].endsWith))) && + contentType != String(FPSTR(mimeTable[gz].mimeType)) && + contentType != String(FPSTR(mimeTable[none].mimeType))) { + sendHeader(F("Content-Encoding"), F("gzip")); + } + send(200, contentType, ""); +} + +String WebServer::pathArg(unsigned int i) { + if (_currentHandler != nullptr) + return _currentHandler->pathArg(i); + return ""; +} + +String WebServer::arg(String name) { + for (int j = 0; j < _postArgsLen; ++j) { + if ( _postArgs[j].key == name ) + return _postArgs[j].value; + } + for (int i = 0; i < _currentArgCount; ++i) { + if ( _currentArgs[i].key == name ) + return _currentArgs[i].value; + } + return ""; +} + +String WebServer::arg(int i) { + if (i < _currentArgCount) + return _currentArgs[i].value; + return ""; +} + +String WebServer::argName(int i) { + if (i < _currentArgCount) + return _currentArgs[i].key; + return ""; +} + +int WebServer::args() { + return _currentArgCount; +} + +bool WebServer::hasArg(String name) { + for (int j = 0; j < _postArgsLen; ++j) { + if (_postArgs[j].key == name) + return true; + } + for (int i = 0; i < _currentArgCount; ++i) { + if (_currentArgs[i].key == name) + return true; + } + return false; +} + + +String WebServer::header(String name) { + for (int i = 0; i < _headerKeysCount; ++i) { + if (_currentHeaders[i].key.equalsIgnoreCase(name)) + return _currentHeaders[i].value; + } + return ""; +} + +void WebServer::collectHeaders(const char* headerKeys[], const size_t headerKeysCount) { + _headerKeysCount = headerKeysCount + 1; + if (_currentHeaders) + delete[]_currentHeaders; + _currentHeaders = new RequestArgument[_headerKeysCount]; + _currentHeaders[0].key = FPSTR(AUTHORIZATION_HEADER); + for (int i = 1; i < _headerKeysCount; i++){ + _currentHeaders[i].key = headerKeys[i-1]; + } +} + +String WebServer::header(int i) { + if (i < _headerKeysCount) + return _currentHeaders[i].value; + return ""; +} + +String WebServer::headerName(int i) { + if (i < _headerKeysCount) + return _currentHeaders[i].key; + return ""; +} + +int WebServer::headers() { + return _headerKeysCount; +} + +bool WebServer::hasHeader(String name) { + for (int i = 0; i < _headerKeysCount; ++i) { + if ((_currentHeaders[i].key.equalsIgnoreCase(name)) && (_currentHeaders[i].value.length() > 0)) + return true; + } + return false; +} + +String WebServer::hostHeader() { + return _hostHeader; +} + +void WebServer::onFileUpload(THandlerFunction fn) { + _fileUploadHandler = fn; +} + +void WebServer::onNotFound(THandlerFunction fn) { + _notFoundHandler = fn; +} + +void WebServer::_handleRequest() { + bool handled = false; + if (!_currentHandler){ + log_e("request handler not found"); + } + else { + handled = _currentHandler->handle(*this, _currentMethod, _currentUri); + if (!handled) { + log_e("request handler failed to handle request"); + } + } + if (!handled && _notFoundHandler) { + _notFoundHandler(); + handled = true; + } + if (!handled) { + using namespace mime; + send(404, String(FPSTR(mimeTable[html].mimeType)), String(F("Not found: ")) + _currentUri); + handled = true; + } + if (handled) { + _finalizeResponse(); + } + _currentUri = ""; +} + + +void WebServer::_finalizeResponse() { + if (_chunked) { + sendContent(""); + } +} + +String WebServer::_responseCodeToString(int code) { + switch (code) { + case 100: return F("Continue"); + case 101: return F("Switching Protocols"); + case 200: return F("OK"); + case 201: return F("Created"); + case 202: return F("Accepted"); + case 203: return F("Non-Authoritative Information"); + case 204: return F("No Content"); + case 205: return F("Reset Content"); + case 206: return F("Partial Content"); + case 300: return F("Multiple Choices"); + case 301: return F("Moved Permanently"); + case 302: return F("Found"); + case 303: return F("See Other"); + case 304: return F("Not Modified"); + case 305: return F("Use Proxy"); + case 307: return F("Temporary Redirect"); + case 400: return F("Bad Request"); + case 401: return F("Unauthorized"); + case 402: return F("Payment Required"); + case 403: return F("Forbidden"); + case 404: return F("Not Found"); + case 405: return F("Method Not Allowed"); + case 406: return F("Not Acceptable"); + case 407: return F("Proxy Authentication Required"); + case 408: return F("Request Time-out"); + case 409: return F("Conflict"); + case 410: return F("Gone"); + case 411: return F("Length Required"); + case 412: return F("Precondition Failed"); + case 413: return F("Request Entity Too Large"); + case 414: return F("Request-URI Too Large"); + case 415: return F("Unsupported Media Type"); + case 416: return F("Requested range not satisfiable"); + case 417: return F("Expectation Failed"); + case 500: return F("Internal Server Error"); + case 501: return F("Not Implemented"); + case 502: return F("Bad Gateway"); + case 503: return F("Service Unavailable"); + case 504: return F("Gateway Time-out"); + case 505: return F("HTTP Version not supported"); + default: return F(""); + } +} diff --git a/esp32s2_WebServer_Patch/WebServer.h b/esp32s2_WebServer_Patch/WebServer.h new file mode 100644 index 0000000..38c1016 --- /dev/null +++ b/esp32s2_WebServer_Patch/WebServer.h @@ -0,0 +1,211 @@ +/* + WebServer.h - Dead simple web-server. + Supports only one simultaneous client, knows how to handle GET and POST. + + Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling) +*/ + + +#ifndef WEBSERVER_H +#define WEBSERVER_H + +#include +#include +#include +#include "HTTP_Method.h" +#include "Uri.h" + +enum HTTPUploadStatus { UPLOAD_FILE_START, UPLOAD_FILE_WRITE, UPLOAD_FILE_END, + UPLOAD_FILE_ABORTED }; +enum HTTPClientStatus { HC_NONE, HC_WAIT_READ, HC_WAIT_CLOSE }; +enum HTTPAuthMethod { BASIC_AUTH, DIGEST_AUTH }; + +#define HTTP_DOWNLOAD_UNIT_SIZE 1436 + +#ifndef HTTP_UPLOAD_BUFLEN +#define HTTP_UPLOAD_BUFLEN 1436 +#endif + +#define HTTP_MAX_DATA_WAIT 5000 //ms to wait for the client to send the request +#define HTTP_MAX_POST_WAIT 5000 //ms to wait for POST data to arrive +#define HTTP_MAX_SEND_WAIT 5000 //ms to wait for data chunk to be ACKed +#define HTTP_MAX_CLOSE_WAIT 2000 //ms to wait for the client to close the connection + +#define CONTENT_LENGTH_UNKNOWN ((size_t) -1) +#define CONTENT_LENGTH_NOT_SET ((size_t) -2) + +class WebServer; + +typedef struct { + HTTPUploadStatus status; + String filename; + String name; + String type; + size_t totalSize; // file size + size_t currentSize; // size of data currently in buf + uint8_t buf[HTTP_UPLOAD_BUFLEN]; +} HTTPUpload; + +#include "detail/RequestHandler.h" + +namespace fs { +class FS; +} + +class WebServer +{ +public: + WebServer(IPAddress addr, int port = 80); + WebServer(int port = 80); + virtual ~WebServer(); + + virtual void begin(); + virtual void begin(uint16_t port); + virtual void handleClient(); + + virtual void close(); + void stop(); + + bool authenticate(const char * username, const char * password); + void requestAuthentication(HTTPAuthMethod mode = BASIC_AUTH, const char* realm = NULL, const String& authFailMsg = String("") ); + + typedef std::function THandlerFunction; + void on(const Uri &uri, THandlerFunction handler); + void on(const Uri &uri, HTTPMethod method, THandlerFunction fn); + void on(const Uri &uri, HTTPMethod method, THandlerFunction fn, THandlerFunction ufn); + void addHandler(RequestHandler* handler); + void serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_header = NULL ); + void onNotFound(THandlerFunction fn); //called when handler is not assigned + void onFileUpload(THandlerFunction fn); //handle file uploads + + String uri() { return _currentUri; } + HTTPMethod method() { return _currentMethod; } + virtual WiFiClient client() { return _currentClient; } + HTTPUpload& upload() { return *_currentUpload; } + + String pathArg(unsigned int i); // get request path argument by number + String arg(String name); // get request argument value by name + String arg(int i); // get request argument value by number + String argName(int i); // get request argument name by number + int args(); // get arguments count + bool hasArg(String name); // check if argument exists + void collectHeaders(const char* headerKeys[], const size_t headerKeysCount); // set the request headers to collect + String header(String name); // get request header value by name + String header(int i); // get request header value by number + String headerName(int i); // get request header name by number + int headers(); // get header count + bool hasHeader(String name); // check if header exists + + String hostHeader(); // get request host header if available or empty String if not + + // send response to the client + // code - HTTP response code, can be 200 or 404 + // content_type - HTTP content type, like "text/plain" or "image/png" + // content - actual content body + void send(int code, const char* content_type = NULL, const String& content = String("")); + void send(int code, char* content_type, const String& content); + void send(int code, const String& content_type, const String& content); + void send_P(int code, PGM_P content_type, PGM_P content); + void send_P(int code, PGM_P content_type, PGM_P content, size_t contentLength); + + void enableDelay(boolean value); + void enableCORS(boolean value = true); + void enableCrossOrigin(boolean value = true); + + void setContentLength(const size_t contentLength); + void sendHeader(const String& name, const String& value, bool first = false); + void sendContent(const String& content); + void sendContent(const char* content, size_t contentLength); + void sendContent_P(PGM_P content); + void sendContent_P(PGM_P content, size_t size); + + static String urlDecode(const String& text); + + template + size_t streamFile(T &file, const String& contentType) { + _streamFileCore(file.size(), file.name(), contentType); + return _currentClient.write(file); + } + +protected: + virtual size_t _currentClientWrite(const char* b, size_t l) { return _currentClient.write( b, l ); } + virtual size_t _currentClientWrite_P(PGM_P b, size_t l) { return _currentClient.write_P( b, l ); } + void _addRequestHandler(RequestHandler* handler); + void _handleRequest(); + void _finalizeResponse(); + bool _parseRequest(WiFiClient& client); + void _parseArguments(String data); + static String _responseCodeToString(int code); + bool _parseForm(WiFiClient& client, String boundary, uint32_t len); + bool _parseFormUploadAborted(); + void _uploadWriteByte(uint8_t b); + int _uploadReadByte(WiFiClient& client); + void _prepareHeader(String& response, int code, const char* content_type, size_t contentLength); + bool _collectHeader(const char* headerName, const char* headerValue); + + void _streamFileCore(const size_t fileSize, const String & fileName, const String & contentType); + + String _getRandomHexString(); + // for extracting Auth parameters + String _extractParam(String& authReq,const String& param,const char delimit = '"'); + + struct RequestArgument { + String key; + String value; + }; + + boolean _corsEnabled; + WiFiServer _server; + + WiFiClient _currentClient; + HTTPMethod _currentMethod; + String _currentUri; + uint8_t _currentVersion; + HTTPClientStatus _currentStatus; + unsigned long _statusChange; + bool _nullDelay; + + RequestHandler* _currentHandler; + RequestHandler* _firstHandler; + RequestHandler* _lastHandler; + THandlerFunction _notFoundHandler; + THandlerFunction _fileUploadHandler; + + int _currentArgCount; + RequestArgument* _currentArgs; + int _postArgsLen; + RequestArgument* _postArgs; + + std::unique_ptr _currentUpload; + + int _headerKeysCount; + RequestArgument* _currentHeaders; + size_t _contentLength; + String _responseHeaders; + + String _hostHeader; + bool _chunked; + + String _snonce; // Store noance and opaque for future comparison + String _sopaque; + String _srealm; // Store the Auth realm between Calls + +}; + + +#endif //ESP8266WEBSERVER_H diff --git a/examples/AsyncCustomHeader_STM32/AsyncCustomHeader_STM32.ino b/examples/AsyncCustomHeader_STM32/AsyncCustomHeader_STM32.ino index 37ab6b2..00f6978 100644 --- a/examples/AsyncCustomHeader_STM32/AsyncCustomHeader_STM32.ino +++ b/examples/AsyncCustomHeader_STM32/AsyncCustomHeader_STM32.ino @@ -17,7 +17,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . - Version: 1.1.2 + Version: 1.1.3 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -27,6 +27,7 @@ 1.1.0 K Hoang 23/12/2020 Add HTTP PUT, PATCH, DELETE and HEAD methods 1.1.1 K Hoang 24/12/2020 Prevent crash if request and/or method not correct. 1.1.2 K Hoang 11/02/2021 Rename _lock and _unlock to avoid conflict with AsyncWebServer library + 1.1.3 K Hoang 25/02/2021 Fix non-persistent Connection header bug *****************************************************************************************************************************/ #include "defines.h" @@ -35,8 +36,8 @@ //char GET_ServerAddress[] = "192.168.2.110/"; char GET_ServerAddress[] = "http://worldtimeapi.org/api/timezone/America/Toronto.txt"; -// 600s = 10 minutes to not flooding, 10s in testing -#define HTTP_REQUEST_INTERVAL_MS 10000 //600000 +// 600s = 10 minutes to not flooding, 60s in testing +#define HTTP_REQUEST_INTERVAL_MS 60000 //600000 #include // https://github.com/khoih-prog/AsyncHTTPRequest_Generic diff --git a/examples/AsyncCustomHeader_STM32/defines.h b/examples/AsyncCustomHeader_STM32/defines.h index e8e5fa4..264966f 100644 --- a/examples/AsyncCustomHeader_STM32/defines.h +++ b/examples/AsyncCustomHeader_STM32/defines.h @@ -19,7 +19,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . - Version: 1.1.2 + Version: 1.1.3 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -29,6 +29,7 @@ 1.1.0 K Hoang 23/12/2020 Add HTTP PUT, PATCH, DELETE and HEAD methods 1.1.1 K Hoang 24/12/2020 Prevent crash if request and/or method not correct. 1.1.2 K Hoang 11/02/2021 Rename _lock and _unlock to avoid conflict with AsyncWebServer library + 1.1.3 K Hoang 25/02/2021 Fix non-persistent Connection header bug *****************************************************************************************************************************/ /* Currently support diff --git a/examples/AsyncDweetGet_STM32/AsyncDweetGet_STM32.ino b/examples/AsyncDweetGet_STM32/AsyncDweetGet_STM32.ino index ff3b40b..52d1b83 100644 --- a/examples/AsyncDweetGet_STM32/AsyncDweetGet_STM32.ino +++ b/examples/AsyncDweetGet_STM32/AsyncDweetGet_STM32.ino @@ -17,7 +17,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . - Version: 1.1.2 + Version: 1.1.3 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -27,6 +27,7 @@ 1.1.0 K Hoang 23/12/2020 Add HTTP PUT, PATCH, DELETE and HEAD methods 1.1.1 K Hoang 24/12/2020 Prevent crash if request and/or method not correct. 1.1.2 K Hoang 11/02/2021 Rename _lock and _unlock to avoid conflict with AsyncWebServer library + 1.1.3 K Hoang 25/02/2021 Fix non-persistent Connection header bug *****************************************************************************************************************************/ /** @@ -46,8 +47,8 @@ const char GET_ServerAddress[] = "dweet.io"; // use your own thing name here String dweetName = "/dweet/for/currentSecond?second="; -// 10s = 10 seconds to not flooding the server -#define HTTP_REQUEST_INTERVAL_MS 10000 +// 60s = 60 seconds to not flooding the server +#define HTTP_REQUEST_INTERVAL_MS 60000 #include // https://github.com/khoih-prog/AsyncHTTPRequest_Generic diff --git a/examples/AsyncDweetGet_STM32/defines.h b/examples/AsyncDweetGet_STM32/defines.h index e8e5fa4..264966f 100644 --- a/examples/AsyncDweetGet_STM32/defines.h +++ b/examples/AsyncDweetGet_STM32/defines.h @@ -19,7 +19,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . - Version: 1.1.2 + Version: 1.1.3 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -29,6 +29,7 @@ 1.1.0 K Hoang 23/12/2020 Add HTTP PUT, PATCH, DELETE and HEAD methods 1.1.1 K Hoang 24/12/2020 Prevent crash if request and/or method not correct. 1.1.2 K Hoang 11/02/2021 Rename _lock and _unlock to avoid conflict with AsyncWebServer library + 1.1.3 K Hoang 25/02/2021 Fix non-persistent Connection header bug *****************************************************************************************************************************/ /* Currently support diff --git a/examples/AsyncDweetPost_STM32/AsyncDweetPost_STM32.ino b/examples/AsyncDweetPost_STM32/AsyncDweetPost_STM32.ino index 0908186..f868638 100644 --- a/examples/AsyncDweetPost_STM32/AsyncDweetPost_STM32.ino +++ b/examples/AsyncDweetPost_STM32/AsyncDweetPost_STM32.ino @@ -17,7 +17,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . - Version: 1.1.2 + Version: 1.1.3 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -27,6 +27,7 @@ 1.1.0 K Hoang 23/12/2020 Add HTTP PUT, PATCH, DELETE and HEAD methods 1.1.1 K Hoang 24/12/2020 Prevent crash if request and/or method not correct. 1.1.2 K Hoang 11/02/2021 Rename _lock and _unlock to avoid conflict with AsyncWebServer library + 1.1.3 K Hoang 25/02/2021 Fix non-persistent Connection header bug *****************************************************************************************************************************/ // Dweet.io POST client. Connects to dweet.io once every ten seconds, sends a POST request and a request body. @@ -40,8 +41,8 @@ const char POST_ServerAddress[] = "dweet.io"; // use your own thing name here String dweetName = "/dweet/for/pinA0-Read?"; -// 10s = 10 seconds to not flooding the server -#define HTTP_REQUEST_INTERVAL_MS 10000 +// 60s = 60 seconds to not flooding the server +#define HTTP_REQUEST_INTERVAL_MS 60000 #include // https://github.com/khoih-prog/AsyncHTTPRequest_Generic diff --git a/examples/AsyncDweetPost_STM32/defines.h b/examples/AsyncDweetPost_STM32/defines.h index e8e5fa4..264966f 100644 --- a/examples/AsyncDweetPost_STM32/defines.h +++ b/examples/AsyncDweetPost_STM32/defines.h @@ -19,7 +19,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . - Version: 1.1.2 + Version: 1.1.3 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -29,6 +29,7 @@ 1.1.0 K Hoang 23/12/2020 Add HTTP PUT, PATCH, DELETE and HEAD methods 1.1.1 K Hoang 24/12/2020 Prevent crash if request and/or method not correct. 1.1.2 K Hoang 11/02/2021 Rename _lock and _unlock to avoid conflict with AsyncWebServer library + 1.1.3 K Hoang 25/02/2021 Fix non-persistent Connection header bug *****************************************************************************************************************************/ /* Currently support diff --git a/examples/AsyncHTTPMultiRequests_ESP/AsyncHTTPMultiRequests_ESP.ino b/examples/AsyncHTTPMultiRequests_ESP/AsyncHTTPMultiRequests_ESP.ino index 61fa38e..f90ba02 100644 --- a/examples/AsyncHTTPMultiRequests_ESP/AsyncHTTPMultiRequests_ESP.ino +++ b/examples/AsyncHTTPMultiRequests_ESP/AsyncHTTPMultiRequests_ESP.ino @@ -17,7 +17,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . - Version: 1.1.2 + Version: 1.1.3 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -27,6 +27,7 @@ 1.1.0 K Hoang 23/12/2020 Add HTTP PUT, PATCH, DELETE and HEAD methods 1.1.1 K Hoang 24/12/2020 Prevent crash if request and/or method not correct. 1.1.2 K Hoang 11/02/2021 Rename _lock and _unlock to avoid conflict with AsyncWebServer library + 1.1.3 K Hoang 25/02/2021 Fix non-persistent Connection header bug *****************************************************************************************************************************/ //************************************************************************************************************ // @@ -60,7 +61,7 @@ #define _ASYNC_HTTP_LOGLEVEL_ 1 // 300s = 5 minutes to not flooding -#define HTTP_REQUEST_INTERVAL 30 //300 +#define HTTP_REQUEST_INTERVAL 60 //300 // 10s #define HEARTBEAT_INTERVAL 10 diff --git a/examples/AsyncHTTPRequest_ESP/AsyncHTTPRequest_ESP.ino b/examples/AsyncHTTPRequest_ESP/AsyncHTTPRequest_ESP.ino index 179ff4c..c8ed46e 100644 --- a/examples/AsyncHTTPRequest_ESP/AsyncHTTPRequest_ESP.ino +++ b/examples/AsyncHTTPRequest_ESP/AsyncHTTPRequest_ESP.ino @@ -17,7 +17,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . - Version: 1.1.2 + Version: 1.1.3 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -27,6 +27,7 @@ 1.1.0 K Hoang 23/12/2020 Add HTTP PUT, PATCH, DELETE and HEAD methods 1.1.1 K Hoang 24/12/2020 Prevent crash if request and/or method not correct. 1.1.2 K Hoang 11/02/2021 Rename _lock and _unlock to avoid conflict with AsyncWebServer library + 1.1.3 K Hoang 25/02/2021 Fix non-persistent Connection header bug *****************************************************************************************************************************/ //************************************************************************************************************ // @@ -60,7 +61,7 @@ #define _ASYNC_HTTP_LOGLEVEL_ 1 // 300s = 5 minutes to not flooding -#define HTTP_REQUEST_INTERVAL 30 //300 +#define HTTP_REQUEST_INTERVAL 60 //300 // 10s #define HEARTBEAT_INTERVAL 10 diff --git a/examples/AsyncHTTPRequest_ESP_WiFiManager/AsyncHTTPRequest_ESP_WiFiManager.ino b/examples/AsyncHTTPRequest_ESP_WiFiManager/AsyncHTTPRequest_ESP_WiFiManager.ino index 28985c8..fb428f9 100644 --- a/examples/AsyncHTTPRequest_ESP_WiFiManager/AsyncHTTPRequest_ESP_WiFiManager.ino +++ b/examples/AsyncHTTPRequest_ESP_WiFiManager/AsyncHTTPRequest_ESP_WiFiManager.ino @@ -17,7 +17,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . - Version: 1.1.2 + Version: 1.1.3 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -27,6 +27,7 @@ 1.1.0 K Hoang 23/12/2020 Add HTTP PUT, PATCH, DELETE and HEAD methods 1.1.1 K Hoang 24/12/2020 Prevent crash if request and/or method not correct. 1.1.2 K Hoang 11/02/2021 Rename _lock and _unlock to avoid conflict with AsyncWebServer library + 1.1.3 K Hoang 25/02/2021 Fix non-persistent Connection header bug *****************************************************************************************************************************/ //************************************************************************************************************ // @@ -61,8 +62,8 @@ #define ASYNC_HTTP_DEBUG_PORT Serial #define _ASYNC_HTTP_LOGLEVEL_ 1 -// 300s = 5 minutes to not flooding, 10s in testing -#define HTTP_REQUEST_INTERVAL 10 //300 +// 300s = 5 minutes to not flooding, 60s in testing +#define HTTP_REQUEST_INTERVAL 60 //300 //Ported to ESP32 #ifdef ESP32 @@ -74,9 +75,22 @@ #include WiFiMulti wifiMulti; - #define USE_SPIFFS true + // LittleFS has higher priority than SPIFFS + #define USE_LITTLEFS true + #define USE_SPIFFS false - #if USE_SPIFFS + #if USE_LITTLEFS + // Use LittleFS + #include "FS.h" + + // The library will be depreciated after being merged to future major Arduino esp32 core release 2.x + // At that time, just remove this library inclusion + #include // https://github.com/lorol/LITTLEFS + + FS* filesystem = &LITTLEFS; + #define FileFS LITTLEFS + #define FS_Name "LittleFS" + #elif USE_SPIFFS #include FS* filesystem = &SPIFFS; #define FileFS SPIFFS diff --git a/examples/AsyncHTTPRequest_STM32/AsyncHTTPRequest_STM32.ino b/examples/AsyncHTTPRequest_STM32/AsyncHTTPRequest_STM32.ino index 8f87f5a..08e171b 100644 --- a/examples/AsyncHTTPRequest_STM32/AsyncHTTPRequest_STM32.ino +++ b/examples/AsyncHTTPRequest_STM32/AsyncHTTPRequest_STM32.ino @@ -17,7 +17,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . - Version: 1.1.2 + Version: 1.1.3 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -27,6 +27,7 @@ 1.1.0 K Hoang 23/12/2020 Add HTTP PUT, PATCH, DELETE and HEAD methods 1.1.1 K Hoang 24/12/2020 Prevent crash if request and/or method not correct. 1.1.2 K Hoang 11/02/2021 Rename _lock and _unlock to avoid conflict with AsyncWebServer library + 1.1.3 K Hoang 25/02/2021 Fix non-persistent Connection header bug *****************************************************************************************************************************/ //************************************************************************************************************ // @@ -53,8 +54,8 @@ #include "defines.h" -// 600s = 10 minutes to not flooding, 10s in testing -#define HTTP_REQUEST_INTERVAL_MS 10000 //600000 +// 600s = 10 minutes to not flooding, 60s in testing +#define HTTP_REQUEST_INTERVAL_MS 60000 //600000 #include // https://github.com/khoih-prog/AsyncHTTPRequest_Generic diff --git a/examples/AsyncHTTPRequest_STM32/defines.h b/examples/AsyncHTTPRequest_STM32/defines.h index e8e5fa4..264966f 100644 --- a/examples/AsyncHTTPRequest_STM32/defines.h +++ b/examples/AsyncHTTPRequest_STM32/defines.h @@ -19,7 +19,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . - Version: 1.1.2 + Version: 1.1.3 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -29,6 +29,7 @@ 1.1.0 K Hoang 23/12/2020 Add HTTP PUT, PATCH, DELETE and HEAD methods 1.1.1 K Hoang 24/12/2020 Prevent crash if request and/or method not correct. 1.1.2 K Hoang 11/02/2021 Rename _lock and _unlock to avoid conflict with AsyncWebServer library + 1.1.3 K Hoang 25/02/2021 Fix non-persistent Connection header bug *****************************************************************************************************************************/ /* Currently support diff --git a/examples/AsyncSimpleGET_STM32/AsyncSimpleGET_STM32.ino b/examples/AsyncSimpleGET_STM32/AsyncSimpleGET_STM32.ino index 4ce31b5..3373178 100644 --- a/examples/AsyncSimpleGET_STM32/AsyncSimpleGET_STM32.ino +++ b/examples/AsyncSimpleGET_STM32/AsyncSimpleGET_STM32.ino @@ -17,7 +17,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . - Version: 1.1.2 + Version: 1.1.3 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -27,6 +27,7 @@ 1.1.0 K Hoang 23/12/2020 Add HTTP PUT, PATCH, DELETE and HEAD methods 1.1.1 K Hoang 24/12/2020 Prevent crash if request and/or method not correct. 1.1.2 K Hoang 11/02/2021 Rename _lock and _unlock to avoid conflict with AsyncWebServer library + 1.1.3 K Hoang 25/02/2021 Fix non-persistent Connection header bug *****************************************************************************************************************************/ #include "defines.h" @@ -35,8 +36,8 @@ //char GET_ServerAddress[] = "ipv4bot.whatismyipaddress.com/"; char GET_ServerAddress[] = "http://worldtimeapi.org/api/timezone/America/Toronto.txt"; -// 600s = 10 minutes to not flooding, 10s in testing -#define HTTP_REQUEST_INTERVAL_MS 10000 //600000 +// 600s = 10 minutes to not flooding, 60s in testing +#define HTTP_REQUEST_INTERVAL_MS 60000 //600000 #include // https://github.com/khoih-prog/AsyncHTTPRequest_Generic diff --git a/examples/AsyncSimpleGET_STM32/defines.h b/examples/AsyncSimpleGET_STM32/defines.h index 9a04371..a9f9fd3 100644 --- a/examples/AsyncSimpleGET_STM32/defines.h +++ b/examples/AsyncSimpleGET_STM32/defines.h @@ -19,7 +19,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . - Version: 1.1.2 + Version: 1.1.3 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -29,6 +29,7 @@ 1.1.0 K Hoang 23/12/2020 Add HTTP PUT, PATCH, DELETE and HEAD methods 1.1.1 K Hoang 24/12/2020 Prevent crash if request and/or method not correct. 1.1.2 K Hoang 11/02/2021 Rename _lock and _unlock to avoid conflict with AsyncWebServer library + 1.1.3 K Hoang 25/02/2021 Fix non-persistent Connection header bug *****************************************************************************************************************************/ /* Currently support diff --git a/examples/AsyncWebClientRepeating_STM32/AsyncWebClientRepeating_STM32.ino b/examples/AsyncWebClientRepeating_STM32/AsyncWebClientRepeating_STM32.ino index d4b9f40..83fcc05 100644 --- a/examples/AsyncWebClientRepeating_STM32/AsyncWebClientRepeating_STM32.ino +++ b/examples/AsyncWebClientRepeating_STM32/AsyncWebClientRepeating_STM32.ino @@ -17,7 +17,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . - Version: 1.1.2 + Version: 1.1.3 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -27,6 +27,7 @@ 1.1.0 K Hoang 23/12/2020 Add HTTP PUT, PATCH, DELETE and HEAD methods 1.1.1 K Hoang 24/12/2020 Prevent crash if request and/or method not correct. 1.1.2 K Hoang 11/02/2021 Rename _lock and _unlock to avoid conflict with AsyncWebServer library + 1.1.3 K Hoang 25/02/2021 Fix non-persistent Connection header bug *****************************************************************************************************************************/ #include "defines.h" @@ -37,8 +38,8 @@ const char GET_ServerAddress[] = "arduino.cc"; // GET location String GET_Location = "/asciilogo.txt"; -// 10s = 10 seconds to not flooding the server -#define HTTP_REQUEST_INTERVAL_MS 10000 +// 60s = 60 seconds to not flooding the server +#define HTTP_REQUEST_INTERVAL_MS 60000 #include // https://github.com/khoih-prog/AsyncHTTPRequest_Generic diff --git a/examples/AsyncWebClientRepeating_STM32/defines.h b/examples/AsyncWebClientRepeating_STM32/defines.h index e8e5fa4..264966f 100644 --- a/examples/AsyncWebClientRepeating_STM32/defines.h +++ b/examples/AsyncWebClientRepeating_STM32/defines.h @@ -19,7 +19,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . - Version: 1.1.2 + Version: 1.1.3 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -29,6 +29,7 @@ 1.1.0 K Hoang 23/12/2020 Add HTTP PUT, PATCH, DELETE and HEAD methods 1.1.1 K Hoang 24/12/2020 Prevent crash if request and/or method not correct. 1.1.2 K Hoang 11/02/2021 Rename _lock and _unlock to avoid conflict with AsyncWebServer library + 1.1.3 K Hoang 25/02/2021 Fix non-persistent Connection header bug *****************************************************************************************************************************/ /* Currently support diff --git a/library.json b/library.json index 2de4045..5a53903 100644 --- a/library.json +++ b/library.json @@ -1,8 +1,8 @@ { "name":"AsyncHTTPRequest_Generic", - "version": "1.1.2", - "description":"Simple Async HTTP Request library, supporting GET, POST, PUT, PATCH, DELETE and HEAD, on top of AsyncTCP libraries, such as AsyncTCP, ESPAsyncTCP, AsyncTCP_STM32, etc.. for ESP32, ESP8266 and currently STM32 with built-in LAN8742A Ethernet.", - "keywords":"async,tcp,http,ESP8266,ESP32,ESPAsyncTCP,AsyncTCP,stm32,ethernet,wifi,lan8742a", + "version": "1.1.3", + "description":"Simple Async HTTP Request library, supporting GET, POST, PUT, PATCH, DELETE and HEAD, on top of AsyncTCP libraries, such as AsyncTCP, ESPAsyncTCP, AsyncTCP_STM32, etc.. for ESP32 (including ESP32-S2), ESP8266 and currently STM32 with built-in LAN8742A Ethernet.", + "keywords":"async,tcp,http,ESP8266,ESP32,ESP32-S2,ESPAsyncTCP,AsyncTCP,stm32,ethernet,wifi,lan8742a", "authors": [ { "name": "Bob Lemaire", @@ -39,7 +39,7 @@ { "owner": "khoih.prog", "name": "ESPAsync_WiFiManager", - "version": ">=1.4.3", + "version": ">=1.6.0", "platforms": ["espressif8266", "espressif32"] }, { @@ -54,6 +54,12 @@ "version": ">=1.2.0", "platforms": "ststm32" }, + { + "owner": "lorol", + "name": "LittleFS_esp32", + "version": ">=1.0.5", + "platforms": ["espressif32"] + }, { "name": "external-repo", "version": "https://github.com/philbowles/STM32AsyncTCP" diff --git a/library.properties b/library.properties index 1594f17..3b356e9 100644 --- a/library.properties +++ b/library.properties @@ -1,12 +1,12 @@ name=AsyncHTTPRequest_Generic -version=1.1.2 +version=1.1.3 author=Bob Lemaire,Khoi Hoang maintainer=Khoi Hoang license=MIT -sentence=Simple Async HTTP Request library, supporting GET, POST, PUT, PATCH, DELETE and HEAD, on top of AsyncTCP libraries, such as AsyncTCP, ESPAsyncTCP, AsyncTCP_STM32, etc.. for ESP32, ESP8266 and currently STM32 with built-in LAN8742A Ethernet. -paragraph=This AsyncHTTPRequest_Generic Library, supporting GET and POST, for ESP32, ESP8266 and STM32 with built-in LAN8742A Ethernet, such as Nucleo-144 F767ZI, etc. +sentence=Simple Async HTTP Request library, supporting GET, POST, PUT, PATCH, DELETE and HEAD, on top of AsyncTCP libraries, such as AsyncTCP, ESPAsyncTCP, AsyncTCP_STM32, etc.. for ESP32 (including ESP32-S2), ESP8266 and currently STM32 with built-in LAN8742A Ethernet. +paragraph=This AsyncHTTPRequest_Generic Library, supporting GET, POST, PUT, PATCH, DELETE and HEAD, for ESP32 (including ESP32-S2), ESP8266 and STM32 with built-in LAN8742A Ethernet, such as Nucleo-144 F767ZI, etc. category=Communication,AsyncTCP,AsyncHTTP url=https://github.com/khoih-prog/AsyncHTTPRequest_Generic architectures=* -depends=AsyncTCP,ESP AsyncTCP,ESPAsync_WiFiManager,STM32duino LwIP,STM32duino STM32Ethernet,STM32 AsyncTCP +depends=AsyncTCP,ESP AsyncTCP,ESPAsync_WiFiManager,STM32duino LwIP,STM32duino STM32Ethernet,STM32 AsyncTCP,LittleFS_esp32 includes=AsyncHTTPRequest_Generic.h diff --git a/platformio/platformio.ini b/platformio/platformio.ini index 62dad80..cb5d2c7 100644 --- a/platformio/platformio.ini +++ b/platformio/platformio.ini @@ -40,14 +40,16 @@ lib_deps = STM32AsyncTCP@>=1.0.0 STM32duino LwIP@>=2.1.2 STM32duino STM32Ethernet@>=1.2.0 - ESPAsync_WiFiManager@>=1.4.1 + ESPAsync_WiFiManager@>=1.6.0 + LittleFS_esp32@>=1.0.5 ; PlatformIO 5.x ; me-no-dev/AsyncTCP@>=1.1.1 ; me-no-dev/ESPAsyncTCP@>=1.2.2 ; philbowles/STM32AsyncTCP@>=1.0.0 ; stm32duino/STM32duino LwIP@>=2.1.2 ; stm32duino/STM32duino STM32Ethernet@>=1.2.0 -; khoih-prog/ESPAsync_WiFiManager@>=1.4.1 +; khoih-prog/ESPAsync_WiFiManager@>=1.6.0 +; lorol/LittleFS_esp32@>=1.0.5 build_flags = ; set your debug output (default=Serial) diff --git a/src/AsyncHTTPRequest_Debug_Generic.h b/src/AsyncHTTPRequest_Debug_Generic.h index 59d6e44..6396018 100644 --- a/src/AsyncHTTPRequest_Debug_Generic.h +++ b/src/AsyncHTTPRequest_Debug_Generic.h @@ -17,7 +17,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . - Version: 1.1.2 + Version: 1.1.3 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -27,6 +27,7 @@ 1.1.0 K Hoang 23/12/2020 Add HTTP PUT, PATCH, DELETE and HEAD methods 1.1.1 K Hoang 24/12/2020 Prevent crash if request and/or method not correct. 1.1.2 K Hoang 11/02/2021 Rename _lock and _unlock to avoid conflict with AsyncWebServer library + 1.1.3 K Hoang 25/02/2021 Fix non-persistent Connection header bug *****************************************************************************************************************************/ #pragma once @@ -51,29 +52,49 @@ #define _ASYNC_HTTP_LOGLEVEL_ 0 #endif -#define AHTTP_LOGERROR(x) if(_ASYNC_HTTP_LOGLEVEL_>0) { A_DBG_PORT.print("[AHTTP] "); A_DBG_PORT.println(x); } -#define AHTTP_LOGERROR0(x) if(_ASYNC_HTTP_LOGLEVEL_>0) { A_DBG_PORT.print(x); } -#define AHTTP_LOGERROR1(x,y) if(_ASYNC_HTTP_LOGLEVEL_>0) { A_DBG_PORT.print("[AHTTP] "); A_DBG_PORT.print(x); A_DBG_PORT.print(" "); A_DBG_PORT.println(y); } -#define AHTTP_LOGERROR2(x,y,z) if(_ASYNC_HTTP_LOGLEVEL_>0) { A_DBG_PORT.print("[AHTTP] "); A_DBG_PORT.print(x); A_DBG_PORT.print(" "); A_DBG_PORT.print(y); A_DBG_PORT.print(" "); A_DBG_PORT.println(z); } -#define AHTTP_LOGERROR3(x,y,z,w) if(_ASYNC_HTTP_LOGLEVEL_>0) { A_DBG_PORT.print("[AHTTP] "); A_DBG_PORT.print(x); A_DBG_PORT.print(" "); A_DBG_PORT.print(y); A_DBG_PORT.print(" "); A_DBG_PORT.print(z); A_DBG_PORT.print(" "); A_DBG_PORT.println(w); } +///////////////////////////////////////////////////////// -#define AHTTP_LOGWARN(x) if(_ASYNC_HTTP_LOGLEVEL_>1) { A_DBG_PORT.print("[AHTTP] "); A_DBG_PORT.println(x); } -#define AHTTP_LOGWARN0(x) if(_ASYNC_HTTP_LOGLEVEL_>1) { A_DBG_PORT.print(x); } -#define AHTTP_LOGWARN1(x,y) if(_ASYNC_HTTP_LOGLEVEL_>1) { A_DBG_PORT.print("[AHTTP] "); A_DBG_PORT.print(x); A_DBG_PORT.print(" "); A_DBG_PORT.println(y); } -#define AHTTP_LOGWARN2(x,y,z) if(_ASYNC_HTTP_LOGLEVEL_>1) { A_DBG_PORT.print("[AHTTP] "); A_DBG_PORT.print(x); A_DBG_PORT.print(" "); A_DBG_PORT.print(y); A_DBG_PORT.print(" "); A_DBG_PORT.println(z); } -#define AHTTP_LOGWARN3(x,y,z,w) if(_ASYNC_HTTP_LOGLEVEL_>1) { A_DBG_PORT.print("[AHTTP] "); A_DBG_PORT.print(x); A_DBG_PORT.print(" "); A_DBG_PORT.print(y); A_DBG_PORT.print(" "); A_DBG_PORT.print(z); A_DBG_PORT.print(" "); A_DBG_PORT.println(w); } +const char AHTTP_MARK[] = "[AHTTP] "; -#define AHTTP_LOGINFO(x) if(_ASYNC_HTTP_LOGLEVEL_>2) { A_DBG_PORT.print("[AHTTP] "); A_DBG_PORT.println(x); } -#define AHTTP_LOGINFO0(x) if(_ASYNC_HTTP_LOGLEVEL_>2) { A_DBG_PORT.print(x); } -#define AHTTP_LOGINFO1(x,y) if(_ASYNC_HTTP_LOGLEVEL_>2) { A_DBG_PORT.print("[AHTTP] "); A_DBG_PORT.print(x); A_DBG_PORT.print(" "); A_DBG_PORT.println(y); } -#define AHTTP_LOGINFO2(x,y,z) if(_ASYNC_HTTP_LOGLEVEL_>2) { A_DBG_PORT.print("[AHTTP] "); A_DBG_PORT.print(x); A_DBG_PORT.print(" "); A_DBG_PORT.print(y); A_DBG_PORT.print(" "); A_DBG_PORT.println(z); } -#define AHTTP_LOGINFO3(x,y,z,w) if(_ASYNC_HTTP_LOGLEVEL_>2) { A_DBG_PORT.print("[AHTTP] "); A_DBG_PORT.print(x); A_DBG_PORT.print(" "); A_DBG_PORT.print(y); A_DBG_PORT.print(" "); A_DBG_PORT.print(z); A_DBG_PORT.print(" "); A_DBG_PORT.println(w); } +#define AHTTP_PRINT_MARK AHTTP_PRINT(AHTTP_MARK) +#define AHTTP_PRINT_SP A_DBG_PORT.print(" ") -#define AHTTP_LOGDEBUG(x) if(_ASYNC_HTTP_LOGLEVEL_>3) { A_DBG_PORT.print("[AHTTP] "); A_DBG_PORT.println(x); } -#define AHTTP_LOGDEBUG0(x) if(_ASYNC_HTTP_LOGLEVEL_>3) { A_DBG_PORT.print(x); } -#define AHTTP_LOGDEBUG1(x,y) if(_ASYNC_HTTP_LOGLEVEL_>3) { A_DBG_PORT.print("[AHTTP] "); A_DBG_PORT.print(x); A_DBG_PORT.print(" "); A_DBG_PORT.println(y); } -#define AHTTP_LOGDEBUG2(x,y,z) if(_ASYNC_HTTP_LOGLEVEL_>3) { A_DBG_PORT.print("[AHTTP] "); A_DBG_PORT.print(x); A_DBG_PORT.print(" "); A_DBG_PORT.print(y); A_DBG_PORT.print(" "); A_DBG_PORT.println(z); } -#define AHTTP_LOGDEBUG3(x,y,z,w) if(_ASYNC_HTTP_LOGLEVEL_>3) { A_DBG_PORT.print("[AHTTP] "); A_DBG_PORT.print(x); A_DBG_PORT.print(" "); A_DBG_PORT.print(y); A_DBG_PORT.print(" "); A_DBG_PORT.print(z); A_DBG_PORT.print(" "); A_DBG_PORT.println(w); } +#define AHTTP_PRINT A_DBG_PORT.print +#define AHTTP_PRINTLN A_DBG_PORT.println + +///////////////////////////////////////////////////////// + +#define AHTTP_LOGERROR(x) if(_ASYNC_HTTP_LOGLEVEL_>0) { AHTTP_PRINT_MARK; AHTTP_PRINTLN(x); } +#define AHTTP_LOGERROR0(x) if(_ASYNC_HTTP_LOGLEVEL_>0) { AHTTP_PRINT(x); } +#define AHTTP_LOGERROR1(x,y) if(_ASYNC_HTTP_LOGLEVEL_>0) { AHTTP_PRINT_MARK; AHTTP_PRINT(x); AHTTP_PRINT_SP; AHTTP_PRINTLN(y); } +#define AHTTP_LOGERROR2(x,y,z) if(_ASYNC_HTTP_LOGLEVEL_>0) { AHTTP_PRINT_MARK; AHTTP_PRINT(x); AHTTP_PRINT_SP; AHTTP_PRINT(y); AHTTP_PRINT_SP; AHTTP_PRINTLN(z); } +#define AHTTP_LOGERROR3(x,y,z,w) if(_ASYNC_HTTP_LOGLEVEL_>0) { AHTTP_PRINT_MARK; AHTTP_PRINT(x); AHTTP_PRINT_SP; AHTTP_PRINT(y); AHTTP_PRINT_SP; AHTTP_PRINT(z); AHTTP_PRINT_SP; AHTTP_PRINTLN(w); } + +///////////////////////////////////////////////////////// + +#define AHTTP_LOGWARN(x) if(_ASYNC_HTTP_LOGLEVEL_>1) { AHTTP_PRINT_MARK; AHTTP_PRINTLN(x); } +#define AHTTP_LOGWARN0(x) if(_ASYNC_HTTP_LOGLEVEL_>1) { AHTTP_PRINT(x); } +#define AHTTP_LOGWARN1(x,y) if(_ASYNC_HTTP_LOGLEVEL_>1) { AHTTP_PRINT_MARK; AHTTP_PRINT(x); AHTTP_PRINT_SP; AHTTP_PRINTLN(y); } +#define AHTTP_LOGWARN2(x,y,z) if(_ASYNC_HTTP_LOGLEVEL_>1) { AHTTP_PRINT_MARK; AHTTP_PRINT(x); AHTTP_PRINT_SP; AHTTP_PRINT(y); AHTTP_PRINT_SP; AHTTP_PRINTLN(z); } +#define AHTTP_LOGWARN3(x,y,z,w) if(_ASYNC_HTTP_LOGLEVEL_>1) { AHTTP_PRINT_MARK; AHTTP_PRINT(x); AHTTP_PRINT_SP; AHTTP_PRINT(y); AHTTP_PRINT_SP; AHTTP_PRINT(z); AHTTP_PRINT_SP; AHTTP_PRINTLN(w); } + +///////////////////////////////////////////////////////// + +#define AHTTP_LOGINFO(x) if(_ASYNC_HTTP_LOGLEVEL_>2) { AHTTP_PRINT_MARK; AHTTP_PRINTLN(x); } +#define AHTTP_LOGINFO0(x) if(_ASYNC_HTTP_LOGLEVEL_>2) { AHTTP_PRINT(x); } +#define AHTTP_LOGINFO1(x,y) if(_ASYNC_HTTP_LOGLEVEL_>2) { AHTTP_PRINT_MARK; AHTTP_PRINT(x); AHTTP_PRINT_SP; AHTTP_PRINTLN(y); } +#define AHTTP_LOGINFO2(x,y,z) if(_ASYNC_HTTP_LOGLEVEL_>2) { AHTTP_PRINT_MARK; AHTTP_PRINT(x); AHTTP_PRINT_SP; AHTTP_PRINT(y); AHTTP_PRINT_SP; AHTTP_PRINTLN(z); } +#define AHTTP_LOGINFO3(x,y,z,w) if(_ASYNC_HTTP_LOGLEVEL_>2) { AHTTP_PRINT_MARK; AHTTP_PRINT(x); AHTTP_PRINT_SP; AHTTP_PRINT(y); AHTTP_PRINT_SP; AHTTP_PRINT(z); AHTTP_PRINT_SP; AHTTP_PRINTLN(w); } + +///////////////////////////////////////////////////////// + +#define AHTTP_LOGDEBUG(x) if(_ASYNC_HTTP_LOGLEVEL_>3) { AHTTP_PRINT_MARK; AHTTP_PRINTLN(x); } +#define AHTTP_LOGDEBUG0(x) if(_ASYNC_HTTP_LOGLEVEL_>3) { AHTTP_PRINT(x); } +#define AHTTP_LOGDEBUG1(x,y) if(_ASYNC_HTTP_LOGLEVEL_>3) { AHTTP_PRINT_MARK; AHTTP_PRINT(x); AHTTP_PRINT_SP; AHTTP_PRINTLN(y); } +#define AHTTP_LOGDEBUG2(x,y,z) if(_ASYNC_HTTP_LOGLEVEL_>3) { AHTTP_PRINT_MARK; AHTTP_PRINT(x); AHTTP_PRINT_SP; AHTTP_PRINT(y); AHTTP_PRINT_SP; AHTTP_PRINTLN(z); } +#define AHTTP_LOGDEBUG3(x,y,z,w) if(_ASYNC_HTTP_LOGLEVEL_>3) { AHTTP_PRINT_MARK; AHTTP_PRINT(x); AHTTP_PRINT_SP; AHTTP_PRINT(y); AHTTP_PRINT_SP; AHTTP_PRINT(z); AHTTP_PRINT_SP; AHTTP_PRINTLN(w); } + +///////////////////////////////////////////////////////// #endif // ASYNC_HTTP_REQUEST_DEBUG_GENERIC_H diff --git a/src/AsyncHTTPRequest_Generic.h b/src/AsyncHTTPRequest_Generic.h index 001b9e5..644cf38 100644 --- a/src/AsyncHTTPRequest_Generic.h +++ b/src/AsyncHTTPRequest_Generic.h @@ -17,7 +17,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . - Version: 1.1.2 + Version: 1.1.3 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -27,6 +27,7 @@ 1.1.0 K Hoang 23/12/2020 Add HTTP PUT, PATCH, DELETE and HEAD methods 1.1.1 K Hoang 24/12/2020 Prevent crash if request and/or method not correct. 1.1.2 K Hoang 11/02/2021 Rename _lock and _unlock to avoid conflict with AsyncWebServer library + 1.1.3 K Hoang 25/02/2021 Fix non-persistent Connection header bug *****************************************************************************************************************************/ #pragma once @@ -34,7 +35,7 @@ #ifndef ASYNC_HTTP_REQUEST_GENERIC_H #define ASYNC_HTTP_REQUEST_GENERIC_H -#define ASYNC_HTTP_REQUEST_GENERIC_VERSION "AsyncHTTPRequest_Generic v1.1.2" +#define ASYNC_HTTP_REQUEST_GENERIC_VERSION "AsyncHTTPRequest_Generic v1.1.3" #include diff --git a/src/AsyncHTTPRequest_Impl_Generic.h b/src/AsyncHTTPRequest_Impl_Generic.h index 2d59a3c..ae236f4 100644 --- a/src/AsyncHTTPRequest_Impl_Generic.h +++ b/src/AsyncHTTPRequest_Impl_Generic.h @@ -17,7 +17,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . - Version: 1.1.2 + Version: 1.1.3 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -27,6 +27,7 @@ 1.1.0 K Hoang 23/12/2020 Add HTTP PUT, PATCH, DELETE and HEAD methods 1.1.1 K Hoang 24/12/2020 Prevent crash if request and/or method not correct. 1.1.2 K Hoang 11/02/2021 Rename _lock and _unlock to avoid conflict with AsyncWebServer library + 1.1.3 K Hoang 25/02/2021 Fix non-persistent Connection header bug *****************************************************************************************************************************/ #pragma once @@ -873,7 +874,7 @@ void AsyncHTTPRequest::_processChunks() { char* connectionHdr = respHeaderValue("connection"); - if (connectionHdr && (strcasecmp_P(connectionHdr, PSTR("disconnect")) == 0)) + if (connectionHdr && (strcasecmp_P(connectionHdr, PSTR("close")) == 0)) { AHTTP_LOGDEBUG("*all chunks received - closing TCP"); @@ -1078,7 +1079,7 @@ void AsyncHTTPRequest::_onData(void* Vbuf, size_t len) { char* connectionHdr = respHeaderValue("connection"); - if (connectionHdr && (strcasecmp_P(connectionHdr, PSTR("disconnect")) == 0)) + if (connectionHdr && (strcasecmp_P(connectionHdr, PSTR("close")) == 0)) { AHTTP_LOGDEBUG("*all data received - closing TCP"); diff --git a/src/utility/xbuf.h b/src/utility/xbuf.h index 4828847..1dd8493 100644 --- a/src/utility/xbuf.h +++ b/src/utility/xbuf.h @@ -17,7 +17,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . - Version: 1.1.2 + Version: 1.1.3 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -27,6 +27,7 @@ 1.1.0 K Hoang 23/12/2020 Add HTTP PUT, PATCH, DELETE and HEAD methods 1.1.1 K Hoang 24/12/2020 Prevent crash if request and/or method not correct. 1.1.2 K Hoang 11/02/2021 Rename _lock and _unlock to avoid conflict with AsyncWebServer library + 1.1.3 K Hoang 25/02/2021 Fix non-persistent Connection header bug *****************************************************************************************************************************/ /******************************************************************************************** diff --git a/src/utility/xbuf_Impl.h b/src/utility/xbuf_Impl.h index b048979..333c6e9 100644 --- a/src/utility/xbuf_Impl.h +++ b/src/utility/xbuf_Impl.h @@ -17,7 +17,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . - Version: 1.1.2 + Version: 1.1.3 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -27,6 +27,7 @@ 1.1.0 K Hoang 23/12/2020 Add HTTP PUT, PATCH, DELETE and HEAD methods 1.1.1 K Hoang 24/12/2020 Prevent crash if request and/or method not correct. 1.1.2 K Hoang 11/02/2021 Rename _lock and _unlock to avoid conflict with AsyncWebServer library + 1.1.3 K Hoang 25/02/2021 Fix non-persistent Connection header bug *****************************************************************************************************************************/ #pragma once diff --git a/src_cpp/AsyncHTTPRequest_Debug_Generic.h b/src_cpp/AsyncHTTPRequest_Debug_Generic.h index 59d6e44..6396018 100644 --- a/src_cpp/AsyncHTTPRequest_Debug_Generic.h +++ b/src_cpp/AsyncHTTPRequest_Debug_Generic.h @@ -17,7 +17,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . - Version: 1.1.2 + Version: 1.1.3 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -27,6 +27,7 @@ 1.1.0 K Hoang 23/12/2020 Add HTTP PUT, PATCH, DELETE and HEAD methods 1.1.1 K Hoang 24/12/2020 Prevent crash if request and/or method not correct. 1.1.2 K Hoang 11/02/2021 Rename _lock and _unlock to avoid conflict with AsyncWebServer library + 1.1.3 K Hoang 25/02/2021 Fix non-persistent Connection header bug *****************************************************************************************************************************/ #pragma once @@ -51,29 +52,49 @@ #define _ASYNC_HTTP_LOGLEVEL_ 0 #endif -#define AHTTP_LOGERROR(x) if(_ASYNC_HTTP_LOGLEVEL_>0) { A_DBG_PORT.print("[AHTTP] "); A_DBG_PORT.println(x); } -#define AHTTP_LOGERROR0(x) if(_ASYNC_HTTP_LOGLEVEL_>0) { A_DBG_PORT.print(x); } -#define AHTTP_LOGERROR1(x,y) if(_ASYNC_HTTP_LOGLEVEL_>0) { A_DBG_PORT.print("[AHTTP] "); A_DBG_PORT.print(x); A_DBG_PORT.print(" "); A_DBG_PORT.println(y); } -#define AHTTP_LOGERROR2(x,y,z) if(_ASYNC_HTTP_LOGLEVEL_>0) { A_DBG_PORT.print("[AHTTP] "); A_DBG_PORT.print(x); A_DBG_PORT.print(" "); A_DBG_PORT.print(y); A_DBG_PORT.print(" "); A_DBG_PORT.println(z); } -#define AHTTP_LOGERROR3(x,y,z,w) if(_ASYNC_HTTP_LOGLEVEL_>0) { A_DBG_PORT.print("[AHTTP] "); A_DBG_PORT.print(x); A_DBG_PORT.print(" "); A_DBG_PORT.print(y); A_DBG_PORT.print(" "); A_DBG_PORT.print(z); A_DBG_PORT.print(" "); A_DBG_PORT.println(w); } +///////////////////////////////////////////////////////// -#define AHTTP_LOGWARN(x) if(_ASYNC_HTTP_LOGLEVEL_>1) { A_DBG_PORT.print("[AHTTP] "); A_DBG_PORT.println(x); } -#define AHTTP_LOGWARN0(x) if(_ASYNC_HTTP_LOGLEVEL_>1) { A_DBG_PORT.print(x); } -#define AHTTP_LOGWARN1(x,y) if(_ASYNC_HTTP_LOGLEVEL_>1) { A_DBG_PORT.print("[AHTTP] "); A_DBG_PORT.print(x); A_DBG_PORT.print(" "); A_DBG_PORT.println(y); } -#define AHTTP_LOGWARN2(x,y,z) if(_ASYNC_HTTP_LOGLEVEL_>1) { A_DBG_PORT.print("[AHTTP] "); A_DBG_PORT.print(x); A_DBG_PORT.print(" "); A_DBG_PORT.print(y); A_DBG_PORT.print(" "); A_DBG_PORT.println(z); } -#define AHTTP_LOGWARN3(x,y,z,w) if(_ASYNC_HTTP_LOGLEVEL_>1) { A_DBG_PORT.print("[AHTTP] "); A_DBG_PORT.print(x); A_DBG_PORT.print(" "); A_DBG_PORT.print(y); A_DBG_PORT.print(" "); A_DBG_PORT.print(z); A_DBG_PORT.print(" "); A_DBG_PORT.println(w); } +const char AHTTP_MARK[] = "[AHTTP] "; -#define AHTTP_LOGINFO(x) if(_ASYNC_HTTP_LOGLEVEL_>2) { A_DBG_PORT.print("[AHTTP] "); A_DBG_PORT.println(x); } -#define AHTTP_LOGINFO0(x) if(_ASYNC_HTTP_LOGLEVEL_>2) { A_DBG_PORT.print(x); } -#define AHTTP_LOGINFO1(x,y) if(_ASYNC_HTTP_LOGLEVEL_>2) { A_DBG_PORT.print("[AHTTP] "); A_DBG_PORT.print(x); A_DBG_PORT.print(" "); A_DBG_PORT.println(y); } -#define AHTTP_LOGINFO2(x,y,z) if(_ASYNC_HTTP_LOGLEVEL_>2) { A_DBG_PORT.print("[AHTTP] "); A_DBG_PORT.print(x); A_DBG_PORT.print(" "); A_DBG_PORT.print(y); A_DBG_PORT.print(" "); A_DBG_PORT.println(z); } -#define AHTTP_LOGINFO3(x,y,z,w) if(_ASYNC_HTTP_LOGLEVEL_>2) { A_DBG_PORT.print("[AHTTP] "); A_DBG_PORT.print(x); A_DBG_PORT.print(" "); A_DBG_PORT.print(y); A_DBG_PORT.print(" "); A_DBG_PORT.print(z); A_DBG_PORT.print(" "); A_DBG_PORT.println(w); } +#define AHTTP_PRINT_MARK AHTTP_PRINT(AHTTP_MARK) +#define AHTTP_PRINT_SP A_DBG_PORT.print(" ") -#define AHTTP_LOGDEBUG(x) if(_ASYNC_HTTP_LOGLEVEL_>3) { A_DBG_PORT.print("[AHTTP] "); A_DBG_PORT.println(x); } -#define AHTTP_LOGDEBUG0(x) if(_ASYNC_HTTP_LOGLEVEL_>3) { A_DBG_PORT.print(x); } -#define AHTTP_LOGDEBUG1(x,y) if(_ASYNC_HTTP_LOGLEVEL_>3) { A_DBG_PORT.print("[AHTTP] "); A_DBG_PORT.print(x); A_DBG_PORT.print(" "); A_DBG_PORT.println(y); } -#define AHTTP_LOGDEBUG2(x,y,z) if(_ASYNC_HTTP_LOGLEVEL_>3) { A_DBG_PORT.print("[AHTTP] "); A_DBG_PORT.print(x); A_DBG_PORT.print(" "); A_DBG_PORT.print(y); A_DBG_PORT.print(" "); A_DBG_PORT.println(z); } -#define AHTTP_LOGDEBUG3(x,y,z,w) if(_ASYNC_HTTP_LOGLEVEL_>3) { A_DBG_PORT.print("[AHTTP] "); A_DBG_PORT.print(x); A_DBG_PORT.print(" "); A_DBG_PORT.print(y); A_DBG_PORT.print(" "); A_DBG_PORT.print(z); A_DBG_PORT.print(" "); A_DBG_PORT.println(w); } +#define AHTTP_PRINT A_DBG_PORT.print +#define AHTTP_PRINTLN A_DBG_PORT.println + +///////////////////////////////////////////////////////// + +#define AHTTP_LOGERROR(x) if(_ASYNC_HTTP_LOGLEVEL_>0) { AHTTP_PRINT_MARK; AHTTP_PRINTLN(x); } +#define AHTTP_LOGERROR0(x) if(_ASYNC_HTTP_LOGLEVEL_>0) { AHTTP_PRINT(x); } +#define AHTTP_LOGERROR1(x,y) if(_ASYNC_HTTP_LOGLEVEL_>0) { AHTTP_PRINT_MARK; AHTTP_PRINT(x); AHTTP_PRINT_SP; AHTTP_PRINTLN(y); } +#define AHTTP_LOGERROR2(x,y,z) if(_ASYNC_HTTP_LOGLEVEL_>0) { AHTTP_PRINT_MARK; AHTTP_PRINT(x); AHTTP_PRINT_SP; AHTTP_PRINT(y); AHTTP_PRINT_SP; AHTTP_PRINTLN(z); } +#define AHTTP_LOGERROR3(x,y,z,w) if(_ASYNC_HTTP_LOGLEVEL_>0) { AHTTP_PRINT_MARK; AHTTP_PRINT(x); AHTTP_PRINT_SP; AHTTP_PRINT(y); AHTTP_PRINT_SP; AHTTP_PRINT(z); AHTTP_PRINT_SP; AHTTP_PRINTLN(w); } + +///////////////////////////////////////////////////////// + +#define AHTTP_LOGWARN(x) if(_ASYNC_HTTP_LOGLEVEL_>1) { AHTTP_PRINT_MARK; AHTTP_PRINTLN(x); } +#define AHTTP_LOGWARN0(x) if(_ASYNC_HTTP_LOGLEVEL_>1) { AHTTP_PRINT(x); } +#define AHTTP_LOGWARN1(x,y) if(_ASYNC_HTTP_LOGLEVEL_>1) { AHTTP_PRINT_MARK; AHTTP_PRINT(x); AHTTP_PRINT_SP; AHTTP_PRINTLN(y); } +#define AHTTP_LOGWARN2(x,y,z) if(_ASYNC_HTTP_LOGLEVEL_>1) { AHTTP_PRINT_MARK; AHTTP_PRINT(x); AHTTP_PRINT_SP; AHTTP_PRINT(y); AHTTP_PRINT_SP; AHTTP_PRINTLN(z); } +#define AHTTP_LOGWARN3(x,y,z,w) if(_ASYNC_HTTP_LOGLEVEL_>1) { AHTTP_PRINT_MARK; AHTTP_PRINT(x); AHTTP_PRINT_SP; AHTTP_PRINT(y); AHTTP_PRINT_SP; AHTTP_PRINT(z); AHTTP_PRINT_SP; AHTTP_PRINTLN(w); } + +///////////////////////////////////////////////////////// + +#define AHTTP_LOGINFO(x) if(_ASYNC_HTTP_LOGLEVEL_>2) { AHTTP_PRINT_MARK; AHTTP_PRINTLN(x); } +#define AHTTP_LOGINFO0(x) if(_ASYNC_HTTP_LOGLEVEL_>2) { AHTTP_PRINT(x); } +#define AHTTP_LOGINFO1(x,y) if(_ASYNC_HTTP_LOGLEVEL_>2) { AHTTP_PRINT_MARK; AHTTP_PRINT(x); AHTTP_PRINT_SP; AHTTP_PRINTLN(y); } +#define AHTTP_LOGINFO2(x,y,z) if(_ASYNC_HTTP_LOGLEVEL_>2) { AHTTP_PRINT_MARK; AHTTP_PRINT(x); AHTTP_PRINT_SP; AHTTP_PRINT(y); AHTTP_PRINT_SP; AHTTP_PRINTLN(z); } +#define AHTTP_LOGINFO3(x,y,z,w) if(_ASYNC_HTTP_LOGLEVEL_>2) { AHTTP_PRINT_MARK; AHTTP_PRINT(x); AHTTP_PRINT_SP; AHTTP_PRINT(y); AHTTP_PRINT_SP; AHTTP_PRINT(z); AHTTP_PRINT_SP; AHTTP_PRINTLN(w); } + +///////////////////////////////////////////////////////// + +#define AHTTP_LOGDEBUG(x) if(_ASYNC_HTTP_LOGLEVEL_>3) { AHTTP_PRINT_MARK; AHTTP_PRINTLN(x); } +#define AHTTP_LOGDEBUG0(x) if(_ASYNC_HTTP_LOGLEVEL_>3) { AHTTP_PRINT(x); } +#define AHTTP_LOGDEBUG1(x,y) if(_ASYNC_HTTP_LOGLEVEL_>3) { AHTTP_PRINT_MARK; AHTTP_PRINT(x); AHTTP_PRINT_SP; AHTTP_PRINTLN(y); } +#define AHTTP_LOGDEBUG2(x,y,z) if(_ASYNC_HTTP_LOGLEVEL_>3) { AHTTP_PRINT_MARK; AHTTP_PRINT(x); AHTTP_PRINT_SP; AHTTP_PRINT(y); AHTTP_PRINT_SP; AHTTP_PRINTLN(z); } +#define AHTTP_LOGDEBUG3(x,y,z,w) if(_ASYNC_HTTP_LOGLEVEL_>3) { AHTTP_PRINT_MARK; AHTTP_PRINT(x); AHTTP_PRINT_SP; AHTTP_PRINT(y); AHTTP_PRINT_SP; AHTTP_PRINT(z); AHTTP_PRINT_SP; AHTTP_PRINTLN(w); } + +///////////////////////////////////////////////////////// #endif // ASYNC_HTTP_REQUEST_DEBUG_GENERIC_H diff --git a/src_cpp/AsyncHTTPRequest_Generic.cpp b/src_cpp/AsyncHTTPRequest_Generic.cpp index 9d586fd..8281d3d 100644 --- a/src_cpp/AsyncHTTPRequest_Generic.cpp +++ b/src_cpp/AsyncHTTPRequest_Generic.cpp @@ -17,7 +17,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . - Version: 1.1.2 + Version: 1.1.3 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -27,6 +27,7 @@ 1.1.0 K Hoang 23/12/2020 Add HTTP PUT, PATCH, DELETE and HEAD methods 1.1.1 K Hoang 24/12/2020 Prevent crash if request and/or method not correct. 1.1.2 K Hoang 11/02/2021 Rename _lock and _unlock to avoid conflict with AsyncWebServer library + 1.1.3 K Hoang 25/02/2021 Fix non-persistent Connection header bug *****************************************************************************************************************************/ #include "AsyncHTTPRequest_Debug_Generic.h" @@ -96,6 +97,8 @@ bool AsyncHTTPRequest::open(const char* method, const char* URL) if (_readyState != readyStateUnsent && _readyState != readyStateDone) { + AHTTP_LOGDEBUG("open: not ready"); + return false; } @@ -148,17 +151,23 @@ bool AsyncHTTPRequest::open(const char* method, const char* URL) ////// else { + AHTTP_LOGDEBUG("open: Bad method"); + return false; } if (!_parseURL(URL)) { + AHTTP_LOGDEBUG("open: error parsing URL"); + return false; } if ( _client && _client->connected() && (strcmp(_URL->host, _connectedHost) != 0 || _URL->port != _connectedPort)) { + AHTTP_LOGDEBUG("open: not connected"); + return false; } @@ -176,11 +185,17 @@ bool AsyncHTTPRequest::open(const char* method, const char* URL) // New in v1.1.1 _requestReadyToSend = true; ////// + + AHTTP_LOGDEBUG1("open: conneting to hostname =", hostName); return _connect(); } else + { + AHTTP_LOGDEBUG("open: error alloc"); + return false; + } } //************************************************************************************************************** void AsyncHTTPRequest::onReadyStateChange(readyStateChangeCB cb, void* arg) @@ -422,13 +437,61 @@ String AsyncHTTPRequest::responseText() localString = _response->readString(avail); _contentRead += localString.length(); - AHTTP_LOGDEBUG3("responseText(char)", localString.substring(0, 16).c_str(), ", avail =", avail); + //AHTTP_LOGDEBUG3("responseText(char)", localString.substring(0, 16).c_str(), ", avail =", avail); + AHTTP_LOGDEBUG3("responseText(char)", localString, ", avail =", avail); _AHTTP_unlock; return localString; } +//************************************************************************************************************** + +#if 1 + +#if (ESP32) + #define GLOBAL_STR_LEN (32 * 1024) +#elif (ESP8266) + #define GLOBAL_STR_LEN (16 * 1024) +#else + #define GLOBAL_STR_LEN (4 * 1024) +#endif + +char globalLongString[GLOBAL_STR_LEN + 1]; + +char* AsyncHTTPRequest::responseLongText() +{ + AHTTP_LOGDEBUG("responseLongText()"); + + MUTEX_LOCK(NULL) + + if ( ! _response || _readyState < readyStateLoading || ! available()) + { + AHTTP_LOGDEBUG("responseText() no data"); + + _AHTTP_unlock; + + //return String(); + return NULL; + } + + // String localString; + size_t avail = available(); + size_t lenToCopy = (avail <= GLOBAL_STR_LEN) ? avail : GLOBAL_STR_LEN; + + strncpy(globalLongString, _response->readString(avail).c_str(), lenToCopy ); + globalLongString[ lenToCopy + 1 ] = 0; + + _contentRead += _response->readString(avail).length(); + + AHTTP_LOGDEBUG3("responseLongText(char)", globalLongString, ", avail =", avail); + + _AHTTP_unlock; + + return globalLongString; +} +#endif + //************************************************************************************************************** size_t AsyncHTTPRequest::responseRead(uint8_t* buf, size_t len) { @@ -810,7 +873,7 @@ void AsyncHTTPRequest::_processChunks() { char* connectionHdr = respHeaderValue("connection"); - if (connectionHdr && (strcasecmp_P(connectionHdr, PSTR("disconnect")) == 0)) + if (connectionHdr && (strcasecmp_P(connectionHdr, PSTR("close")) == 0)) { AHTTP_LOGDEBUG("*all chunks received - closing TCP"); @@ -850,12 +913,19 @@ void AsyncHTTPRequest::_onConnect(AsyncClient* client) _client = client; _setReadyState(readyStateOpened); + // KH test _response = new xbuf; + //_response = new xbuf(256); + ////// if (!_response) { _AHTTP_unlock; + // KH, to remove + AHTTP_LOGDEBUG("_onConnect: Can't new _responser"); + /////// + return; } @@ -865,11 +935,17 @@ void AsyncHTTPRequest::_onConnect(AsyncClient* client) _client->onAck([](void* obj, AsyncClient * client, size_t len, uint32_t time) { + (void) client; + (void) len; + (void) time; + ((AsyncHTTPRequest*)(obj))->_send(); }, this); _client->onData([](void* obj, AsyncClient * client, void* data, size_t len) { + (void) client; + ((AsyncHTTPRequest*)(obj))->_onData(data, len); }, this); @@ -886,6 +962,8 @@ void AsyncHTTPRequest::_onConnect(AsyncClient* client) //************************************************************************************************************** void AsyncHTTPRequest::_onPoll(AsyncClient* client) { + (void) client; + MUTEX_LOCK_NR if (_timeout && (millis() - _lastActivity) > (_timeout * 1000)) @@ -907,6 +985,8 @@ void AsyncHTTPRequest::_onPoll(AsyncClient* client) //************************************************************************************************************** void AsyncHTTPRequest::_onError(AsyncClient* client, int8_t error) { + (void) client; + AHTTP_LOGDEBUG1("_onError handler error =", error); _HTTPcode = error; @@ -915,6 +995,8 @@ void AsyncHTTPRequest::_onError(AsyncClient* client, int8_t error) //************************************************************************************************************** void AsyncHTTPRequest::_onDisconnect(AsyncClient* client) { + (void) client; + AHTTP_LOGDEBUG("\n_onDisconnect handler"); MUTEX_LOCK_NR @@ -958,6 +1040,11 @@ void AsyncHTTPRequest::_onData(void* Vbuf, size_t len) if (_chunks) { _chunks->write((uint8_t*)Vbuf, len); + + // KH, to remove + AHTTP_LOGDEBUG("_onData: _processChunks"); + /////// + _processChunks(); } else @@ -972,6 +1059,10 @@ void AsyncHTTPRequest::_onData(void* Vbuf, size_t len) { _AHTTP_unlock; + // KH, to remove + AHTTP_LOGDEBUG("_onData: headers not complete"); + /////// + return; } } @@ -987,7 +1078,7 @@ void AsyncHTTPRequest::_onData(void* Vbuf, size_t len) { char* connectionHdr = respHeaderValue("connection"); - if (connectionHdr && (strcasecmp_P(connectionHdr, PSTR("disconnect")) == 0)) + if (connectionHdr && (strcasecmp_P(connectionHdr, PSTR("close")) == 0)) { AHTTP_LOGDEBUG("*all data received - closing TCP"); diff --git a/src_cpp/AsyncHTTPRequest_Generic.h b/src_cpp/AsyncHTTPRequest_Generic.h index 87000af..e57f8da 100644 --- a/src_cpp/AsyncHTTPRequest_Generic.h +++ b/src_cpp/AsyncHTTPRequest_Generic.h @@ -17,7 +17,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . - Version: 1.1.2 + Version: 1.1.3 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -27,6 +27,7 @@ 1.1.0 K Hoang 23/12/2020 Add HTTP PUT, PATCH, DELETE and HEAD methods 1.1.1 K Hoang 24/12/2020 Prevent crash if request and/or method not correct. 1.1.2 K Hoang 11/02/2021 Rename _lock and _unlock to avoid conflict with AsyncWebServer library + 1.1.3 K Hoang 25/02/2021 Fix non-persistent Connection header bug *****************************************************************************************************************************/ #pragma once @@ -34,7 +35,7 @@ #ifndef ASYNC_HTTP_REQUEST_GENERIC_H #define ASYNC_HTTP_REQUEST_GENERIC_H -#define ASYNC_HTTP_REQUEST_GENERIC_VERSION "AsyncHTTPRequest_Generic v1.1.2" +#define ASYNC_HTTP_REQUEST_GENERIC_VERSION "AsyncHTTPRequest_Generic v1.1.3" #include @@ -224,8 +225,11 @@ class AsyncHTTPRequest size_t responseLength(); // indicated response length or sum of chunks to date int responseHTTPcode(); // HTTP response code or (negative) error code String responseText(); // response (whole* or partial* as string) + + char* responseLongText(); // response long (whole* or partial* as string) + size_t responseRead(uint8_t* buffer, size_t len); // Read response into buffer - uint32_t elapsedTime(); // Elapsed time of in progress transaction or last completed (ms) + uint32_t elapsedTime(); // Elapsed time of in progress transaction or last completed (ms) String version(); // Version of AsyncHTTPRequest //___________________________________________________________________________________________________________________________________ diff --git a/src_cpp/utility/xbuf.cpp b/src_cpp/utility/xbuf.cpp index aefab95..2217c92 100644 --- a/src_cpp/utility/xbuf.cpp +++ b/src_cpp/utility/xbuf.cpp @@ -17,7 +17,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . - Version: 1.1.2 + Version: 1.1.3 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -27,6 +27,7 @@ 1.1.0 K Hoang 23/12/2020 Add HTTP PUT, PATCH, DELETE and HEAD methods 1.1.1 K Hoang 24/12/2020 Prevent crash if request and/or method not correct. 1.1.2 K Hoang 11/02/2021 Rename _lock and _unlock to avoid conflict with AsyncWebServer library + 1.1.3 K Hoang 25/02/2021 Fix non-persistent Connection header bug *****************************************************************************************************************************/ #include "utility/xbuf.h" diff --git a/src_cpp/utility/xbuf.h b/src_cpp/utility/xbuf.h index 9d6e9e8..bc77d0e 100644 --- a/src_cpp/utility/xbuf.h +++ b/src_cpp/utility/xbuf.h @@ -17,7 +17,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . - Version: 1.1.2 + Version: 1.1.3 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -27,6 +27,7 @@ 1.1.0 K Hoang 23/12/2020 Add HTTP PUT, PATCH, DELETE and HEAD methods 1.1.1 K Hoang 24/12/2020 Prevent crash if request and/or method not correct. 1.1.2 K Hoang 11/02/2021 Rename _lock and _unlock to avoid conflict with AsyncWebServer library + 1.1.3 K Hoang 25/02/2021 Fix non-persistent Connection header bug *****************************************************************************************************************************/ /******************************************************************************************** diff --git a/src_h/AsyncHTTPRequest_Debug_Generic.h b/src_h/AsyncHTTPRequest_Debug_Generic.h index 59d6e44..6396018 100644 --- a/src_h/AsyncHTTPRequest_Debug_Generic.h +++ b/src_h/AsyncHTTPRequest_Debug_Generic.h @@ -17,7 +17,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . - Version: 1.1.2 + Version: 1.1.3 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -27,6 +27,7 @@ 1.1.0 K Hoang 23/12/2020 Add HTTP PUT, PATCH, DELETE and HEAD methods 1.1.1 K Hoang 24/12/2020 Prevent crash if request and/or method not correct. 1.1.2 K Hoang 11/02/2021 Rename _lock and _unlock to avoid conflict with AsyncWebServer library + 1.1.3 K Hoang 25/02/2021 Fix non-persistent Connection header bug *****************************************************************************************************************************/ #pragma once @@ -51,29 +52,49 @@ #define _ASYNC_HTTP_LOGLEVEL_ 0 #endif -#define AHTTP_LOGERROR(x) if(_ASYNC_HTTP_LOGLEVEL_>0) { A_DBG_PORT.print("[AHTTP] "); A_DBG_PORT.println(x); } -#define AHTTP_LOGERROR0(x) if(_ASYNC_HTTP_LOGLEVEL_>0) { A_DBG_PORT.print(x); } -#define AHTTP_LOGERROR1(x,y) if(_ASYNC_HTTP_LOGLEVEL_>0) { A_DBG_PORT.print("[AHTTP] "); A_DBG_PORT.print(x); A_DBG_PORT.print(" "); A_DBG_PORT.println(y); } -#define AHTTP_LOGERROR2(x,y,z) if(_ASYNC_HTTP_LOGLEVEL_>0) { A_DBG_PORT.print("[AHTTP] "); A_DBG_PORT.print(x); A_DBG_PORT.print(" "); A_DBG_PORT.print(y); A_DBG_PORT.print(" "); A_DBG_PORT.println(z); } -#define AHTTP_LOGERROR3(x,y,z,w) if(_ASYNC_HTTP_LOGLEVEL_>0) { A_DBG_PORT.print("[AHTTP] "); A_DBG_PORT.print(x); A_DBG_PORT.print(" "); A_DBG_PORT.print(y); A_DBG_PORT.print(" "); A_DBG_PORT.print(z); A_DBG_PORT.print(" "); A_DBG_PORT.println(w); } +///////////////////////////////////////////////////////// -#define AHTTP_LOGWARN(x) if(_ASYNC_HTTP_LOGLEVEL_>1) { A_DBG_PORT.print("[AHTTP] "); A_DBG_PORT.println(x); } -#define AHTTP_LOGWARN0(x) if(_ASYNC_HTTP_LOGLEVEL_>1) { A_DBG_PORT.print(x); } -#define AHTTP_LOGWARN1(x,y) if(_ASYNC_HTTP_LOGLEVEL_>1) { A_DBG_PORT.print("[AHTTP] "); A_DBG_PORT.print(x); A_DBG_PORT.print(" "); A_DBG_PORT.println(y); } -#define AHTTP_LOGWARN2(x,y,z) if(_ASYNC_HTTP_LOGLEVEL_>1) { A_DBG_PORT.print("[AHTTP] "); A_DBG_PORT.print(x); A_DBG_PORT.print(" "); A_DBG_PORT.print(y); A_DBG_PORT.print(" "); A_DBG_PORT.println(z); } -#define AHTTP_LOGWARN3(x,y,z,w) if(_ASYNC_HTTP_LOGLEVEL_>1) { A_DBG_PORT.print("[AHTTP] "); A_DBG_PORT.print(x); A_DBG_PORT.print(" "); A_DBG_PORT.print(y); A_DBG_PORT.print(" "); A_DBG_PORT.print(z); A_DBG_PORT.print(" "); A_DBG_PORT.println(w); } +const char AHTTP_MARK[] = "[AHTTP] "; -#define AHTTP_LOGINFO(x) if(_ASYNC_HTTP_LOGLEVEL_>2) { A_DBG_PORT.print("[AHTTP] "); A_DBG_PORT.println(x); } -#define AHTTP_LOGINFO0(x) if(_ASYNC_HTTP_LOGLEVEL_>2) { A_DBG_PORT.print(x); } -#define AHTTP_LOGINFO1(x,y) if(_ASYNC_HTTP_LOGLEVEL_>2) { A_DBG_PORT.print("[AHTTP] "); A_DBG_PORT.print(x); A_DBG_PORT.print(" "); A_DBG_PORT.println(y); } -#define AHTTP_LOGINFO2(x,y,z) if(_ASYNC_HTTP_LOGLEVEL_>2) { A_DBG_PORT.print("[AHTTP] "); A_DBG_PORT.print(x); A_DBG_PORT.print(" "); A_DBG_PORT.print(y); A_DBG_PORT.print(" "); A_DBG_PORT.println(z); } -#define AHTTP_LOGINFO3(x,y,z,w) if(_ASYNC_HTTP_LOGLEVEL_>2) { A_DBG_PORT.print("[AHTTP] "); A_DBG_PORT.print(x); A_DBG_PORT.print(" "); A_DBG_PORT.print(y); A_DBG_PORT.print(" "); A_DBG_PORT.print(z); A_DBG_PORT.print(" "); A_DBG_PORT.println(w); } +#define AHTTP_PRINT_MARK AHTTP_PRINT(AHTTP_MARK) +#define AHTTP_PRINT_SP A_DBG_PORT.print(" ") -#define AHTTP_LOGDEBUG(x) if(_ASYNC_HTTP_LOGLEVEL_>3) { A_DBG_PORT.print("[AHTTP] "); A_DBG_PORT.println(x); } -#define AHTTP_LOGDEBUG0(x) if(_ASYNC_HTTP_LOGLEVEL_>3) { A_DBG_PORT.print(x); } -#define AHTTP_LOGDEBUG1(x,y) if(_ASYNC_HTTP_LOGLEVEL_>3) { A_DBG_PORT.print("[AHTTP] "); A_DBG_PORT.print(x); A_DBG_PORT.print(" "); A_DBG_PORT.println(y); } -#define AHTTP_LOGDEBUG2(x,y,z) if(_ASYNC_HTTP_LOGLEVEL_>3) { A_DBG_PORT.print("[AHTTP] "); A_DBG_PORT.print(x); A_DBG_PORT.print(" "); A_DBG_PORT.print(y); A_DBG_PORT.print(" "); A_DBG_PORT.println(z); } -#define AHTTP_LOGDEBUG3(x,y,z,w) if(_ASYNC_HTTP_LOGLEVEL_>3) { A_DBG_PORT.print("[AHTTP] "); A_DBG_PORT.print(x); A_DBG_PORT.print(" "); A_DBG_PORT.print(y); A_DBG_PORT.print(" "); A_DBG_PORT.print(z); A_DBG_PORT.print(" "); A_DBG_PORT.println(w); } +#define AHTTP_PRINT A_DBG_PORT.print +#define AHTTP_PRINTLN A_DBG_PORT.println + +///////////////////////////////////////////////////////// + +#define AHTTP_LOGERROR(x) if(_ASYNC_HTTP_LOGLEVEL_>0) { AHTTP_PRINT_MARK; AHTTP_PRINTLN(x); } +#define AHTTP_LOGERROR0(x) if(_ASYNC_HTTP_LOGLEVEL_>0) { AHTTP_PRINT(x); } +#define AHTTP_LOGERROR1(x,y) if(_ASYNC_HTTP_LOGLEVEL_>0) { AHTTP_PRINT_MARK; AHTTP_PRINT(x); AHTTP_PRINT_SP; AHTTP_PRINTLN(y); } +#define AHTTP_LOGERROR2(x,y,z) if(_ASYNC_HTTP_LOGLEVEL_>0) { AHTTP_PRINT_MARK; AHTTP_PRINT(x); AHTTP_PRINT_SP; AHTTP_PRINT(y); AHTTP_PRINT_SP; AHTTP_PRINTLN(z); } +#define AHTTP_LOGERROR3(x,y,z,w) if(_ASYNC_HTTP_LOGLEVEL_>0) { AHTTP_PRINT_MARK; AHTTP_PRINT(x); AHTTP_PRINT_SP; AHTTP_PRINT(y); AHTTP_PRINT_SP; AHTTP_PRINT(z); AHTTP_PRINT_SP; AHTTP_PRINTLN(w); } + +///////////////////////////////////////////////////////// + +#define AHTTP_LOGWARN(x) if(_ASYNC_HTTP_LOGLEVEL_>1) { AHTTP_PRINT_MARK; AHTTP_PRINTLN(x); } +#define AHTTP_LOGWARN0(x) if(_ASYNC_HTTP_LOGLEVEL_>1) { AHTTP_PRINT(x); } +#define AHTTP_LOGWARN1(x,y) if(_ASYNC_HTTP_LOGLEVEL_>1) { AHTTP_PRINT_MARK; AHTTP_PRINT(x); AHTTP_PRINT_SP; AHTTP_PRINTLN(y); } +#define AHTTP_LOGWARN2(x,y,z) if(_ASYNC_HTTP_LOGLEVEL_>1) { AHTTP_PRINT_MARK; AHTTP_PRINT(x); AHTTP_PRINT_SP; AHTTP_PRINT(y); AHTTP_PRINT_SP; AHTTP_PRINTLN(z); } +#define AHTTP_LOGWARN3(x,y,z,w) if(_ASYNC_HTTP_LOGLEVEL_>1) { AHTTP_PRINT_MARK; AHTTP_PRINT(x); AHTTP_PRINT_SP; AHTTP_PRINT(y); AHTTP_PRINT_SP; AHTTP_PRINT(z); AHTTP_PRINT_SP; AHTTP_PRINTLN(w); } + +///////////////////////////////////////////////////////// + +#define AHTTP_LOGINFO(x) if(_ASYNC_HTTP_LOGLEVEL_>2) { AHTTP_PRINT_MARK; AHTTP_PRINTLN(x); } +#define AHTTP_LOGINFO0(x) if(_ASYNC_HTTP_LOGLEVEL_>2) { AHTTP_PRINT(x); } +#define AHTTP_LOGINFO1(x,y) if(_ASYNC_HTTP_LOGLEVEL_>2) { AHTTP_PRINT_MARK; AHTTP_PRINT(x); AHTTP_PRINT_SP; AHTTP_PRINTLN(y); } +#define AHTTP_LOGINFO2(x,y,z) if(_ASYNC_HTTP_LOGLEVEL_>2) { AHTTP_PRINT_MARK; AHTTP_PRINT(x); AHTTP_PRINT_SP; AHTTP_PRINT(y); AHTTP_PRINT_SP; AHTTP_PRINTLN(z); } +#define AHTTP_LOGINFO3(x,y,z,w) if(_ASYNC_HTTP_LOGLEVEL_>2) { AHTTP_PRINT_MARK; AHTTP_PRINT(x); AHTTP_PRINT_SP; AHTTP_PRINT(y); AHTTP_PRINT_SP; AHTTP_PRINT(z); AHTTP_PRINT_SP; AHTTP_PRINTLN(w); } + +///////////////////////////////////////////////////////// + +#define AHTTP_LOGDEBUG(x) if(_ASYNC_HTTP_LOGLEVEL_>3) { AHTTP_PRINT_MARK; AHTTP_PRINTLN(x); } +#define AHTTP_LOGDEBUG0(x) if(_ASYNC_HTTP_LOGLEVEL_>3) { AHTTP_PRINT(x); } +#define AHTTP_LOGDEBUG1(x,y) if(_ASYNC_HTTP_LOGLEVEL_>3) { AHTTP_PRINT_MARK; AHTTP_PRINT(x); AHTTP_PRINT_SP; AHTTP_PRINTLN(y); } +#define AHTTP_LOGDEBUG2(x,y,z) if(_ASYNC_HTTP_LOGLEVEL_>3) { AHTTP_PRINT_MARK; AHTTP_PRINT(x); AHTTP_PRINT_SP; AHTTP_PRINT(y); AHTTP_PRINT_SP; AHTTP_PRINTLN(z); } +#define AHTTP_LOGDEBUG3(x,y,z,w) if(_ASYNC_HTTP_LOGLEVEL_>3) { AHTTP_PRINT_MARK; AHTTP_PRINT(x); AHTTP_PRINT_SP; AHTTP_PRINT(y); AHTTP_PRINT_SP; AHTTP_PRINT(z); AHTTP_PRINT_SP; AHTTP_PRINTLN(w); } + +///////////////////////////////////////////////////////// #endif // ASYNC_HTTP_REQUEST_DEBUG_GENERIC_H diff --git a/src_h/AsyncHTTPRequest_Generic.h b/src_h/AsyncHTTPRequest_Generic.h index 001b9e5..644cf38 100644 --- a/src_h/AsyncHTTPRequest_Generic.h +++ b/src_h/AsyncHTTPRequest_Generic.h @@ -17,7 +17,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . - Version: 1.1.2 + Version: 1.1.3 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -27,6 +27,7 @@ 1.1.0 K Hoang 23/12/2020 Add HTTP PUT, PATCH, DELETE and HEAD methods 1.1.1 K Hoang 24/12/2020 Prevent crash if request and/or method not correct. 1.1.2 K Hoang 11/02/2021 Rename _lock and _unlock to avoid conflict with AsyncWebServer library + 1.1.3 K Hoang 25/02/2021 Fix non-persistent Connection header bug *****************************************************************************************************************************/ #pragma once @@ -34,7 +35,7 @@ #ifndef ASYNC_HTTP_REQUEST_GENERIC_H #define ASYNC_HTTP_REQUEST_GENERIC_H -#define ASYNC_HTTP_REQUEST_GENERIC_VERSION "AsyncHTTPRequest_Generic v1.1.2" +#define ASYNC_HTTP_REQUEST_GENERIC_VERSION "AsyncHTTPRequest_Generic v1.1.3" #include diff --git a/src_h/AsyncHTTPRequest_Impl_Generic.h b/src_h/AsyncHTTPRequest_Impl_Generic.h index 2d59a3c..ae236f4 100644 --- a/src_h/AsyncHTTPRequest_Impl_Generic.h +++ b/src_h/AsyncHTTPRequest_Impl_Generic.h @@ -17,7 +17,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . - Version: 1.1.2 + Version: 1.1.3 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -27,6 +27,7 @@ 1.1.0 K Hoang 23/12/2020 Add HTTP PUT, PATCH, DELETE and HEAD methods 1.1.1 K Hoang 24/12/2020 Prevent crash if request and/or method not correct. 1.1.2 K Hoang 11/02/2021 Rename _lock and _unlock to avoid conflict with AsyncWebServer library + 1.1.3 K Hoang 25/02/2021 Fix non-persistent Connection header bug *****************************************************************************************************************************/ #pragma once @@ -873,7 +874,7 @@ void AsyncHTTPRequest::_processChunks() { char* connectionHdr = respHeaderValue("connection"); - if (connectionHdr && (strcasecmp_P(connectionHdr, PSTR("disconnect")) == 0)) + if (connectionHdr && (strcasecmp_P(connectionHdr, PSTR("close")) == 0)) { AHTTP_LOGDEBUG("*all chunks received - closing TCP"); @@ -1078,7 +1079,7 @@ void AsyncHTTPRequest::_onData(void* Vbuf, size_t len) { char* connectionHdr = respHeaderValue("connection"); - if (connectionHdr && (strcasecmp_P(connectionHdr, PSTR("disconnect")) == 0)) + if (connectionHdr && (strcasecmp_P(connectionHdr, PSTR("close")) == 0)) { AHTTP_LOGDEBUG("*all data received - closing TCP"); diff --git a/src_h/utility/xbuf.h b/src_h/utility/xbuf.h index 4828847..1dd8493 100644 --- a/src_h/utility/xbuf.h +++ b/src_h/utility/xbuf.h @@ -17,7 +17,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . - Version: 1.1.2 + Version: 1.1.3 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -27,6 +27,7 @@ 1.1.0 K Hoang 23/12/2020 Add HTTP PUT, PATCH, DELETE and HEAD methods 1.1.1 K Hoang 24/12/2020 Prevent crash if request and/or method not correct. 1.1.2 K Hoang 11/02/2021 Rename _lock and _unlock to avoid conflict with AsyncWebServer library + 1.1.3 K Hoang 25/02/2021 Fix non-persistent Connection header bug *****************************************************************************************************************************/ /******************************************************************************************** diff --git a/src_h/utility/xbuf_Impl.h b/src_h/utility/xbuf_Impl.h index b048979..333c6e9 100644 --- a/src_h/utility/xbuf_Impl.h +++ b/src_h/utility/xbuf_Impl.h @@ -17,7 +17,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . - Version: 1.1.2 + Version: 1.1.3 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -27,6 +27,7 @@ 1.1.0 K Hoang 23/12/2020 Add HTTP PUT, PATCH, DELETE and HEAD methods 1.1.1 K Hoang 24/12/2020 Prevent crash if request and/or method not correct. 1.1.2 K Hoang 11/02/2021 Rename _lock and _unlock to avoid conflict with AsyncWebServer library + 1.1.3 K Hoang 25/02/2021 Fix non-persistent Connection header bug *****************************************************************************************************************************/ #pragma once