forked from khoih-prog/AsyncHTTPRequest_Generic
Improved even more
This commit is contained in:
@ -47,6 +47,10 @@
|
|||||||
{
|
{
|
||||||
"name": "STM32 AsyncTCP",
|
"name": "STM32 AsyncTCP",
|
||||||
"platforms": "ststm32"
|
"platforms": "ststm32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "https://github.com/0xFEEDC0DE64/optional.git",
|
||||||
|
"platforms": ["espressif8266", "espressif32"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -57,16 +57,50 @@ private:
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
//**************************************************************************************************************
|
|
||||||
AsyncHTTPRequest::AsyncHTTPRequest(): _readyState(ReadyState::Idle), _HTTPcode(0), _chunked(false), _debug(DEBUG_IOTA_HTTP_SET)
|
tl::optional<URL> parseURL(const String &url)
|
||||||
, _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{}, _onDataCB(nullptr), _onDataCBarg(nullptr)
|
|
||||||
, _request(nullptr), _response(nullptr), _chunks(nullptr), _headers(nullptr)
|
|
||||||
{
|
{
|
||||||
#ifdef ESP32
|
int hostBeg = 0;
|
||||||
threadLock = xSemaphoreCreateRecursiveMutex();
|
URL _URL;
|
||||||
#endif
|
_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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -76,12 +110,10 @@ AsyncHTTPRequest::~AsyncHTTPRequest()
|
|||||||
if (_client)
|
if (_client)
|
||||||
_client->close(true);
|
_client->close(true);
|
||||||
|
|
||||||
delete _URL;
|
|
||||||
delete _headers;
|
delete _headers;
|
||||||
delete _request;
|
delete _request;
|
||||||
delete _response;
|
delete _response;
|
||||||
delete _chunks;
|
delete _chunks;
|
||||||
delete[] _connectedHost;
|
|
||||||
|
|
||||||
#ifdef ESP32
|
#ifdef ESP32
|
||||||
vSemaphoreDelete(threadLock);
|
vSemaphoreDelete(threadLock);
|
||||||
@ -107,24 +139,27 @@ bool AsyncHTTPRequest::debug() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
//**************************************************************************************************************
|
//**************************************************************************************************************
|
||||||
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 != ReadyState::Idle && _readyState != ReadyState::Unsent && _readyState != ReadyState::Done)
|
switch (_readyState)
|
||||||
{
|
{
|
||||||
|
case ReadyState::Idle:
|
||||||
|
case ReadyState::Unsent:
|
||||||
|
case ReadyState::Done:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
_requestStartTime = millis();
|
_requestStartTime = millis();
|
||||||
|
|
||||||
delete _URL;
|
|
||||||
delete _headers;
|
delete _headers;
|
||||||
delete _request;
|
delete _request;
|
||||||
delete _response;
|
delete _response;
|
||||||
delete _chunks;
|
delete _chunks;
|
||||||
|
|
||||||
_URL = nullptr;
|
|
||||||
_headers = nullptr;
|
_headers = nullptr;
|
||||||
_response = nullptr;
|
_response = nullptr;
|
||||||
_request = nullptr;
|
_request = nullptr;
|
||||||
@ -132,30 +167,16 @@ bool AsyncHTTPRequest::open(const char* method, const char* URL)
|
|||||||
_chunked = false;
|
_chunked = false;
|
||||||
_contentRead = 0;
|
_contentRead = 0;
|
||||||
_readyState = ReadyState::Unsent;
|
_readyState = ReadyState::Unsent;
|
||||||
|
_HTTPmethod = method;
|
||||||
|
|
||||||
if (strcmp(method, "GET") == 0)
|
_URL = url;
|
||||||
{
|
|
||||||
_HTTPmethod = HTTPmethodGET;
|
|
||||||
}
|
|
||||||
else if (strcmp(method, "POST") == 0)
|
|
||||||
{
|
|
||||||
_HTTPmethod = HTTPmethodPOST;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!_parseURL(URL))
|
if ( _client && _client->connected() && (_URL.host != _connectedHost || _URL.port != _connectedPort))
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if ( _client && _client->connected() && (strcmp(_URL->host, _connectedHost) != 0 || _URL->port != _connectedPort))
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
char hostName[strlen(_URL->host) + 10];
|
_addHeader("host", _URL.host + ':' + _URL.port);
|
||||||
sprintf(hostName, "%s:%d", _URL->host, _URL->port);
|
|
||||||
_addHeader("host", hostName);
|
|
||||||
_lastActivity = millis();
|
_lastActivity = millis();
|
||||||
|
|
||||||
return _connect();
|
return _connect();
|
||||||
@ -188,7 +209,7 @@ bool AsyncHTTPRequest::send()
|
|||||||
|
|
||||||
_lock;
|
_lock;
|
||||||
|
|
||||||
if ( ! _buildRequest())
|
if (!_buildRequest())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
_send();
|
_send();
|
||||||
@ -433,64 +454,6 @@ String AsyncHTTPRequest::version() const
|
|||||||
P R R OOO T EEEEE CCC T EEEEE DDDD
|
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()
|
bool AsyncHTTPRequest::_connect()
|
||||||
{
|
{
|
||||||
@ -501,11 +464,8 @@ bool AsyncHTTPRequest::_connect()
|
|||||||
_client = new AsyncClient();
|
_client = new AsyncClient();
|
||||||
}
|
}
|
||||||
|
|
||||||
delete[] _connectedHost;
|
_connectedHost = _URL.host;
|
||||||
|
_connectedPort = _URL.port;
|
||||||
_connectedHost = new char[strlen(_URL->host) + 1];
|
|
||||||
strcpy(_connectedHost, _URL->host);
|
|
||||||
_connectedPort = _URL->port;
|
|
||||||
|
|
||||||
_client->onConnect([](void *obj, AsyncClient * client)
|
_client->onConnect([](void *obj, AsyncClient * client)
|
||||||
{
|
{
|
||||||
@ -529,9 +489,9 @@ bool AsyncHTTPRequest::_connect()
|
|||||||
|
|
||||||
if ( ! _client->connected())
|
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;
|
_HTTPcode = HTTPCODE_NOT_CONNECTED;
|
||||||
_setReadyState(ReadyState::Done);
|
_setReadyState(ReadyState::Done);
|
||||||
@ -558,14 +518,14 @@ bool AsyncHTTPRequest::_buildRequest()
|
|||||||
if ( ! _request)
|
if ( ! _request)
|
||||||
_request = new xbuf;
|
_request = new xbuf;
|
||||||
|
|
||||||
_request->write(_HTTPmethod == HTTPmethodGET ? "GET " : "POST ");
|
_request->write(toString<String>(_HTTPmethod));
|
||||||
_request->write(_URL->path);
|
_request->write(' ');
|
||||||
_request->write(_URL->query);
|
_request->write(_URL.path);
|
||||||
|
_request->write(_URL.query);
|
||||||
_request->write(" HTTP/1.1\r\n");
|
_request->write(" HTTP/1.1\r\n");
|
||||||
|
|
||||||
delete _URL;
|
_URL = {};
|
||||||
|
|
||||||
_URL = nullptr;
|
|
||||||
header* hdr = _headers;
|
header* hdr = _headers;
|
||||||
|
|
||||||
while (hdr)
|
while (hdr)
|
||||||
@ -579,6 +539,7 @@ bool AsyncHTTPRequest::_buildRequest()
|
|||||||
|
|
||||||
delete _headers;
|
delete _headers;
|
||||||
_headers = nullptr;
|
_headers = nullptr;
|
||||||
|
|
||||||
_request->write("\r\n");
|
_request->write("\r\n");
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -672,9 +633,9 @@ void AsyncHTTPRequest::_processChunks()
|
|||||||
|
|
||||||
if (chunkLength == 0)
|
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");
|
AHTTP_LOGDEBUG("*all chunks received - closing TCP");
|
||||||
|
|
||||||
@ -785,10 +746,9 @@ void AsyncHTTPRequest::_onDisconnect(AsyncClient* client)
|
|||||||
delete _client;
|
delete _client;
|
||||||
_client = nullptr;
|
_client = nullptr;
|
||||||
|
|
||||||
delete[] _connectedHost;
|
_connectedHost = String{};
|
||||||
_connectedHost = nullptr;
|
|
||||||
|
|
||||||
_connectedPort = -1;
|
_connectedPort = -1;
|
||||||
|
|
||||||
_requestEndTime = millis();
|
_requestEndTime = millis();
|
||||||
_lastActivity = 0;
|
_lastActivity = 0;
|
||||||
_setReadyState(ReadyState::Done);
|
_setReadyState(ReadyState::Done);
|
||||||
@ -829,9 +789,9 @@ void AsyncHTTPRequest::_onData(void* Vbuf, size_t len)
|
|||||||
// If not chunked and all data read, close it up.
|
// If not chunked and all data read, close it up.
|
||||||
if ( ! _chunked && (_response->available() + _contentRead) >= _contentLength)
|
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");
|
AHTTP_LOGDEBUG("*all data received - closing TCP");
|
||||||
|
|
||||||
@ -905,13 +865,13 @@ bool AsyncHTTPRequest::_collectHeaders()
|
|||||||
|
|
||||||
if (hdr)
|
if (hdr)
|
||||||
{
|
{
|
||||||
_contentLength = strtol(hdr->value, nullptr, 10);
|
_contentLength = hdr->value.toInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
// If chunked specified, try to set _contentLength to size of first chunk
|
// If chunked specified, try to set _contentLength to size of first chunk
|
||||||
hdr = _getHeader("Transfer-Encoding");
|
hdr = _getHeader("Transfer-Encoding");
|
||||||
|
|
||||||
if (hdr && strcasecmp_P(hdr->value, PSTR("chunked")) == 0)
|
if (hdr && hdr->value == "chunked")
|
||||||
{
|
{
|
||||||
AHTTP_LOGDEBUG("*transfer-encoding: chunked");
|
AHTTP_LOGDEBUG("*transfer-encoding: chunked");
|
||||||
|
|
||||||
@ -1022,76 +982,69 @@ int AsyncHTTPRequest::respHeaderCount()
|
|||||||
}
|
}
|
||||||
|
|
||||||
//**************************************************************************************************************
|
//**************************************************************************************************************
|
||||||
char* AsyncHTTPRequest::respHeaderName(int ndx)
|
String AsyncHTTPRequest::respHeaderName(int ndx)
|
||||||
{
|
{
|
||||||
if (_readyState < ReadyState::HdrsRecvd)
|
if (_readyState < ReadyState::HdrsRecvd)
|
||||||
return nullptr;
|
return {};
|
||||||
|
|
||||||
header* hdr = _getHeader(ndx);
|
header* hdr = _getHeader(ndx);
|
||||||
|
|
||||||
if ( ! hdr)
|
if ( ! hdr)
|
||||||
return nullptr;
|
return {};
|
||||||
|
|
||||||
return hdr->name;
|
return hdr->name;
|
||||||
}
|
}
|
||||||
|
|
||||||
//**************************************************************************************************************
|
//**************************************************************************************************************
|
||||||
char* AsyncHTTPRequest::respHeaderValue(const char* name)
|
String AsyncHTTPRequest::respHeaderValue(const String &name)
|
||||||
{
|
{
|
||||||
if (_readyState < ReadyState::HdrsRecvd)
|
if (_readyState < ReadyState::HdrsRecvd)
|
||||||
return nullptr;
|
return {};
|
||||||
|
|
||||||
header* hdr = _getHeader(name);
|
header* hdr = _getHeader(name);
|
||||||
|
|
||||||
if ( ! hdr)
|
if ( ! hdr)
|
||||||
return nullptr;
|
return {};
|
||||||
|
|
||||||
return hdr->value;
|
return hdr->value;
|
||||||
}
|
}
|
||||||
|
|
||||||
//**************************************************************************************************************
|
//**************************************************************************************************************
|
||||||
char* AsyncHTTPRequest::respHeaderValue(int ndx)
|
String AsyncHTTPRequest::respHeaderValue(int ndx)
|
||||||
{
|
{
|
||||||
if (_readyState < ReadyState::HdrsRecvd)
|
if (_readyState < ReadyState::HdrsRecvd)
|
||||||
return nullptr;
|
return {};
|
||||||
|
|
||||||
header* hdr = _getHeader(ndx);
|
header* hdr = _getHeader(ndx);
|
||||||
|
|
||||||
if ( ! hdr)
|
if ( ! hdr)
|
||||||
return nullptr;
|
return {};
|
||||||
|
|
||||||
return hdr->value;
|
return hdr->value;
|
||||||
}
|
}
|
||||||
|
|
||||||
//**************************************************************************************************************
|
//**************************************************************************************************************
|
||||||
bool AsyncHTTPRequest::respHeaderExists(const char* name)
|
bool AsyncHTTPRequest::respHeaderExists(const String &name)
|
||||||
{
|
{
|
||||||
if (_readyState < ReadyState::HdrsRecvd)
|
if (_readyState < ReadyState::HdrsRecvd)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
header* hdr = _getHeader(name);
|
return _getHeader(name);
|
||||||
|
|
||||||
if ( ! hdr)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#if (ESP32 || ESP8266)
|
#if (ESP32 || ESP8266)
|
||||||
|
|
||||||
//**************************************************************************************************************
|
//**************************************************************************************************************
|
||||||
char* AsyncHTTPRequest::respHeaderValue(const __FlashStringHelper *name)
|
String AsyncHTTPRequest::respHeaderValue(const __FlashStringHelper *name)
|
||||||
{
|
{
|
||||||
if (_readyState < ReadyState::HdrsRecvd)
|
if (_readyState < ReadyState::HdrsRecvd)
|
||||||
return nullptr;
|
return {};
|
||||||
|
|
||||||
char* _name = _charstar(name);
|
header* hdr = _getHeader(name);
|
||||||
header* hdr = _getHeader(_name);
|
|
||||||
delete[] _name;
|
|
||||||
|
|
||||||
if ( ! hdr)
|
if ( ! hdr)
|
||||||
return nullptr;
|
return {};
|
||||||
|
|
||||||
return hdr->value;
|
return hdr->value;
|
||||||
}
|
}
|
||||||
@ -1102,14 +1055,7 @@ bool AsyncHTTPRequest::respHeaderExists(const __FlashStringHelper *name)
|
|||||||
if (_readyState < ReadyState::HdrsRecvd)
|
if (_readyState < ReadyState::HdrsRecvd)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
char* _name = _charstar(name);
|
return _getHeader(name);
|
||||||
header* hdr = _getHeader(_name);
|
|
||||||
delete[] _name;
|
|
||||||
|
|
||||||
if ( ! hdr)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@ -1137,14 +1083,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;
|
_lock;
|
||||||
header* hdr = (header*) &_headers;
|
header* hdr = (header*) &_headers;
|
||||||
|
|
||||||
while (hdr->next)
|
while (hdr->next)
|
||||||
{
|
{
|
||||||
if (strcasecmp(name, hdr->next->name) == 0)
|
if (name.equalsIgnoreCase(hdr->next->name))
|
||||||
{
|
{
|
||||||
header* oldHdr = hdr->next;
|
header* oldHdr = hdr->next;
|
||||||
hdr->next = hdr->next->next;
|
hdr->next = hdr->next->next;
|
||||||
@ -1158,24 +1104,22 @@ AsyncHTTPRequest::header* AsyncHTTPRequest::_addHeader(const char* name, const
|
|||||||
}
|
}
|
||||||
|
|
||||||
hdr->next = new header;
|
hdr->next = new header;
|
||||||
hdr->next->name = new char[strlen(name) + 1];
|
hdr->next->name = name;
|
||||||
strcpy(hdr->next->name, name);
|
hdr->next->value = value;
|
||||||
hdr->next->value = new char[strlen(value) + 1];
|
|
||||||
strcpy(hdr->next->value, value);
|
|
||||||
_unlock;
|
_unlock;
|
||||||
|
|
||||||
return hdr->next;
|
return hdr->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
//**************************************************************************************************************
|
//**************************************************************************************************************
|
||||||
AsyncHTTPRequest::header* AsyncHTTPRequest::_getHeader(const char* name)
|
AsyncHTTPRequest::header* AsyncHTTPRequest::_getHeader(const String &name)
|
||||||
{
|
{
|
||||||
_lock;
|
_lock;
|
||||||
header* hdr = _headers;
|
header* hdr = _headers;
|
||||||
|
|
||||||
while (hdr)
|
while (hdr)
|
||||||
{
|
{
|
||||||
if (strcasecmp(name, hdr->name) == 0)
|
if (name.equalsIgnoreCase(hdr->name))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
hdr = hdr->next;
|
hdr = hdr->next;
|
||||||
|
@ -29,6 +29,9 @@
|
|||||||
#define AsyncHTTPRequest_Generic_version "1.0.0"
|
#define AsyncHTTPRequest_Generic_version "1.0.0"
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
#include <WString.h>
|
||||||
|
|
||||||
|
#include <include/tl/optional.hpp>
|
||||||
|
|
||||||
#include "AsyncHTTPRequest_Debug.h"
|
#include "AsyncHTTPRequest_Debug.h"
|
||||||
|
|
||||||
@ -88,60 +91,88 @@ enum class ReadyState
|
|||||||
Loading, // receiving, partial data available
|
Loading, // receiving, partial data available
|
||||||
Done // Request complete, all 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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
tl::optional<URL> parseURL(const String &url);
|
||||||
|
|
||||||
class AsyncHTTPRequest
|
class AsyncHTTPRequest
|
||||||
{
|
{
|
||||||
using callback_arg_t = void*;
|
using callback_arg_t = void*;
|
||||||
|
|
||||||
struct header
|
struct header
|
||||||
{
|
{
|
||||||
header* next;
|
header* next{};
|
||||||
char* name;
|
String name;
|
||||||
char* value;
|
String value;
|
||||||
|
|
||||||
header(): next(nullptr), name(nullptr), value(nullptr)
|
|
||||||
{};
|
|
||||||
|
|
||||||
~header()
|
~header()
|
||||||
{
|
{
|
||||||
delete[] name;
|
|
||||||
delete[] value;
|
|
||||||
delete next;
|
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;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
using readyStateChangeCB = std::function<void(callback_arg_t, AsyncHTTPRequest*, ReadyState readyState)>;
|
using readyStateChangeCB = std::function<void(callback_arg_t, AsyncHTTPRequest*, ReadyState readyState)>;
|
||||||
using onDataCB = std::function<void(void*, AsyncHTTPRequest*, size_t available)>;
|
using onDataCB = std::function<void(void*, AsyncHTTPRequest*, size_t available)>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AsyncHTTPRequest();
|
|
||||||
~AsyncHTTPRequest();
|
~AsyncHTTPRequest();
|
||||||
|
|
||||||
|
|
||||||
@ -150,7 +181,7 @@ class AsyncHTTPRequest
|
|||||||
void setDebug(bool); // Turn debug message on/off
|
void setDebug(bool); // Turn debug message on/off
|
||||||
bool debug() const; // is debug on or off?
|
bool debug() const; // is debug on or off?
|
||||||
|
|
||||||
bool open(const char* method, const char* URL); // Initiate a request
|
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 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
|
void onReadyStateChangeArg(callback_arg_t arg = 0); // set event handlers arg
|
||||||
// or you can simply poll readyState()
|
// or you can simply poll readyState()
|
||||||
@ -176,14 +207,14 @@ class AsyncHTTPRequest
|
|||||||
ReadyState readyState() const; // Return the ready state
|
ReadyState readyState() const; // Return the ready state
|
||||||
|
|
||||||
int respHeaderCount(); // Retrieve count of response headers
|
int respHeaderCount(); // Retrieve count of response headers
|
||||||
char* respHeaderName(int index); // Return header name by index
|
String respHeaderName(int index); // Return header name by index
|
||||||
char* respHeaderValue(int index); // Return header value by index
|
String respHeaderValue(int index); // Return header value by index
|
||||||
char* respHeaderValue(const char* name); // Return header value by name
|
String respHeaderValue(const String &name); // Return header value by name
|
||||||
|
|
||||||
bool respHeaderExists(const char* name); // Does header exist by name?
|
bool respHeaderExists(const String &name); // Does header exist by name?
|
||||||
|
|
||||||
#if (ESP32 || ESP8266)
|
#if (ESP32 || ESP8266)
|
||||||
char* respHeaderValue(const __FlashStringHelper *name);
|
String respHeaderValue(const __FlashStringHelper *name);
|
||||||
bool respHeaderExists(const __FlashStringHelper *name);
|
bool respHeaderExists(const __FlashStringHelper *name);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -200,48 +231,45 @@ class AsyncHTTPRequest
|
|||||||
//___________________________________________________________________________________________________________________________________
|
//___________________________________________________________________________________________________________________________________
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
HTTPmethod _HTTPmethod;
|
||||||
|
|
||||||
enum {HTTPmethodGET, HTTPmethodPOST} _HTTPmethod;
|
ReadyState _readyState{ReadyState::Idle};
|
||||||
|
|
||||||
ReadyState _readyState;
|
int16_t _HTTPcode{0}; // HTTP response code or (negative) exception code
|
||||||
|
bool _chunked{false}; // Processing chunked response
|
||||||
int16_t _HTTPcode; // HTTP response code or (negative) exception code
|
bool _debug{DEBUG_IOTA_HTTP_SET}; // Debug state
|
||||||
bool _chunked; // Processing chunked response
|
uint32_t _timeout{DEFAULT_RX_TIMEOUT}; // Default or user overide RxTimeout in seconds
|
||||||
bool _debug; // Debug state
|
uint32_t _lastActivity{0}; // Time of last activity
|
||||||
uint32_t _timeout; // Default or user overide RxTimeout in seconds
|
uint32_t _requestStartTime{0}; // Time last open() issued
|
||||||
uint32_t _lastActivity; // Time of last activity
|
uint32_t _requestEndTime{0}; // Time of last disconnect
|
||||||
uint32_t _requestStartTime; // Time last open() issued
|
URL _URL{}; // -> URL data structure
|
||||||
uint32_t _requestEndTime; // Time of last disconnect
|
String _connectedHost; // Host when connected
|
||||||
URL* _URL; // -> URL data structure
|
int _connectedPort{-1}; // Port when connected
|
||||||
char* _connectedHost; // Host when connected
|
AsyncClient* _client{nullptr}; // ESPAsyncTCP AsyncClient instance
|
||||||
int _connectedPort; // Port when connected
|
size_t _contentLength{0}; // content-length header value or sum of chunk headers
|
||||||
AsyncClient* _client; // ESPAsyncTCP AsyncClient instance
|
size_t _contentRead{0}; // number of bytes retrieved by user since last open()
|
||||||
size_t _contentLength; // content-length header value or sum of chunk headers
|
readyStateChangeCB _readyStateChangeCB{}; // optional callback for readyState change
|
||||||
size_t _contentRead; // number of bytes retrieved by user since last open()
|
callback_arg_t _readyStateChangeCBarg{}; // associated user argument
|
||||||
readyStateChangeCB _readyStateChangeCB; // optional callback for readyState change
|
onDataCB _onDataCB{nullptr}; // optional callback when data received
|
||||||
callback_arg_t _readyStateChangeCBarg; // associated user argument
|
void* _onDataCBarg{nullptr}; // associated user argument
|
||||||
onDataCB _onDataCB; // optional callback when data received
|
|
||||||
void* _onDataCBarg; // associated user argument
|
|
||||||
|
|
||||||
#ifdef ESP32
|
#ifdef ESP32
|
||||||
SemaphoreHandle_t threadLock;
|
SemaphoreHandle_t threadLock{xSemaphoreCreateRecursiveMutex()};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// request and response String buffers and header list (same queue for request and response).
|
// request and response String buffers and header list (same queue for request and response).
|
||||||
|
|
||||||
xbuf* _request; // Tx data buffer
|
xbuf* _request{nullptr}; // Tx data buffer
|
||||||
xbuf* _response; // Rx data buffer for headers
|
xbuf* _response{nullptr}; // Rx data buffer for headers
|
||||||
xbuf* _chunks; // First stage for chunked response
|
xbuf* _chunks{nullptr}; // First stage for chunked response
|
||||||
header* _headers; // request or (readyState > readyStateHdrsRcvd) response headers
|
header* _headers{nullptr}; // request or (readyState > readyStateHdrsRcvd) response headers
|
||||||
|
|
||||||
// Protected functions
|
// Protected functions
|
||||||
|
|
||||||
header* _addHeader(const char*, const char*);
|
header* _addHeader(const String &name, const String &value);
|
||||||
header* _getHeader(const char*);
|
header* _getHeader(const String &name);
|
||||||
header* _getHeader(int);
|
header* _getHeader(int idx);
|
||||||
bool _buildRequest();
|
bool _buildRequest();
|
||||||
bool _parseURL(const char*);
|
|
||||||
bool _parseURL(String);
|
|
||||||
void _processChunks();
|
void _processChunks();
|
||||||
bool _connect();
|
bool _connect();
|
||||||
size_t _send();
|
size_t _send();
|
||||||
|
Reference in New Issue
Block a user