Corrected the isReplyAvailable methode

This commit is contained in:
Th3maz1ng 2020-07-14 11:48:43 +02:00
parent 2f430b305c
commit f76639745f
2 changed files with 129 additions and 84 deletions

View File

@ -7,20 +7,8 @@
* End of HttpClientHelper * End of HttpClientHelper
*/ */
HttpClient::HttpClient() : WiFiClient(), _connectionStatus(NO_ATTEMPT), _resource(NULL), _address(NULL), _port(0), _keepAlive(false), _maxRetries(-1), _retries(0), _httpCode(HTTP_CODE::UNDEFINED_CODE), _bodyReadyToRead(false) HttpClient::HttpClient(const char *address, uint16_t port) : WiFiClient(), _connectionStatus(NO_ATTEMPT), _resource(NULL), _pAddress(address), _port(0), _keepAlive(false), _maxRetries(-1), _retries(0), _isIp(false), _httpCode(HTTP_CODE::UNDEFINED_CODE), _httpCodeParsed(false)
{ {
}
HttpClient::HttpClient(const char *address, uint16_t port) : HttpClient()
{
if(address != NULL)
{
_address = (char *)malloc(strlen(address) * sizeof(char) + 1);
strcpy(_address, address);
}
_port = port; _port = port;
connectByHostOrIp(); connectByHostOrIp();
@ -45,34 +33,27 @@ HttpClient::HttpClient(const HttpClient &object)
else else
_resource = NULL; _resource = NULL;
if(object._address != NULL) _pAddress = object._pAddress;
{
_address = (char *)malloc(strlen(object._address) * sizeof(char) + 1);
strcpy(_address, object._address);
}
else
_address = NULL;
_connectionStatus = object._connectionStatus; _connectionStatus = object._connectionStatus;
_keepAlive = object._keepAlive; _keepAlive = object._keepAlive;
_maxRetries = object._maxRetries; _maxRetries = object._maxRetries;
_retries = object._retries; _retries = object._retries;
_port = object._port; _port = object._port;
_httpCode = object._httpCode; _httpCode = object._httpCode;
_bodyReadyToRead = object._bodyReadyToRead; _httpCodeParsed = object._httpCodeParsed;
} }
HttpClient::~HttpClient() HttpClient::~HttpClient()
{ {
if(_resource != NULL)free(_resource); if(_resource != NULL)free(_resource);
if(_address != NULL)free(_address);
} }
boolean HttpClient::connectByHostOrIp() boolean HttpClient::connectByHostOrIp()
{ {
IPAddress ipAddress; IPAddress ipAddress;
if(ipAddress.fromString(_address)) if(ipAddress.fromString(_pAddress))
{ {
_isIp = true;
_connectionStatus = connect(ipAddress, _port) == 1 ? SUCCESSFUL : FAILED; _connectionStatus = connect(ipAddress, _port) == 1 ? SUCCESSFUL : FAILED;
#ifdef DEBUG_HTTP_CLIENT #ifdef DEBUG_HTTP_CLIENT
Serial.printf("Correct ip address. Connection status : %d\n", _connectionStatus); Serial.printf("Correct ip address. Connection status : %d\n", _connectionStatus);
@ -80,7 +61,8 @@ boolean HttpClient::connectByHostOrIp()
} }
else else
{ {
_connectionStatus = connect(_address, _port) == 1 ? SUCCESSFUL : FAILED; _isIp = false;
_connectionStatus = connect(_pAddress, _port) == 1 ? SUCCESSFUL : FAILED;
#ifdef DEBUG_HTTP_CLIENT #ifdef DEBUG_HTTP_CLIENT
Serial.printf("Probably a hostname. Connection status : %d\n", _connectionStatus); Serial.printf("Probably a hostname. Connection status : %d\n", _connectionStatus);
#endif #endif
@ -108,7 +90,7 @@ boolean HttpClient::sendHttpQuery(HttpRequestMethod method, Dictionary<Dictionar
{ {
//We reset this two flags //We reset this two flags
_httpCode = HTTP_CODE::UNDEFINED_CODE; _httpCode = HTTP_CODE::UNDEFINED_CODE;
_bodyReadyToRead = false; _httpCodeParsed = false;
#ifdef DEBUG_HTTP_CLIENT #ifdef DEBUG_HTTP_CLIENT
if(_keepAlive) if(_keepAlive)
@ -127,9 +109,9 @@ boolean HttpClient::sendHttpQuery(HttpRequestMethod method, Dictionary<Dictionar
#ifdef DEBUG_HTTP_CLIENT #ifdef DEBUG_HTTP_CLIENT
if(_keepAlive) if(_keepAlive)
Serial.printf("Link broken, we try to reconnect : addr : %s port %u\nretries : %u\n", _address, _port, _retries); Serial.printf("Link broken, we try to reconnect : addr : %s port %u\nretries : %u\n", _pAddress, _port, _retries);
else else
Serial.printf("We start a new connection : %s port %u\nretries : %u\n", _address, _port, _retries); Serial.printf("We start a new connection : %s port %u\nretries : %u\n", _pAddress, _port, _retries);
#endif #endif
connectByHostOrIp(); connectByHostOrIp();
@ -180,71 +162,125 @@ boolean HttpClient::sendHttpQuery(HttpRequestMethod method, Dictionary<Dictionar
return true; return true;
} }
HttpClient::HTTP_CODE HttpClient::isReplyAvailable() HttpClient::HTTP_CODE HttpClient::isReplyAvailable(uint16_t timeout)
{ {
uint32_t bytesAvailable(available()); uint32_t ts(millis());
uint8_t readBuffer[100]; char buffer[100];
uint8_t safeSize;
if(bytesAvailable && !_bodyReadyToRead) if(!_httpCodeParsed)_httpCodeParsed = true;
else
{ {
safeSize = bytesAvailable > 99 ? 99 : bytesAvailable; return _httpCode;
peekBytes(readBuffer, safeSize); }
readBuffer[safeSize] = '\0'; #ifdef DEBUG_HTTP_CLIENT
Serial.println("Before timeout");
#endif
#ifdef DEBUG_HTTP_CLIENT //This is the loop where we parse the data
Serial.printf("Data chunk from server size -> %u - %u : %s\n", safeSize, bytesAvailable, readBuffer); while(available() || millis() - ts < timeout)
#endif {
if(available())
//We try to extract the http return code:
if(_httpCode == HTTP_CODE::UNDEFINED_CODE)
{ {
char *code = strchr((char *)readBuffer, ' '), *endP; //If we do not have the HTTP response code yet, we parse it
if(_httpCode == HTTP_CODE::UNDEFINED_CODE)
if(code != NULL)
{ {
endP = strchr(code + 1, ' '); uint8_t bytesRed = buffer[peekBytes((uint8_t*)buffer,99)] = '\0';
if(endP != NULL) //We look for the end of the first line ie : HTTP/1.1 200 OK\r\n
char *pNewLine = strstr(buffer, "\r\n");
//If we found the new line, we can retrieve the code
if(pNewLine)
{ {
*endP = '\0';
#ifdef DEBUG_HTTP_CLIENT #ifdef DEBUG_HTTP_CLIENT
Serial.printf("Http code : %s\n", code + 1); Serial.println("New line found");
#endif #endif
//We extract the code
char *code = strchr((char *)buffer, ' '), *endP;
if(code)
{
endP = strchr(code + 1, ' ');
if(endP != NULL)
{
*endP = '\0';
#ifdef DEBUG_HTTP_CLIENT
Serial.printf("Http code : %s\n", code + 1);
#endif
_httpCode = numberToHTTP_CODE(strtoul(code+1, NULL, 10)); _httpCode = numberToHTTP_CODE(strtoul(code+1, NULL, 10));
read(readBuffer, safeSize - 4); //To remove the peek bytes //We can now discard the first line
#ifdef DEBUG_HTTP_CLIENT
Serial.printf("First line length : %u\n",(pNewLine - buffer) + 2);
#endif
read((uint8_t *)buffer, (pNewLine - buffer) + 2);
#ifdef DEBUG_HTTP_CLIENT
buffer[peekBytes((uint8_t*)buffer,99)] = '\0';
Serial.printf("Next chunk is : %s\n",buffer);
#endif
}
}
else
{
#ifdef DEBUG_HTTP_CLIENT
Serial.println("Code delimiter NOT found, leaving");
#endif
break;
}
}
else if(millis() - ts >= timeout)//Time out is over, received data is probably junk.
{
//we leave the loop;
#ifdef DEBUG_HTTP_CLIENT
Serial.println("New line NOT found, leaving");
#endif
break;
} }
} }
} else//We found the HTTP code, now we discard all the header data
if(!_bodyReadyToRead)
{
char *endP = strstr((char *)readBuffer, "\r\n\r\n");
if(endP != NULL)
{ {
#ifdef DEBUG_HTTP_CLIENT char *bodyDelimiter(NULL);
Serial.printf("Body found, bytes to discard : %u\n", (uint8_t *)endP - readBuffer + 4); char *newLineDelimiter;
#endif do
read(readBuffer, (uint8_t *)endP - readBuffer + 4); {
_bodyReadyToRead = true; buffer[peekBytes((uint8_t*)buffer,99)] = '\0';
} bodyDelimiter = strstr(buffer, "\r\n\r\n");
else newLineDelimiter = strstr(buffer, "\r\n");
{ if(!bodyDelimiter)
read(readBuffer, safeSize - 3); {
if(newLineDelimiter)
{
read((uint8_t *)buffer, (newLineDelimiter - buffer) + 2);
}
else
{
read((uint8_t *)buffer, strlen(buffer));
}
}
else
{
#ifdef DEBUG_HTTP_CLIENT
Serial.println("We found the body delimiter");
#endif
read((uint8_t *)buffer, (bodyDelimiter - buffer) + 4);
#ifdef DEBUG_HTTP_CLIENT
buffer[peekBytes((uint8_t*)buffer,99)] = '\0';
Serial.printf("Body chunk is : %s\n",buffer);
#endif
}
}while(!bodyDelimiter);
break;
} }
} }
} }
if(_httpCode != HTTP_CODE::UNDEFINED_CODE && _bodyReadyToRead) #ifdef DEBUG_HTTP_CLIENT
return _httpCode; Serial.println("\nAfter timeout or all data is received");
#endif
return HTTP_CODE::UNDEFINED_CODE; return _httpCode;
} }
uint16_t HttpClient::readHttpReply(uint8_t *buffer, uint32_t size) uint16_t HttpClient::readHttpBody(uint8_t *buffer, uint32_t size)
{ {
uint32_t bytesAvailable(available()); uint32_t bytesAvailable(available());
uint8_t safeSize(0); uint8_t safeSize(0);
@ -310,7 +346,12 @@ void HttpClient::keepAlive(boolean enabled)
void HttpClient::sendHeader(HttpMIMEType contentType, uint64_t contentLength, HttpVersion httpVersion) void HttpClient::sendHeader(HttpMIMEType contentType, uint64_t contentLength, HttpVersion httpVersion)
{ {
char mime[255] = "", httpVer[15] = ""; char mime[255] = "", httpVer[15] = "";
printf(" %s\r\nHost: %u.%u.%u.%u:%u\r\nConnection: %s\r\n", httpVersionToString(httpVersion, httpVer), remoteIP()[0], remoteIP()[1], remoteIP()[2], remoteIP()[3], remotePort(), _keepAlive ? "keep-alive" : "close"); //Host could be an IP address or a host name
if(_isIp)
printf(" %s\r\nHost: %u.%u.%u.%u:%u\r\nConnection: %s\r\n", httpVersionToString(httpVersion, httpVer), remoteIP()[0], remoteIP()[1], remoteIP()[2], remoteIP()[3], remotePort(), _keepAlive ? "keep-alive" : "close");
else
printf(" %s\r\nHost: %s\r\nConnection: %s\r\n", httpVersionToString(httpVersion, httpVer), _pAddress, _keepAlive ? "keep-alive" : "close");
if(contentLength > 0) if(contentLength > 0)
{ {
printf("Content-Length: %u\r\n", contentLength); printf("Content-Length: %u\r\n", contentLength);

View File

@ -1,3 +1,7 @@
/**
* Anatole SCHRAMM-HENRY
* Updated on 12/07/2020
*/
#ifndef HTTPCLIENT_H #ifndef HTTPCLIENT_H
#define HTTPCLIENT_H #define HTTPCLIENT_H
@ -15,7 +19,6 @@ class HttpClient : public WiFiClient, public HttpConstants
public: public:
enum ConnectionStatus { NO_ATTEMPT = 0, SUCCESSFUL, FAILED }; enum ConnectionStatus { NO_ATTEMPT = 0, SUCCESSFUL, FAILED };
HttpClient();
HttpClient(const char *address, uint16_t port = 80); HttpClient(const char *address, uint16_t port = 80);
HttpClient(const char *address, const char *resource, uint16_t port = 80); HttpClient(const char *address, const char *resource, uint16_t port = 80);
@ -26,10 +29,10 @@ class HttpClient : public WiFiClient, public HttpConstants
boolean sendHttpQuery(HttpRequestMethod method = HttpRequestMethod::GET, Dictionary<DictionaryHelper::StringEntity> *getData = NULL, Dictionary<DictionaryHelper::StringEntity> *postData = NULL); boolean sendHttpQuery(HttpRequestMethod method = HttpRequestMethod::GET, Dictionary<DictionaryHelper::StringEntity> *getData = NULL, Dictionary<DictionaryHelper::StringEntity> *postData = NULL);
void keepAlive(boolean enabled); void keepAlive(boolean enabled);
void setMaxRetries(int16_t retries); void setMaxRetries(int16_t retries);
//100 ms is the default timeout
HTTP_CODE isReplyAvailable(uint16_t timeout = 100);
HTTP_CODE isReplyAvailable(); uint16_t readHttpBody(uint8_t *buffer, uint32_t size);
uint16_t readHttpReply(uint8_t *buffer, uint32_t size);
protected: protected:
private: private:
boolean connectByHostOrIp(); boolean connectByHostOrIp();
@ -40,13 +43,14 @@ class HttpClient : public WiFiClient, public HttpConstants
ConnectionStatus _connectionStatus; ConnectionStatus _connectionStatus;
char *_resource; char *_resource;
char *_address; const char *_pAddress;
uint16_t _port; uint16_t _port;
boolean _keepAlive; boolean _keepAlive;
int16_t _maxRetries; int16_t _maxRetries;
uint16_t _retries; uint16_t _retries;
boolean _isIp;
HTTP_CODE _httpCode; HTTP_CODE _httpCode;
boolean _bodyReadyToRead; boolean _httpCodeParsed;
}; };
#endif //HTTPCLIENT_H #endif //HTTPCLIENT_H