From b5831c36a13273ec019f33700493bc25c73dacb0 Mon Sep 17 00:00:00 2001 From: Khoi Hoang <57012152+khoih-prog@users.noreply.github.com> Date: Mon, 9 Nov 2020 03:59:44 -0500 Subject: [PATCH] v1.0.2 ### Releases v1.0.2 1. Make Mutex Lock and delete more reliable and error-proof to prevent random crash. --- CONTRIBUTING.md | 22 +- README.md | 16 +- .../AsyncCustomHeader_STM32.ino | 13 +- examples/AsyncCustomHeader_STM32/defines.h | 3 +- .../AsyncDweetGet_STM32.ino | 3 +- examples/AsyncDweetGet_STM32/defines.h | 4 +- .../AsyncDweetPost_STM32.ino | 3 +- examples/AsyncDweetPost_STM32/defines.h | 4 +- .../AsyncHTTPRequest_ESP.ino | 19 +- .../AsyncHTTPRequest_ESP_WiFiManager.ino | 7 +- .../AsyncHTTPRequest_STM32.ino | 7 +- examples/AsyncHTTPRequest_STM32/defines.h | 4 +- .../AsyncSimpleGET_STM32.ino | 7 +- examples/AsyncSimpleGET_STM32/defines.h | 6 +- .../AsyncWebClientRepeating_STM32.ino | 3 +- .../AsyncWebClientRepeating_STM32/defines.h | 4 +- library.json | 2 +- library.properties | 4 +- src/AsyncHTTPRequest_Debug_Generic.h | 3 +- src/AsyncHTTPRequest_Generic.h | 45 +- src/AsyncHTTPRequest_Impl_Generic.h | 278 +++- src/utility/xbuf.h | 3 +- src/utility/xbuf_Impl.h | 11 +- src_cpp/AsyncHTTPRequest_Debug_Generic.h | 5 +- src_cpp/AsyncHTTPRequest_Generic.cpp | 281 +++- src_cpp/AsyncHTTPRequest_Generic.h | 48 +- src_cpp/utility/xbuf.cpp | 13 +- src_cpp/utility/xbuf.h | 5 +- src_h/AsyncHTTPRequest_Debug_Generic.h | 71 + src_h/AsyncHTTPRequest_Generic.h | 288 ++++ src_h/AsyncHTTPRequest_Impl_Generic.h | 1330 +++++++++++++++++ src_h/utility/xbuf.h | 151 ++ src_h/utility/xbuf_Impl.h | 399 +++++ 33 files changed, 2832 insertions(+), 230 deletions(-) create mode 100644 src_h/AsyncHTTPRequest_Debug_Generic.h create mode 100644 src_h/AsyncHTTPRequest_Generic.h create mode 100644 src_h/AsyncHTTPRequest_Impl_Generic.h create mode 100644 src_h/utility/xbuf.h create mode 100644 src_h/utility/xbuf_Impl.h diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0166a0d7..de67bd20 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,21 +1,21 @@ -## Contributing to ESP_WiFiManager +## Contributing to AsyncHTTPRequest_Generic ### Reporting Bugs -Please report bugs in ESP_WiFiManager if you find them. +Please report bugs in [AsyncHTTPRequest_Generic](https://github.com/khoih-prog/AsyncHTTPRequest_Generic/issues/new) if you find them. However, before reporting a bug please check through the following: -* [Existing Open Issues](https://github.com/khoih-prog/ESP_WiFiManager/issues) - someone might have already encountered this. +* [Existing Open Issues](https://github.com/khoih-prog/AsyncHTTPRequest_Generic/issues) - someone might have already encountered this. -If you don't find anything, please [open a new issue](https://github.com/khoih-prog/ESP_WiFiManager/issues/new). +If you don't find anything, please [open a new issue](https://github.com/khoih-prog/AsyncHTTPRequest_Generic/issues/new). ### How to submit a bug report Please ensure to specify the following: -* Arduino IDE version (e.g. 1.8.11) or Platform.io version -* `ESP8266` or `ESP32` Core Version (e.g. ESP8266 core v2.6.3 or ESP32 v1.0.4) +* 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) * 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: @@ -26,10 +26,10 @@ Please ensure to specify the following: ### Example ``` -Arduino IDE version: 1.8.11 -ESP8266 Core Version 2.6.3 -OS: Ubuntu 16.04 LTS -Linux Inspiron 4.4.0-170-generic #199-Ubuntu SMP Thu Nov 14 01:45:04 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux +Arduino IDE version: 1.8.13 +ESP32 Core Version 1.0.4 +OS: Ubuntu 20.04 LTS +Linux xy-Inspiron-3593 5.4.0-51-generic #56-Ubuntu SMP Mon Oct 5 14:28:49 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux Context: I encountered an endless loop while trying to connect to Local WiFi. @@ -44,7 +44,7 @@ Steps to reproduce: Feel free to post feature requests. It's helpful if you can explain exactly why the feature would be useful. -There are usually some outstanding feature requests in the [existing issues list](https://github.com/khoih-prog/ESP_WiFiManager/issues?q=is%3Aopen+is%3Aissue+label%3Aenhancement), feel free to add comments to them. +There are usually some outstanding feature requests in the [existing issues list](https://github.com/khoih-prog/AsyncHTTPRequest_Generic/issues?q=is%3Aopen+is%3Aissue+label%3Aenhancement), feel free to add comments to them. ### Sending Pull Requests diff --git a/README.md b/README.md index de6ad740..645cbd53 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,10 @@ Chunked responses are recognized and handled transparently. --- --- +### Releases v1.0.2 + +1. Make Mutex Lock and delete more reliable and error-proof to prevent random crash. + ### Releases v1.0.1 1. Restore cpp code besides Impl.h code to use in case of `multiple definition` linker error. 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). See [**HOWTO Fix `Multiple Definitions` Linker Error**](https://github.com/khoih-prog/AsyncHTTPRequest_Generic#HOWTO-Fix-Multiple-Definitions-Linker-Error) @@ -293,12 +297,12 @@ Please take a look at other examples, as well. ```cpp #include "defines.h" -// 600s = 10 minutes to not flooding -#define HTTP_REQUEST_INTERVAL_MS 600000 +// 600s = 10 minutes to not flooding, 10s in testing +#define HTTP_REQUEST_INTERVAL_MS 10000 // 600000 -#include // https://github.com/khoih-prog/AsyncHTTPRequest_Generic +#include // https://github.com/khoih-prog/AsyncHTTPRequest_Generic -#include // https://github.com/sstaub/Ticker +#include // https://github.com/sstaub/Ticker AsyncHTTPRequest request; @@ -748,6 +752,10 @@ Submit issues to: [AsyncHTTPRequest_Generic issues](https://github.com/khoih-pro --- --- +### Releases v1.0.2 + +1. Make Mutex Lock and delete more reliable and error-proof to prevent random crash. + ### Releases v1.0.1 1. Restore cpp code besides Impl.h code to use in case of `multiple definition` linker error. 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). See [**HOWTO Fix `Multiple Definitions` Linker Error**](https://github.com/khoih-prog/AsyncHTTPRequest_Generic#HOWTO-Fix-Multiple-Definitions-Linker-Error) diff --git a/examples/AsyncCustomHeader_STM32/AsyncCustomHeader_STM32.ino b/examples/AsyncCustomHeader_STM32/AsyncCustomHeader_STM32.ino index f627ba27..e5bf7a3d 100644 --- a/examples/AsyncCustomHeader_STM32/AsyncCustomHeader_STM32.ino +++ b/examples/AsyncCustomHeader_STM32/AsyncCustomHeader_STM32.ino @@ -17,22 +17,23 @@ 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.0.1 + Version: 1.0.2 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K Hoang 14/09/2020 Initial coding to add support to STM32 using built-in Ethernet (Nucleo-144, DISCOVERY, etc). 1.0.1 K Hoang 09/10/2020 Restore cpp code besides Impl.h code. + 1.0.2 K Hoang 09/11/2020 Make Mutex Lock and delete more reliable and error-proof *****************************************************************************************************************************/ #include "defines.h" // Select a test server address -char GET_ServerAddress[] = "192.168.2.110/"; -//char GET_ServerAddress[] = "http://worldtimeapi.org/api/timezone/America/Toronto.txt"; +//char GET_ServerAddress[] = "192.168.2.110/"; +char GET_ServerAddress[] = "http://worldtimeapi.org/api/timezone/America/Toronto.txt"; -// 600s = 10 minutes to not flooding -#define HTTP_REQUEST_INTERVAL_MS 600000 +// 600s = 10 minutes to not flooding, 10s in testing +#define HTTP_REQUEST_INTERVAL_MS 10000 //600000 #include // https://github.com/khoih-prog/AsyncHTTPRequest_Generic @@ -52,7 +53,7 @@ void sendRequest(void) Serial.println("\nSending GET Request to " + String(GET_ServerAddress)); request.open("GET", GET_ServerAddress); - request.setReqHeader("X-CUSTOM-HEADER", "custom_value"); + //request.setReqHeader("X-CUSTOM-HEADER", "custom_value"); request.send(); } } diff --git a/examples/AsyncCustomHeader_STM32/defines.h b/examples/AsyncCustomHeader_STM32/defines.h index 1c727f9b..b42e5ab6 100644 --- a/examples/AsyncCustomHeader_STM32/defines.h +++ b/examples/AsyncCustomHeader_STM32/defines.h @@ -19,12 +19,13 @@ 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.0.1 + Version: 1.0.2 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K Hoang 14/09/2020 Initial coding to add support to STM32 using built-in Ethernet (Nucleo-144, DISCOVERY, etc). 1.0.1 K Hoang 09/10/2020 Restore cpp code besides Impl.h code. + 1.0.2 K Hoang 09/11/2020 Make Mutex Lock and delete more reliable and error-proof *****************************************************************************************************************************/ /* Currently support diff --git a/examples/AsyncDweetGet_STM32/AsyncDweetGet_STM32.ino b/examples/AsyncDweetGet_STM32/AsyncDweetGet_STM32.ino index ced0abcb..603ec5a2 100644 --- a/examples/AsyncDweetGet_STM32/AsyncDweetGet_STM32.ino +++ b/examples/AsyncDweetGet_STM32/AsyncDweetGet_STM32.ino @@ -17,12 +17,13 @@ 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.0.1 + Version: 1.0.2 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K Hoang 14/09/2020 Initial coding to add support to STM32 using built-in Ethernet (Nucleo-144, DISCOVERY, etc). 1.0.1 K Hoang 09/10/2020 Restore cpp code besides Impl.h code. + 1.0.2 K Hoang 09/11/2020 Make Mutex Lock and delete more reliable and error-proof *****************************************************************************************************************************/ /** diff --git a/examples/AsyncDweetGet_STM32/defines.h b/examples/AsyncDweetGet_STM32/defines.h index 27afabb8..b42e5ab6 100644 --- a/examples/AsyncDweetGet_STM32/defines.h +++ b/examples/AsyncDweetGet_STM32/defines.h @@ -19,11 +19,13 @@ 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.0.0 + Version: 1.0.2 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K Hoang 14/09/2020 Initial coding to add support to STM32 using built-in Ethernet (Nucleo-144, DISCOVERY, etc). + 1.0.1 K Hoang 09/10/2020 Restore cpp code besides Impl.h code. + 1.0.2 K Hoang 09/11/2020 Make Mutex Lock and delete more reliable and error-proof *****************************************************************************************************************************/ /* Currently support diff --git a/examples/AsyncDweetPost_STM32/AsyncDweetPost_STM32.ino b/examples/AsyncDweetPost_STM32/AsyncDweetPost_STM32.ino index feaccb8f..ce7eef06 100644 --- a/examples/AsyncDweetPost_STM32/AsyncDweetPost_STM32.ino +++ b/examples/AsyncDweetPost_STM32/AsyncDweetPost_STM32.ino @@ -17,12 +17,13 @@ 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.0.1 + Version: 1.0.2 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K Hoang 14/09/2020 Initial coding to add support to STM32 using built-in Ethernet (Nucleo-144, DISCOVERY, etc). 1.0.1 K Hoang 09/10/2020 Restore cpp code besides Impl.h code. + 1.0.2 K Hoang 09/11/2020 Make Mutex Lock and delete more reliable and error-proof *****************************************************************************************************************************/ // Dweet.io POST client. Connects to dweet.io once every ten seconds, sends a POST request and a request body. diff --git a/examples/AsyncDweetPost_STM32/defines.h b/examples/AsyncDweetPost_STM32/defines.h index 27afabb8..b42e5ab6 100644 --- a/examples/AsyncDweetPost_STM32/defines.h +++ b/examples/AsyncDweetPost_STM32/defines.h @@ -19,11 +19,13 @@ 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.0.0 + Version: 1.0.2 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K Hoang 14/09/2020 Initial coding to add support to STM32 using built-in Ethernet (Nucleo-144, DISCOVERY, etc). + 1.0.1 K Hoang 09/10/2020 Restore cpp code besides Impl.h code. + 1.0.2 K Hoang 09/11/2020 Make Mutex Lock and delete more reliable and error-proof *****************************************************************************************************************************/ /* Currently support diff --git a/examples/AsyncHTTPRequest_ESP/AsyncHTTPRequest_ESP.ino b/examples/AsyncHTTPRequest_ESP/AsyncHTTPRequest_ESP.ino index 869b5391..0c763292 100644 --- a/examples/AsyncHTTPRequest_ESP/AsyncHTTPRequest_ESP.ino +++ b/examples/AsyncHTTPRequest_ESP/AsyncHTTPRequest_ESP.ino @@ -17,12 +17,13 @@ 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.0.1 + Version: 1.0.2 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K Hoang 14/09/2020 Initial coding to add support to STM32 using built-in Ethernet (Nucleo-144, DISCOVERY, etc). 1.0.1 K Hoang 09/10/2020 Restore cpp code besides Impl.h code. + 1.0.2 K Hoang 09/11/2020 Make Mutex Lock and delete more reliable and error-proof *****************************************************************************************************************************/ //************************************************************************************************************ // @@ -56,7 +57,7 @@ #define _ASYNC_HTTP_LOGLEVEL_ 1 // 300s = 5 minutes to not flooding -#define HTTP_REQUEST_INTERVAL 300 +#define HTTP_REQUEST_INTERVAL 30 //300 // 10s #define HEARTBEAT_INTERVAL 10 @@ -77,6 +78,7 @@ const char* password = "your_pass"; AsyncHTTPRequest request; Ticker ticker; +Ticker ticker1; void heartBeatPrint(void) { @@ -106,6 +108,10 @@ void sendRequest() request.open("GET", "http://worldtimeapi.org/api/timezone/America/Toronto.txt"); request.send(); } + else + { + Serial.println("Can't send request"); + } } void requestCB(void* optParm, AsyncHTTPRequest* request, int readyState) @@ -130,13 +136,6 @@ void setup() WiFi.mode(WIFI_STA); - if (WiFi.status() == WL_NO_SHIELD) - { - Serial.println(F("WiFi shield not present")); - // don't continue - while (true); - } - WiFi.begin(ssid, password); Serial.println("Connecting to WiFi SSID: " + String(ssid)); @@ -155,7 +154,7 @@ void setup() request.onReadyStateChange(requestCB); ticker.attach(HTTP_REQUEST_INTERVAL, sendRequest); - ticker.attach(HEARTBEAT_INTERVAL, heartBeatPrint); + ticker1.attach(HEARTBEAT_INTERVAL, heartBeatPrint); // Send first request now sendRequest(); diff --git a/examples/AsyncHTTPRequest_ESP_WiFiManager/AsyncHTTPRequest_ESP_WiFiManager.ino b/examples/AsyncHTTPRequest_ESP_WiFiManager/AsyncHTTPRequest_ESP_WiFiManager.ino index 6e433651..598ce8a9 100644 --- a/examples/AsyncHTTPRequest_ESP_WiFiManager/AsyncHTTPRequest_ESP_WiFiManager.ino +++ b/examples/AsyncHTTPRequest_ESP_WiFiManager/AsyncHTTPRequest_ESP_WiFiManager.ino @@ -17,12 +17,13 @@ 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.0.1 + Version: 1.0.2 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K Hoang 14/09/2020 Initial coding to add support to STM32 using built-in Ethernet (Nucleo-144, DISCOVERY, etc). 1.0.1 K Hoang 09/10/2020 Restore cpp code besides Impl.h code. + 1.0.2 K Hoang 09/11/2020 Make Mutex Lock and delete more reliable and error-proof *****************************************************************************************************************************/ //************************************************************************************************************ // @@ -55,8 +56,8 @@ #define ASYNC_HTTP_DEBUG_PORT Serial #define _ASYNC_HTTP_LOGLEVEL_ 1 -// 300s = 5 minutes to not flooding -#define HTTP_REQUEST_INTERVAL 300 +// 300s = 5 minutes to not flooding, 10s in testing +#define HTTP_REQUEST_INTERVAL 10 //300 //Ported to ESP32 #ifdef ESP32 diff --git a/examples/AsyncHTTPRequest_STM32/AsyncHTTPRequest_STM32.ino b/examples/AsyncHTTPRequest_STM32/AsyncHTTPRequest_STM32.ino index f728ce5e..6f3be21c 100644 --- a/examples/AsyncHTTPRequest_STM32/AsyncHTTPRequest_STM32.ino +++ b/examples/AsyncHTTPRequest_STM32/AsyncHTTPRequest_STM32.ino @@ -17,12 +17,13 @@ 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.0.1 + Version: 1.0.2 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K Hoang 14/09/2020 Initial coding to add support to STM32 using built-in Ethernet (Nucleo-144, DISCOVERY, etc). 1.0.1 K Hoang 09/10/2020 Restore cpp code besides Impl.h code. + 1.0.2 K Hoang 09/11/2020 Make Mutex Lock and delete more reliable and error-proof *****************************************************************************************************************************/ //************************************************************************************************************ // @@ -49,8 +50,8 @@ #include "defines.h" -// 600s = 10 minutes to not flooding -#define HTTP_REQUEST_INTERVAL_MS 600000 +// 600s = 10 minutes to not flooding, 10s in testing +#define HTTP_REQUEST_INTERVAL_MS 10000 //600000 #include // https://github.com/khoih-prog/AsyncHTTPRequest_Generic diff --git a/examples/AsyncHTTPRequest_STM32/defines.h b/examples/AsyncHTTPRequest_STM32/defines.h index 27afabb8..b42e5ab6 100644 --- a/examples/AsyncHTTPRequest_STM32/defines.h +++ b/examples/AsyncHTTPRequest_STM32/defines.h @@ -19,11 +19,13 @@ 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.0.0 + Version: 1.0.2 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K Hoang 14/09/2020 Initial coding to add support to STM32 using built-in Ethernet (Nucleo-144, DISCOVERY, etc). + 1.0.1 K Hoang 09/10/2020 Restore cpp code besides Impl.h code. + 1.0.2 K Hoang 09/11/2020 Make Mutex Lock and delete more reliable and error-proof *****************************************************************************************************************************/ /* Currently support diff --git a/examples/AsyncSimpleGET_STM32/AsyncSimpleGET_STM32.ino b/examples/AsyncSimpleGET_STM32/AsyncSimpleGET_STM32.ino index fa84bd85..c12b1d3e 100644 --- a/examples/AsyncSimpleGET_STM32/AsyncSimpleGET_STM32.ino +++ b/examples/AsyncSimpleGET_STM32/AsyncSimpleGET_STM32.ino @@ -17,12 +17,13 @@ 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.0.1 + Version: 1.0.2 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K Hoang 14/09/2020 Initial coding to add support to STM32 using built-in Ethernet (Nucleo-144, DISCOVERY, etc). 1.0.1 K Hoang 09/10/2020 Restore cpp code besides Impl.h code. + 1.0.2 K Hoang 09/11/2020 Make Mutex Lock and delete more reliable and error-proof *****************************************************************************************************************************/ #include "defines.h" @@ -31,8 +32,8 @@ //char GET_ServerAddress[] = "ipv4bot.whatismyipaddress.com/"; char GET_ServerAddress[] = "http://worldtimeapi.org/api/timezone/America/Toronto.txt"; -// 600s = 10 minutes to not flooding -#define HTTP_REQUEST_INTERVAL_MS 600000 +// 600s = 10 minutes to not flooding, 10s in testing +#define HTTP_REQUEST_INTERVAL_MS 10000 //600000 #include // https://github.com/khoih-prog/AsyncHTTPRequest_Generic diff --git a/examples/AsyncSimpleGET_STM32/defines.h b/examples/AsyncSimpleGET_STM32/defines.h index c171fde9..df9f4125 100644 --- a/examples/AsyncSimpleGET_STM32/defines.h +++ b/examples/AsyncSimpleGET_STM32/defines.h @@ -19,11 +19,13 @@ 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.0.0 - + Version: 1.0.2 + Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K Hoang 14/09/2020 Initial coding to add support to STM32 using built-in Ethernet (Nucleo-144, DISCOVERY, etc). + 1.0.1 K Hoang 09/10/2020 Restore cpp code besides Impl.h code. + 1.0.2 K Hoang 09/11/2020 Make Mutex Lock and delete more reliable and error-proof *****************************************************************************************************************************/ /* Currently support diff --git a/examples/AsyncWebClientRepeating_STM32/AsyncWebClientRepeating_STM32.ino b/examples/AsyncWebClientRepeating_STM32/AsyncWebClientRepeating_STM32.ino index d0ddb9a8..280f6d52 100644 --- a/examples/AsyncWebClientRepeating_STM32/AsyncWebClientRepeating_STM32.ino +++ b/examples/AsyncWebClientRepeating_STM32/AsyncWebClientRepeating_STM32.ino @@ -17,12 +17,13 @@ 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.0.1 + Version: 1.0.2 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K Hoang 14/09/2020 Initial coding to add support to STM32 using built-in Ethernet (Nucleo-144, DISCOVERY, etc). 1.0.1 K Hoang 09/10/2020 Restore cpp code besides Impl.h code. + 1.0.2 K Hoang 09/11/2020 Make Mutex Lock and delete more reliable and error-proof *****************************************************************************************************************************/ #include "defines.h" diff --git a/examples/AsyncWebClientRepeating_STM32/defines.h b/examples/AsyncWebClientRepeating_STM32/defines.h index 27afabb8..b42e5ab6 100644 --- a/examples/AsyncWebClientRepeating_STM32/defines.h +++ b/examples/AsyncWebClientRepeating_STM32/defines.h @@ -19,11 +19,13 @@ 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.0.0 + Version: 1.0.2 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K Hoang 14/09/2020 Initial coding to add support to STM32 using built-in Ethernet (Nucleo-144, DISCOVERY, etc). + 1.0.1 K Hoang 09/10/2020 Restore cpp code besides Impl.h code. + 1.0.2 K Hoang 09/11/2020 Make Mutex Lock and delete more reliable and error-proof *****************************************************************************************************************************/ /* Currently support diff --git a/library.json b/library.json index 5c90250d..c8690b00 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name":"AsyncHTTPRequest_Generic", - "version": "1.0.1", + "version": "1.0.2", "description":"Simple Async HTTP Request library, supporting GET and POST, 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", "authors": [ diff --git a/library.properties b/library.properties index bc54bd90..f440d8c4 100644 --- a/library.properties +++ b/library.properties @@ -1,6 +1,6 @@ name=AsyncHTTPRequest_Generic -version=1.0.1 -author=Bob Lemaire,Khoi Hoang +version=1.0.2 +author=Bob Lemaire,Khoi Hoang maintainer=Khoi Hoang license=MIT sentence=Simple Async HTTP Request library, supporting GET and POST, on top of AsyncTCP libraries, such as AsyncTCP, ESPAsyncTCP, AsyncTCP_STM32, etc.. for ESP32, ESP8266 and currently STM32 with built-in LAN8742A Ethernet. diff --git a/src/AsyncHTTPRequest_Debug_Generic.h b/src/AsyncHTTPRequest_Debug_Generic.h index b6e50b41..d5e1bd43 100644 --- a/src/AsyncHTTPRequest_Debug_Generic.h +++ b/src/AsyncHTTPRequest_Debug_Generic.h @@ -17,12 +17,13 @@ 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.0.1 + Version: 1.0.2 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K Hoang 14/09/2020 Initial coding to add support to STM32 using built-in Ethernet (Nucleo-144, DISCOVERY, etc). 1.0.1 K Hoang 09/10/2020 Restore cpp code besides Impl.h code. + 1.0.2 K Hoang 09/11/2020 Make Mutex Lock and delete more reliable and error-proof *****************************************************************************************************************************/ #pragma once diff --git a/src/AsyncHTTPRequest_Generic.h b/src/AsyncHTTPRequest_Generic.h index 2457f587..0d97a9e2 100644 --- a/src/AsyncHTTPRequest_Generic.h +++ b/src/AsyncHTTPRequest_Generic.h @@ -17,17 +17,18 @@ 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.0.1 + Version: 1.0.2 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K Hoang 14/09/2020 Initial coding to add support to STM32 using built-in Ethernet (Nucleo-144, DISCOVERY, etc). 1.0.1 K Hoang 09/10/2020 Restore cpp code besides Impl.h code. + 1.0.2 K Hoang 09/11/2020 Make Mutex Lock and delete more reliable and error-proof *****************************************************************************************************************************/ #pragma once -#define AsyncHTTPRequest_Generic_version "1.0.0" +#define ASYNC_HTTP_REQUEST_GENERIC_VERSION "1.0.2" #include @@ -43,15 +44,29 @@ #define DEBUG_IOTA_HTTP_SET false #endif + +// KH add +#define SAFE_DELETE(object) if (object) { delete object;} +#define SAFE_DELETE_ARRAY(object) if (object) { delete[] object;} + #if ESP32 #include + + // KH mod + #define MUTEX_LOCK_NR if (xSemaphoreTakeRecursive(threadLock,portMAX_DELAY) != pdTRUE) { return;} + #define MUTEX_LOCK(returnVal) if (xSemaphoreTakeRecursive(threadLock,portMAX_DELAY) != pdTRUE) { return returnVal;} + #define _lock xSemaphoreTakeRecursive(threadLock,portMAX_DELAY) #define _unlock xSemaphoreGiveRecursive(threadLock) #elif ESP8266 #include + + #define MUTEX_LOCK_NR + #define MUTEX_LOCK(returnVal) + #define _lock #define _unlock @@ -60,6 +75,9 @@ defined(STM32WB) || defined(STM32MP1) ) #include "STM32AsyncTCP.h" + + #define MUTEX_LOCK_NR + #define MUTEX_LOCK(returnVal) #define _lock #define _unlock @@ -108,9 +126,12 @@ class AsyncHTTPRequest ~header() { - delete[] name; - delete[] value; - delete next; + SAFE_DELETE_ARRAY(name) + SAFE_DELETE_ARRAY(value) + SAFE_DELETE(next) + //delete[] name; + //delete[] value; + //delete next; } }; @@ -131,13 +152,13 @@ class AsyncHTTPRequest ~URL() { - delete[] scheme; - delete[] user; - delete[] pwd; - delete[] host; - delete[] path; - delete[] query; - delete[] fragment; + SAFE_DELETE_ARRAY(scheme) + SAFE_DELETE_ARRAY(user) + SAFE_DELETE_ARRAY(pwd) + SAFE_DELETE_ARRAY(host) + SAFE_DELETE_ARRAY(path) + SAFE_DELETE_ARRAY(query) + SAFE_DELETE_ARRAY(fragment) } }; diff --git a/src/AsyncHTTPRequest_Impl_Generic.h b/src/AsyncHTTPRequest_Impl_Generic.h index 853a3292..3f5ac508 100644 --- a/src/AsyncHTTPRequest_Impl_Generic.h +++ b/src/AsyncHTTPRequest_Impl_Generic.h @@ -17,12 +17,13 @@ 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.0.1 + Version: 1.0.2 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K Hoang 14/09/2020 Initial coding to add support to STM32 using built-in Ethernet (Nucleo-144, DISCOVERY, etc). 1.0.1 K Hoang 09/10/2020 Restore cpp code besides Impl.h code. + 1.0.2 K Hoang 09/11/2020 Make Mutex Lock and delete more reliable and error-proof *****************************************************************************************************************************/ #pragma once @@ -46,15 +47,20 @@ AsyncHTTPRequest::~AsyncHTTPRequest() if (_client) _client->close(true); - delete _URL; - delete _headers; - delete _request; - delete _response; - delete _chunks; - delete[] _connectedHost; + + SAFE_DELETE(_URL) + SAFE_DELETE(_headers) + SAFE_DELETE(_request) + SAFE_DELETE(_response) + SAFE_DELETE(_chunks) + SAFE_DELETE_ARRAY(_connectedHost) #ifdef ESP32 - vSemaphoreDelete(threadLock); + // KH add + if (threadLock) + { + vSemaphoreDelete(threadLock); + } #endif } @@ -65,8 +71,9 @@ void AsyncHTTPRequest::setDebug(bool debug) { _debug = true; - AHTTP_LOGDEBUG3("setDebug(", debug ? "on" : "off", ") version", AsyncHTTPRequest_Generic_version); + AHTTP_LOGDEBUG3("setDebug(", debug ? "on" : "off", ") version", ASYNC_HTTP_REQUEST_GENERIC_VERSION); } + _debug = debug; } @@ -88,13 +95,13 @@ bool AsyncHTTPRequest::open(const char* method, const char* URL) _requestStartTime = millis(); - delete _URL; - delete _headers; - delete _request; - delete _response; - delete _chunks; + SAFE_DELETE(_URL) + SAFE_DELETE(_headers) + SAFE_DELETE(_request) + SAFE_DELETE(_response) + SAFE_DELETE(_chunks) - _URL = nullptr; + _URL = nullptr; _headers = nullptr; _response = nullptr; _request = nullptr; @@ -124,12 +131,20 @@ bool AsyncHTTPRequest::open(const char* method, const char* URL) } char* hostName = new char[strlen(_URL->host) + 10]; - sprintf(hostName, "%s:%d", _URL->host, _URL->port); - _addHeader("host", hostName); - delete[] hostName; - _lastActivity = millis(); + + if (hostName) + { + sprintf(hostName, "%s:%d", _URL->host, _URL->port); + _addHeader("host", hostName); + + SAFE_DELETE_ARRAY(hostName) + + _lastActivity = millis(); - return _connect(); + return _connect(); + } + else + return false; } //************************************************************************************************************** void AsyncHTTPRequest::onReadyStateChange(readyStateChangeCB cb, void* arg) @@ -151,12 +166,13 @@ bool AsyncHTTPRequest::send() { AHTTP_LOGDEBUG("send()"); - _lock; + MUTEX_LOCK(false) if ( ! _buildRequest()) return false; _send(); + _unlock; return true; @@ -167,17 +183,20 @@ bool AsyncHTTPRequest::send(String body) { AHTTP_LOGDEBUG3("send(String)", body.substring(0, 16).c_str(), ", length =", body.length()); - _lock; + MUTEX_LOCK(false) + _addHeader("Content-Length", String(body.length()).c_str()); if ( ! _buildRequest()) { _unlock; + return false; } _request->write(body); _send(); + _unlock; return true; @@ -188,7 +207,8 @@ bool AsyncHTTPRequest::send(const char* body) { AHTTP_LOGDEBUG3("send(char)", body, ", length =", strlen(body)); - _lock; + MUTEX_LOCK(false) + _addHeader("Content-Length", String(strlen(body)).c_str()); if ( ! _buildRequest()) @@ -200,6 +220,7 @@ bool AsyncHTTPRequest::send(const char* body) _request->write(body); _send(); + _unlock; return true; @@ -210,7 +231,8 @@ bool AsyncHTTPRequest::send(const uint8_t* body, size_t len) { AHTTP_LOGDEBUG3("send(char)", (char*) body, ", length =", len); - _lock; + MUTEX_LOCK(false) + _addHeader("Content-Length", String(len).c_str()); if ( ! _buildRequest()) @@ -222,6 +244,7 @@ bool AsyncHTTPRequest::send(const uint8_t* body, size_t len) _request->write(body, len); _send(); + _unlock; return true; @@ -232,7 +255,8 @@ bool AsyncHTTPRequest::send(xbuf* body, size_t len) { AHTTP_LOGDEBUG3("send(char)", body->peekString(16).c_str(), ", length =", len); - _lock; + MUTEX_LOCK(false) + _addHeader("Content-Length", String(len).c_str()); if ( ! _buildRequest()) @@ -244,6 +268,7 @@ bool AsyncHTTPRequest::send(xbuf* body, size_t len) _request->write(body, len); _send(); + _unlock; return true; @@ -254,12 +279,15 @@ void AsyncHTTPRequest::abort() { AHTTP_LOGDEBUG("abort()"); - _lock; - - if (! _client) + if (! _client) + { return; + } + + MUTEX_LOCK_NR _client->abort(); + _unlock; } //************************************************************************************************************** @@ -279,7 +307,7 @@ String AsyncHTTPRequest::responseText() { AHTTP_LOGDEBUG("responseText()"); - _lock; + MUTEX_LOCK(String()) if ( ! _response || _readyState < readyStateLoading || ! available()) { @@ -299,12 +327,13 @@ String AsyncHTTPRequest::responseText() _HTTPcode = HTTPCODE_TOO_LESS_RAM; _client->abort(); + _unlock; return String(); } - localString = _response->readString(avail); + localString = _response->readString(avail); _contentRead += localString.length(); AHTTP_LOGDEBUG3("responseText(char)", localString.substring(0, 16).c_str(), ", avail =", avail); @@ -319,19 +348,20 @@ size_t AsyncHTTPRequest::responseRead(uint8_t* buf, size_t len) { if ( ! _response || _readyState < readyStateLoading || ! available()) { - //DEBUG_HTTP("responseRead() no data\r\n"); AHTTP_LOGDEBUG("responseRead() no data"); return 0; } - _lock; + MUTEX_LOCK(0) + size_t avail = available() > len ? len : available(); _response->read(buf, avail); AHTTP_LOGDEBUG3("responseRead(char)", (char*) buf, ", avail =", avail); _contentRead += avail; + _unlock; return avail; @@ -386,7 +416,7 @@ uint32_t AsyncHTTPRequest::elapsedTime() //************************************************************************************************************** String AsyncHTTPRequest::version() { - return String(AsyncHTTPRequest_Generic_version); + return String(ASYNC_HTTP_REQUEST_GENERIC_VERSION); } /*______________________________________________________________________________________________________________ @@ -407,11 +437,22 @@ bool AsyncHTTPRequest::_parseURL(const char* url) //************************************************************************************************************** bool AsyncHTTPRequest::_parseURL(String url) { - delete _URL; + SAFE_DELETE(_URL) int hostBeg = 0; + _URL = new URL; - _URL->scheme = new char[8]; + + if (_URL) + { + _URL->scheme = new char[8]; + + if (! (_URL->scheme) ) + return false; + } + else + return false; + strcpy(_URL->scheme, "HTTP://"); if (url.substring(0, 7).equalsIgnoreCase("HTTP://")) @@ -438,6 +479,10 @@ bool AsyncHTTPRequest::_parseURL(String url) } _URL->host = new char[hostEnd - hostBeg + 1]; + + if (_URL->host == nullptr) + return false; + strcpy(_URL->host, url.substring(hostBeg, hostEnd).c_str()); int queryBeg = url.indexOf('?'); @@ -446,8 +491,17 @@ bool AsyncHTTPRequest::_parseURL(String url) queryBeg = url.length(); _URL->path = new char[queryBeg - pathBeg + 1]; + + if (_URL->path == nullptr) + return false; + strcpy(_URL->path, url.substring(pathBeg, queryBeg).c_str()); + _URL->query = new char[url.length() - queryBeg + 1]; + + if (_URL->query == nullptr) + return false; + strcpy(_URL->query, url.substring(queryBeg).c_str()); AHTTP_LOGDEBUG2("_parseURL(): scheme+host", _URL->scheme, _URL->host); @@ -464,11 +518,18 @@ bool AsyncHTTPRequest::_connect() if ( ! _client) { _client = new AsyncClient(); + + if (! _client) + return false; } - delete[] _connectedHost; + SAFE_DELETE_ARRAY(_connectedHost) _connectedHost = new char[strlen(_URL->host) + 1]; + + if (_connectedHost == nullptr) + return false; + strcpy(_connectedHost, _URL->host); _connectedPort = _URL->port; @@ -521,7 +582,12 @@ bool AsyncHTTPRequest::_buildRequest() // Build the header. if ( ! _request) + { _request = new xbuf; + + if ( ! _request) + return false; + } _request->write(_HTTPmethod == HTTPmethodGET ? "GET " : "POST "); _request->write(_URL->path); @@ -530,7 +596,7 @@ bool AsyncHTTPRequest::_buildRequest() AHTTP_LOGDEBUG3(_HTTPmethod == HTTPmethodGET ? "GET " : "POST ", _URL->path, _URL->query, " HTTP/1.1\r\n" ); - delete _URL; + SAFE_DELETE(_URL) _URL = nullptr; header* hdr = _headers; @@ -547,7 +613,8 @@ bool AsyncHTTPRequest::_buildRequest() hdr = hdr->next; } - delete _headers; + SAFE_DELETE(_headers) + _headers = nullptr; _request->write("\r\n"); @@ -577,19 +644,26 @@ size_t AsyncHTTPRequest::_send() size_t sent = 0; uint8_t* temp = new uint8_t[100]; + + if (!temp) + return 0; while (supply) { size_t chunk = supply < 100 ? supply : 100; - supply -= _request->read(temp, chunk); - sent += _client->add((char*)temp, chunk); + + supply -= _request->read(temp, chunk); + sent += _client->add((char*)temp, chunk); } - delete temp; + // KH, Must be delete [] temp; + SAFE_DELETE_ARRAY(temp) if (_request->available() == 0) { - delete _request; + //delete _request; + SAFE_DELETE(_request) + _request = nullptr; } @@ -679,10 +753,20 @@ void AsyncHTTPRequest::_onConnect(AsyncClient* client) { AHTTP_LOGDEBUG("_onConnect handler"); - _lock; + MUTEX_LOCK_NR + _client = client; _setReadyState(readyStateOpened); + _response = new xbuf; + + if (!_response) + { + _unlock; + + return; + } + _contentLength = 0; _contentRead = 0; _chunked = false; @@ -703,13 +787,14 @@ void AsyncHTTPRequest::_onConnect(AsyncClient* client) } _lastActivity = millis(); + _unlock; } //************************************************************************************************************** void AsyncHTTPRequest::_onPoll(AsyncClient* client) { - _lock; + MUTEX_LOCK_NR if (_timeout && (millis() - _lastActivity) > (_timeout * 1000)) { @@ -740,7 +825,7 @@ void AsyncHTTPRequest::_onDisconnect(AsyncClient* client) { AHTTP_LOGDEBUG("\n_onDisconnect handler"); - _lock; + MUTEX_LOCK_NR if (_readyState < readyStateOpened) { @@ -752,16 +837,19 @@ void AsyncHTTPRequest::_onDisconnect(AsyncClient* client) _HTTPcode = HTTPCODE_CONNECTION_LOST; } - delete _client; + SAFE_DELETE(_client) + _client = nullptr; - delete[] _connectedHost; + SAFE_DELETE_ARRAY(_connectedHost) + _connectedHost = nullptr; - _connectedPort = -1; + _connectedPort = -1; _requestEndTime = millis(); - _lastActivity = 0; + _lastActivity = 0; _setReadyState(readyStateDone); + _unlock; } @@ -770,6 +858,8 @@ void AsyncHTTPRequest::_onData(void* Vbuf, size_t len) { AHTTP_LOGDEBUG3("_onData handler", (char*) Vbuf, ", len =", len); + MUTEX_LOCK_NR + _lastActivity = millis(); // Transfer data to xbuf @@ -786,8 +876,12 @@ void AsyncHTTPRequest::_onData(void* Vbuf, size_t len) // if headers not complete, collect them. If still not complete, just return. if (_readyState == readyStateOpened) { - if ( ! _collectHeaders()) + if ( ! _collectHeaders()) + { + _unlock; + return; + } } // If there's data in the buffer and not Done, advance readyState to Loading. @@ -932,7 +1026,8 @@ void AsyncHTTPRequest::setReqHeader(const char* name, const __FlashStringHelper* { char* _value = _charstar(value); _addHeader(name, _value); - delete[] _value; + + SAFE_DELETE_ARRAY(_value) } } @@ -943,7 +1038,8 @@ void AsyncHTTPRequest::setReqHeader(const __FlashStringHelper *name, const char* { char* _name = _charstar(name); _addHeader(_name, value); - delete[] _name; + + SAFE_DELETE_ARRAY(_name) } } @@ -955,8 +1051,9 @@ void AsyncHTTPRequest::setReqHeader(const __FlashStringHelper *name, const __Fla char* _name = _charstar(name); char* _value = _charstar(value); _addHeader(_name, _value); - delete[] _name; - delete[] _value; + + SAFE_DELETE_ARRAY(_name) + SAFE_DELETE_ARRAY(_value) } } @@ -967,7 +1064,8 @@ void AsyncHTTPRequest::setReqHeader(const __FlashStringHelper *name, int32_t val { char* _name = _charstar(name); setReqHeader(_name, String(value).c_str()); - delete[] _name; + + SAFE_DELETE_ARRAY(_name) } } @@ -1058,7 +1156,8 @@ char* AsyncHTTPRequest::respHeaderValue(const __FlashStringHelper *name) char* _name = _charstar(name); header* hdr = _getHeader(_name); - delete[] _name; + + SAFE_DELETE_ARRAY(_name) if ( ! hdr) return nullptr; @@ -1074,7 +1173,8 @@ bool AsyncHTTPRequest::respHeaderExists(const __FlashStringHelper *name) char* _name = _charstar(name); header* hdr = _getHeader(_name); - delete[] _name; + + SAFE_DELETE_ARRAY(_name) if ( ! hdr) return false; @@ -1087,7 +1187,8 @@ bool AsyncHTTPRequest::respHeaderExists(const __FlashStringHelper *name) //************************************************************************************************************** String AsyncHTTPRequest::headers() { - _lock; + MUTEX_LOCK(String()) + String _response = ""; header* hdr = _headers; @@ -1101,6 +1202,7 @@ String AsyncHTTPRequest::headers() } _response += "\r\n"; + _unlock; return _response; @@ -1109,17 +1211,19 @@ String AsyncHTTPRequest::headers() //************************************************************************************************************** AsyncHTTPRequest::header* AsyncHTTPRequest::_addHeader(const char* name, const char* value) { - _lock; + MUTEX_LOCK(nullptr) + header* hdr = (header*) &_headers; while (hdr->next) { if (strcasecmp(name, hdr->next->name) == 0) { - header* oldHdr = hdr->next; - hdr->next = hdr->next->next; - oldHdr->next = nullptr; - delete oldHdr; + header* oldHdr = hdr->next; + hdr->next = hdr->next->next; + oldHdr->next = nullptr; + + SAFE_DELETE(oldHdr) } else { @@ -1128,10 +1232,37 @@ AsyncHTTPRequest::header* AsyncHTTPRequest::_addHeader(const char* name, const } hdr->next = new header; - hdr->next->name = new char[strlen(name) + 1]; - strcpy(hdr->next->name, name); - hdr->next->value = new char[strlen(value) + 1]; - strcpy(hdr->next->value, value); + + if (hdr->next) + { + hdr->next->name = new char[strlen(name) + 1]; + + if (hdr->next->name) + strcpy(hdr->next->name, name); + else + { + SAFE_DELETE(hdr->next) + + return nullptr; + } + + hdr->next->value = new char[strlen(value) + 1]; + + if (hdr->next->value) + strcpy(hdr->next->value, value); + else + { + SAFE_DELETE_ARRAY(hdr->next->name) + SAFE_DELETE(hdr->next) + + return nullptr; + } + } + else + { + return nullptr; + } + _unlock; return hdr->next; @@ -1140,7 +1271,8 @@ AsyncHTTPRequest::header* AsyncHTTPRequest::_addHeader(const char* name, const //************************************************************************************************************** AsyncHTTPRequest::header* AsyncHTTPRequest::_getHeader(const char* name) { - _lock; + MUTEX_LOCK(nullptr) + header* hdr = _headers; while (hdr) @@ -1159,7 +1291,8 @@ AsyncHTTPRequest::header* AsyncHTTPRequest::_getHeader(const char* name) //************************************************************************************************************** AsyncHTTPRequest::header* AsyncHTTPRequest::_getHeader(int ndx) { - _lock; + MUTEX_LOCK(nullptr) + header* hdr = _headers; while (hdr) @@ -1184,8 +1317,13 @@ char* AsyncHTTPRequest::_charstar(const __FlashStringHelper * str) return nullptr; char* ptr = new char[strlen_P((PGM_P)str) + 1]; - strcpy_P(ptr, (PGM_P)str); + if (ptr) + { + strcpy_P(ptr, (PGM_P)str); + } + + // Rturn good ptr or nullptr return ptr; } diff --git a/src/utility/xbuf.h b/src/utility/xbuf.h index 84bdab60..ba30d8c2 100644 --- a/src/utility/xbuf.h +++ b/src/utility/xbuf.h @@ -17,12 +17,13 @@ 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.0.1 + Version: 1.0.2 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K Hoang 14/09/2020 Initial coding to add support to STM32 using built-in Ethernet (Nucleo-144, DISCOVERY, etc). 1.0.1 K Hoang 09/10/2020 Restore cpp code besides Impl.h code. + 1.0.2 K Hoang 09/11/2020 Make Mutex Lock and delete more reliable and error-proof *****************************************************************************************************************************/ /******************************************************************************************** diff --git a/src/utility/xbuf_Impl.h b/src/utility/xbuf_Impl.h index 5431af66..764e857f 100644 --- a/src/utility/xbuf_Impl.h +++ b/src/utility/xbuf_Impl.h @@ -17,12 +17,13 @@ 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.0.1 + Version: 1.0.2 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K Hoang 14/09/2020 Initial coding to add support to STM32 using built-in Ethernet (Nucleo-144, DISCOVERY, etc). 1.0.1 K Hoang 09/10/2020 Restore cpp code besides Impl.h code. + 1.0.2 K Hoang 09/11/2020 Make Mutex Lock and delete more reliable and error-proof *****************************************************************************************************************************/ #pragma once @@ -361,14 +362,20 @@ void xbuf::addSeg() if (_tail) { _tail->next = (xseg*) new uint32_t[_segSize / 4 + 1]; + + // KH, Must check NULL here _tail = _tail->next; } else { + // KH, Must check NULL here _tail = _head = (xseg*) new uint32_t[_segSize / 4 + 1]; } - _tail->next = nullptr; + // KH, Must check NULL here + if (_tail) + _tail->next = nullptr; + _free += _segSize; } diff --git a/src_cpp/AsyncHTTPRequest_Debug_Generic.h b/src_cpp/AsyncHTTPRequest_Debug_Generic.h index de5a7760..d5e1bd43 100644 --- a/src_cpp/AsyncHTTPRequest_Debug_Generic.h +++ b/src_cpp/AsyncHTTPRequest_Debug_Generic.h @@ -1,5 +1,5 @@ /**************************************************************************************************************************** - src_cpp/AsyncHTTPRequest_Debug_Generic.h - Dead simple AsyncHTTPRequest for ESP8266, ESP32 and currently STM32 with built-in LAN8742A Ethernet + AsyncHTTPRequest_Debug_Generic.h - Dead simple AsyncHTTPRequest for ESP8266, ESP32 and currently STM32 with built-in LAN8742A Ethernet For ESP8266, ESP32 and STM32 with built-in LAN8742A Ethernet (Nucleo-144, DISCOVERY, etc) @@ -17,12 +17,13 @@ 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.0.1 + Version: 1.0.2 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K Hoang 14/09/2020 Initial coding to add support to STM32 using built-in Ethernet (Nucleo-144, DISCOVERY, etc). 1.0.1 K Hoang 09/10/2020 Restore cpp code besides Impl.h code. + 1.0.2 K Hoang 09/11/2020 Make Mutex Lock and delete more reliable and error-proof *****************************************************************************************************************************/ #pragma once diff --git a/src_cpp/AsyncHTTPRequest_Generic.cpp b/src_cpp/AsyncHTTPRequest_Generic.cpp index 532ec94b..d3c5c8dc 100644 --- a/src_cpp/AsyncHTTPRequest_Generic.cpp +++ b/src_cpp/AsyncHTTPRequest_Generic.cpp @@ -1,5 +1,5 @@ /**************************************************************************************************************************** - src_cpp/AsyncHTTPRequest_Generic.cpp - Dead simple AsyncHTTPRequest for ESP8266, ESP32 and currently STM32 with built-in LAN8742A Ethernet + AsyncHTTPRequest_Generic.cpp - Dead simple AsyncHTTPRequest for ESP8266, ESP32 and currently STM32 with built-in LAN8742A Ethernet For ESP8266, ESP32 and STM32 with built-in LAN8742A Ethernet (Nucleo-144, DISCOVERY, etc) @@ -17,14 +17,16 @@ 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.0.1 + Version: 1.0.2 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K Hoang 14/09/2020 Initial coding to add support to STM32 using built-in Ethernet (Nucleo-144, DISCOVERY, etc). 1.0.1 K Hoang 09/10/2020 Restore cpp code besides Impl.h code. + 1.0.2 K Hoang 09/11/2020 Make Mutex Lock and delete more reliable and error-proof *****************************************************************************************************************************/ +#include "AsyncHTTPRequest_Debug_Generic.h" #include "AsyncHTTPRequest_Generic.h" @@ -46,15 +48,20 @@ AsyncHTTPRequest::~AsyncHTTPRequest() if (_client) _client->close(true); - delete _URL; - delete _headers; - delete _request; - delete _response; - delete _chunks; - delete[] _connectedHost; + + SAFE_DELETE(_URL) + SAFE_DELETE(_headers) + SAFE_DELETE(_request) + SAFE_DELETE(_response) + SAFE_DELETE(_chunks) + SAFE_DELETE_ARRAY(_connectedHost) #ifdef ESP32 - vSemaphoreDelete(threadLock); + // KH add + if (threadLock) + { + vSemaphoreDelete(threadLock); + } #endif } @@ -65,8 +72,9 @@ void AsyncHTTPRequest::setDebug(bool debug) { _debug = true; - AHTTP_LOGDEBUG3("setDebug(", debug ? "on" : "off", ") version", AsyncHTTPRequest_Generic_version); + AHTTP_LOGDEBUG3("setDebug(", debug ? "on" : "off", ") version", ASYNC_HTTP_REQUEST_GENERIC_VERSION); } + _debug = debug; } @@ -88,13 +96,13 @@ bool AsyncHTTPRequest::open(const char* method, const char* URL) _requestStartTime = millis(); - delete _URL; - delete _headers; - delete _request; - delete _response; - delete _chunks; + SAFE_DELETE(_URL) + SAFE_DELETE(_headers) + SAFE_DELETE(_request) + SAFE_DELETE(_response) + SAFE_DELETE(_chunks) - _URL = nullptr; + _URL = nullptr; _headers = nullptr; _response = nullptr; _request = nullptr; @@ -124,12 +132,20 @@ bool AsyncHTTPRequest::open(const char* method, const char* URL) } char* hostName = new char[strlen(_URL->host) + 10]; - sprintf(hostName, "%s:%d", _URL->host, _URL->port); - _addHeader("host", hostName); - delete[] hostName; - _lastActivity = millis(); + + if (hostName) + { + sprintf(hostName, "%s:%d", _URL->host, _URL->port); + _addHeader("host", hostName); + + SAFE_DELETE_ARRAY(hostName) + + _lastActivity = millis(); - return _connect(); + return _connect(); + } + else + return false; } //************************************************************************************************************** void AsyncHTTPRequest::onReadyStateChange(readyStateChangeCB cb, void* arg) @@ -151,12 +167,13 @@ bool AsyncHTTPRequest::send() { AHTTP_LOGDEBUG("send()"); - _lock; + MUTEX_LOCK(false) if ( ! _buildRequest()) return false; _send(); + _unlock; return true; @@ -167,17 +184,20 @@ bool AsyncHTTPRequest::send(String body) { AHTTP_LOGDEBUG3("send(String)", body.substring(0, 16).c_str(), ", length =", body.length()); - _lock; + MUTEX_LOCK(false) + _addHeader("Content-Length", String(body.length()).c_str()); if ( ! _buildRequest()) { _unlock; + return false; } _request->write(body); _send(); + _unlock; return true; @@ -188,7 +208,8 @@ bool AsyncHTTPRequest::send(const char* body) { AHTTP_LOGDEBUG3("send(char)", body, ", length =", strlen(body)); - _lock; + MUTEX_LOCK(false) + _addHeader("Content-Length", String(strlen(body)).c_str()); if ( ! _buildRequest()) @@ -200,6 +221,7 @@ bool AsyncHTTPRequest::send(const char* body) _request->write(body); _send(); + _unlock; return true; @@ -210,7 +232,8 @@ bool AsyncHTTPRequest::send(const uint8_t* body, size_t len) { AHTTP_LOGDEBUG3("send(char)", (char*) body, ", length =", len); - _lock; + MUTEX_LOCK(false) + _addHeader("Content-Length", String(len).c_str()); if ( ! _buildRequest()) @@ -222,6 +245,7 @@ bool AsyncHTTPRequest::send(const uint8_t* body, size_t len) _request->write(body, len); _send(); + _unlock; return true; @@ -232,7 +256,8 @@ bool AsyncHTTPRequest::send(xbuf* body, size_t len) { AHTTP_LOGDEBUG3("send(char)", body->peekString(16).c_str(), ", length =", len); - _lock; + MUTEX_LOCK(false) + _addHeader("Content-Length", String(len).c_str()); if ( ! _buildRequest()) @@ -244,6 +269,7 @@ bool AsyncHTTPRequest::send(xbuf* body, size_t len) _request->write(body, len); _send(); + _unlock; return true; @@ -254,12 +280,15 @@ void AsyncHTTPRequest::abort() { AHTTP_LOGDEBUG("abort()"); - _lock; - - if (! _client) + if (! _client) + { return; + } + + MUTEX_LOCK_NR _client->abort(); + _unlock; } //************************************************************************************************************** @@ -279,7 +308,7 @@ String AsyncHTTPRequest::responseText() { AHTTP_LOGDEBUG("responseText()"); - _lock; + MUTEX_LOCK(String()) if ( ! _response || _readyState < readyStateLoading || ! available()) { @@ -299,12 +328,13 @@ String AsyncHTTPRequest::responseText() _HTTPcode = HTTPCODE_TOO_LESS_RAM; _client->abort(); + _unlock; return String(); } - localString = _response->readString(avail); + localString = _response->readString(avail); _contentRead += localString.length(); AHTTP_LOGDEBUG3("responseText(char)", localString.substring(0, 16).c_str(), ", avail =", avail); @@ -319,19 +349,20 @@ size_t AsyncHTTPRequest::responseRead(uint8_t* buf, size_t len) { if ( ! _response || _readyState < readyStateLoading || ! available()) { - //DEBUG_HTTP("responseRead() no data\r\n"); AHTTP_LOGDEBUG("responseRead() no data"); return 0; } - _lock; + MUTEX_LOCK(0) + size_t avail = available() > len ? len : available(); _response->read(buf, avail); AHTTP_LOGDEBUG3("responseRead(char)", (char*) buf, ", avail =", avail); _contentRead += avail; + _unlock; return avail; @@ -386,7 +417,7 @@ uint32_t AsyncHTTPRequest::elapsedTime() //************************************************************************************************************** String AsyncHTTPRequest::version() { - return String(AsyncHTTPRequest_Generic_version); + return String(ASYNC_HTTP_REQUEST_GENERIC_VERSION); } /*______________________________________________________________________________________________________________ @@ -407,11 +438,22 @@ bool AsyncHTTPRequest::_parseURL(const char* url) //************************************************************************************************************** bool AsyncHTTPRequest::_parseURL(String url) { - delete _URL; + SAFE_DELETE(_URL) int hostBeg = 0; + _URL = new URL; - _URL->scheme = new char[8]; + + if (_URL) + { + _URL->scheme = new char[8]; + + if (! (_URL->scheme) ) + return false; + } + else + return false; + strcpy(_URL->scheme, "HTTP://"); if (url.substring(0, 7).equalsIgnoreCase("HTTP://")) @@ -438,6 +480,10 @@ bool AsyncHTTPRequest::_parseURL(String url) } _URL->host = new char[hostEnd - hostBeg + 1]; + + if (_URL->host == nullptr) + return false; + strcpy(_URL->host, url.substring(hostBeg, hostEnd).c_str()); int queryBeg = url.indexOf('?'); @@ -446,8 +492,17 @@ bool AsyncHTTPRequest::_parseURL(String url) queryBeg = url.length(); _URL->path = new char[queryBeg - pathBeg + 1]; + + if (_URL->path == nullptr) + return false; + strcpy(_URL->path, url.substring(pathBeg, queryBeg).c_str()); + _URL->query = new char[url.length() - queryBeg + 1]; + + if (_URL->query == nullptr) + return false; + strcpy(_URL->query, url.substring(queryBeg).c_str()); AHTTP_LOGDEBUG2("_parseURL(): scheme+host", _URL->scheme, _URL->host); @@ -464,11 +519,18 @@ bool AsyncHTTPRequest::_connect() if ( ! _client) { _client = new AsyncClient(); + + if (! _client) + return false; } - delete[] _connectedHost; + SAFE_DELETE_ARRAY(_connectedHost) _connectedHost = new char[strlen(_URL->host) + 1]; + + if (_connectedHost == nullptr) + return false; + strcpy(_connectedHost, _URL->host); _connectedPort = _URL->port; @@ -521,7 +583,12 @@ bool AsyncHTTPRequest::_buildRequest() // Build the header. if ( ! _request) + { _request = new xbuf; + + if ( ! _request) + return false; + } _request->write(_HTTPmethod == HTTPmethodGET ? "GET " : "POST "); _request->write(_URL->path); @@ -530,7 +597,7 @@ bool AsyncHTTPRequest::_buildRequest() AHTTP_LOGDEBUG3(_HTTPmethod == HTTPmethodGET ? "GET " : "POST ", _URL->path, _URL->query, " HTTP/1.1\r\n" ); - delete _URL; + SAFE_DELETE(_URL) _URL = nullptr; header* hdr = _headers; @@ -547,7 +614,8 @@ bool AsyncHTTPRequest::_buildRequest() hdr = hdr->next; } - delete _headers; + SAFE_DELETE(_headers) + _headers = nullptr; _request->write("\r\n"); @@ -577,19 +645,26 @@ size_t AsyncHTTPRequest::_send() size_t sent = 0; uint8_t* temp = new uint8_t[100]; + + if (!temp) + return 0; while (supply) { size_t chunk = supply < 100 ? supply : 100; - supply -= _request->read(temp, chunk); - sent += _client->add((char*)temp, chunk); + + supply -= _request->read(temp, chunk); + sent += _client->add((char*)temp, chunk); } - delete temp; + // KH, Must be delete [] temp; + SAFE_DELETE_ARRAY(temp) if (_request->available() == 0) { - delete _request; + //delete _request; + SAFE_DELETE(_request) + _request = nullptr; } @@ -679,10 +754,20 @@ void AsyncHTTPRequest::_onConnect(AsyncClient* client) { AHTTP_LOGDEBUG("_onConnect handler"); - _lock; + MUTEX_LOCK_NR + _client = client; _setReadyState(readyStateOpened); + _response = new xbuf; + + if (!_response) + { + _unlock; + + return; + } + _contentLength = 0; _contentRead = 0; _chunked = false; @@ -703,13 +788,14 @@ void AsyncHTTPRequest::_onConnect(AsyncClient* client) } _lastActivity = millis(); + _unlock; } //************************************************************************************************************** void AsyncHTTPRequest::_onPoll(AsyncClient* client) { - _lock; + MUTEX_LOCK_NR if (_timeout && (millis() - _lastActivity) > (_timeout * 1000)) { @@ -740,7 +826,7 @@ void AsyncHTTPRequest::_onDisconnect(AsyncClient* client) { AHTTP_LOGDEBUG("\n_onDisconnect handler"); - _lock; + MUTEX_LOCK_NR if (_readyState < readyStateOpened) { @@ -752,16 +838,19 @@ void AsyncHTTPRequest::_onDisconnect(AsyncClient* client) _HTTPcode = HTTPCODE_CONNECTION_LOST; } - delete _client; + SAFE_DELETE(_client) + _client = nullptr; - delete[] _connectedHost; + SAFE_DELETE_ARRAY(_connectedHost) + _connectedHost = nullptr; - _connectedPort = -1; + _connectedPort = -1; _requestEndTime = millis(); - _lastActivity = 0; + _lastActivity = 0; _setReadyState(readyStateDone); + _unlock; } @@ -770,6 +859,8 @@ void AsyncHTTPRequest::_onData(void* Vbuf, size_t len) { AHTTP_LOGDEBUG3("_onData handler", (char*) Vbuf, ", len =", len); + MUTEX_LOCK_NR + _lastActivity = millis(); // Transfer data to xbuf @@ -786,8 +877,12 @@ void AsyncHTTPRequest::_onData(void* Vbuf, size_t len) // if headers not complete, collect them. If still not complete, just return. if (_readyState == readyStateOpened) { - if ( ! _collectHeaders()) + if ( ! _collectHeaders()) + { + _unlock; + return; + } } // If there's data in the buffer and not Done, advance readyState to Loading. @@ -932,7 +1027,8 @@ void AsyncHTTPRequest::setReqHeader(const char* name, const __FlashStringHelper* { char* _value = _charstar(value); _addHeader(name, _value); - delete[] _value; + + SAFE_DELETE_ARRAY(_value) } } @@ -943,7 +1039,8 @@ void AsyncHTTPRequest::setReqHeader(const __FlashStringHelper *name, const char* { char* _name = _charstar(name); _addHeader(_name, value); - delete[] _name; + + SAFE_DELETE_ARRAY(_name) } } @@ -955,8 +1052,9 @@ void AsyncHTTPRequest::setReqHeader(const __FlashStringHelper *name, const __Fla char* _name = _charstar(name); char* _value = _charstar(value); _addHeader(_name, _value); - delete[] _name; - delete[] _value; + + SAFE_DELETE_ARRAY(_name) + SAFE_DELETE_ARRAY(_value) } } @@ -967,7 +1065,8 @@ void AsyncHTTPRequest::setReqHeader(const __FlashStringHelper *name, int32_t val { char* _name = _charstar(name); setReqHeader(_name, String(value).c_str()); - delete[] _name; + + SAFE_DELETE_ARRAY(_name) } } @@ -1058,7 +1157,8 @@ char* AsyncHTTPRequest::respHeaderValue(const __FlashStringHelper *name) char* _name = _charstar(name); header* hdr = _getHeader(_name); - delete[] _name; + + SAFE_DELETE_ARRAY(_name) if ( ! hdr) return nullptr; @@ -1074,7 +1174,8 @@ bool AsyncHTTPRequest::respHeaderExists(const __FlashStringHelper *name) char* _name = _charstar(name); header* hdr = _getHeader(_name); - delete[] _name; + + SAFE_DELETE_ARRAY(_name) if ( ! hdr) return false; @@ -1087,7 +1188,8 @@ bool AsyncHTTPRequest::respHeaderExists(const __FlashStringHelper *name) //************************************************************************************************************** String AsyncHTTPRequest::headers() { - _lock; + MUTEX_LOCK(String()) + String _response = ""; header* hdr = _headers; @@ -1101,6 +1203,7 @@ String AsyncHTTPRequest::headers() } _response += "\r\n"; + _unlock; return _response; @@ -1109,17 +1212,19 @@ String AsyncHTTPRequest::headers() //************************************************************************************************************** AsyncHTTPRequest::header* AsyncHTTPRequest::_addHeader(const char* name, const char* value) { - _lock; + MUTEX_LOCK(nullptr) + header* hdr = (header*) &_headers; while (hdr->next) { if (strcasecmp(name, hdr->next->name) == 0) { - header* oldHdr = hdr->next; - hdr->next = hdr->next->next; - oldHdr->next = nullptr; - delete oldHdr; + header* oldHdr = hdr->next; + hdr->next = hdr->next->next; + oldHdr->next = nullptr; + + SAFE_DELETE(oldHdr) } else { @@ -1128,10 +1233,37 @@ AsyncHTTPRequest::header* AsyncHTTPRequest::_addHeader(const char* name, const } hdr->next = new header; - hdr->next->name = new char[strlen(name) + 1]; - strcpy(hdr->next->name, name); - hdr->next->value = new char[strlen(value) + 1]; - strcpy(hdr->next->value, value); + + if (hdr->next) + { + hdr->next->name = new char[strlen(name) + 1]; + + if (hdr->next->name) + strcpy(hdr->next->name, name); + else + { + SAFE_DELETE(hdr->next) + + return nullptr; + } + + hdr->next->value = new char[strlen(value) + 1]; + + if (hdr->next->value) + strcpy(hdr->next->value, value); + else + { + SAFE_DELETE_ARRAY(hdr->next->name) + SAFE_DELETE(hdr->next) + + return nullptr; + } + } + else + { + return nullptr; + } + _unlock; return hdr->next; @@ -1140,7 +1272,8 @@ AsyncHTTPRequest::header* AsyncHTTPRequest::_addHeader(const char* name, const //************************************************************************************************************** AsyncHTTPRequest::header* AsyncHTTPRequest::_getHeader(const char* name) { - _lock; + MUTEX_LOCK(nullptr) + header* hdr = _headers; while (hdr) @@ -1159,7 +1292,8 @@ AsyncHTTPRequest::header* AsyncHTTPRequest::_getHeader(const char* name) //************************************************************************************************************** AsyncHTTPRequest::header* AsyncHTTPRequest::_getHeader(int ndx) { - _lock; + MUTEX_LOCK(nullptr) + header* hdr = _headers; while (hdr) @@ -1184,8 +1318,13 @@ char* AsyncHTTPRequest::_charstar(const __FlashStringHelper * str) return nullptr; char* ptr = new char[strlen_P((PGM_P)str) + 1]; - strcpy_P(ptr, (PGM_P)str); + if (ptr) + { + strcpy_P(ptr, (PGM_P)str); + } + + // Rturn good ptr or nullptr return ptr; } diff --git a/src_cpp/AsyncHTTPRequest_Generic.h b/src_cpp/AsyncHTTPRequest_Generic.h index 7d6f54df..682d7a2c 100644 --- a/src_cpp/AsyncHTTPRequest_Generic.h +++ b/src_cpp/AsyncHTTPRequest_Generic.h @@ -1,5 +1,5 @@ /**************************************************************************************************************************** - src_cpp/AsyncHTTPRequest_Generic.h - Dead simple AsyncHTTPRequest for ESP8266, ESP32 and currently STM32 with built-in LAN8742A Ethernet + AsyncHTTPRequest_Generic.h - Dead simple AsyncHTTPRequest for ESP8266, ESP32 and currently STM32 with built-in LAN8742A Ethernet For ESP8266, ESP32 and STM32 with built-in LAN8742A Ethernet (Nucleo-144, DISCOVERY, etc) @@ -17,17 +17,18 @@ 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.0.1 + Version: 1.0.2 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K Hoang 14/09/2020 Initial coding to add support to STM32 using built-in Ethernet (Nucleo-144, DISCOVERY, etc). 1.0.1 K Hoang 09/10/2020 Restore cpp code besides Impl.h code. + 1.0.2 K Hoang 09/11/2020 Make Mutex Lock and delete more reliable and error-proof *****************************************************************************************************************************/ #pragma once -#define AsyncHTTPRequest_Generic_version "1.0.0" +#define ASYNC_HTTP_REQUEST_GENERIC_VERSION "1.0.2" #include @@ -43,15 +44,29 @@ #define DEBUG_IOTA_HTTP_SET false #endif + +// KH add +#define SAFE_DELETE(object) if (object) { delete object;} +#define SAFE_DELETE_ARRAY(object) if (object) { delete[] object;} + #if ESP32 #include + + // KH mod + #define MUTEX_LOCK_NR if (xSemaphoreTakeRecursive(threadLock,portMAX_DELAY) != pdTRUE) { return;} + #define MUTEX_LOCK(returnVal) if (xSemaphoreTakeRecursive(threadLock,portMAX_DELAY) != pdTRUE) { return returnVal;} + #define _lock xSemaphoreTakeRecursive(threadLock,portMAX_DELAY) #define _unlock xSemaphoreGiveRecursive(threadLock) #elif ESP8266 #include + + #define MUTEX_LOCK_NR + #define MUTEX_LOCK(returnVal) + #define _lock #define _unlock @@ -60,6 +75,9 @@ defined(STM32WB) || defined(STM32MP1) ) #include "STM32AsyncTCP.h" + + #define MUTEX_LOCK_NR + #define MUTEX_LOCK(returnVal) #define _lock #define _unlock @@ -108,9 +126,12 @@ class AsyncHTTPRequest ~header() { - delete[] name; - delete[] value; - delete next; + SAFE_DELETE_ARRAY(name) + SAFE_DELETE_ARRAY(value) + SAFE_DELETE(next) + //delete[] name; + //delete[] value; + //delete next; } }; @@ -131,13 +152,13 @@ class AsyncHTTPRequest ~URL() { - delete[] scheme; - delete[] user; - delete[] pwd; - delete[] host; - delete[] path; - delete[] query; - delete[] fragment; + SAFE_DELETE_ARRAY(scheme) + SAFE_DELETE_ARRAY(user) + SAFE_DELETE_ARRAY(pwd) + SAFE_DELETE_ARRAY(host) + SAFE_DELETE_ARRAY(path) + SAFE_DELETE_ARRAY(query) + SAFE_DELETE_ARRAY(fragment) } }; @@ -263,3 +284,4 @@ class AsyncHTTPRequest void _onPoll(AsyncClient*); bool _collectHeaders(); }; + diff --git a/src_cpp/utility/xbuf.cpp b/src_cpp/utility/xbuf.cpp index 108283ee..aa494263 100644 --- a/src_cpp/utility/xbuf.cpp +++ b/src_cpp/utility/xbuf.cpp @@ -1,5 +1,5 @@ /**************************************************************************************************************************** - src_cpp/utility/xbuf_Impl.h - Dead simple AsyncHTTPRequest for ESP8266, ESP32 and currently STM32 with built-in LAN8742A Ethernet + xbuf_Impl.h - Dead simple AsyncHTTPRequest for ESP8266, ESP32 and currently STM32 with built-in LAN8742A Ethernet For ESP8266, ESP32 and STM32 with built-in LAN8742A Ethernet (Nucleo-144, DISCOVERY, etc) @@ -17,12 +17,13 @@ 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.0.1 + Version: 1.0.2 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K Hoang 14/09/2020 Initial coding to add support to STM32 using built-in Ethernet (Nucleo-144, DISCOVERY, etc). 1.0.1 K Hoang 09/10/2020 Restore cpp code besides Impl.h code. + 1.0.2 K Hoang 09/11/2020 Make Mutex Lock and delete more reliable and error-proof *****************************************************************************************************************************/ #include "utility/xbuf.h" @@ -361,14 +362,20 @@ void xbuf::addSeg() if (_tail) { _tail->next = (xseg*) new uint32_t[_segSize / 4 + 1]; + + // KH, Must check NULL here _tail = _tail->next; } else { + // KH, Must check NULL here _tail = _head = (xseg*) new uint32_t[_segSize / 4 + 1]; } - _tail->next = nullptr; + // KH, Must check NULL here + if (_tail) + _tail->next = nullptr; + _free += _segSize; } diff --git a/src_cpp/utility/xbuf.h b/src_cpp/utility/xbuf.h index e63198df..32c7aafd 100644 --- a/src_cpp/utility/xbuf.h +++ b/src_cpp/utility/xbuf.h @@ -17,12 +17,13 @@ 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.0.1 + Version: 1.0.2 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K Hoang 14/09/2020 Initial coding to add support to STM32 using built-in Ethernet (Nucleo-144, DISCOVERY, etc). 1.0.1 K Hoang 09/10/2020 Restore cpp code besides Impl.h code. + 1.0.2 K Hoang 09/11/2020 Make Mutex Lock and delete more reliable and error-proof *****************************************************************************************************************************/ /******************************************************************************************** @@ -43,7 +44,6 @@ It could be extended but didn't seem to be a practical consideration. ********************************************************************************************/ - #pragma once #include @@ -144,5 +144,6 @@ class xbuf: public Print void addSeg(); void remSeg(); + }; diff --git a/src_h/AsyncHTTPRequest_Debug_Generic.h b/src_h/AsyncHTTPRequest_Debug_Generic.h new file mode 100644 index 00000000..d5e1bd43 --- /dev/null +++ b/src_h/AsyncHTTPRequest_Debug_Generic.h @@ -0,0 +1,71 @@ +/**************************************************************************************************************************** + AsyncHTTPRequest_Debug_Generic.h - Dead simple AsyncHTTPRequest for ESP8266, ESP32 and currently STM32 with built-in LAN8742A Ethernet + + For ESP8266, ESP32 and STM32 with built-in LAN8742A Ethernet (Nucleo-144, DISCOVERY, etc) + + AsyncHTTPRequest_STM32 is a library for the ESP8266, ESP32 and currently STM32 run built-in Ethernet WebServer + + Based on and modified from asyncHTTPrequest Library (https://github.com/boblemaire/asyncHTTPrequest) + + Built by Khoi Hoang https://github.com/khoih-prog/AsyncHTTPRequest_Generic + Licensed under MIT license + + Copyright (C) <2018> + This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License + as published bythe Free Software Foundation, either version 3 of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + You should have received a copy of the GNU General Public License along with this program. If not, see . + + Version: 1.0.2 + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 1.0.0 K Hoang 14/09/2020 Initial coding to add support to STM32 using built-in Ethernet (Nucleo-144, DISCOVERY, etc). + 1.0.1 K Hoang 09/10/2020 Restore cpp code besides Impl.h code. + 1.0.2 K Hoang 09/11/2020 Make Mutex Lock and delete more reliable and error-proof + *****************************************************************************************************************************/ + +#pragma once + +#ifdef ASYNC_HTTP_DEBUG_PORT + #define A_DBG_PORT ASYNC_HTTP_DEBUG_PORT +#else + #define A_DBG_PORT Serial +#endif + +// Change _ASYNC_HTTP_LOGLEVEL_ to set tracing and logging verbosity +// 0: DISABLED: no logging +// 1: ERROR: errors +// 2: WARN: errors and warnings +// 3: INFO: errors, warnings and informational (default) +// 4: DEBUG: errors, warnings, informational and debug + +#ifndef _ASYNC_HTTP_LOGLEVEL_ + #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); } + +#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_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); } + diff --git a/src_h/AsyncHTTPRequest_Generic.h b/src_h/AsyncHTTPRequest_Generic.h new file mode 100644 index 00000000..0d97a9e2 --- /dev/null +++ b/src_h/AsyncHTTPRequest_Generic.h @@ -0,0 +1,288 @@ +/**************************************************************************************************************************** + AsyncHTTPRequest_Generic.h - Dead simple AsyncHTTPRequest for ESP8266, ESP32 and currently STM32 with built-in LAN8742A Ethernet + + For ESP8266, ESP32 and STM32 with built-in LAN8742A Ethernet (Nucleo-144, DISCOVERY, etc) + + AsyncHTTPRequest_STM32 is a library for the ESP8266, ESP32 and currently STM32 run built-in Ethernet WebServer + + Based on and modified from asyncHTTPrequest Library (https://github.com/boblemaire/asyncHTTPrequest) + + Built by Khoi Hoang https://github.com/khoih-prog/AsyncHTTPRequest_Generic + Licensed under MIT license + + Copyright (C) <2018> + This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License + as published bythe Free Software Foundation, either version 3 of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + You should have received a copy of the GNU General Public License along with this program. If not, see . + + Version: 1.0.2 + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 1.0.0 K Hoang 14/09/2020 Initial coding to add support to STM32 using built-in Ethernet (Nucleo-144, DISCOVERY, etc). + 1.0.1 K Hoang 09/10/2020 Restore cpp code besides Impl.h code. + 1.0.2 K Hoang 09/11/2020 Make Mutex Lock and delete more reliable and error-proof + *****************************************************************************************************************************/ + +#pragma once + +#define ASYNC_HTTP_REQUEST_GENERIC_VERSION "1.0.2" + +#include + +#include "AsyncHTTPRequest_Debug_Generic.h" + +#ifndef DEBUG_IOTA_PORT + #define DEBUG_IOTA_PORT Serial +#endif + +#ifdef DEBUG_IOTA_HTTP + #define DEBUG_IOTA_HTTP_SET true +#else + #define DEBUG_IOTA_HTTP_SET false +#endif + + +// KH add +#define SAFE_DELETE(object) if (object) { delete object;} +#define SAFE_DELETE_ARRAY(object) if (object) { delete[] object;} + +#if ESP32 + + #include + + // KH mod + #define MUTEX_LOCK_NR if (xSemaphoreTakeRecursive(threadLock,portMAX_DELAY) != pdTRUE) { return;} + #define MUTEX_LOCK(returnVal) if (xSemaphoreTakeRecursive(threadLock,portMAX_DELAY) != pdTRUE) { return returnVal;} + + #define _lock xSemaphoreTakeRecursive(threadLock,portMAX_DELAY) + #define _unlock xSemaphoreGiveRecursive(threadLock) + +#elif ESP8266 + + #include + + #define MUTEX_LOCK_NR + #define MUTEX_LOCK(returnVal) + + #define _lock + #define _unlock + +#elif ( defined(STM32F0) || defined(STM32F1) || defined(STM32F2) || defined(STM32F3) ||defined(STM32F4) || defined(STM32F7) || \ + defined(STM32L0) || defined(STM32L1) || defined(STM32L4) || defined(STM32H7) ||defined(STM32G0) || defined(STM32G4) || \ + defined(STM32WB) || defined(STM32MP1) ) + + #include "STM32AsyncTCP.h" + + #define MUTEX_LOCK_NR + #define MUTEX_LOCK(returnVal) + #define _lock + #define _unlock + +#endif + +#include +#include + +#define DEBUG_HTTP(format,...) if(_debug){\ + DEBUG_IOTA_PORT.printf("Debug(%3ld): ", millis()-_requestStartTime);\ + DEBUG_IOTA_PORT.printf_P(PSTR(format),##__VA_ARGS__);} + +#define DEFAULT_RX_TIMEOUT 3 // Seconds for timeout + +#define HTTPCODE_CONNECTION_REFUSED (-1) +#define HTTPCODE_SEND_HEADER_FAILED (-2) +#define HTTPCODE_SEND_PAYLOAD_FAILED (-3) +#define HTTPCODE_NOT_CONNECTED (-4) +#define HTTPCODE_CONNECTION_LOST (-5) +#define HTTPCODE_NO_STREAM (-6) +#define HTTPCODE_NO_HTTP_SERVER (-7) +#define HTTPCODE_TOO_LESS_RAM (-8) +#define HTTPCODE_ENCODING (-9) +#define HTTPCODE_STREAM_WRITE (-10) +#define HTTPCODE_TIMEOUT (-11) + +typedef enum +{ + readyStateUnsent = 0, // Client created, open not yet called + readyStateOpened = 1, // open() has been called, connected + readyStateHdrsRecvd = 2, // send() called, response headers available + readyStateLoading = 3, // receiving, partial data available + readyStateDone = 4 // Request complete, all data available. +} reqStates; + +class AsyncHTTPRequest +{ + struct header + { + header* next; + char* name; + char* value; + + header(): next(nullptr), name(nullptr), value(nullptr) + {}; + + ~header() + { + SAFE_DELETE_ARRAY(name) + SAFE_DELETE_ARRAY(value) + SAFE_DELETE(next) + //delete[] name; + //delete[] value; + //delete next; + } + }; + + struct URL + { + char* scheme; + char* user; + char* pwd; + char* host; + int port; + char* path; + char* query; + char* fragment; + + URL(): scheme(nullptr), user(nullptr), pwd(nullptr), host(nullptr), + port(80), path(nullptr), query(nullptr), fragment(nullptr) + {}; + + ~URL() + { + SAFE_DELETE_ARRAY(scheme) + SAFE_DELETE_ARRAY(user) + SAFE_DELETE_ARRAY(pwd) + SAFE_DELETE_ARRAY(host) + SAFE_DELETE_ARRAY(path) + SAFE_DELETE_ARRAY(query) + SAFE_DELETE_ARRAY(fragment) + } + }; + + typedef std::function readyStateChangeCB; + typedef std::function onDataCB; + + public: + AsyncHTTPRequest(); + ~AsyncHTTPRequest(); + + + //External functions in typical order of use: + //__________________________________________________________________________________________________________*/ + void setDebug(bool); // Turn debug message on/off + bool debug(); // is debug on or off? + + bool open(const char* /*GET/POST*/, const char* URL); // Initiate a request + void onReadyStateChange(readyStateChangeCB, void* arg = 0); // Optional event handler for ready state change + // or you can simply poll readyState() + void setTimeout(int); // overide default timeout (seconds) + + void setReqHeader(const char* name, const char* value); // add a request header + void setReqHeader(const char* name, int32_t value); // overload to use integer value + +#if (ESP32 || ESP8266) + void setReqHeader(const char* name, const __FlashStringHelper* value); + void setReqHeader(const __FlashStringHelper *name, const char* value); + void setReqHeader(const __FlashStringHelper *name, const __FlashStringHelper* value); + void setReqHeader(const __FlashStringHelper *name, int32_t value); +#endif + + bool send(); // Send the request (GET) + bool send(String body); // Send the request (POST) + bool send(const char* body); // Send the request (POST) + bool send(const uint8_t* buffer, size_t len); // Send the request (POST) (binary data?) + bool send(xbuf* body, size_t len); // Send the request (POST) data in an xbuf + void abort(); // Abort the current operation + + reqStates readyState(); // Return the ready state + + int respHeaderCount(); // Retrieve count of response headers + char* respHeaderName(int index); // Return header name by index + char* respHeaderValue(int index); // Return header value by index + char* respHeaderValue(const char* name); // Return header value by name + + bool respHeaderExists(const char* name); // Does header exist by name? + +#if (ESP32 || ESP8266) + char* respHeaderValue(const __FlashStringHelper *name); + bool respHeaderExists(const __FlashStringHelper *name); +#endif + + String headers(); // Return all headers as String + + void onData(onDataCB, void* arg = 0); // Notify when min data is available + size_t available(); // response available + 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) + 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) + String version(); // Version of AsyncHTTPRequest + //___________________________________________________________________________________________________________________________________ + + private: + + enum {HTTPmethodGET, HTTPmethodPOST} _HTTPmethod; + + reqStates _readyState; + + int16_t _HTTPcode; // HTTP response code or (negative) exception code + bool _chunked; // Processing chunked response + bool _debug; // Debug state + uint32_t _timeout; // Default or user overide RxTimeout in seconds + uint32_t _lastActivity; // Time of last activity + uint32_t _requestStartTime; // Time last open() issued + uint32_t _requestEndTime; // Time of last disconnect + URL* _URL; // -> URL data structure + char* _connectedHost; // Host when connected + int _connectedPort; // Port when connected + AsyncClient* _client; // ESPAsyncTCP AsyncClient instance + size_t _contentLength; // content-length header value or sum of chunk headers + size_t _contentRead; // number of bytes retrieved by user since last open() + readyStateChangeCB _readyStateChangeCB; // optional callback for readyState change + void* _readyStateChangeCBarg; // associated user argument + onDataCB _onDataCB; // optional callback when data received + void* _onDataCBarg; // associated user argument + +#ifdef ESP32 + SemaphoreHandle_t threadLock; +#endif + + // request and response String buffers and header list (same queue for request and response). + + xbuf* _request; // Tx data buffer + xbuf* _response; // Rx data buffer for headers + xbuf* _chunks; // First stage for chunked response + header* _headers; // request or (readyState > readyStateHdrsRcvd) response headers + + // Protected functions + + header* _addHeader(const char*, const char*); + header* _getHeader(const char*); + header* _getHeader(int); + bool _buildRequest(); + bool _parseURL(const char*); + bool _parseURL(String); + void _processChunks(); + bool _connect(); + size_t _send(); + void _setReadyState(reqStates); + +#if (ESP32 || ESP8266) + char* _charstar(const __FlashStringHelper *str); +#endif + + // callbacks + + void _onConnect(AsyncClient*); + void _onDisconnect(AsyncClient*); + void _onData(void*, size_t); + void _onError(AsyncClient*, int8_t); + void _onPoll(AsyncClient*); + bool _collectHeaders(); +}; + +#include "AsyncHTTPRequest_Impl_Generic.h" diff --git a/src_h/AsyncHTTPRequest_Impl_Generic.h b/src_h/AsyncHTTPRequest_Impl_Generic.h new file mode 100644 index 00000000..3f5ac508 --- /dev/null +++ b/src_h/AsyncHTTPRequest_Impl_Generic.h @@ -0,0 +1,1330 @@ +/**************************************************************************************************************************** + AsyncHTTPRequest_Impl_Generic.h - Dead simple AsyncHTTPRequest for ESP8266, ESP32 and currently STM32 with built-in LAN8742A Ethernet + + For ESP8266, ESP32 and STM32 with built-in LAN8742A Ethernet (Nucleo-144, DISCOVERY, etc) + + AsyncHTTPRequest_STM32 is a library for the ESP8266, ESP32 and currently STM32 run built-in Ethernet WebServer + + Based on and modified from asyncHTTPrequest Library (https://github.com/boblemaire/asyncHTTPrequest) + + Built by Khoi Hoang https://github.com/khoih-prog/AsyncHTTPRequest_Generic + Licensed under MIT license + + Copyright (C) <2018> + This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License + as published bythe Free Software Foundation, either version 3 of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + You should have received a copy of the GNU General Public License along with this program. If not, see . + + Version: 1.0.2 + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 1.0.0 K Hoang 14/09/2020 Initial coding to add support to STM32 using built-in Ethernet (Nucleo-144, DISCOVERY, etc). + 1.0.1 K Hoang 09/10/2020 Restore cpp code besides Impl.h code. + 1.0.2 K Hoang 09/11/2020 Make Mutex Lock and delete more reliable and error-proof + *****************************************************************************************************************************/ + +#pragma once + + +//************************************************************************************************************** +AsyncHTTPRequest::AsyncHTTPRequest(): _readyState(readyStateUnsent), _HTTPcode(0), _chunked(false), _debug(DEBUG_IOTA_HTTP_SET) + , _timeout(DEFAULT_RX_TIMEOUT), _lastActivity(0), _requestStartTime(0), _requestEndTime(0), _URL(nullptr) + , _connectedHost(nullptr), _connectedPort(-1), _client(nullptr), _contentLength(0), _contentRead(0) + , _readyStateChangeCB(nullptr), _readyStateChangeCBarg(nullptr), _onDataCB(nullptr), _onDataCBarg(nullptr) + , _request(nullptr), _response(nullptr), _chunks(nullptr), _headers(nullptr) +{ +#ifdef ESP32 + threadLock = xSemaphoreCreateRecursiveMutex(); +#endif +} + +//************************************************************************************************************** +AsyncHTTPRequest::~AsyncHTTPRequest() +{ + if (_client) + _client->close(true); + + + SAFE_DELETE(_URL) + SAFE_DELETE(_headers) + SAFE_DELETE(_request) + SAFE_DELETE(_response) + SAFE_DELETE(_chunks) + SAFE_DELETE_ARRAY(_connectedHost) + +#ifdef ESP32 + // KH add + if (threadLock) + { + vSemaphoreDelete(threadLock); + } +#endif +} + +//************************************************************************************************************** +void AsyncHTTPRequest::setDebug(bool debug) +{ + if (_debug || debug) + { + _debug = true; + + AHTTP_LOGDEBUG3("setDebug(", debug ? "on" : "off", ") version", ASYNC_HTTP_REQUEST_GENERIC_VERSION); + } + + _debug = debug; +} + +//************************************************************************************************************** +bool AsyncHTTPRequest::debug() +{ + return (_debug); +} + +//************************************************************************************************************** +bool AsyncHTTPRequest::open(const char* method, const char* URL) +{ + AHTTP_LOGDEBUG3("open(", method, ", url =", URL); + + if (_readyState != readyStateUnsent && _readyState != readyStateDone) + { + return false; + } + + _requestStartTime = millis(); + + SAFE_DELETE(_URL) + SAFE_DELETE(_headers) + SAFE_DELETE(_request) + SAFE_DELETE(_response) + SAFE_DELETE(_chunks) + + _URL = nullptr; + _headers = nullptr; + _response = nullptr; + _request = nullptr; + _chunks = nullptr; + _chunked = false; + _contentRead = 0; + _readyState = readyStateUnsent; + + if (strcmp(method, "GET") == 0) + { + _HTTPmethod = HTTPmethodGET; + } + else if (strcmp(method, "POST") == 0) + { + _HTTPmethod = HTTPmethodPOST; + } + else + return false; + + if (!_parseURL(URL)) + { + return false; + } + if ( _client && _client->connected() && (strcmp(_URL->host, _connectedHost) != 0 || _URL->port != _connectedPort)) + { + return false; + } + + char* hostName = new char[strlen(_URL->host) + 10]; + + if (hostName) + { + sprintf(hostName, "%s:%d", _URL->host, _URL->port); + _addHeader("host", hostName); + + SAFE_DELETE_ARRAY(hostName) + + _lastActivity = millis(); + + return _connect(); + } + else + return false; +} +//************************************************************************************************************** +void AsyncHTTPRequest::onReadyStateChange(readyStateChangeCB cb, void* arg) +{ + _readyStateChangeCB = cb; + _readyStateChangeCBarg = arg; +} + +//************************************************************************************************************** +void AsyncHTTPRequest::setTimeout(int seconds) +{ + AHTTP_LOGDEBUG1("setTimeout = ", seconds); + + _timeout = seconds; +} + +//************************************************************************************************************** +bool AsyncHTTPRequest::send() +{ + AHTTP_LOGDEBUG("send()"); + + MUTEX_LOCK(false) + + if ( ! _buildRequest()) + return false; + + _send(); + + _unlock; + + return true; +} + +//************************************************************************************************************** +bool AsyncHTTPRequest::send(String body) +{ + AHTTP_LOGDEBUG3("send(String)", body.substring(0, 16).c_str(), ", length =", body.length()); + + MUTEX_LOCK(false) + + _addHeader("Content-Length", String(body.length()).c_str()); + + if ( ! _buildRequest()) + { + _unlock; + + return false; + } + + _request->write(body); + _send(); + + _unlock; + + return true; +} + +//************************************************************************************************************** +bool AsyncHTTPRequest::send(const char* body) +{ + AHTTP_LOGDEBUG3("send(char)", body, ", length =", strlen(body)); + + MUTEX_LOCK(false) + + _addHeader("Content-Length", String(strlen(body)).c_str()); + + if ( ! _buildRequest()) + { + _unlock; + + return false; + } + + _request->write(body); + _send(); + + _unlock; + + return true; +} + +//************************************************************************************************************** +bool AsyncHTTPRequest::send(const uint8_t* body, size_t len) +{ + AHTTP_LOGDEBUG3("send(char)", (char*) body, ", length =", len); + + MUTEX_LOCK(false) + + _addHeader("Content-Length", String(len).c_str()); + + if ( ! _buildRequest()) + { + _unlock; + + return false; + } + + _request->write(body, len); + _send(); + + _unlock; + + return true; +} + +//************************************************************************************************************** +bool AsyncHTTPRequest::send(xbuf* body, size_t len) +{ + AHTTP_LOGDEBUG3("send(char)", body->peekString(16).c_str(), ", length =", len); + + MUTEX_LOCK(false) + + _addHeader("Content-Length", String(len).c_str()); + + if ( ! _buildRequest()) + { + _unlock; + + return false; + } + + _request->write(body, len); + _send(); + + _unlock; + + return true; +} + +//************************************************************************************************************** +void AsyncHTTPRequest::abort() +{ + AHTTP_LOGDEBUG("abort()"); + + if (! _client) + { + return; + } + + MUTEX_LOCK_NR + + _client->abort(); + + _unlock; +} +//************************************************************************************************************** +reqStates AsyncHTTPRequest::readyState() +{ + return _readyState; +} + +//************************************************************************************************************** +int AsyncHTTPRequest::responseHTTPcode() +{ + return _HTTPcode; +} + +//************************************************************************************************************** +String AsyncHTTPRequest::responseText() +{ + AHTTP_LOGDEBUG("responseText()"); + + MUTEX_LOCK(String()) + + if ( ! _response || _readyState < readyStateLoading || ! available()) + { + AHTTP_LOGDEBUG("responseText() no data"); + + _unlock; + + return String(); + } + + String localString; + size_t avail = available(); + + if ( ! localString.reserve(avail)) + { + AHTTP_LOGDEBUG("responseText() no buffer"); + + _HTTPcode = HTTPCODE_TOO_LESS_RAM; + _client->abort(); + + _unlock; + + return String(); + } + + localString = _response->readString(avail); + _contentRead += localString.length(); + + AHTTP_LOGDEBUG3("responseText(char)", localString.substring(0, 16).c_str(), ", avail =", avail); + + _unlock; + + return localString; +} + +//************************************************************************************************************** +size_t AsyncHTTPRequest::responseRead(uint8_t* buf, size_t len) +{ + if ( ! _response || _readyState < readyStateLoading || ! available()) + { + AHTTP_LOGDEBUG("responseRead() no data"); + + return 0; + } + + MUTEX_LOCK(0) + + size_t avail = available() > len ? len : available(); + _response->read(buf, avail); + + AHTTP_LOGDEBUG3("responseRead(char)", (char*) buf, ", avail =", avail); + + _contentRead += avail; + + _unlock; + + return avail; +} + +//************************************************************************************************************** +size_t AsyncHTTPRequest::available() +{ + if (_readyState < readyStateLoading) + return 0; + + if (_chunked && (_contentLength - _contentRead) < _response->available()) + { + return _contentLength - _contentRead; + } + + return _response->available(); +} + +//************************************************************************************************************** +size_t AsyncHTTPRequest::responseLength() +{ + if (_readyState < readyStateLoading) + return 0; + + return _contentLength; +} + +//************************************************************************************************************** +void AsyncHTTPRequest::onData(onDataCB cb, void* arg) +{ + AHTTP_LOGDEBUG("onData() CB set"); + + _onDataCB = cb; + _onDataCBarg = arg; +} + +//************************************************************************************************************** +uint32_t AsyncHTTPRequest::elapsedTime() +{ + if (_readyState <= readyStateOpened) + return 0; + + if (_readyState != readyStateDone) + { + return millis() - _requestStartTime; + } + + return _requestEndTime - _requestStartTime; +} + +//************************************************************************************************************** +String AsyncHTTPRequest::version() +{ + return String(ASYNC_HTTP_REQUEST_GENERIC_VERSION); +} + +/*______________________________________________________________________________________________________________ + + PPPP RRRR OOO TTTTT EEEEE CCC TTTTT EEEEE DDDD + P P R R O O T E C C T E D D + PPPP RRRR O O T EEE C T EEE D D + P R R O O T E C C T E D D + P R R OOO T EEEEE CCC T EEEEE DDDD + _______________________________________________________________________________________________________________*/ + +//************************************************************************************************************** +bool AsyncHTTPRequest::_parseURL(const char* url) +{ + return _parseURL(String(url)); +} + +//************************************************************************************************************** +bool AsyncHTTPRequest::_parseURL(String url) +{ + SAFE_DELETE(_URL) + + int hostBeg = 0; + + _URL = new URL; + + if (_URL) + { + _URL->scheme = new char[8]; + + if (! (_URL->scheme) ) + return false; + } + else + return false; + + strcpy(_URL->scheme, "HTTP://"); + + if (url.substring(0, 7).equalsIgnoreCase("HTTP://")) + { + hostBeg += 7; + } + else if (url.substring(0, 8).equalsIgnoreCase("HTTPS://")) + { + return false; + } + + int pathBeg = url.indexOf('/', hostBeg); + + if (pathBeg < 0) + return false; + + int hostEnd = pathBeg; + int portBeg = url.indexOf(':', hostBeg); + + if (portBeg > 0 && portBeg < pathBeg) + { + _URL->port = url.substring(portBeg + 1, pathBeg).toInt(); + hostEnd = portBeg; + } + + _URL->host = new char[hostEnd - hostBeg + 1]; + + if (_URL->host == nullptr) + return false; + + strcpy(_URL->host, url.substring(hostBeg, hostEnd).c_str()); + + int queryBeg = url.indexOf('?'); + + if (queryBeg < 0) + queryBeg = url.length(); + + _URL->path = new char[queryBeg - pathBeg + 1]; + + if (_URL->path == nullptr) + return false; + + strcpy(_URL->path, url.substring(pathBeg, queryBeg).c_str()); + + _URL->query = new char[url.length() - queryBeg + 1]; + + if (_URL->query == nullptr) + return false; + + strcpy(_URL->query, url.substring(queryBeg).c_str()); + + AHTTP_LOGDEBUG2("_parseURL(): scheme+host", _URL->scheme, _URL->host); + AHTTP_LOGDEBUG3("_parseURL(): port+path+query", _URL->port, _URL->path, _URL->query); + + return true; +} + +//************************************************************************************************************** +bool AsyncHTTPRequest::_connect() +{ + AHTTP_LOGDEBUG("_connect()"); + + if ( ! _client) + { + _client = new AsyncClient(); + + if (! _client) + return false; + } + + SAFE_DELETE_ARRAY(_connectedHost) + + _connectedHost = new char[strlen(_URL->host) + 1]; + + if (_connectedHost == nullptr) + return false; + + strcpy(_connectedHost, _URL->host); + _connectedPort = _URL->port; + + _client->onConnect([](void *obj, AsyncClient * client) + { + ((AsyncHTTPRequest*)(obj))->_onConnect(client); + }, this); + + _client->onDisconnect([](void *obj, AsyncClient * client) + { + ((AsyncHTTPRequest*)(obj))->_onDisconnect(client); + }, this); + + _client->onPoll([](void *obj, AsyncClient * client) + { + ((AsyncHTTPRequest*)(obj))->_onPoll(client); + }, this); + + _client->onError([](void *obj, AsyncClient * client, uint32_t error) + { + ((AsyncHTTPRequest*)(obj))->_onError(client, error); + }, this); + + if ( ! _client->connected()) + { + if ( ! _client->connect(_URL->host, _URL->port)) + { + AHTTP_LOGDEBUG3("client.connect failed:", _URL->host, ",", _URL->port); + + _HTTPcode = HTTPCODE_NOT_CONNECTED; + _setReadyState(readyStateDone); + + return false; + } + } + else + { + _onConnect(_client); + } + + _lastActivity = millis(); + + return true; +} + +//************************************************************************************************************** +bool AsyncHTTPRequest::_buildRequest() +{ + AHTTP_LOGDEBUG("_buildRequest()"); + + // Build the header. + if ( ! _request) + { + _request = new xbuf; + + if ( ! _request) + return false; + } + + _request->write(_HTTPmethod == HTTPmethodGET ? "GET " : "POST "); + _request->write(_URL->path); + _request->write(_URL->query); + _request->write(" HTTP/1.1\r\n"); + + AHTTP_LOGDEBUG3(_HTTPmethod == HTTPmethodGET ? "GET " : "POST ", _URL->path, _URL->query, " HTTP/1.1\r\n" ); + + SAFE_DELETE(_URL) + + _URL = nullptr; + header* hdr = _headers; + + while (hdr) + { + _request->write(hdr->name); + _request->write(':'); + _request->write(hdr->value); + _request->write("\r\n"); + + AHTTP_LOGDEBUG3(hdr->name, ":", hdr->value, "\r\n" ); + + hdr = hdr->next; + } + + SAFE_DELETE(_headers) + + _headers = nullptr; + _request->write("\r\n"); + + return true; +} + +//************************************************************************************************************** +size_t AsyncHTTPRequest::_send() +{ + if ( ! _request) + return 0; + + AHTTP_LOGDEBUG1("_send(), _request->available =", _request->available()); + + if ( ! _client->connected() || ! _client->canSend()) + { + AHTTP_LOGDEBUG("*can't send"); + + return 0; + } + + size_t supply = _request->available(); + size_t demand = _client->space(); + + if (supply > demand) + supply = demand; + + size_t sent = 0; + uint8_t* temp = new uint8_t[100]; + + if (!temp) + return 0; + + while (supply) + { + size_t chunk = supply < 100 ? supply : 100; + + supply -= _request->read(temp, chunk); + sent += _client->add((char*)temp, chunk); + } + + // KH, Must be delete [] temp; + SAFE_DELETE_ARRAY(temp) + + if (_request->available() == 0) + { + //delete _request; + SAFE_DELETE(_request) + + _request = nullptr; + } + + _client->send(); + + AHTTP_LOGDEBUG1("*send", sent); + + _lastActivity = millis(); + + return sent; +} + +//************************************************************************************************************** +void AsyncHTTPRequest::_setReadyState(reqStates newState) +{ + if (_readyState != newState) + { + _readyState = newState; + + AHTTP_LOGDEBUG1("_setReadyState :", _readyState); + + if (_readyStateChangeCB) + { + _readyStateChangeCB(_readyStateChangeCBarg, this, _readyState); + } + } +} + +//************************************************************************************************************** +void AsyncHTTPRequest::_processChunks() +{ + while (_chunks->available()) + { + AHTTP_LOGDEBUG3("_processChunks()", _chunks->peekString(16).c_str(), ", chunks available =", _chunks->available()); + + size_t _chunkRemaining = _contentLength - _contentRead - _response->available(); + _chunkRemaining -= _response->write(_chunks, _chunkRemaining); + + if (_chunks->indexOf("\r\n") == -1) + { + return; + } + + String chunkHeader = _chunks->readStringUntil("\r\n"); + + AHTTP_LOGDEBUG3("*getChunkHeader", chunkHeader.c_str(), ", chunkHeader length =", chunkHeader.length()); + + size_t chunkLength = strtol(chunkHeader.c_str(), nullptr, 16); + _contentLength += chunkLength; + + if (chunkLength == 0) + { + char* connectionHdr = respHeaderValue("connection"); + + if (connectionHdr && (strcasecmp_P(connectionHdr, PSTR("disconnect")) == 0)) + { + AHTTP_LOGDEBUG("*all chunks received - closing TCP"); + + _client->close(); + } + else + { + AHTTP_LOGDEBUG("*all chunks received - no disconnect"); + } + + _requestEndTime = millis(); + _lastActivity = 0; + _timeout = 0; + _setReadyState(readyStateDone); + + return; + } + } +} + +/*______________________________________________________________________________________________________________ + + EEEEE V V EEEEE N N TTTTT H H AAA N N DDDD L EEEEE RRRR SSS + E V V E NN N T H H A A NN N D D L E R R S + EEE V V EEE N N N T HHHHH AAAAA N N N D D L EEE RRRR SSS + E V V E N NN T H H A A N NN D D L E R R S + EEEEE V EEEEE N N T H H A A N N DDDD LLLLL EEEEE R R SSS + _______________________________________________________________________________________________________________*/ + +//************************************************************************************************************** +void AsyncHTTPRequest::_onConnect(AsyncClient* client) +{ + AHTTP_LOGDEBUG("_onConnect handler"); + + MUTEX_LOCK_NR + + _client = client; + _setReadyState(readyStateOpened); + + _response = new xbuf; + + if (!_response) + { + _unlock; + + return; + } + + _contentLength = 0; + _contentRead = 0; + _chunked = false; + + _client->onAck([](void* obj, AsyncClient * client, size_t len, uint32_t time) + { + ((AsyncHTTPRequest*)(obj))->_send(); + }, this); + + _client->onData([](void* obj, AsyncClient * client, void* data, size_t len) + { + ((AsyncHTTPRequest*)(obj))->_onData(data, len); + }, this); + + if (_client->canSend()) + { + _send(); + } + + _lastActivity = millis(); + + _unlock; +} + +//************************************************************************************************************** +void AsyncHTTPRequest::_onPoll(AsyncClient* client) +{ + MUTEX_LOCK_NR + + if (_timeout && (millis() - _lastActivity) > (_timeout * 1000)) + { + _client->close(); + _HTTPcode = HTTPCODE_TIMEOUT; + + AHTTP_LOGDEBUG("_onPoll timeout"); + } + + if (_onDataCB && available()) + { + _onDataCB(_onDataCBarg, this, available()); + } + + _unlock; +} + +//************************************************************************************************************** +void AsyncHTTPRequest::_onError(AsyncClient* client, int8_t error) +{ + AHTTP_LOGDEBUG1("_onError handler error =", error); + + _HTTPcode = error; +} + +//************************************************************************************************************** +void AsyncHTTPRequest::_onDisconnect(AsyncClient* client) +{ + AHTTP_LOGDEBUG("\n_onDisconnect handler"); + + MUTEX_LOCK_NR + + if (_readyState < readyStateOpened) + { + _HTTPcode = HTTPCODE_NOT_CONNECTED; + } + else if (_HTTPcode > 0 && + (_readyState < readyStateHdrsRecvd || (_contentRead + _response->available()) < _contentLength)) + { + _HTTPcode = HTTPCODE_CONNECTION_LOST; + } + + SAFE_DELETE(_client) + + _client = nullptr; + + SAFE_DELETE_ARRAY(_connectedHost) + + _connectedHost = nullptr; + + _connectedPort = -1; + _requestEndTime = millis(); + _lastActivity = 0; + _setReadyState(readyStateDone); + + _unlock; +} + +//************************************************************************************************************** +void AsyncHTTPRequest::_onData(void* Vbuf, size_t len) +{ + AHTTP_LOGDEBUG3("_onData handler", (char*) Vbuf, ", len =", len); + + MUTEX_LOCK_NR + + _lastActivity = millis(); + + // Transfer data to xbuf + if (_chunks) + { + _chunks->write((uint8_t*)Vbuf, len); + _processChunks(); + } + else + { + _response->write((uint8_t*)Vbuf, len); + } + + // if headers not complete, collect them. If still not complete, just return. + if (_readyState == readyStateOpened) + { + if ( ! _collectHeaders()) + { + _unlock; + + return; + } + } + + // If there's data in the buffer and not Done, advance readyState to Loading. + if (_response->available() && _readyState != readyStateDone) + { + _setReadyState(readyStateLoading); + } + + // If not chunked and all data read, close it up. + if ( ! _chunked && (_response->available() + _contentRead) >= _contentLength) + { + char* connectionHdr = respHeaderValue("connection"); + + if (connectionHdr && (strcasecmp_P(connectionHdr, PSTR("disconnect")) == 0)) + { + AHTTP_LOGDEBUG("*all data received - closing TCP"); + + _client->close(); + } + else + { + AHTTP_LOGDEBUG("*all data received - no disconnect"); + } + + _requestEndTime = millis(); + _lastActivity = 0; + _timeout = 0; + _setReadyState(readyStateDone); + } + + // If onData callback requested, do so. + if (_onDataCB && available()) + { + _onDataCB(_onDataCBarg, this, available()); + } + + _unlock; + +} + +//************************************************************************************************************** +bool AsyncHTTPRequest::_collectHeaders() +{ + AHTTP_LOGDEBUG("_collectHeaders()"); + + // Loop to parse off each header line. Drop out and return false if no \r\n (incomplete) + do + { + String headerLine = _response->readStringUntil("\r\n"); + + // If no line, return false. + if ( ! headerLine.length()) + { + return false; + } + + // If empty line, all headers are in, advance readyState. + if (headerLine.length() == 2) + { + _setReadyState(readyStateHdrsRecvd); + } + // If line is HTTP header, capture HTTPcode. + else if (headerLine.substring(0, 7) == "HTTP/1.") + { + _HTTPcode = headerLine.substring(9, headerLine.indexOf(' ', 9)).toInt(); + } + // Ordinary header, add to header list. + else + { + int colon = headerLine.indexOf(':'); + + if (colon != -1) + { + String name = headerLine.substring(0, colon); + name.trim(); + String value = headerLine.substring(colon + 1); + value.trim(); + _addHeader(name.c_str(), value.c_str()); + } + } + } while (_readyState == readyStateOpened); + + // If content-Length header, set _contentLength + header *hdr = _getHeader("Content-Length"); + + if (hdr) + { + _contentLength = strtol(hdr->value, nullptr, 10); + } + + // If chunked specified, try to set _contentLength to size of first chunk + hdr = _getHeader("Transfer-Encoding"); + + if (hdr && strcasecmp_P(hdr->value, PSTR("chunked")) == 0) + { + AHTTP_LOGDEBUG("*transfer-encoding: chunked"); + + _chunked = true; + _contentLength = 0; + _chunks = new xbuf; + _chunks->write(_response, _response->available()); + _processChunks(); + } + + return true; +} + + +/*_____________________________________________________________________________________________________________ + + H H EEEEE AAA DDDD EEEEE RRRR SSS + H H E A A D D E R R S + HHHHH EEE AAAAA D D EEE RRRR SSS + H H E A A D D E R R S + H H EEEEE A A DDDD EEEEE R R SSS + ______________________________________________________________________________________________________________*/ + +//************************************************************************************************************** +void AsyncHTTPRequest::setReqHeader(const char* name, const char* value) +{ + if (_readyState <= readyStateOpened && _headers) + { + _addHeader(name, value); + } +} + +//************************************************************************************************************** +void AsyncHTTPRequest::setReqHeader(const char* name, int32_t value) +{ + if (_readyState <= readyStateOpened && _headers) + { + setReqHeader(name, String(value).c_str()); + } +} + +#if (ESP32 || ESP8266) + +//************************************************************************************************************** +void AsyncHTTPRequest::setReqHeader(const char* name, const __FlashStringHelper* value) +{ + if (_readyState <= readyStateOpened && _headers) + { + char* _value = _charstar(value); + _addHeader(name, _value); + + SAFE_DELETE_ARRAY(_value) + } +} + +//************************************************************************************************************** +void AsyncHTTPRequest::setReqHeader(const __FlashStringHelper *name, const char* value) +{ + if (_readyState <= readyStateOpened && _headers) + { + char* _name = _charstar(name); + _addHeader(_name, value); + + SAFE_DELETE_ARRAY(_name) + } +} + +//************************************************************************************************************** +void AsyncHTTPRequest::setReqHeader(const __FlashStringHelper *name, const __FlashStringHelper* value) +{ + if (_readyState <= readyStateOpened && _headers) + { + char* _name = _charstar(name); + char* _value = _charstar(value); + _addHeader(_name, _value); + + SAFE_DELETE_ARRAY(_name) + SAFE_DELETE_ARRAY(_value) + } +} + +//************************************************************************************************************** +void AsyncHTTPRequest::setReqHeader(const __FlashStringHelper *name, int32_t value) +{ + if (_readyState <= readyStateOpened && _headers) + { + char* _name = _charstar(name); + setReqHeader(_name, String(value).c_str()); + + SAFE_DELETE_ARRAY(_name) + } +} + +#endif + +//************************************************************************************************************** +int AsyncHTTPRequest::respHeaderCount() +{ + if (_readyState < readyStateHdrsRecvd) + return 0; + + int count = 0; + header* hdr = _headers; + + while (hdr) + { + count++; + hdr = hdr->next; + } + + return count; +} + +//************************************************************************************************************** +char* AsyncHTTPRequest::respHeaderName(int ndx) +{ + if (_readyState < readyStateHdrsRecvd) + return nullptr; + + header* hdr = _getHeader(ndx); + + if ( ! hdr) + return nullptr; + + return hdr->name; +} + +//************************************************************************************************************** +char* AsyncHTTPRequest::respHeaderValue(const char* name) +{ + if (_readyState < readyStateHdrsRecvd) + return nullptr; + + header* hdr = _getHeader(name); + + if ( ! hdr) + return nullptr; + + return hdr->value; +} + +//************************************************************************************************************** +char* AsyncHTTPRequest::respHeaderValue(int ndx) +{ + if (_readyState < readyStateHdrsRecvd) + return nullptr; + + header* hdr = _getHeader(ndx); + + if ( ! hdr) + return nullptr; + + return hdr->value; +} + +//************************************************************************************************************** +bool AsyncHTTPRequest::respHeaderExists(const char* name) +{ + if (_readyState < readyStateHdrsRecvd) + return false; + + header* hdr = _getHeader(name); + + if ( ! hdr) + return false; + + return true; +} + + +#if (ESP32 || ESP8266) + +//************************************************************************************************************** +char* AsyncHTTPRequest::respHeaderValue(const __FlashStringHelper *name) +{ + if (_readyState < readyStateHdrsRecvd) + return nullptr; + + char* _name = _charstar(name); + header* hdr = _getHeader(_name); + + SAFE_DELETE_ARRAY(_name) + + if ( ! hdr) + return nullptr; + + return hdr->value; +} + +//************************************************************************************************************** +bool AsyncHTTPRequest::respHeaderExists(const __FlashStringHelper *name) +{ + if (_readyState < readyStateHdrsRecvd) + return false; + + char* _name = _charstar(name); + header* hdr = _getHeader(_name); + + SAFE_DELETE_ARRAY(_name) + + if ( ! hdr) + return false; + + return true; +} + +#endif + +//************************************************************************************************************** +String AsyncHTTPRequest::headers() +{ + MUTEX_LOCK(String()) + + String _response = ""; + header* hdr = _headers; + + while (hdr) + { + _response += hdr->name; + _response += ':'; + _response += hdr->value; + _response += "\r\n"; + hdr = hdr->next; + } + + _response += "\r\n"; + + _unlock; + + return _response; +} + +//************************************************************************************************************** +AsyncHTTPRequest::header* AsyncHTTPRequest::_addHeader(const char* name, const char* value) +{ + MUTEX_LOCK(nullptr) + + header* hdr = (header*) &_headers; + + while (hdr->next) + { + if (strcasecmp(name, hdr->next->name) == 0) + { + header* oldHdr = hdr->next; + hdr->next = hdr->next->next; + oldHdr->next = nullptr; + + SAFE_DELETE(oldHdr) + } + else + { + hdr = hdr->next; + } + } + + hdr->next = new header; + + if (hdr->next) + { + hdr->next->name = new char[strlen(name) + 1]; + + if (hdr->next->name) + strcpy(hdr->next->name, name); + else + { + SAFE_DELETE(hdr->next) + + return nullptr; + } + + hdr->next->value = new char[strlen(value) + 1]; + + if (hdr->next->value) + strcpy(hdr->next->value, value); + else + { + SAFE_DELETE_ARRAY(hdr->next->name) + SAFE_DELETE(hdr->next) + + return nullptr; + } + } + else + { + return nullptr; + } + + _unlock; + + return hdr->next; +} + +//************************************************************************************************************** +AsyncHTTPRequest::header* AsyncHTTPRequest::_getHeader(const char* name) +{ + MUTEX_LOCK(nullptr) + + header* hdr = _headers; + + while (hdr) + { + if (strcasecmp(name, hdr->name) == 0) + break; + + hdr = hdr->next; + } + + _unlock; + + return hdr; +} + +//************************************************************************************************************** +AsyncHTTPRequest::header* AsyncHTTPRequest::_getHeader(int ndx) +{ + MUTEX_LOCK(nullptr) + + header* hdr = _headers; + + while (hdr) + { + if ( ! ndx--) + break; + + hdr = hdr->next; + } + + _unlock; + + return hdr; +} + +#if (ESP32 || ESP8266) + +//************************************************************************************************************** +char* AsyncHTTPRequest::_charstar(const __FlashStringHelper * str) +{ + if ( ! str) + return nullptr; + + char* ptr = new char[strlen_P((PGM_P)str) + 1]; + + if (ptr) + { + strcpy_P(ptr, (PGM_P)str); + } + + // Rturn good ptr or nullptr + return ptr; +} + +#endif diff --git a/src_h/utility/xbuf.h b/src_h/utility/xbuf.h new file mode 100644 index 00000000..ba30d8c2 --- /dev/null +++ b/src_h/utility/xbuf.h @@ -0,0 +1,151 @@ +/**************************************************************************************************************************** + xbuf.h - Dead simple AsyncHTTPRequest for ESP8266, ESP32 and currently STM32 with built-in LAN8742A Ethernet + + For ESP8266, ESP32 and STM32 with built-in LAN8742A Ethernet (Nucleo-144, DISCOVERY, etc) + + AsyncHTTPRequest_STM32 is a library for the ESP8266, ESP32 and currently STM32 run built-in Ethernet WebServer + + Based on and modified from asyncHTTPrequest Library (https://github.com/boblemaire/asyncHTTPrequest) + + Built by Khoi Hoang https://github.com/khoih-prog/AsyncHTTPRequest_Generic + Licensed under MIT license + + Copyright (C) <2018> + This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License + as published bythe Free Software Foundation, either version 3 of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + You should have received a copy of the GNU General Public License along with this program. If not, see . + + Version: 1.0.2 + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 1.0.0 K Hoang 14/09/2020 Initial coding to add support to STM32 using built-in Ethernet (Nucleo-144, DISCOVERY, etc). + 1.0.1 K Hoang 09/10/2020 Restore cpp code besides Impl.h code. + 1.0.2 K Hoang 09/11/2020 Make Mutex Lock and delete more reliable and error-proof + *****************************************************************************************************************************/ + +/******************************************************************************************** + xbuf is a dynamic buffering system that supports reading and writing much like cbuf. + The class has it's own provision for writing from buffers, Strings and other xbufs + as well as the inherited Print functions. + Rather than use a large contiguous heap allocation, xbuf uses a linked chain of segments + to dynamically grow and shrink with the contents. + There are other benefits as well to using smaller heap allocation units: + 1) A buffer can work fine in a fragmented heap environment (admittedly contributing to it) + 2) xbuf contents can be copied from one buffer to another without the need for + 2x heap during the copy. + The segment size defaults to 64 but can be dynamically set in the constructor at creation. + The inclusion of indexOf and read/peek until functions make it useful for handling + data streams like HTTP, and in fact is why it was created. + + NOTE: The size of the indexOf() search string is limited to the segment size. + It could be extended but didn't seem to be a practical consideration. + +********************************************************************************************/ +#pragma once + +#include + +struct xseg +{ + xseg *next; + uint8_t data[]; +}; + +class xbuf: public Print +{ + public: + + xbuf(const uint16_t segSize = 64); + virtual ~xbuf(); + + size_t write(const uint8_t); + size_t write(const char*); + size_t write(const uint8_t*, const size_t); + size_t write(xbuf*, const size_t); + size_t write(String); + size_t available(); + int indexOf(const char, const size_t begin = 0); + int indexOf(const char*, const size_t begin = 0); + uint8_t read(); + size_t read(uint8_t*, size_t); + String readStringUntil(const char); + String readStringUntil(const char*); + String readString(int); + + String readString() + { + return readString(available()); + } + + void flush(); + + uint8_t peek(); + size_t peek(uint8_t*, const size_t); + + String peekStringUntil(const char target) + { + return peekString(indexOf(target, 0)); + } + + String peekStringUntil(const char* target) + { + return peekString(indexOf(target, 0)); + } + + String peekString() + { + return peekString(_used); + } + + String peekString(int); + + /* In addition to the above functions, + the following inherited functions from the Print class are available. + + size_t printf(const char * format, ...) __attribute__ ((format (printf, 2, 3))); + size_t printf_P(PGM_P format, ...) __attribute__((format(printf, 2, 3))); + size_t print(const __FlashStringHelper *); + size_t print(const String &); + size_t print(const char[]); + size_t print(char); + size_t print(unsigned char, int = DEC); + size_t print(int, int = DEC); + size_t print(unsigned int, int = DEC); + size_t print(long, int = DEC); + size_t print(unsigned long, int = DEC); + size_t print(double, int = 2); + size_t print(const Printable&); + + size_t println(const __FlashStringHelper *); + size_t println(const String &s); + size_t println(const char[]); + size_t println(char); + size_t println(unsigned char, int = DEC); + size_t println(int, int = DEC); + size_t println(unsigned int, int = DEC); + size_t println(long, int = DEC); + size_t println(unsigned long, int = DEC); + size_t println(double, int = 2); + size_t println(const Printable&); + size_t println(void); + */ + + protected: + + xseg *_head; + xseg *_tail; + uint16_t _used; + uint16_t _free; + uint16_t _offset; + uint16_t _segSize; + + void addSeg(); + void remSeg(); + +}; + +#include "utility/xbuf_Impl.h" + diff --git a/src_h/utility/xbuf_Impl.h b/src_h/utility/xbuf_Impl.h new file mode 100644 index 00000000..764e857f --- /dev/null +++ b/src_h/utility/xbuf_Impl.h @@ -0,0 +1,399 @@ +/**************************************************************************************************************************** + xbuf_Impl.h - Dead simple AsyncHTTPRequest for ESP8266, ESP32 and currently STM32 with built-in LAN8742A Ethernet + + For ESP8266, ESP32 and STM32 with built-in LAN8742A Ethernet (Nucleo-144, DISCOVERY, etc) + + AsyncHTTPRequest_STM32 is a library for the ESP8266, ESP32 and currently STM32 run built-in Ethernet WebServer + + Based on and modified from asyncHTTPrequest Library (https://github.com/boblemaire/asyncHTTPrequest) + + Built by Khoi Hoang https://github.com/khoih-prog/AsyncHTTPRequest_Generic + Licensed under MIT license + + Copyright (C) <2018> + This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License + as published bythe Free Software Foundation, either version 3 of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + You should have received a copy of the GNU General Public License along with this program. If not, see . + + Version: 1.0.2 + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 1.0.0 K Hoang 14/09/2020 Initial coding to add support to STM32 using built-in Ethernet (Nucleo-144, DISCOVERY, etc). + 1.0.1 K Hoang 09/10/2020 Restore cpp code besides Impl.h code. + 1.0.2 K Hoang 09/11/2020 Make Mutex Lock and delete more reliable and error-proof + *****************************************************************************************************************************/ + +#pragma once + +xbuf::xbuf(const uint16_t segSize) : _head(nullptr), _tail(nullptr), _used(0), _free(0), _offset(0) +{ + _segSize = (segSize + 3) & -4;//((segSize + 3) >> 2) << 2; +} + +//******************************************************************************************************************* +xbuf::~xbuf() +{ + flush(); +} + +//******************************************************************************************************************* +size_t xbuf::write(const uint8_t byte) +{ + return write((uint8_t*) &byte, 1); +} + +//******************************************************************************************************************* +size_t xbuf::write(const char* buf) +{ + return write((uint8_t*)buf, strlen(buf)); +} + +//******************************************************************************************************************* +size_t xbuf::write(String string) +{ + return write((uint8_t*)string.c_str(), string.length()); +} + +//******************************************************************************************************************* +size_t xbuf::write(const uint8_t* buf, const size_t len) +{ + size_t supply = len; + + while (supply) + { + if (!_free) + { + addSeg(); + } + + size_t demand = _free < supply ? _free : supply; + memcpy(_tail->data + ((_offset + _used) % _segSize), buf + (len - supply), demand); + _free -= demand; + _used += demand; + supply -= demand; + } + + return len; +} + +//******************************************************************************************************************* +size_t xbuf::write(xbuf* buf, const size_t len) +{ + size_t supply = len; + + if (supply > buf->available()) + { + supply = buf->available(); + } + + size_t read = 0; + + while (supply) + { + if (!_free) + { + addSeg(); + } + + size_t demand = _free < supply ? _free : supply; + read += buf->read(_tail->data + ((_offset + _used) % _segSize), demand); + _free -= demand; + _used += demand; + supply -= demand; + } + + return read; +} + +//******************************************************************************************************************* +uint8_t xbuf::read() +{ + uint8_t byte = 0; + read((uint8_t*) &byte, 1); + + return byte; +} + +//******************************************************************************************************************* +uint8_t xbuf::peek() +{ + uint8_t byte = 0; + peek((uint8_t*) &byte, 1); + + return byte; +} + +//******************************************************************************************************************* +size_t xbuf::read(uint8_t* buf, const size_t len) +{ + size_t read = 0; + + while (read < len && _used) + { + size_t supply = (_offset + _used) > _segSize ? _segSize - _offset : _used; + size_t demand = len - read; + size_t chunk = supply < demand ? supply : demand; + memcpy(buf + read, _head->data + _offset, chunk); + _offset += chunk; + _used -= chunk; + read += chunk; + + if (_offset == _segSize) + { + remSeg(); + _offset = 0; + } + } + + if ( ! _used) + { + flush(); + } + + return read; +} + +//******************************************************************************************************************* +size_t xbuf::peek(uint8_t* buf, const size_t len) +{ + size_t read = 0; + xseg* seg = _head; + size_t offset = _offset; + size_t used = _used; + + while (read < len && used) + { + size_t supply = (offset + used) > _segSize ? _segSize - offset : used; + size_t demand = len - read; + size_t chunk = supply < demand ? supply : demand; + + memcpy(buf + read, seg->data + offset, chunk); + + offset += chunk; + used -= chunk; + read += chunk; + + if (offset == _segSize) + { + seg = seg->next; + offset = 0; + } + } + + return read; +} + +//******************************************************************************************************************* +size_t xbuf::available() +{ + return _used; +} + +//******************************************************************************************************************* +int xbuf::indexOf(const char target, const size_t begin) +{ + char targetstr[2] = " "; + targetstr[0] = target; + + return indexOf(targetstr, begin); +} + +//******************************************************************************************************************* +int xbuf::indexOf(const char* target, const size_t begin) +{ + size_t targetLen = strlen(target); + + if (targetLen > _segSize || targetLen > _used) + return -1; + + size_t searchPos = _offset + begin; + size_t searchEnd = _offset + _used - targetLen; + + if (searchPos > searchEnd) + return -1; + + size_t searchSeg = searchPos / _segSize; + xseg* seg = _head; + + while (searchSeg) + { + seg = seg->next; + searchSeg --; + } + + size_t segPos = searchPos % _segSize; + + while (searchPos <= searchEnd) + { + size_t compLen = targetLen; + + if (compLen <= (_segSize - segPos)) + { + if (memcmp(target, seg->data + segPos, compLen) == 0) + { + return searchPos - _offset; + } + } + else + { + size_t compLen = _segSize - segPos; + + if (memcmp(target, seg->data + segPos, compLen) == 0) + { + compLen = targetLen - compLen; + + if (memcmp(target + targetLen - compLen, seg->next->data, compLen) == 0) + { + return searchPos - _offset; + } + } + } + + searchPos++; + segPos++; + + if (segPos == _segSize) + { + seg = seg->next; + segPos = 0; + } + } + + return -1; +} + +//******************************************************************************************************************* +String xbuf::readStringUntil(const char target) +{ + return readString(indexOf(target) + 1); +} + +//******************************************************************************************************************* +String xbuf::readStringUntil(const char* target) +{ + int index = indexOf(target); + + if (index < 0) + return String(); + + return readString(index + strlen(target)); +} + +//******************************************************************************************************************* +String xbuf::readString(int endPos) +{ + String result; + + if ( ! result.reserve(endPos + 1)) + { + return result; + } + + if (endPos > _used) + { + endPos = _used; + } + + if (endPos > 0 && result.reserve(endPos + 1)) + { + while (endPos--) + { + result += (char)_head->data[_offset++]; + _used--; + + if (_offset >= _segSize) + { + remSeg(); + } + } + } + + return result; +} + +//******************************************************************************************************************* +String xbuf::peekString(int endPos) +{ + String result; + + xseg* seg = _head; + size_t offset = _offset; + + if (endPos > _used) + { + endPos = _used; + } + + if (endPos > 0 && result.reserve(endPos + 1)) + { + while (endPos--) + { + result += (char)seg->data[offset++]; + + if ( offset >= _segSize) + { + seg = seg->next; + offset = 0; + } + } + } + + return result; +} + +//******************************************************************************************************************* +void xbuf::flush() +{ + while (_head) + remSeg(); + + _tail = nullptr; + _offset = 0; + _used = 0; + _free = 0; +} + +//******************************************************************************************************************* +void xbuf::addSeg() +{ + if (_tail) + { + _tail->next = (xseg*) new uint32_t[_segSize / 4 + 1]; + + // KH, Must check NULL here + _tail = _tail->next; + } + else + { + // KH, Must check NULL here + _tail = _head = (xseg*) new uint32_t[_segSize / 4 + 1]; + } + + // KH, Must check NULL here + if (_tail) + _tail->next = nullptr; + + _free += _segSize; +} + +//******************************************************************************************************************* +void xbuf::remSeg() +{ + if (_head) + { + xseg *next = _head->next; + delete[] (uint32_t*) _head; + _head = next; + + if ( ! _head) + { + _tail = nullptr; + } + } + + _offset = 0; +} +