Renamed former HtmlClient to HttpClient and implemented the methods
This commit is contained in:
parent
ea60399ea3
commit
d0349ea26e
335
src/app/HttpClient.cpp
Normal file
335
src/app/HttpClient.cpp
Normal file
@ -0,0 +1,335 @@
|
||||
#include "HttpClient.h"
|
||||
#include <IPAddress.h>
|
||||
|
||||
#define DEBUG_HTTP_CLIENT
|
||||
|
||||
/*
|
||||
* End of HttpClientHelper
|
||||
*/
|
||||
|
||||
HttpClient::HttpClient() : WiFiClient(), _connectionStatus(NO_ATTEMPT), _resource(NULL), _address(NULL), _port(0), _keepAlive(false), _maxRetries(5), _retries(0), _httpCode(HTTP_CODE::UNDEFINED_CODE), _bodyReadyToRead(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;
|
||||
|
||||
connectByHostOrIp();
|
||||
}
|
||||
|
||||
HttpClient::HttpClient(const char *address, const char *resource, uint16_t port) : HttpClient(address, port)
|
||||
{
|
||||
if(resource != NULL)
|
||||
{
|
||||
_resource = (char *)malloc(strlen(resource) * sizeof(char) + 1);
|
||||
strcpy(_resource, resource);
|
||||
}
|
||||
}
|
||||
|
||||
HttpClient::HttpClient(const HttpClient &object)
|
||||
{
|
||||
if(object._resource != NULL)
|
||||
{
|
||||
_resource = (char *)malloc(strlen(object._resource) * sizeof(char) + 1);
|
||||
strcpy(_resource, object._resource);
|
||||
}
|
||||
else
|
||||
_resource = NULL;
|
||||
|
||||
if(object._address != NULL)
|
||||
{
|
||||
_address = (char *)malloc(strlen(object._address) * sizeof(char) + 1);
|
||||
strcpy(_address, object._address);
|
||||
}
|
||||
else
|
||||
_address = NULL;
|
||||
|
||||
_connectionStatus = object._connectionStatus;
|
||||
_keepAlive = object._keepAlive;
|
||||
_maxRetries = object._maxRetries;
|
||||
_retries = object._retries;
|
||||
_port = object._port;
|
||||
_httpCode = object._httpCode;
|
||||
_bodyReadyToRead = object._bodyReadyToRead;
|
||||
}
|
||||
|
||||
HttpClient::~HttpClient()
|
||||
{
|
||||
if(_resource != NULL)free(_resource);
|
||||
if(_address != NULL)free(_address);
|
||||
}
|
||||
|
||||
boolean HttpClient::connectByHostOrIp()
|
||||
{
|
||||
IPAddress ipAddress;
|
||||
if(ipAddress.fromString(_address))
|
||||
{
|
||||
_connectionStatus = connect(ipAddress, _port) == 1 ? SUCCESSFUL : FAILED;
|
||||
#ifdef DEBUG_HTTP_CLIENT
|
||||
Serial.printf("Correct ip address. Connection status : %d\n", _connectionStatus);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
_connectionStatus = connect(_address, _port) == 1 ? SUCCESSFUL : FAILED;
|
||||
#ifdef DEBUG_HTTP_CLIENT
|
||||
Serial.printf("Probably a hostname. Connection status : %d\n", _connectionStatus);
|
||||
#endif
|
||||
}
|
||||
|
||||
return _connectionStatus == SUCCESSFUL;
|
||||
}
|
||||
|
||||
boolean HttpClient::sendHttpQuery(const char *resource, HttpRequestMethod method, Dictionary<DictionaryHelper::StringEntity> *getData, Dictionary<DictionaryHelper::StringEntity> *postData)
|
||||
{
|
||||
if(resource != NULL) //We overwrite the resource if it has been already defined
|
||||
{
|
||||
if(_resource != NULL)
|
||||
free(_resource);
|
||||
|
||||
_resource = (char *) malloc(strlen(resource) * sizeof(char) + 1);
|
||||
strcpy(_resource, resource);
|
||||
}
|
||||
|
||||
//We check the result
|
||||
return sendHttpQuery(method, getData, postData);
|
||||
}
|
||||
|
||||
boolean HttpClient::sendHttpQuery(HttpRequestMethod method, Dictionary<DictionaryHelper::StringEntity> *getData, Dictionary<DictionaryHelper::StringEntity> *postData)
|
||||
{
|
||||
_httpCode = HTTP_CODE::UNDEFINED_CODE;
|
||||
_bodyReadyToRead = false;
|
||||
#ifdef DEBUG_HTTP_CLIENT
|
||||
Serial.printf("Link status : %d\n", status());
|
||||
#endif
|
||||
|
||||
if(!connected() || _connectionStatus == FAILED)
|
||||
{
|
||||
if(_retries == _maxRetries) return false;
|
||||
if(_connectionStatus == FAILED)
|
||||
{
|
||||
stop();
|
||||
_retries++;
|
||||
}
|
||||
#ifdef DEBUG_HTTP_CLIENT
|
||||
Serial.printf("Link broken, we try to reconnect : addr : %s port %u\nretries : %u\n", _address, _port, _retries);
|
||||
#endif
|
||||
|
||||
connectByHostOrIp();
|
||||
|
||||
if(_connectionStatus == FAILED)
|
||||
{
|
||||
#ifdef DEBUG_HTTP_CLIENT
|
||||
Serial.printf("Failed to reconnect\n");
|
||||
#endif
|
||||
stop();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(connected())
|
||||
{
|
||||
#ifdef DEBUG_HTTP_CLIENT
|
||||
Serial.printf("Server is listening\n", status());
|
||||
#endif
|
||||
|
||||
switch(method)
|
||||
{
|
||||
case HttpRequestMethod::GET:
|
||||
//No Content-Length in this case
|
||||
printf("GET %s", _resource == NULL ? "/" : _resource);
|
||||
//Here we send the parameters
|
||||
sendUriWithGetParams(getData);
|
||||
sendHeader();
|
||||
break;
|
||||
case HttpRequestMethod::POST:
|
||||
//It is necessary to compute the content length
|
||||
printf("POST %s", _resource == NULL ? "/" : _resource);
|
||||
//Here we send the parameters
|
||||
sendUriWithGetParams(getData);
|
||||
sendHeader(HttpMIMEType::APPLICATION_X_WWW_FORM_URLENCODED, computeBodyLength(postData));
|
||||
sendPostData(postData);
|
||||
break;
|
||||
default:
|
||||
#ifdef DEBUG_HTTP_CLIENT
|
||||
Serial.printf("Http verb unspecified\n", status());
|
||||
#endif
|
||||
if(!_keepAlive)stop();
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
HttpClient::HTTP_CODE HttpClient::isReplyAvailable()
|
||||
{
|
||||
uint32_t bytesAvailable(available());
|
||||
uint8_t readBuffer[100];
|
||||
uint8_t safeSize;
|
||||
|
||||
if(bytesAvailable && !_bodyReadyToRead)
|
||||
{
|
||||
safeSize = bytesAvailable > 99 ? 99 : bytesAvailable;
|
||||
peekBytes(readBuffer, safeSize);
|
||||
readBuffer[safeSize] = '\0';
|
||||
|
||||
#ifdef DEBUG_HTTP_CLIENT
|
||||
Serial.printf("Data chunk from server size -> %u - %u : %s\n", safeSize, bytesAvailable, readBuffer);
|
||||
#endif
|
||||
|
||||
//We try to extract the http return code:
|
||||
if(_httpCode == HTTP_CODE::UNDEFINED_CODE)
|
||||
{
|
||||
char *code = strchr((char *)readBuffer, ' '), *endP;
|
||||
|
||||
if(code != NULL)
|
||||
{
|
||||
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));
|
||||
read(readBuffer, safeSize - 4); //To remove the peek bytes
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!_bodyReadyToRead)
|
||||
{
|
||||
char *endP = strstr((char *)readBuffer, "\r\n\r\n");
|
||||
|
||||
if(endP != NULL)
|
||||
{
|
||||
#ifdef DEBUG_HTTP_CLIENT
|
||||
Serial.printf("Body found, bytes to discard : %u\n", (uint8_t *)endP - readBuffer + 4);
|
||||
#endif
|
||||
read(readBuffer, (uint8_t *)endP - readBuffer + 4);
|
||||
_bodyReadyToRead = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
read(readBuffer, safeSize - 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(_httpCode != HTTP_CODE::UNDEFINED_CODE && _bodyReadyToRead)
|
||||
return _httpCode;
|
||||
|
||||
return HTTP_CODE::UNDEFINED_CODE;
|
||||
}
|
||||
|
||||
uint16_t HttpClient::readHttpReply(uint8_t *buffer, uint32_t size)
|
||||
{
|
||||
uint32_t bytesAvailable(available());
|
||||
uint8_t safeSize(0);
|
||||
|
||||
if(bytesAvailable)
|
||||
{
|
||||
safeSize = bytesAvailable > size - 1 ? size - 1 : bytesAvailable;
|
||||
|
||||
read(buffer, safeSize);
|
||||
}
|
||||
|
||||
buffer[safeSize] = '\0';
|
||||
|
||||
return safeSize;
|
||||
}
|
||||
|
||||
void HttpClient::sendUriWithGetParams(Dictionary<DictionaryHelper::StringEntity> *data)
|
||||
{
|
||||
if(data == NULL)return;
|
||||
|
||||
uint8_t count(data->count());
|
||||
|
||||
if(count == 0)return;
|
||||
print("?");
|
||||
|
||||
for(int i(0); i < count; i++)
|
||||
{
|
||||
char str[2] = "";
|
||||
if(data->getAt(i) != NULL)
|
||||
{
|
||||
if(strlen(data->getAt(i)->getString()) > 0)
|
||||
strcpy(str, "=");
|
||||
}
|
||||
printf("%s%s%s", data->getParameter(i), str, data->getAt(i) != NULL ? data->getAt(i)->getString() : "");
|
||||
|
||||
if(i < count - 1)
|
||||
print("&");
|
||||
}
|
||||
}
|
||||
|
||||
void HttpClient::sendPostData(Dictionary<DictionaryHelper::StringEntity> *data)
|
||||
{
|
||||
if(data == NULL)return;
|
||||
|
||||
uint8_t count(data->count());
|
||||
|
||||
if(count == 0)return;
|
||||
|
||||
for(int i(0); i < count; i++)
|
||||
{
|
||||
printf("%s=%s", data->getParameter(i), data->getAt(i) != NULL ? data->getAt(i)->getString() : "");
|
||||
|
||||
if(i < count - 1)
|
||||
print("&");
|
||||
}
|
||||
}
|
||||
|
||||
void HttpClient::keepAlive(boolean enabled)
|
||||
{
|
||||
_keepAlive = enabled;
|
||||
}
|
||||
|
||||
void HttpClient::sendHeader(HttpMIMEType contentType, uint64_t contentLength, HttpVersion httpVersion)
|
||||
{
|
||||
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");
|
||||
if(contentLength > 0)
|
||||
{
|
||||
printf("Content-Length: %u\r\n", contentLength);
|
||||
printf("Content-Type: %s\r\n", httpMIMETypeToString(contentType, mime));
|
||||
}
|
||||
|
||||
printf("\r\n");
|
||||
}
|
||||
|
||||
uint64_t HttpClient::computeBodyLength(Dictionary<DictionaryHelper::StringEntity> *data)
|
||||
{
|
||||
uint64_t length(0);
|
||||
|
||||
if(data == NULL)return length;
|
||||
|
||||
uint8_t count(data->count());
|
||||
|
||||
if(count == 0)return length;
|
||||
|
||||
for(int i(0); i < count; i++)
|
||||
{
|
||||
length += data->getAt(i) != NULL ? strlen(data->getAt(i)->getString()) + 1 : 1;
|
||||
length += strlen(data->getParameter(i));
|
||||
|
||||
if(i < count - 1)
|
||||
length++;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
68
src/app/HttpClient.h
Normal file
68
src/app/HttpClient.h
Normal file
@ -0,0 +1,68 @@
|
||||
#ifndef HTTPCLIENT_H
|
||||
#define HTTPCLIENT_H
|
||||
|
||||
#include <ESP8266WiFi.h>
|
||||
#include "HttpConstants.h"
|
||||
#include "Dictionary.h"
|
||||
|
||||
namespace HttpClientHelper
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
class HttpClient : public WiFiClient, public HttpConstants
|
||||
{
|
||||
public:
|
||||
enum ConnectionStatus { NO_ATTEMPT = 0, SUCCESSFUL, FAILED };
|
||||
|
||||
HttpClient();
|
||||
HttpClient(const char *address, uint16_t port = 80);
|
||||
HttpClient(const char *address, const char *resource, uint16_t port = 80);
|
||||
|
||||
HttpClient(const HttpClient &object);
|
||||
virtual ~HttpClient();
|
||||
|
||||
boolean sendHttpQuery(const char *ressource, 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);
|
||||
|
||||
HTTP_CODE isReplyAvailable();
|
||||
|
||||
uint16_t readHttpReply(uint8_t *buffer, uint32_t size);
|
||||
protected:
|
||||
private:
|
||||
boolean connectByHostOrIp();
|
||||
void sendUriWithGetParams(Dictionary<DictionaryHelper::StringEntity> *data);
|
||||
void sendPostData(Dictionary<DictionaryHelper::StringEntity> *data);
|
||||
void sendHeader(HttpMIMEType contentType = HttpMIMEType::UNKNOWN_MIME, uint64_t contentLength = 0, HttpVersion httpVersion = HttpVersion::HTTP_1_1);
|
||||
uint64_t computeBodyLength(Dictionary<DictionaryHelper::StringEntity> *data);
|
||||
|
||||
ConnectionStatus _connectionStatus;
|
||||
char *_resource;
|
||||
char *_address;
|
||||
uint16_t _port;
|
||||
boolean _keepAlive;
|
||||
uint8_t _maxRetries;
|
||||
uint8_t _retries;
|
||||
HTTP_CODE _httpCode;
|
||||
boolean _bodyReadyToRead;
|
||||
};
|
||||
|
||||
#endif //HTTPCLIENT_H
|
||||
|
||||
/*
|
||||
* TCP status codes :
|
||||
* enum tcp_state {
|
||||
CLOSED = 0,
|
||||
LISTEN = 1,
|
||||
SYN_SENT = 2,
|
||||
SYN_RCVD = 3,
|
||||
ESTABLISHED = 4,
|
||||
FIN_WAIT_1 = 5,
|
||||
FIN_WAIT_2 = 6,
|
||||
CLOSE_WAIT = 7,
|
||||
CLOSING = 8,
|
||||
LAST_ACK = 9,
|
||||
TIME_WAIT = 10
|
||||
};
|
||||
*/
|
Loading…
Reference in New Issue
Block a user