Skip to content

Commit 9f7f857

Browse files
committed
feat: shared HTTP code
1 parent bcf7b07 commit 9f7f857

File tree

10 files changed

+480
-330
lines changed

10 files changed

+480
-330
lines changed

src/HTTPService.cpp

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
2+
#include "HTTPService.h"
3+
#include "Platform.h"
4+
#include "Version.h"
5+
6+
// Uncomment bellow in case of a problem and rebuild sketch
7+
//#define INFLUXDB_CLIENT_DEBUG_ENABLE
8+
#include "util/debug.h"
9+
10+
static const char UserAgent[] PROGMEM = "influxdb-client-arduino/" INFLUXDB_CLIENT_VERSION " (" INFLUXDB_CLIENT_PLATFORM " " INFLUXDB_CLIENT_PLATFORM_VERSION ")";
11+
12+
#if defined(ESP8266)
13+
bool checkMFLN(BearSSL::WiFiClientSecure *client, String url);
14+
#endif
15+
16+
// This cannot be put to PROGMEM due to the way how it is used
17+
static const char *RetryAfter = "Retry-After";
18+
const char *TransferEncoding = "Transfer-Encoding";
19+
20+
HTTPService::HTTPService(const String &serverUrl, const String &authToken, const char *certInfo, bool insecure) {
21+
_authToken = authToken;
22+
_apiURL = serverUrl;
23+
_apiURL += "/api/v2/";
24+
bool https = serverUrl.startsWith("https");
25+
if(https) {
26+
#if defined(ESP8266)
27+
BearSSL::WiFiClientSecure *wifiClientSec = new BearSSL::WiFiClientSecure;
28+
if (insecure) {
29+
wifiClientSec->setInsecure();
30+
} else if(certInfo && strlen_P(certInfo) > 0) {
31+
if(strlen_P(certInfo) > 60 ) { //differentiate fingerprint and cert
32+
_cert = new BearSSL::X509List(certInfo);
33+
wifiClientSec->setTrustAnchors(_cert);
34+
} else {
35+
wifiClientSec->setFingerprint(certInfo);
36+
}
37+
}
38+
checkMFLN(wifiClientSec, serverUrl);
39+
#elif defined(ESP32)
40+
WiFiClientSecure *wifiClientSec = new WiFiClientSecure;
41+
if (insecure) {
42+
#ifndef ARDUINO_ESP32_RELEASE_1_0_4
43+
// This works only in ESP32 SDK 1.0.5 and higher
44+
wifiClientSec->setInsecure();
45+
#endif
46+
} else if(_certInfo && strlen_P(certInfo) > 0) {
47+
wifiClientSec->setCACert(certInfo);
48+
}
49+
#endif
50+
_wifiClient = wifiClientSec;
51+
} else {
52+
_wifiClient = new WiFiClient;
53+
}
54+
if(!_httpClient) {
55+
_httpClient = new HTTPClient;
56+
}
57+
_httpClient->setReuse(_httpOptions._connectionReuse);
58+
59+
_httpClient->setUserAgent(FPSTR(UserAgent));
60+
};
61+
62+
HTTPService::~HTTPService() {
63+
if(_httpClient) {
64+
delete _httpClient;
65+
_httpClient = nullptr;
66+
}
67+
if(_wifiClient) {
68+
delete _wifiClient;
69+
_wifiClient = nullptr;
70+
}
71+
#if defined(ESP8266)
72+
if(_cert) {
73+
delete _cert;
74+
_cert = nullptr;
75+
}
76+
#endif
77+
_lastStatusCode = 0;
78+
_lastErrorResponse = "";
79+
}
80+
81+
82+
void HTTPService::setHTTPOptions(const HTTPOptions & httpOptions) {
83+
_httpOptions = httpOptions;
84+
if(!_httpClient) {
85+
_httpClient = new HTTPClient;
86+
}
87+
_httpClient->setReuse(_httpOptions._connectionReuse);
88+
_httpClient->setTimeout(_httpOptions._httpReadTimeout);
89+
#if defined(ESP32)
90+
_httpClient->setConnectTimeout(_httpOptions._httpReadTimeout);
91+
#endif
92+
}
93+
94+
// parse URL for host and port and call probeMaxFragmentLength
95+
#if defined(ESP8266)
96+
bool checkMFLN(BearSSL::WiFiClientSecure *client, String url) {
97+
int index = url.indexOf(':');
98+
if(index < 0) {
99+
return false;
100+
}
101+
String protocol = url.substring(0, index);
102+
int port = -1;
103+
url.remove(0, (index + 3)); // remove http:// or https://
104+
105+
if (protocol == "http") {
106+
// set default port for 'http'
107+
port = 80;
108+
} else if (protocol == "https") {
109+
// set default port for 'https'
110+
port = 443;
111+
} else {
112+
return false;
113+
}
114+
index = url.indexOf('/');
115+
String host = url.substring(0, index);
116+
url.remove(0, index); // remove host
117+
// check Authorization
118+
index = host.indexOf('@');
119+
if(index >= 0) {
120+
host.remove(0, index + 1); // remove auth part including @
121+
}
122+
// get port
123+
index = host.indexOf(':');
124+
if(index >= 0) {
125+
String portS = host;
126+
host = host.substring(0, index); // hostname
127+
portS.remove(0, (index + 1)); // remove hostname + :
128+
port = portS.toInt(); // get port
129+
}
130+
INFLUXDB_CLIENT_DEBUG("[D] probeMaxFragmentLength to %s:%d\n", host.c_str(), port);
131+
bool mfln = client->probeMaxFragmentLength(host, port, 1024);
132+
INFLUXDB_CLIENT_DEBUG("[D] MFLN:%s\n", mfln ? "yes" : "no");
133+
if (mfln) {
134+
client->setBufferSizes(1024, 1024);
135+
}
136+
return mfln;
137+
}
138+
#endif //ESP8266
139+
140+
bool HTTPService::beforeRequest(const char *url) {
141+
if(!_httpClient->begin(*_wifiClient, url)) {
142+
_lastErrorResponse = F("begin failed");
143+
return false;
144+
}
145+
if(_authToken.length() > 0) {
146+
_httpClient->addHeader(F("Authorization"), "Token " + _authToken);
147+
}
148+
const char * headerKeys[] = {RetryAfter, TransferEncoding} ;
149+
_httpClient->collectHeaders(headerKeys, 2);
150+
return true;
151+
}
152+
153+
bool HTTPService::doPOST(const char *url, const char *data, const char *contentType, int expectedCode, httpResponseCallback cb) {
154+
INFLUXDB_CLIENT_DEBUG("[D] POST request - %s, data: %dbytes, type %s\n", url, strlen(data), contentType);
155+
if(!beforeRequest(url)) {
156+
return false;
157+
}
158+
if(contentType) {
159+
_httpClient->addHeader(F("Content-Type"), FPSTR(contentType));
160+
}
161+
_lastStatusCode = _httpClient->POST((uint8_t *) data, strlen(data));
162+
return afterRequest(expectedCode, cb);
163+
}
164+
165+
bool HTTPService::doGET(const char *url, int expectedCode, httpResponseCallback cb) {
166+
INFLUXDB_CLIENT_DEBUG("[D] GET request - %s\n", url);
167+
if(!beforeRequest(url)) {
168+
return false;
169+
}
170+
_lastStatusCode = _httpClient->GET();
171+
return afterRequest(expectedCode, cb, false);
172+
}
173+
174+
bool HTTPService::afterRequest(int expectedStatusCode, httpResponseCallback cb, bool modifyLastConnStatus) {
175+
if(modifyLastConnStatus) {
176+
_lastRequestTime = millis();
177+
INFLUXDB_CLIENT_DEBUG("[D] HTTP status code - %d\n", _lastStatusCode);
178+
_lastRetryAfter = 0;
179+
if(_lastStatusCode >= 429) { //retryable server errors
180+
if(_httpClient->hasHeader(RetryAfter)) {
181+
_lastRetryAfter = _httpClient->header(RetryAfter).toInt();
182+
INFLUXDB_CLIENT_DEBUG("[D] Reply after - %d\n", _lastRetryAfter);
183+
}
184+
}
185+
}
186+
_lastErrorResponse = "";
187+
bool ret = _lastStatusCode == expectedStatusCode;
188+
bool endConnection = true;
189+
if(!ret) {
190+
if(_lastStatusCode > 0) {
191+
_lastErrorResponse = _httpClient->getString();
192+
INFLUXDB_CLIENT_DEBUG("[D] Response:\n%s\n", _lastErrorResponse.c_str());
193+
} else {
194+
_lastErrorResponse = _httpClient->errorToString(_lastStatusCode);
195+
INFLUXDB_CLIENT_DEBUG("[E] Error - %s\n", _lastErrorResponse.c_str());
196+
}
197+
} else if(cb){
198+
endConnection = cb(_httpClient);
199+
}
200+
if(endConnection) {
201+
_httpClient->end();
202+
}
203+
return ret;
204+
}

src/HTTPService.h

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
#ifndef _HTTP_SERVICE_H_
2+
#define _HTTP_SERVICE_H_
3+
4+
#include <Arduino.h>
5+
#if defined(ESP8266)
6+
# include <WiFiClientSecureBearSSL.h>
7+
# include <ESP8266HTTPClient.h>
8+
#elif defined(ESP32)
9+
# include <HTTPClient.h>
10+
#else
11+
# error "This library currently supports only ESP8266 and ESP32."
12+
#endif
13+
#include "Options.h"
14+
15+
class Test;
16+
17+
typedef std::function<bool(HTTPClient *client)> httpResponseCallback;
18+
extern const char *TransferEncoding;
19+
20+
class HTTPService {
21+
friend class Test;
22+
private:
23+
// Server API URL
24+
String _apiURL;
25+
// authetication token
26+
String _authToken;
27+
// Last time in ms we made are a request to server
28+
uint32_t _lastRequestTime = 0;
29+
// HTTP status code of last request to server
30+
int _lastStatusCode = 0;
31+
// Server reponse or library error message for last failed request
32+
String _lastErrorResponse;
33+
// Underlying HTTPClient instance
34+
HTTPClient *_httpClient = nullptr;
35+
// Underlying connection object
36+
WiFiClient *_wifiClient = nullptr;
37+
// Certificate info
38+
const char *_certInfo = nullptr;
39+
#ifdef ESP8266
40+
BearSSL::X509List *_cert = nullptr;
41+
#endif
42+
// Store retry timeout suggested by server after last request
43+
int _lastRetryAfter = 0;
44+
// HTTP options
45+
HTTPOptions _httpOptions;
46+
protected:
47+
// Sets request params
48+
bool beforeRequest(const char *url);
49+
// Handles response
50+
bool afterRequest(int expectedStatusCode, httpResponseCallback cb, bool modifyLastConnStatus = true);
51+
public:
52+
HTTPService(const String &serverUrl, const String &authToken, const char *certInfo, bool insecure);
53+
~HTTPService();
54+
// Sets custom HTTP options. See HTTPOptions doc for more info.
55+
// Must be called before calling any method initiating a connection to server.
56+
// Example:
57+
// service.setHTTPOptions(HTTPOptions().httpReadTimeout(20000)).
58+
void setHTTPOptions(const HTTPOptions &httpOptions);
59+
HTTPOptions &getHTTPOptions() { return _httpOptions; }
60+
// Performs HTTP POST by sending data. On success calls response call back
61+
bool doPOST(const char *url, const char *data, const char *contentType, int expectedCode, httpResponseCallback cb);
62+
// Performs HTTP GET. On success calls response call back
63+
bool doGET(const char *url, int expectedCode, httpResponseCallback cb);
64+
65+
String getServerAPIURL() const { return _apiURL; }
66+
int getLastRetryAfter() const { return _lastRetryAfter; }
67+
int getLastStatusCode() const { return _lastStatusCode; }
68+
uint32_t getLastRequestTime() const { return _lastRequestTime; }
69+
// Returns last response when operation failed
70+
String getLastErrorMessage() const { return _lastErrorResponse; }
71+
};
72+
73+
#endif //_HTTP_SERVICE_H_

src/InfluxDb.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@
2424
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2525
* SOFTWARE.
2626
*/
27+
#ifndef _INFLUXDB_H_
28+
#define _INFLUXDB_H
29+
2730
#include "InfluxData.h"
2831

2932
class Influxdb : public InfluxDBClient {
@@ -53,3 +56,4 @@ class Influxdb : public InfluxDBClient {
5356

5457
void begin();
5558
};
59+
#endif

0 commit comments

Comments
 (0)