From 7635f3df69422b234b6b55d665d01eb7aae20d76 Mon Sep 17 00:00:00 2001 From: Khoi Hoang <57012152+khoih-prog@users.noreply.github.com> Date: Fri, 9 Oct 2020 16:06:03 -0400 Subject: [PATCH] v1.0.1 ### 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) --- README.md | 39 +- .../AsyncCustomHeader_STM32.ino | 3 +- examples/AsyncCustomHeader_STM32/defines.h | 3 +- .../AsyncDweetGet_STM32.ino | 3 +- .../AsyncDweetPost_STM32.ino | 3 +- .../AsyncHTTPRequest_ESP.ino | 3 +- .../AsyncHTTPRequest_ESP_WiFiManager.ino | 3 +- .../AsyncHTTPRequest_STM32.ino | 3 +- .../AsyncSimpleGET_STM32.ino | 3 +- .../AsyncWebClientRepeating_STM32.ino | 3 +- library.json | 2 +- library.properties | 2 +- src/AsyncHTTPRequest_Debug_Generic.h | 9 +- src/AsyncHTTPRequest_Generic.h | 10 +- src/AsyncHTTPRequest_Impl_Generic.h | 13 +- src/utility/xbuf.h | 7 +- src/utility/xbuf_Impl.h | 9 +- src_cpp/AsyncHTTPRequest_Debug_Generic.h | 70 + src_cpp/AsyncHTTPRequest_Generic.cpp | 1192 +++++++++++++++++ src_cpp/AsyncHTTPRequest_Generic.h | 265 ++++ src_cpp/utility/xbuf.cpp | 392 ++++++ src_cpp/utility/xbuf.h | 148 ++ 22 files changed, 2138 insertions(+), 47 deletions(-) create mode 100644 src_cpp/AsyncHTTPRequest_Debug_Generic.h create mode 100644 src_cpp/AsyncHTTPRequest_Generic.cpp create mode 100644 src_cpp/AsyncHTTPRequest_Generic.h create mode 100644 src_cpp/utility/xbuf.cpp create mode 100644 src_cpp/utility/xbuf.h diff --git a/README.md b/README.md index 3ba453d..13ad2ff 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,11 @@ Chunked responses are recognized and handled transparently. --- --- +### 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) + + ### Releases v1.0.0 1. Initial coding to add support to **STM32F/L/H/G/WB/MP1** using built-in LAN8742A Ethernet (Nucleo-144, DISCOVERY, etc). @@ -212,6 +217,19 @@ theses files must be copied into the corresponding directory: --- --- +### HOWTO Fix `Multiple Definitions` Linker Error + +The current library implementation, using xyz-Impl.h instead of standard xyz.cpp, possibly creates certain `Multiple Definitions` Linker error in certain use cases. Although it's simple to just modify several lines of code, either in the library or in the application, the library is adding a separate source directory, named src_cpp, besides the standard src directory. + +To use the old standard cpp way, just + +1. **Rename the h-only src directory into src_h.** +2. **Then rename the cpp src_cpp directory into src.** +3. Close then reopen the application code in Arduino IDE, etc. to recompile from scratch. + +--- +--- + ### Examples Also see examples: @@ -442,7 +460,7 @@ AsyncHTTPRequest @ IP : 192.168.2.72 ************************************** abbreviation: EDT -client_ip: 216.154.52.212 +client_ip: aaa.bbb.ccc.ddd datetime: 2020-09-13T18:22:59.555816-04:00 day_of_week: 0 day_of_year: 257 @@ -459,7 +477,7 @@ week_number: 37 ************************************** abbreviation: EDT -client_ip: 216.154.52.212 +client_ip: aaa.bbb.ccc.ddd datetime: 2020-09-13T18:27:57.586325-04:00 day_of_week: 0 day_of_year: 257 @@ -489,7 +507,7 @@ After waiting 3.43 secs more in setup(), connection result is connected. Local I H ************************************** abbreviation: EDT -client_ip: 216.154.52.212 +client_ip: aaa.bbb.ccc.ddd datetime: 2020-09-13T19:35:37.951609-04:00 day_of_week: 0 day_of_year: 257 @@ -521,7 +539,7 @@ After waiting 2.35 secs more in setup(), connection result is connected. Local I H ************************************** abbreviation: EDT -client_ip: 216.154.52.212 +client_ip: aaa.bbb.ccc.ddd datetime: 2020-09-13T19:37:02.118166-04:00 day_of_week: 0 day_of_year: 257 @@ -539,7 +557,7 @@ week_number: 37 HHHHHHHHH HHHHHHHHHH HHHHHHHHHH H ************************************** abbreviation: EDT -client_ip: 216.154.52.212 +client_ip: aaa.bbb.ccc.ddd datetime: 2020-09-13T19:42:01.507586-04:00 day_of_week: 0 day_of_year: 257 @@ -570,7 +588,7 @@ HTTP WebServer is @ IP : 192.168.2.81 ************************************** abbreviation: EDT -client_ip: 216.154.52.212 +client_ip: aaa.bbb.ccc.ddd datetime: 2020-09-13T19:56:28.295033-04:00 day_of_week: 0 day_of_year: 257 @@ -690,6 +708,10 @@ Submit issues to: [AsyncHTTPRequest_Generic issues](https://github.com/khoih-pro --- --- +### 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) + ### Releases v1.0.0 1. Initial coding to add support to **STM32F/L/H/G/WB/MP1** using built-in LAN8742A Ethernet (Nucleo-144, DISCOVERY, etc). @@ -702,13 +724,14 @@ Submit issues to: [AsyncHTTPRequest_Generic issues](https://github.com/khoih-pro This library is based on, modified, bug-fixed and improved from: -1. [Bob Lemaire's asyncHTTPrequest Library](https://github.com/boblemaire/asyncHTTPrequest) +1. [Bob Lemaire's **asyncHTTPrequest Library**](https://github.com/boblemaire/asyncHTTPrequest) to use the better **asynchronous** features of these following Async TCP Libraries : ( [`ESPAsyncTCP`](https://github.com/me-no-dev/ESPAsyncTCP), [`AsyncTCP`](https://github.com/me-no-dev/AsyncTCP), and [`STM32AsyncTCP`](https://github.com/philbowles/STM32AsyncTCP) ). -to use the better **asynchronous** features of these following Async TCP Libraries : ( [`ESPAsyncTCP`](https://github.com/me-no-dev/ESPAsyncTCP), [`AsyncTCP`](https://github.com/me-no-dev/AsyncTCP), and [`STM32AsyncTCP`](https://github.com/philbowles/STM32AsyncTCP) ). +2. Thanks to [Daniel Brunner](https://github.com/0xFEEDC0DE64) to report and make PR in [Fixed linker errors when included in multiple .cpp files](https://github.com/khoih-prog/AsyncHTTPRequest_Generic/pull/1) leading to v1.0.1. See [**HOWTO Fix `Multiple Definitions` Linker Error**](https://github.com/khoih-prog/AsyncHTTPRequest_Generic#HOWTO-Fix-Multiple-Definitions-Linker-Error) +
boblemaire
⭐️ Bob Lemaire

0xFEEDC0DE64
Daniel Brunner

diff --git a/examples/AsyncCustomHeader_STM32/AsyncCustomHeader_STM32.ino b/examples/AsyncCustomHeader_STM32/AsyncCustomHeader_STM32.ino index 5583358..f627ba2 100644 --- a/examples/AsyncCustomHeader_STM32/AsyncCustomHeader_STM32.ino +++ b/examples/AsyncCustomHeader_STM32/AsyncCustomHeader_STM32.ino @@ -17,11 +17,12 @@ 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.1 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. *****************************************************************************************************************************/ #include "defines.h" diff --git a/examples/AsyncCustomHeader_STM32/defines.h b/examples/AsyncCustomHeader_STM32/defines.h index 27afabb..1c727f9 100644 --- a/examples/AsyncCustomHeader_STM32/defines.h +++ b/examples/AsyncCustomHeader_STM32/defines.h @@ -19,11 +19,12 @@ 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.1 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. *****************************************************************************************************************************/ /* Currently support diff --git a/examples/AsyncDweetGet_STM32/AsyncDweetGet_STM32.ino b/examples/AsyncDweetGet_STM32/AsyncDweetGet_STM32.ino index bdef64f..ced0abc 100644 --- a/examples/AsyncDweetGet_STM32/AsyncDweetGet_STM32.ino +++ b/examples/AsyncDweetGet_STM32/AsyncDweetGet_STM32.ino @@ -17,11 +17,12 @@ 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.1 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. *****************************************************************************************************************************/ /** diff --git a/examples/AsyncDweetPost_STM32/AsyncDweetPost_STM32.ino b/examples/AsyncDweetPost_STM32/AsyncDweetPost_STM32.ino index 046e26b..feaccb8 100644 --- a/examples/AsyncDweetPost_STM32/AsyncDweetPost_STM32.ino +++ b/examples/AsyncDweetPost_STM32/AsyncDweetPost_STM32.ino @@ -17,11 +17,12 @@ 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.1 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. *****************************************************************************************************************************/ // Dweet.io POST client. Connects to dweet.io once every ten seconds, sends a POST request and a request body. diff --git a/examples/AsyncHTTPRequest_ESP/AsyncHTTPRequest_ESP.ino b/examples/AsyncHTTPRequest_ESP/AsyncHTTPRequest_ESP.ino index 50470c0..869b539 100644 --- a/examples/AsyncHTTPRequest_ESP/AsyncHTTPRequest_ESP.ino +++ b/examples/AsyncHTTPRequest_ESP/AsyncHTTPRequest_ESP.ino @@ -17,11 +17,12 @@ 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.1 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. *****************************************************************************************************************************/ //************************************************************************************************************ // diff --git a/examples/AsyncHTTPRequest_ESP_WiFiManager/AsyncHTTPRequest_ESP_WiFiManager.ino b/examples/AsyncHTTPRequest_ESP_WiFiManager/AsyncHTTPRequest_ESP_WiFiManager.ino index c1bb8f2..6e43365 100644 --- a/examples/AsyncHTTPRequest_ESP_WiFiManager/AsyncHTTPRequest_ESP_WiFiManager.ino +++ b/examples/AsyncHTTPRequest_ESP_WiFiManager/AsyncHTTPRequest_ESP_WiFiManager.ino @@ -17,11 +17,12 @@ 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.1 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. *****************************************************************************************************************************/ //************************************************************************************************************ // diff --git a/examples/AsyncHTTPRequest_STM32/AsyncHTTPRequest_STM32.ino b/examples/AsyncHTTPRequest_STM32/AsyncHTTPRequest_STM32.ino index bac5ad7..f728ce5 100644 --- a/examples/AsyncHTTPRequest_STM32/AsyncHTTPRequest_STM32.ino +++ b/examples/AsyncHTTPRequest_STM32/AsyncHTTPRequest_STM32.ino @@ -17,11 +17,12 @@ 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.1 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. *****************************************************************************************************************************/ //************************************************************************************************************ // diff --git a/examples/AsyncSimpleGET_STM32/AsyncSimpleGET_STM32.ino b/examples/AsyncSimpleGET_STM32/AsyncSimpleGET_STM32.ino index 3e89c05..fa84bd8 100644 --- a/examples/AsyncSimpleGET_STM32/AsyncSimpleGET_STM32.ino +++ b/examples/AsyncSimpleGET_STM32/AsyncSimpleGET_STM32.ino @@ -17,11 +17,12 @@ 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.1 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. *****************************************************************************************************************************/ #include "defines.h" diff --git a/examples/AsyncWebClientRepeating_STM32/AsyncWebClientRepeating_STM32.ino b/examples/AsyncWebClientRepeating_STM32/AsyncWebClientRepeating_STM32.ino index c5aecd8..d0ddb9a 100644 --- a/examples/AsyncWebClientRepeating_STM32/AsyncWebClientRepeating_STM32.ino +++ b/examples/AsyncWebClientRepeating_STM32/AsyncWebClientRepeating_STM32.ino @@ -17,11 +17,12 @@ 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.1 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. *****************************************************************************************************************************/ #include "defines.h" diff --git a/library.json b/library.json index a05d600..5c90250 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name":"AsyncHTTPRequest_Generic", - "version": "1.0.0", + "version": "1.0.1", "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 a9091bf..bc54bd9 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=AsyncHTTPRequest_Generic -version=1.0.0 +version=1.0.1 author=Bob Lemaire,Khoi Hoang maintainer=Khoi Hoang license=MIT diff --git a/src/AsyncHTTPRequest_Debug_Generic.h b/src/AsyncHTTPRequest_Debug_Generic.h index 5825533..b6e50b4 100644 --- a/src/AsyncHTTPRequest_Debug_Generic.h +++ b/src/AsyncHTTPRequest_Debug_Generic.h @@ -17,15 +17,15 @@ 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.1 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. *****************************************************************************************************************************/ - -#ifndef AsyncHTTPRequest_Debug_STM32_H -#define AsyncHTTPRequest_Debug_STM32_H + +#pragma once #ifdef ASYNC_HTTP_DEBUG_PORT #define A_DBG_PORT ASYNC_HTTP_DEBUG_PORT @@ -68,4 +68,3 @@ #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); } -#endif // AsyncHTTPRequest_Debug_STM32_H diff --git a/src/AsyncHTTPRequest_Generic.h b/src/AsyncHTTPRequest_Generic.h index 7ab079d..2457f58 100644 --- a/src/AsyncHTTPRequest_Generic.h +++ b/src/AsyncHTTPRequest_Generic.h @@ -17,15 +17,15 @@ 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.1 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. *****************************************************************************************************************************/ - -#ifndef AsyncHTTPRequest_Generic_h -#define AsyncHTTPRequest_Generic_h + +#pragma once #define AsyncHTTPRequest_Generic_version "1.0.0" @@ -265,5 +265,3 @@ class AsyncHTTPRequest }; #include "AsyncHTTPRequest_Impl_Generic.h" - -#endif // AsyncHTTPRequest_Generic_h diff --git a/src/AsyncHTTPRequest_Impl_Generic.h b/src/AsyncHTTPRequest_Impl_Generic.h index 69d2b97..853a329 100644 --- a/src/AsyncHTTPRequest_Impl_Generic.h +++ b/src/AsyncHTTPRequest_Impl_Generic.h @@ -17,18 +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.0 + Version: 1.0.1 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. *****************************************************************************************************************************/ #pragma once -#ifndef AsyncHTTPRequest_Impl_Generic_h -#define AsyncHTTPRequest_Impl_Generic_h - //************************************************************************************************************** AsyncHTTPRequest::AsyncHTTPRequest(): _readyState(readyStateUnsent), _HTTPcode(0), _chunked(false), _debug(DEBUG_IOTA_HTTP_SET) @@ -529,6 +527,8 @@ bool AsyncHTTPRequest::_buildRequest() _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" ); delete _URL; @@ -541,6 +541,9 @@ bool AsyncHTTPRequest::_buildRequest() _request->write(':'); _request->write(hdr->value); _request->write("\r\n"); + + AHTTP_LOGDEBUG3(hdr->name, ":", hdr->value, "\r\n" ); + hdr = hdr->next; } @@ -1187,5 +1190,3 @@ char* AsyncHTTPRequest::_charstar(const __FlashStringHelper * str) } #endif - -#endif // AsyncHTTPRequest_Impl_Generic_h diff --git a/src/utility/xbuf.h b/src/utility/xbuf.h index debde9d..84bdab6 100644 --- a/src/utility/xbuf.h +++ b/src/utility/xbuf.h @@ -17,11 +17,12 @@ 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.1 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. *****************************************************************************************************************************/ /******************************************************************************************** @@ -44,9 +45,6 @@ ********************************************************************************************/ #pragma once -#ifndef xbuf_h -#define xbuf_h - #include struct xseg @@ -150,4 +148,3 @@ class xbuf: public Print #include "utility/xbuf_Impl.h" -#endif // xbuf_h diff --git a/src/utility/xbuf_Impl.h b/src/utility/xbuf_Impl.h index 8ac130c..5431af6 100644 --- a/src/utility/xbuf_Impl.h +++ b/src/utility/xbuf_Impl.h @@ -17,17 +17,15 @@ 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.1 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. *****************************************************************************************************************************/ -#ifndef xbuf_Impl_h -#define xbuf_Impl_h - -//#include "utility/xbuf.h" +#pragma once xbuf::xbuf(const uint16_t segSize) : _head(nullptr), _tail(nullptr), _used(0), _free(0), _offset(0) { @@ -392,4 +390,3 @@ void xbuf::remSeg() _offset = 0; } -#endif // xbuf_Impl_h diff --git a/src_cpp/AsyncHTTPRequest_Debug_Generic.h b/src_cpp/AsyncHTTPRequest_Debug_Generic.h new file mode 100644 index 0000000..de5a776 --- /dev/null +++ b/src_cpp/AsyncHTTPRequest_Debug_Generic.h @@ -0,0 +1,70 @@ +/**************************************************************************************************************************** + src_cpp/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.1 + + 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. + *****************************************************************************************************************************/ + +#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_cpp/AsyncHTTPRequest_Generic.cpp b/src_cpp/AsyncHTTPRequest_Generic.cpp new file mode 100644 index 0000000..532ec94 --- /dev/null +++ b/src_cpp/AsyncHTTPRequest_Generic.cpp @@ -0,0 +1,1192 @@ +/**************************************************************************************************************************** + src_cpp/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) + + 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.1 + + 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. + *****************************************************************************************************************************/ + +#include "AsyncHTTPRequest_Generic.h" + + +//************************************************************************************************************** +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); + + delete _URL; + delete _headers; + delete _request; + delete _response; + delete _chunks; + delete[] _connectedHost; + +#ifdef ESP32 + vSemaphoreDelete(threadLock); +#endif +} + +//************************************************************************************************************** +void AsyncHTTPRequest::setDebug(bool debug) +{ + if (_debug || debug) + { + _debug = true; + + AHTTP_LOGDEBUG3("setDebug(", debug ? "on" : "off", ") version", AsyncHTTPRequest_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(); + + delete _URL; + delete _headers; + delete _request; + delete _response; + 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]; + sprintf(hostName, "%s:%d", _URL->host, _URL->port); + _addHeader("host", hostName); + delete[] hostName; + _lastActivity = millis(); + + return _connect(); +} +//************************************************************************************************************** +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()"); + + _lock; + + 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()); + + _lock; + _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)); + + _lock; + _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); + + _lock; + _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); + + _lock; + _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()"); + + _lock; + + if (! _client) + return; + + _client->abort(); + _unlock; +} +//************************************************************************************************************** +reqStates AsyncHTTPRequest::readyState() +{ + return _readyState; +} + +//************************************************************************************************************** +int AsyncHTTPRequest::responseHTTPcode() +{ + return _HTTPcode; +} + +//************************************************************************************************************** +String AsyncHTTPRequest::responseText() +{ + AHTTP_LOGDEBUG("responseText()"); + + _lock; + + 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()) + { + //DEBUG_HTTP("responseRead() no data\r\n"); + AHTTP_LOGDEBUG("responseRead() no data"); + + return 0; + } + + _lock; + 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(AsyncHTTPRequest_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) +{ + delete _URL; + + int hostBeg = 0; + _URL = new URL; + _URL->scheme = new char[8]; + 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]; + 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]; + strcpy(_URL->path, url.substring(pathBeg, queryBeg).c_str()); + _URL->query = new char[url.length() - queryBeg + 1]; + 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(); + } + + delete[] _connectedHost; + + _connectedHost = new char[strlen(_URL->host) + 1]; + 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; + + _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" ); + + 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; + } + + 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]; + + while (supply) + { + size_t chunk = supply < 100 ? supply : 100; + supply -= _request->read(temp, chunk); + sent += _client->add((char*)temp, chunk); + } + + delete temp; + + if (_request->available() == 0) + { + 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"); + + _lock; + _client = client; + _setReadyState(readyStateOpened); + _response = new xbuf; + _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) +{ + _lock; + + 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"); + + _lock; + + if (_readyState < readyStateOpened) + { + _HTTPcode = HTTPCODE_NOT_CONNECTED; + } + else if (_HTTPcode > 0 && + (_readyState < readyStateHdrsRecvd || (_contentRead + _response->available()) < _contentLength)) + { + _HTTPcode = HTTPCODE_CONNECTION_LOST; + } + + delete _client; + _client = nullptr; + + delete[] _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); + + _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()) + 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); + delete[] _value; + } +} + +//************************************************************************************************************** +void AsyncHTTPRequest::setReqHeader(const __FlashStringHelper *name, const char* value) +{ + if (_readyState <= readyStateOpened && _headers) + { + char* _name = _charstar(name); + _addHeader(_name, value); + delete[] _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); + delete[] _name; + delete[] _value; + } +} + +//************************************************************************************************************** +void AsyncHTTPRequest::setReqHeader(const __FlashStringHelper *name, int32_t value) +{ + if (_readyState <= readyStateOpened && _headers) + { + char* _name = _charstar(name); + setReqHeader(_name, String(value).c_str()); + delete[] _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); + delete[] _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); + delete[] _name; + + if ( ! hdr) + return false; + + return true; +} + +#endif + +//************************************************************************************************************** +String AsyncHTTPRequest::headers() +{ + _lock; + 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) +{ + _lock; + 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; + } + else + { + hdr = hdr->next; + } + } + + 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); + _unlock; + + return hdr->next; +} + +//************************************************************************************************************** +AsyncHTTPRequest::header* AsyncHTTPRequest::_getHeader(const char* name) +{ + _lock; + header* hdr = _headers; + + while (hdr) + { + if (strcasecmp(name, hdr->name) == 0) + break; + + hdr = hdr->next; + } + + _unlock; + + return hdr; +} + +//************************************************************************************************************** +AsyncHTTPRequest::header* AsyncHTTPRequest::_getHeader(int ndx) +{ + _lock; + 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]; + strcpy_P(ptr, (PGM_P)str); + + return ptr; +} + +#endif diff --git a/src_cpp/AsyncHTTPRequest_Generic.h b/src_cpp/AsyncHTTPRequest_Generic.h new file mode 100644 index 0000000..7d6f54d --- /dev/null +++ b/src_cpp/AsyncHTTPRequest_Generic.h @@ -0,0 +1,265 @@ +/**************************************************************************************************************************** + src_cpp/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.1 + + 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. + *****************************************************************************************************************************/ + +#pragma once + +#define AsyncHTTPRequest_Generic_version "1.0.0" + +#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 + +#if ESP32 + + #include + #define _lock xSemaphoreTakeRecursive(threadLock,portMAX_DELAY) + #define _unlock xSemaphoreGiveRecursive(threadLock) + +#elif ESP8266 + + #include + #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 _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() + { + 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() + { + delete[] scheme; + delete[] user; + delete[] pwd; + delete[] host; + delete[] path; + delete[] query; + delete[] 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(); +}; diff --git a/src_cpp/utility/xbuf.cpp b/src_cpp/utility/xbuf.cpp new file mode 100644 index 0000000..108283e --- /dev/null +++ b/src_cpp/utility/xbuf.cpp @@ -0,0 +1,392 @@ +/**************************************************************************************************************************** + src_cpp/utility/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.1 + + 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. + *****************************************************************************************************************************/ + +#include "utility/xbuf.h" + +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]; + _tail = _tail->next; + } + else + { + _tail = _head = (xseg*) new uint32_t[_segSize / 4 + 1]; + } + + _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; +} + diff --git a/src_cpp/utility/xbuf.h b/src_cpp/utility/xbuf.h new file mode 100644 index 0000000..e63198d --- /dev/null +++ b/src_cpp/utility/xbuf.h @@ -0,0 +1,148 @@ +/**************************************************************************************************************************** + 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.1 + + 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. + *****************************************************************************************************************************/ + +/******************************************************************************************** + 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(); +}; +