Compare commits

...

10 Commits

9 changed files with 517 additions and 628 deletions

View File

@ -1,8 +1,8 @@
{
"name":"AsyncHTTPRequest_Generic",
"version": "1.0.0",
"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",
"description":"Simple Async HTTP Request library, supporting GET and POST, on top of AsyncTCP libraries, such as AsyncTCP, ESPAsyncTCP, etc... for ESP32, ESP8266.",
"keywords":"async,tcp,http,ESP8266,ESP32,ESPAsyncTCP,AsyncTCP,ethernet,wifi,lan8742a",
"authors": [
{
"name": "Bob Lemaire",
@ -13,7 +13,7 @@
"url": "https://github.com/khoih-prog",
"email": "khoih.prog@gmail.com",
"maintainer": true
}
}
],
"repository":
{
@ -37,16 +37,8 @@
"platforms": ["espressif8266", "espressif32"]
},
{
"name": "STM32duino LwIP",
"platforms": "ststm32"
},
{
"name": "STM32duino STM32Ethernet",
"platforms": "ststm32"
},
{
"name": "STM32 AsyncTCP",
"platforms": "ststm32"
"name": "https://github.com/0xFEEDC0DE64/optional.git",
"platforms": ["espressif8266", "espressif32"]
}
]
}

View File

@ -3,10 +3,10 @@ version=1.0.0
author=Bob Lemaire,Khoi Hoang
maintainer=Khoi Hoang <khoih.prog@gmail.com>
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.
paragraph=This AsyncHTTPRequest_Generic Library, supporting GET and POST, for ESP32, ESP8266 and STM32 with built-in LAN8742A Ethernet, such as Nucleo-144 F767ZI, etc.
sentence=Simple Async HTTP Request library, supporting GET and POST, on top of AsyncTCP libraries, such as AsyncTCP, ESPAsyncTCP, etc.. for ESP32, ESP8266
paragraph=This AsyncHTTPRequest_Generic Library, supporting GET and POST, for ESP32, ESP8266.
category=Communication,AsyncTCP,AsyncHTTP
url=https://github.com/khoih-prog/AsyncHTTPRequest_Generic
architectures=*
depends=AsyncTCP,ESP AsyncTCP,ESPAsync_WiFiManager,STM32duino LwIP,STM32duino STM32Ethernet,STM32 AsyncTCP
depends=AsyncTCP,ESP AsyncTCP,ESPAsync_WiFiManager
includes=AsyncHTTPRequest_Generic.h

View File

@ -15,13 +15,11 @@
; ESP32
; SAMD
; NRF52
; STM32
; ============================================================
;default_envs = ESP8266
default_envs = ESP32
;default_envs = SAMD
;default_envs = NRF52
;default_envs = STM32
[env]
; ============================================================
@ -33,22 +31,16 @@ upload_speed = 921600
;monitor_speed = 9600
;monitor_port = COM11
lib_deps =
lib_deps =
; PlatformIO 4.x
AsyncTCP@~1.1.1
ESPAsyncTCP@~1.2.2
STM32AsyncTCP@~1.0.0
STM32duino LwIP@~2.1.2
STM32duino STM32Ethernet@~1.2.0
ESPAsync_WiFiManager@~1.1.2
; PlatformIO 5.x
; PlatformIO 5.x
; me-no-dev/AsyncTCP@~1.1.1
; me-no-dev/ESPAsyncTCP@~1.2.2
; philbowles/STM32AsyncTCP@~1.0.0
; stm32duino/STM32duino LwIP@~2.1.2
; stm32duino/STM32duino STM32Ethernet@~1.2.0
; khoih-prog/ESPAsync_WiFiManager@~1.1.2
build_flags =
; set your debug output (default=Serial)
; -D DEBUG_ESP_PORT=Serial
@ -234,115 +226,3 @@ board = feather52840
;board = mdbt50qrx
;board = ninab302
;board = ninab112
[env:STM32]
platform = ststm32
framework = arduino
; ============================================================
; Choose your board by uncommenting one of the following lines
; ============================================================
; ============================================================
; Board configuration Nucleo-144
; ============================================================
;board = nucleo_f207zg
;board = nucleo_f429zi
;board = nucleo_f746zg
;board = nucleo_f756zg
;board = nucleo_f767zi
;board = nucleo_h743zi
;board = nucleo_l496zg
;board = nucleo_l496zg-p
;board = nucleo_l4r5zi
;board = nucleo_l4r5zi-p
; ============================================================
; Board configuration Nucleo-64
; ============================================================
;board = nucleo_f030r8
;board = nucleo_f072rb
;board = nucleo_f091rc
;board = nucleo_f103rb
;board = nucleo_f302r8
;board = nucleo_f303re
;board = nucleo_f401re
;board = nucleo_f411re
;board = nucleo_f446re
;board = nucleo_g071rb
;board = nucleo_g431rb
;board = nucleo_g474re
;board = nucleo_l053r8
;board = nucleo_l073rz
;board = nucleo_l152re
;board = nucleo_l433rc_p
;board = nucleo_l452re
;board = nucleo_l452re-p
;board = nucleo_l476rg
;board = pnucleo_wb55rg
; ============================================================
; Board configuration Nucleo-32
; ============================================================
;board = nucleo_f031k6
;board = nucleo_l031k6
;board = nucleo_l412kb
;board = nucleo_l432lc
;board = nucleo_f303k8
;board = nucleo_g431kb
; ============================================================
; Board configuration Discovery Boards
; ============================================================
;board = disco_f030r8
;board = disco_f072rb
;board = disco_f030r8
;board = disco_f100rb
;board = disco_f407vg
;board = disco_f413zh
;board = disco_f746ng
;board = disco_g0316
;board = disco_l475vg_iot
;board = disco_f072cz-lrwan1
; ============================================================
; Board configuration STM32MP1 Boards
; ============================================================
;board = stm32mp157a-dk1
;board = stm32mp157c-dk2
; ============================================================
; Board configuration Generic Boards
; ============================================================
;board = bluepill_f103c6
;board = bluepill_f103c8
;board = blackpill_f103c8
;board = stm32f103cx
;board = stm32f103rx
;board = stm32f103tx
;board = stm32f103vx
;board = stm32f103zx
;board = stm32f103zet6
;board = maplemini_f103cb
;board = blackpill_f303cc
;board = black_f407ve
;board = black_f407vg
;board = black_f407ze
;board = black_f407zg
;board = blue_f407ve_mini
;board = blackpill_f401cc
;board = blackpill_f411ce
;board = coreboard_f401rc
;board = feather_f405
; ============================================================
; Board configuration Many more Boards to be filled
; ============================================================

View File

@ -1,5 +1,5 @@
/****************************************************************************************************************************
AsyncHTTPRequest_Impl_Generic.h - Dead simple AsyncHTTPRequest for ESP8266, ESP32 and currently STM32 with built-in LAN8742A Ethernet
AsyncHTTPRequest.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)
@ -23,37 +23,97 @@
------- ----------- ---------- -----------
1.0.0 K Hoang 14/09/2020 Initial coding to add support to STM32 using built-in Ethernet (Nucleo-144, DISCOVERY, etc).
*****************************************************************************************************************************/
#pragma once
#ifndef AsyncHTTPRequest_Impl_Generic_h
#define AsyncHTTPRequest_Impl_Generic_h
#include "AsyncHTTPRequest.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)
namespace {
#if ESP32
class LockHelper
{
#ifdef ESP32
threadLock = xSemaphoreCreateRecursiveMutex();
public:
LockHelper(QueueHandle_t _xMutex) :
xMutex{_xMutex}
{
xSemaphoreTakeRecursive(xMutex, portMAX_DELAY);
}
~LockHelper()
{
xSemaphoreGiveRecursive(xMutex);
}
private:
const QueueHandle_t xMutex;
};
#define _lock LockHelper lock{this->threadLock}
#define _unlock
#elif ESP8266
#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) )
#define _lock
#define _unlock
#endif
}
std::optional<URL> parseURL(const String &url)
{
int hostBeg = 0;
URL _URL;
_URL.scheme = "HTTP://";
if (url.substring(0, 7).equalsIgnoreCase("HTTP://"))
{
hostBeg += 7;
}
else if (url.substring(0, 8).equalsIgnoreCase("HTTPS://"))
return {};
int pathBeg = url.indexOf('/', hostBeg);
if (pathBeg < 0)
return {};
int hostEnd = pathBeg;
int portBeg = url.indexOf(':', hostBeg);
if (portBeg > 0 && portBeg < pathBeg)
{
_URL.port = url.substring(portBeg + 1, pathBeg).toInt();
hostEnd = portBeg;
}
else
_URL.port = 80;
_URL.host = url.substring(hostBeg, hostEnd);
int queryBeg = url.indexOf('?');
if (queryBeg < 0)
queryBeg = url.length();
_URL.path = url.substring(pathBeg, queryBeg);
_URL.query = url.substring(queryBeg);
AHTTP_LOGDEBUG2("_parseURL(): scheme+host", _URL.scheme, _URL.host);
AHTTP_LOGDEBUG3("_parseURL(): port+path+query", _URL.port, _URL.path, _URL.query);
return _URL;
}
//**************************************************************************************************************
AsyncHTTPRequest::~AsyncHTTPRequest()
{
if (_client)
_client->close(true);
delete _URL;
delete _headers;
delete _request;
delete _response;
delete _chunks;
delete[] _connectedHost;
#ifdef ESP32
vSemaphoreDelete(threadLock);
@ -73,73 +133,67 @@ void AsyncHTTPRequest::setDebug(bool debug)
}
//**************************************************************************************************************
bool AsyncHTTPRequest::debug()
bool AsyncHTTPRequest::debug() const
{
return (_debug);
return _debug;
}
//**************************************************************************************************************
bool AsyncHTTPRequest::open(const char* method, const char* URL)
bool AsyncHTTPRequest::open(const URL &url, HTTPmethod method)
{
AHTTP_LOGDEBUG3("open(", method, ", url =", URL);
AHTTP_LOGDEBUG3("open(url =", url.toString(), ", method =", toString<String>(method));
if (_readyState != readyStateUnsent && _readyState != readyStateDone)
switch (_readyState)
{
case ReadyState::Idle:
case ReadyState::Unsent:
case ReadyState::Done:
break;
default:
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;
_readyState = ReadyState::Unsent;
_HTTPmethod = method;
if (strcmp(method, "GET") == 0)
{
_HTTPmethod = HTTPmethodGET;
}
else if (strcmp(method, "POST") == 0)
{
_HTTPmethod = HTTPmethodPOST;
}
else
return false;
_URL = url;
if (!_parseURL(URL))
{
return false;
}
if ( _client && _client->connected() && (strcmp(_URL->host, _connectedHost) != 0 || _URL->port != _connectedPort))
if ( _client && _client->connected() && (_URL.host != _connectedHost || _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;
_addHeader("host", _URL.host + ':' + _URL.port);
_lastActivity = millis();
return _connect();
}
//**************************************************************************************************************
void AsyncHTTPRequest::onReadyStateChange(readyStateChangeCB cb, void* arg)
void AsyncHTTPRequest::onReadyStateChange(readyStateChangeCB cb, callback_arg_t arg)
{
_readyStateChangeCB = cb;
_readyStateChangeCBarg = arg;
}
//**************************************************************************************************************
void AsyncHTTPRequest::onReadyStateChangeArg(callback_arg_t arg)
{
_readyStateChangeCBarg = arg;
}
//**************************************************************************************************************
void AsyncHTTPRequest::setTimeout(int seconds)
{
@ -155,7 +209,7 @@ bool AsyncHTTPRequest::send()
_lock;
if ( ! _buildRequest())
if (!_buildRequest())
return false;
_send();
@ -265,13 +319,13 @@ void AsyncHTTPRequest::abort()
_unlock;
}
//**************************************************************************************************************
reqStates AsyncHTTPRequest::readyState()
ReadyState AsyncHTTPRequest::readyState() const
{
return _readyState;
}
//**************************************************************************************************************
int AsyncHTTPRequest::responseHTTPcode()
int AsyncHTTPRequest::responseHTTPcode() const
{
return _HTTPcode;
}
@ -283,7 +337,7 @@ String AsyncHTTPRequest::responseText()
_lock;
if ( ! _response || _readyState < readyStateLoading || ! available())
if ( ! _response || _readyState < ReadyState::Loading || ! available())
{
AHTTP_LOGDEBUG("responseText() no data");
@ -299,7 +353,7 @@ String AsyncHTTPRequest::responseText()
{
AHTTP_LOGDEBUG("responseText() no buffer");
_HTTPcode = HTTPCODE_TOO_LESS_RAM;
_HTTPcode = HttpCode::TOO_LESS_RAM;
_client->abort();
_unlock;
@ -319,7 +373,7 @@ String AsyncHTTPRequest::responseText()
//**************************************************************************************************************
size_t AsyncHTTPRequest::responseRead(uint8_t* buf, size_t len)
{
if ( ! _response || _readyState < readyStateLoading || ! available())
if ( ! _response || _readyState < ReadyState::Loading || ! available())
{
//DEBUG_HTTP("responseRead() no data\r\n");
AHTTP_LOGDEBUG("responseRead() no data");
@ -340,9 +394,9 @@ size_t AsyncHTTPRequest::responseRead(uint8_t* buf, size_t len)
}
//**************************************************************************************************************
size_t AsyncHTTPRequest::available()
size_t AsyncHTTPRequest::available() const
{
if (_readyState < readyStateLoading)
if (_readyState < ReadyState::Loading)
return 0;
if (_chunked && (_contentLength - _contentRead) < _response->available())
@ -354,9 +408,9 @@ size_t AsyncHTTPRequest::available()
}
//**************************************************************************************************************
size_t AsyncHTTPRequest::responseLength()
size_t AsyncHTTPRequest::responseLength() const
{
if (_readyState < readyStateLoading)
if (_readyState < ReadyState::Loading)
return 0;
return _contentLength;
@ -372,12 +426,12 @@ void AsyncHTTPRequest::onData(onDataCB cb, void* arg)
}
//**************************************************************************************************************
uint32_t AsyncHTTPRequest::elapsedTime()
uint32_t AsyncHTTPRequest::elapsedTime() const
{
if (_readyState <= readyStateOpened)
if (_readyState <= ReadyState::Opened)
return 0;
if (_readyState != readyStateDone)
if (_readyState != ReadyState::Done)
{
return millis() - _requestStartTime;
}
@ -386,7 +440,7 @@ uint32_t AsyncHTTPRequest::elapsedTime()
}
//**************************************************************************************************************
String AsyncHTTPRequest::version()
String AsyncHTTPRequest::version() const
{
return String(AsyncHTTPRequest_Generic_version);
}
@ -400,64 +454,6 @@ String AsyncHTTPRequest::version()
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()
{
@ -468,11 +464,8 @@ bool AsyncHTTPRequest::_connect()
_client = new AsyncClient();
}
delete[] _connectedHost;
_connectedHost = new char[strlen(_URL->host) + 1];
strcpy(_connectedHost, _URL->host);
_connectedPort = _URL->port;
_connectedHost = _URL.host;
_connectedPort = _URL.port;
_client->onConnect([](void *obj, AsyncClient * client)
{
@ -496,12 +489,12 @@ bool AsyncHTTPRequest::_connect()
if ( ! _client->connected())
{
if ( ! _client->connect(_URL->host, _URL->port))
if ( ! _client->connect(_URL.host.c_str(), _URL.port))
{
AHTTP_LOGDEBUG3("client.connect failed:", _URL->host, ",", _URL->port);
AHTTP_LOGDEBUG3("client.connect failed:", _URL.host, ",", _URL.port);
_HTTPcode = HTTPCODE_NOT_CONNECTED;
_setReadyState(readyStateDone);
_HTTPcode = HttpCode::NOT_CONNECTED;
_setReadyState(ReadyState::Done);
return false;
}
@ -525,14 +518,12 @@ bool AsyncHTTPRequest::_buildRequest()
if ( ! _request)
_request = new xbuf;
_request->write(_HTTPmethod == HTTPmethodGET ? "GET " : "POST ");
_request->write(_URL->path);
_request->write(_URL->query);
_request->write(toString<String>(_HTTPmethod));
_request->write(' ');
_request->write(_URL.path);
_request->write(_URL.query);
_request->write(" HTTP/1.1\r\n");
delete _URL;
_URL = nullptr;
header* hdr = _headers;
while (hdr)
@ -546,6 +537,7 @@ bool AsyncHTTPRequest::_buildRequest()
delete _headers;
_headers = nullptr;
_request->write("\r\n");
return true;
@ -600,13 +592,13 @@ size_t AsyncHTTPRequest::_send()
}
//**************************************************************************************************************
void AsyncHTTPRequest::_setReadyState(reqStates newState)
void AsyncHTTPRequest::_setReadyState(ReadyState readyState)
{
if (_readyState != newState)
if (_readyState != readyState)
{
_readyState = newState;
_readyState = readyState;
AHTTP_LOGDEBUG1("_setReadyState :", _readyState);
AHTTP_LOGDEBUG1("_setReadyState :", int(_readyState));
if (_readyStateChangeCB)
{
@ -639,9 +631,9 @@ void AsyncHTTPRequest::_processChunks()
if (chunkLength == 0)
{
char* connectionHdr = respHeaderValue("connection");
const auto connectionHdr = respHeaderValue("connection");
if (connectionHdr && (strcasecmp_P(connectionHdr, PSTR("disconnect")) == 0))
if (connectionHdr && connectionHdr == "disconnect")
{
AHTTP_LOGDEBUG("*all chunks received - closing TCP");
@ -655,7 +647,7 @@ void AsyncHTTPRequest::_processChunks()
_requestEndTime = millis();
_lastActivity = 0;
_timeout = 0;
_setReadyState(readyStateDone);
_setReadyState(ReadyState::Done);
return;
}
@ -678,7 +670,7 @@ void AsyncHTTPRequest::_onConnect(AsyncClient* client)
_lock;
_client = client;
_setReadyState(readyStateOpened);
_setReadyState(ReadyState::Opened);
_response = new xbuf;
_contentLength = 0;
_contentRead = 0;
@ -711,7 +703,7 @@ void AsyncHTTPRequest::_onPoll(AsyncClient* client)
if (_timeout && (millis() - _lastActivity) > (_timeout * 1000))
{
_client->close();
_HTTPcode = HTTPCODE_TIMEOUT;
_HTTPcode = HttpCode::TIMEOUT;
AHTTP_LOGDEBUG("_onPoll timeout");
}
@ -739,26 +731,25 @@ void AsyncHTTPRequest::_onDisconnect(AsyncClient* client)
_lock;
if (_readyState < readyStateOpened)
if (_readyState < ReadyState::Opened)
{
_HTTPcode = HTTPCODE_NOT_CONNECTED;
_HTTPcode = HttpCode::NOT_CONNECTED;
}
else if (_HTTPcode > 0 &&
(_readyState < readyStateHdrsRecvd || (_contentRead + _response->available()) < _contentLength))
(_readyState < ReadyState::HdrsRecvd || (_contentRead + _response->available()) < _contentLength))
{
_HTTPcode = HTTPCODE_CONNECTION_LOST;
_HTTPcode = HttpCode::CONNECTION_LOST;
}
delete _client;
_client = nullptr;
delete[] _connectedHost;
_connectedHost = nullptr;
_connectedHost = String{};
_connectedPort = -1;
_requestEndTime = millis();
_lastActivity = 0;
_setReadyState(readyStateDone);
_setReadyState(ReadyState::Done);
_unlock;
}
@ -781,24 +772,24 @@ void AsyncHTTPRequest::_onData(void* Vbuf, size_t len)
}
// if headers not complete, collect them. If still not complete, just return.
if (_readyState == readyStateOpened)
if (_readyState == ReadyState::Opened)
{
if ( ! _collectHeaders())
return;
}
// If there's data in the buffer and not Done, advance readyState to Loading.
if (_response->available() && _readyState != readyStateDone)
if (_response->available() && _readyState != ReadyState::Done)
{
_setReadyState(readyStateLoading);
_setReadyState(ReadyState::Loading);
}
// If not chunked and all data read, close it up.
if ( ! _chunked && (_response->available() + _contentRead) >= _contentLength)
{
char* connectionHdr = respHeaderValue("connection");
const auto connectionHdr = respHeaderValue("connection");
if (connectionHdr && (strcasecmp_P(connectionHdr, PSTR("disconnect")) == 0))
if (connectionHdr && connectionHdr == "disconnect")
{
AHTTP_LOGDEBUG("*all data received - closing TCP");
@ -812,7 +803,7 @@ void AsyncHTTPRequest::_onData(void* Vbuf, size_t len)
_requestEndTime = millis();
_lastActivity = 0;
_timeout = 0;
_setReadyState(readyStateDone);
_setReadyState(ReadyState::Done);
}
// If onData callback requested, do so.
@ -844,7 +835,7 @@ bool AsyncHTTPRequest::_collectHeaders()
// If empty line, all headers are in, advance readyState.
if (headerLine.length() == 2)
{
_setReadyState(readyStateHdrsRecvd);
_setReadyState(ReadyState::HdrsRecvd);
}
// If line is HTTP header, capture HTTPcode.
else if (headerLine.substring(0, 7) == "HTTP/1.")
@ -865,20 +856,20 @@ bool AsyncHTTPRequest::_collectHeaders()
_addHeader(name.c_str(), value.c_str());
}
}
} while (_readyState == readyStateOpened);
} while (_readyState == ReadyState::Opened);
// If content-Length header, set _contentLength
header *hdr = _getHeader("Content-Length");
if (hdr)
{
_contentLength = strtol(hdr->value, nullptr, 10);
_contentLength = hdr->value.toInt();
}
// 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)
if (hdr && hdr->value == "chunked")
{
AHTTP_LOGDEBUG("*transfer-encoding: chunked");
@ -905,7 +896,7 @@ bool AsyncHTTPRequest::_collectHeaders()
//**************************************************************************************************************
void AsyncHTTPRequest::setReqHeader(const char* name, const char* value)
{
if (_readyState <= readyStateOpened && _headers)
if (_readyState <= ReadyState::Opened && _headers)
{
_addHeader(name, value);
}
@ -914,7 +905,7 @@ void AsyncHTTPRequest::setReqHeader(const char* name, const char* value)
//**************************************************************************************************************
void AsyncHTTPRequest::setReqHeader(const char* name, int32_t value)
{
if (_readyState <= readyStateOpened && _headers)
if (_readyState <= ReadyState::Opened && _headers)
{
setReqHeader(name, String(value).c_str());
}
@ -925,7 +916,7 @@ void AsyncHTTPRequest::setReqHeader(const char* name, int32_t value)
//**************************************************************************************************************
void AsyncHTTPRequest::setReqHeader(const char* name, const __FlashStringHelper* value)
{
if (_readyState <= readyStateOpened && _headers)
if (_readyState <= ReadyState::Opened && _headers)
{
char* _value = _charstar(value);
_addHeader(name, _value);
@ -936,7 +927,7 @@ void AsyncHTTPRequest::setReqHeader(const char* name, const __FlashStringHelper*
//**************************************************************************************************************
void AsyncHTTPRequest::setReqHeader(const __FlashStringHelper *name, const char* value)
{
if (_readyState <= readyStateOpened && _headers)
if (_readyState <= ReadyState::Opened && _headers)
{
char* _name = _charstar(name);
_addHeader(_name, value);
@ -947,7 +938,7 @@ void AsyncHTTPRequest::setReqHeader(const __FlashStringHelper *name, const char*
//**************************************************************************************************************
void AsyncHTTPRequest::setReqHeader(const __FlashStringHelper *name, const __FlashStringHelper* value)
{
if (_readyState <= readyStateOpened && _headers)
if (_readyState <= ReadyState::Opened && _headers)
{
char* _name = _charstar(name);
char* _value = _charstar(value);
@ -960,7 +951,7 @@ void AsyncHTTPRequest::setReqHeader(const __FlashStringHelper *name, const __Fla
//**************************************************************************************************************
void AsyncHTTPRequest::setReqHeader(const __FlashStringHelper *name, int32_t value)
{
if (_readyState <= readyStateOpened && _headers)
if (_readyState <= ReadyState::Opened && _headers)
{
char* _name = _charstar(name);
setReqHeader(_name, String(value).c_str());
@ -973,7 +964,7 @@ void AsyncHTTPRequest::setReqHeader(const __FlashStringHelper *name, int32_t val
//**************************************************************************************************************
int AsyncHTTPRequest::respHeaderCount()
{
if (_readyState < readyStateHdrsRecvd)
if (_readyState < ReadyState::HdrsRecvd)
return 0;
int count = 0;
@ -989,76 +980,69 @@ int AsyncHTTPRequest::respHeaderCount()
}
//**************************************************************************************************************
char* AsyncHTTPRequest::respHeaderName(int ndx)
String AsyncHTTPRequest::respHeaderName(int ndx)
{
if (_readyState < readyStateHdrsRecvd)
return nullptr;
if (_readyState < ReadyState::HdrsRecvd)
return {};
header* hdr = _getHeader(ndx);
if ( ! hdr)
return nullptr;
return {};
return hdr->name;
}
//**************************************************************************************************************
char* AsyncHTTPRequest::respHeaderValue(const char* name)
String AsyncHTTPRequest::respHeaderValue(const String &name)
{
if (_readyState < readyStateHdrsRecvd)
return nullptr;
if (_readyState < ReadyState::HdrsRecvd)
return {};
header* hdr = _getHeader(name);
if ( ! hdr)
return nullptr;
return {};
return hdr->value;
}
//**************************************************************************************************************
char* AsyncHTTPRequest::respHeaderValue(int ndx)
String AsyncHTTPRequest::respHeaderValue(int ndx)
{
if (_readyState < readyStateHdrsRecvd)
return nullptr;
if (_readyState < ReadyState::HdrsRecvd)
return {};
header* hdr = _getHeader(ndx);
if ( ! hdr)
return nullptr;
return {};
return hdr->value;
}
//**************************************************************************************************************
bool AsyncHTTPRequest::respHeaderExists(const char* name)
bool AsyncHTTPRequest::respHeaderExists(const String &name)
{
if (_readyState < readyStateHdrsRecvd)
if (_readyState < ReadyState::HdrsRecvd)
return false;
header* hdr = _getHeader(name);
if ( ! hdr)
return false;
return true;
return _getHeader(name);
}
#if (ESP32 || ESP8266)
//**************************************************************************************************************
char* AsyncHTTPRequest::respHeaderValue(const __FlashStringHelper *name)
String AsyncHTTPRequest::respHeaderValue(const __FlashStringHelper *name)
{
if (_readyState < readyStateHdrsRecvd)
return nullptr;
if (_readyState < ReadyState::HdrsRecvd)
return {};
char* _name = _charstar(name);
header* hdr = _getHeader(_name);
delete[] _name;
header* hdr = _getHeader(name);
if ( ! hdr)
return nullptr;
return {};
return hdr->value;
}
@ -1066,17 +1050,10 @@ char* AsyncHTTPRequest::respHeaderValue(const __FlashStringHelper *name)
//**************************************************************************************************************
bool AsyncHTTPRequest::respHeaderExists(const __FlashStringHelper *name)
{
if (_readyState < readyStateHdrsRecvd)
if (_readyState < ReadyState::HdrsRecvd)
return false;
char* _name = _charstar(name);
header* hdr = _getHeader(_name);
delete[] _name;
if ( ! hdr)
return false;
return true;
return _getHeader(name);
}
#endif
@ -1104,14 +1081,14 @@ String AsyncHTTPRequest::headers()
}
//**************************************************************************************************************
AsyncHTTPRequest::header* AsyncHTTPRequest::_addHeader(const char* name, const char* value)
AsyncHTTPRequest::header* AsyncHTTPRequest::_addHeader(const String &name, const String &value)
{
_lock;
header* hdr = (header*) &_headers;
while (hdr->next)
{
if (strcasecmp(name, hdr->next->name) == 0)
if (name.equalsIgnoreCase(hdr->next->name))
{
header* oldHdr = hdr->next;
hdr->next = hdr->next->next;
@ -1125,24 +1102,22 @@ 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);
hdr->next->name = name;
hdr->next->value = value;
_unlock;
return hdr->next;
}
//**************************************************************************************************************
AsyncHTTPRequest::header* AsyncHTTPRequest::_getHeader(const char* name)
AsyncHTTPRequest::header* AsyncHTTPRequest::_getHeader(const String &name)
{
_lock;
header* hdr = _headers;
while (hdr)
{
if (strcasecmp(name, hdr->name) == 0)
if (name.equalsIgnoreCase(hdr->name))
break;
hdr = hdr->next;
@ -1187,5 +1162,3 @@ char* AsyncHTTPRequest::_charstar(const __FlashStringHelper * str)
}
#endif
#endif // AsyncHTTPRequest_Impl_Generic_h

318
src/AsyncHTTPRequest.h Normal file
View File

@ -0,0 +1,318 @@
/****************************************************************************************************************************
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> <Bob Lemaire, IoTaWatt, Inc.>
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 <https://www.gnu.org/licenses/>.
Version: 1.0.0
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).
*****************************************************************************************************************************/
#pragma once
#define AsyncHTTPRequest_Generic_version "1.0.0"
#include <Arduino.h>
#include <WString.h>
#include <optional>
#include "AsyncHTTPRequest_Debug.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 <AsyncTCP.h>
#elif ESP8266
#include <ESPAsyncTCP.h>
#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"
#endif
#include <pgmspace.h>
#include <utility/xbuf.h>
#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
namespace HttpCode
{
enum
{
CONNECTION_REFUSED = -1,
SEND_HEADER_FAILED = -2,
SEND_PAYLOAD_FAILED = -3,
NOT_CONNECTED = -4,
CONNECTION_LOST = -5,
NO_STREAM = -6,
NO_HTTP_SERVER = -7,
TOO_LESS_RAM = -8,
ENCODING = -9,
STREAM_WRITE = -10,
TIMEOUT = -11,
};
inline String toString(int code)
{
switch (code)
{
case CONNECTION_REFUSED: return "CONNECTION_REFUSED";
case SEND_HEADER_FAILED: return "SEND_HEADER_FAILED";
case SEND_PAYLOAD_FAILED: return "SEND_PAYLOAD_FAILED";
case NOT_CONNECTED: return "NOT_CONNECTED";
case CONNECTION_LOST: return "CONNECTION_LOST";
case NO_STREAM: return "NO_STREAM";
case NO_HTTP_SERVER: return "NO_HTTP_SERVER";
case TOO_LESS_RAM: return "TOO_LESS_RAM";
case ENCODING: return "ENCODING";
case STREAM_WRITE: return "STREAM_WRITE";
case TIMEOUT: return "TIMEOUT";
}
return String{code};
}
}
enum class ReadyState
{
Idle, // Client created, open not yet called
Unsent, // open() has been called, not connected
Opened, // open() has been called, connected
HdrsRecvd, // send() called, response headers available
Loading, // receiving, partial data available
Done // Request complete, all data available.
};
namespace {
//! Helper utility to convert a ReadyState into a string
//! String library agnostic, can be used with std::string or Arduino's WString
template<typename T>
T toString(ReadyState state)
{
switch (state)
{
case ReadyState::Idle: return "Idle";
case ReadyState::Unsent: return "Unsent";
case ReadyState::Opened: return "Opened";
case ReadyState::HdrsRecvd: return "HdrsRecvd";
case ReadyState::Loading: return "Loading";
case ReadyState::Done: return "Done";
}
return T{"Unknown ReadyState("} + int(state) + ')';
}
}
enum class HTTPmethod
{
GET,
POST
};
namespace {
//! Helper utility to convert a HTTPmethod into a string
//! String library agnostic, can be used with std::string or Arduino's WString
template<typename T>
T toString(HTTPmethod method)
{
switch (method)
{
case HTTPmethod::GET: return "GET";
case HTTPmethod::POST: return "POST";
}
return T{"Unknown HTTPmethod("} + int(method) + ')';
}
}
struct URL
{
String scheme;
String user;
String pwd;
String host;
int port;
String path;
String query;
String fragment;
String toString() const
{
return scheme + user + pwd + host + ':' + port + path + query + fragment;
}
};
std::optional<URL> parseURL(const String &url);
class AsyncHTTPRequest
{
using callback_arg_t = void*;
struct header
{
header* next{};
String name;
String value;
~header()
{
delete next;
}
};
using readyStateChangeCB = std::function<void(callback_arg_t, AsyncHTTPRequest*, ReadyState readyState)>;
using onDataCB = std::function<void(void*, AsyncHTTPRequest*, size_t available)>;
public:
~AsyncHTTPRequest();
//External functions in typical order of use:
//__________________________________________________________________________________________________________*/
void setDebug(bool); // Turn debug message on/off
bool debug() const; // is debug on or off?
bool open(const URL &url, HTTPmethod method = HTTPmethod::GET); // Initiate a request
void onReadyStateChange(readyStateChangeCB, callback_arg_t arg = 0); // Optional event handler for ready state change
void onReadyStateChangeArg(callback_arg_t arg = 0); // set event handlers arg
// or you can simply poll readyState()
void setTimeout(int seconds); // 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
ReadyState readyState() const; // Return the ready state
int respHeaderCount(); // Retrieve count of response headers
String respHeaderName(int index); // Return header name by index
String respHeaderValue(int index); // Return header value by index
String respHeaderValue(const String &name); // Return header value by name
bool respHeaderExists(const String &name); // Does header exist by name?
#if (ESP32 || ESP8266)
String respHeaderValue(const __FlashStringHelper *name);
bool respHeaderExists(const __FlashStringHelper *name);
#endif
const URL &url() const { return _URL; }
String headers(); // Return all headers as String
void onData(onDataCB, void* arg = 0); // Notify when min data is available
size_t available() const; // response available
size_t responseLength() const; // indicated response length or sum of chunks to date
int responseHTTPcode() const; // 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() const; // Elapsed time of in progress transaction or last completed (ms)
String version() const; // Version of AsyncHTTPRequest
//___________________________________________________________________________________________________________________________________
private:
HTTPmethod _HTTPmethod;
ReadyState _readyState{ReadyState::Idle};
int16_t _HTTPcode{0}; // HTTP response code or (negative) exception code
bool _chunked{false}; // Processing chunked response
bool _debug{DEBUG_IOTA_HTTP_SET}; // Debug state
uint32_t _timeout{DEFAULT_RX_TIMEOUT}; // Default or user overide RxTimeout in seconds
uint32_t _lastActivity{0}; // Time of last activity
uint32_t _requestStartTime{0}; // Time last open() issued
uint32_t _requestEndTime{0}; // Time of last disconnect
URL _URL{}; // -> URL data structure
String _connectedHost; // Host when connected
int _connectedPort{-1}; // Port when connected
AsyncClient* _client{nullptr}; // ESPAsyncTCP AsyncClient instance
size_t _contentLength{0}; // content-length header value or sum of chunk headers
size_t _contentRead{0}; // number of bytes retrieved by user since last open()
readyStateChangeCB _readyStateChangeCB{}; // optional callback for readyState change
callback_arg_t _readyStateChangeCBarg{}; // associated user argument
onDataCB _onDataCB{nullptr}; // optional callback when data received
void* _onDataCBarg{nullptr}; // associated user argument
#ifdef ESP32
SemaphoreHandle_t threadLock{xSemaphoreCreateRecursiveMutex()};
#endif
// request and response String buffers and header list (same queue for request and response).
xbuf* _request{nullptr}; // Tx data buffer
xbuf* _response{nullptr}; // Rx data buffer for headers
xbuf* _chunks{nullptr}; // First stage for chunked response
header* _headers{nullptr}; // request or (readyState > readyStateHdrsRcvd) response headers
// Protected functions
header* _addHeader(const String &name, const String &value);
header* _getHeader(const String &name);
header* _getHeader(int idx);
bool _buildRequest();
void _processChunks();
bool _connect();
size_t _send();
void _setReadyState(ReadyState readyState);
#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();
};

View File

@ -24,8 +24,7 @@
1.0.0 K Hoang 14/09/2020 Initial coding to add support to STM32 using built-in Ethernet (Nucleo-144, DISCOVERY, etc).
*****************************************************************************************************************************/
#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
@ -67,5 +66,3 @@
#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); }
#endif // AsyncHTTPRequest_Debug_STM32_H

View File

@ -1,269 +0,0 @@
/****************************************************************************************************************************
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> <Bob Lemaire, IoTaWatt, Inc.>
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 <https://www.gnu.org/licenses/>.
Version: 1.0.0
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).
*****************************************************************************************************************************/
#ifndef AsyncHTTPRequest_Generic_h
#define AsyncHTTPRequest_Generic_h
#define AsyncHTTPRequest_Generic_version "1.0.0"
#include <Arduino.h>
#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 <AsyncTCP.h>
#define _lock xSemaphoreTakeRecursive(threadLock,portMAX_DELAY)
#define _unlock xSemaphoreGiveRecursive(threadLock)
#elif ESP8266
#include <ESPAsyncTCP.h>
#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 <pgmspace.h>
#include <utility/xbuf.h>
#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<void(void*, AsyncHTTPRequest*, int readyState)> readyStateChangeCB;
typedef std::function<void(void*, AsyncHTTPRequest*, size_t available)> 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"
#endif // AsyncHTTPRequest_Generic_h

View File

@ -27,7 +27,7 @@
#ifndef xbuf_Impl_h
#define xbuf_Impl_h
//#include "utility/xbuf.h"
#include "utility/xbuf.h"
xbuf::xbuf(const uint16_t segSize) : _head(nullptr), _tail(nullptr), _used(0), _free(0), _offset(0)
{

View File

@ -148,6 +148,4 @@ class xbuf: public Print
};
#include "utility/xbuf_Impl.h"
#endif // xbuf_h