weatherstation/firmware/libraries/ESP8266_Influxdb/src/HTTPService 2.cpp
2022-09-15 10:23:02 +02:00

220 lines
7.2 KiB
C++

#include "HTTPService.h"
#include "Platform.h"
#include "Version.h"
#include "util/debug.h"
static const char UserAgent[] PROGMEM = "influxdb-client-arduino/" INFLUXDB_CLIENT_VERSION " (" INFLUXDB_CLIENT_PLATFORM " " INFLUXDB_CLIENT_PLATFORM_VERSION ")";
#if defined(ESP8266)
bool checkMFLN(BearSSL::WiFiClientSecure *client, String url);
#endif
// This cannot be put to PROGMEM due to the way how it is used
static const char *RetryAfter = "Retry-After";
const char *TransferEncoding = "Transfer-Encoding";
HTTPService::HTTPService(ConnectionInfo *pConnInfo):_pConnInfo(pConnInfo) {
_apiURL = pConnInfo->serverUrl;
_apiURL += "/api/v2/";
bool https = pConnInfo->serverUrl.startsWith("https");
if(https) {
#if defined(ESP8266)
BearSSL::WiFiClientSecure *wifiClientSec = new BearSSL::WiFiClientSecure;
if (pConnInfo->insecure) {
wifiClientSec->setInsecure();
} else if(pConnInfo->certInfo && strlen_P(pConnInfo->certInfo) > 0) {
if(strlen_P(pConnInfo->certInfo) > 60 ) { //differentiate fingerprint and cert
_cert = new BearSSL::X509List(pConnInfo->certInfo);
wifiClientSec->setTrustAnchors(_cert);
} else {
wifiClientSec->setFingerprint(pConnInfo->certInfo);
}
}
checkMFLN(wifiClientSec, pConnInfo->serverUrl);
#elif defined(ESP32)
WiFiClientSecure *wifiClientSec = new WiFiClientSecure;
if (pConnInfo->insecure) {
#ifndef ARDUINO_ESP32_RELEASE_1_0_4
// This works only in ESP32 SDK 1.0.5 and higher
wifiClientSec->setInsecure();
#endif
} else if(pConnInfo->certInfo && strlen_P(pConnInfo->certInfo) > 0) {
wifiClientSec->setCACert(pConnInfo->certInfo);
}
#endif
_wifiClient = wifiClientSec;
} else {
_wifiClient = new WiFiClient;
}
if(!_httpClient) {
_httpClient = new HTTPClient;
}
_httpClient->setReuse(_httpOptions._connectionReuse);
_httpClient->setUserAgent(FPSTR(UserAgent));
};
HTTPService::~HTTPService() {
if(_httpClient) {
delete _httpClient;
_httpClient = nullptr;
}
if(_wifiClient) {
delete _wifiClient;
_wifiClient = nullptr;
}
#if defined(ESP8266)
if(_cert) {
delete _cert;
_cert = nullptr;
}
#endif
}
void HTTPService::setHTTPOptions(const HTTPOptions & httpOptions) {
_httpOptions = httpOptions;
if(!_httpClient) {
_httpClient = new HTTPClient;
}
_httpClient->setReuse(_httpOptions._connectionReuse);
_httpClient->setTimeout(_httpOptions._httpReadTimeout);
#if defined(ESP32)
_httpClient->setConnectTimeout(_httpOptions._httpReadTimeout);
#endif
}
// parse URL for host and port and call probeMaxFragmentLength
#if defined(ESP8266)
bool checkMFLN(BearSSL::WiFiClientSecure *client, String url) {
int index = url.indexOf(':');
if(index < 0) {
return false;
}
String protocol = url.substring(0, index);
int port = -1;
url.remove(0, (index + 3)); // remove http:// or https://
if (protocol == "http") {
// set default port for 'http'
port = 80;
} else if (protocol == "https") {
// set default port for 'https'
port = 443;
} else {
return false;
}
index = url.indexOf('/');
String host = url.substring(0, index);
url.remove(0, index); // remove host
// check Authorization
index = host.indexOf('@');
if(index >= 0) {
host.remove(0, index + 1); // remove auth part including @
}
// get port
index = host.indexOf(':');
if(index >= 0) {
String portS = host;
host = host.substring(0, index); // hostname
portS.remove(0, (index + 1)); // remove hostname + :
port = portS.toInt(); // get port
}
INFLUXDB_CLIENT_DEBUG("[D] probeMaxFragmentLength to %s:%d\n", host.c_str(), port);
bool mfln = client->probeMaxFragmentLength(host, port, 1024);
INFLUXDB_CLIENT_DEBUG("[D] MFLN:%s\n", mfln ? "yes" : "no");
if (mfln) {
client->setBufferSizes(1024, 1024);
}
return mfln;
}
#endif //ESP8266
bool HTTPService::beforeRequest(const char *url) {
if(!_httpClient->begin(*_wifiClient, url)) {
_pConnInfo->lastError = F("begin failed");
return false;
}
if(_pConnInfo->authToken.length() > 0) {
_httpClient->addHeader(F("Authorization"), "Token " + _pConnInfo->authToken);
}
const char * headerKeys[] = {RetryAfter, TransferEncoding} ;
_httpClient->collectHeaders(headerKeys, 2);
return true;
}
bool HTTPService::doPOST(const char *url, const char *data, const char *contentType, int expectedCode, httpResponseCallback cb) {
INFLUXDB_CLIENT_DEBUG("[D] POST request - %s, data: %dbytes, type %s\n", url, strlen(data), contentType);
if(!beforeRequest(url)) {
return false;
}
if(contentType) {
_httpClient->addHeader(F("Content-Type"), FPSTR(contentType));
}
_lastStatusCode = _httpClient->POST((uint8_t *) data, strlen(data));
return afterRequest(expectedCode, cb);
}
bool HTTPService::doPOST(const char *url, Stream *stream, const char *contentType, int expectedCode, httpResponseCallback cb) {
INFLUXDB_CLIENT_DEBUG("[D] POST request - %s, data: %dbytes, type %s\n", url, stream->available(), contentType);
if(!beforeRequest(url)) {
return false;
}
if(contentType) {
_httpClient->addHeader(F("Content-Type"), FPSTR(contentType));
}
_lastStatusCode = _httpClient->sendRequest("POST", stream, stream->available());
return afterRequest(expectedCode, cb);
}
bool HTTPService::doGET(const char *url, int expectedCode, httpResponseCallback cb) {
INFLUXDB_CLIENT_DEBUG("[D] GET request - %s\n", url);
if(!beforeRequest(url)) {
return false;
}
_lastStatusCode = _httpClient->GET();
return afterRequest(expectedCode, cb, false);
}
bool HTTPService::doDELETE(const char *url, int expectedCode, httpResponseCallback cb) {
INFLUXDB_CLIENT_DEBUG("[D] DELETE - %s\n", url);
if(!beforeRequest(url)) {
return false;
}
_lastStatusCode = _httpClient->sendRequest("DELETE");
return afterRequest(expectedCode, cb, false);
}
bool HTTPService::afterRequest(int expectedStatusCode, httpResponseCallback cb, bool modifyLastConnStatus) {
if(modifyLastConnStatus) {
_lastRequestTime = millis();
INFLUXDB_CLIENT_DEBUG("[D] HTTP status code - %d\n", _lastStatusCode);
_lastRetryAfter = 0;
if(_lastStatusCode >= 429) { //retryable server errors
if(_httpClient->hasHeader(RetryAfter)) {
_lastRetryAfter = _httpClient->header(RetryAfter).toInt();
INFLUXDB_CLIENT_DEBUG("[D] Reply after - %d\n", _lastRetryAfter);
}
}
}
_pConnInfo->lastError = (char *)nullptr;
bool ret = _lastStatusCode == expectedStatusCode;
bool endConnection = true;
if(!ret) {
if(_lastStatusCode > 0) {
_pConnInfo->lastError = _httpClient->getString();
INFLUXDB_CLIENT_DEBUG("[D] Response:\n%s\n", _pConnInfo->lastError.c_str());
} else {
_pConnInfo->lastError = _httpClient->errorToString(_lastStatusCode);
INFLUXDB_CLIENT_DEBUG("[E] Error - %s\n", _pConnInfo->lastError.c_str());
}
} else if(cb){
endConnection = cb(_httpClient);
}
if(endConnection) {
_httpClient->end();
}
return ret;
}