Skip to content

Commit cae4039

Browse files
committed
HTTPClient: decouple transport layer handling
1 parent 93d57fa commit cae4039

File tree

2 files changed

+198
-165
lines changed

2 files changed

+198
-165
lines changed

libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp

Lines changed: 155 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -30,138 +30,178 @@
3030

3131
#include "ESP8266HTTPClient.h"
3232

33-
/**
34-
* constractor
35-
*/
36-
HTTPClient::HTTPClient() {
37-
_tcp = NULL;
38-
_tcps = NULL;
33+
class TransportTraits {
34+
public:
35+
virtual std::unique_ptr<WiFiClient> create()
36+
{
37+
return std::unique_ptr<WiFiClient>(new WiFiClient());
38+
}
3939

40-
_port = 0;
40+
virtual bool verify(WiFiClient& client, const char* host)
41+
{
42+
return true;
43+
}
44+
};
4145

42-
_reuse = false;
43-
_tcpTimeout = HTTPCLIENT_DEFAULT_TCP_TIMEOUT;
44-
_useHTTP10 = false;
46+
class TLSTraits : public TransportTraits {
47+
public:
48+
TLSTraits(const String& fingerprint) :
49+
_fingerprint(fingerprint)
50+
{
51+
}
4552

46-
_https = false;
53+
std::unique_ptr<WiFiClient> create() override
54+
{
55+
return std::unique_ptr<WiFiClient>(new WiFiClientSecure());
56+
}
4757

48-
_userAgent = "ESP8266HTTPClient";
58+
bool verify(WiFiClient& client, const char* host) override
59+
{
60+
auto wcs = reinterpret_cast<WiFiClientSecure&>(client);
61+
return wcs.verify(_fingerprint.c_str(), host);
62+
}
4963

50-
_headerKeysCount = 0;
51-
_currentHeaders = NULL;
52-
53-
_returnCode = 0;
54-
_size = -1;
55-
_canReuse = false;
56-
_transferEncoding = HTTPC_TE_IDENTITY;
64+
protected:
65+
String _fingerprint;
66+
};
5767

68+
/**
69+
* constructor
70+
*/
71+
HTTPClient::HTTPClient()
72+
{
5873
}
5974

6075
/**
61-
* deconstractor
76+
* destructor
6277
*/
63-
HTTPClient::~HTTPClient() {
64-
65-
if(_tcps) {
66-
_tcps->stop();
67-
delete _tcps;
68-
_tcps = NULL;
69-
_tcp = NULL;
70-
} else if(_tcp) {
78+
HTTPClient::~HTTPClient()
79+
{
80+
if(_tcp) {
7181
_tcp->stop();
72-
delete _tcp;
73-
_tcp = NULL;
7482
}
75-
7683
if(_currentHeaders) {
7784
delete[] _currentHeaders;
7885
}
7986
}
8087

81-
/**
82-
* phasing the url for all needed informations
83-
* @param url String
84-
* @param httpsFingerprint String
85-
*/
86-
void HTTPClient::begin(String url, String httpsFingerprint) {
8788

88-
DEBUG_HTTPCLIENT("[HTTP-Client][begin] url: %s\n", url.c_str());
89+
bool HTTPClient::begin(String url, String httpsFingerprint) {
90+
if (httpsFingerprint.length() == 0) {
91+
return false;
92+
}
93+
if (!begin(url)) {
94+
return false;
95+
}
96+
_transportTraits = TransportTraitsPtr(new TLSTraits(httpsFingerprint));
97+
DEBUG_HTTPCLIENT("[HTTP-Client][begin] httpsFingerprint: %s\n", httpsFingerprint.c_str());
98+
return true;
99+
}
89100

90-
_httpsFingerprint = httpsFingerprint;
101+
void HTTPClient::clear()
102+
{
91103
_returnCode = 0;
92104
_size = -1;
105+
_headers = "";
106+
}
93107

94-
_Headers = "";
95108

96-
String protocol;
97-
// check for : (http: or https:
98-
int index = url.indexOf(':');
99-
//int index2;
109+
/**
110+
* parsing the url for all needed parameters
111+
* @param url String
112+
*/
113+
bool HTTPClient::begin(String url)
114+
{
115+
DEBUG_HTTPCLIENT("[HTTP-Client][begin] url: %s\n", url.c_str());
100116
bool hasPort = false;
101-
if(index >= 0) {
102-
protocol = url.substring(0, index);
103-
url.remove(0, (index + 3)); // remove http:// or https://
104-
105-
index = url.indexOf('/');
106-
String host = url.substring(0, index);
107-
url.remove(0, index); // remove host part
108-
109-
// get Authorization
110-
index = host.indexOf('@');
111-
if(index >= 0) {
112-
// auth info
113-
String auth = host.substring(0, index);
114-
host.remove(0, index + 1); // remove auth part including @
115-
_base64Authorization = base64::encode(auth);
116-
}
117+
clear();
117118

118-
// get port
119-
index = host.indexOf(':');
120-
if(index >= 0) {
121-
_host = host.substring(0, index); // hostname
122-
host.remove(0, (index + 1)); // remove hostname + :
123-
_port = host.toInt(); // get port
124-
hasPort = true;
125-
} else {
126-
_host = host;
127-
}
119+
// check for : (http: or https:
120+
int index = url.indexOf(':');
121+
if(index < 0) {
122+
DEBUG_HTTPCLIENT("[HTTP-Client][begin] failed to parse protocol\n");
123+
return false;
124+
}
128125

129-
_url = url;
126+
_protocol = url.substring(0, index);
127+
url.remove(0, (index + 3)); // remove http:// or https://
130128

131-
if(protocol.equalsIgnoreCase("http")) {
132-
_https = false;
133-
if(!hasPort) {
134-
_port = 80;
135-
}
136-
} else if(protocol.equalsIgnoreCase("https")) {
137-
_https = true;
138-
if(!hasPort) {
139-
_port = 443;
140-
}
141-
} else {
142-
DEBUG_HTTPCLIENT("[HTTP-Client][begin] protocol: %s unknown?!\n", protocol.c_str());
143-
return;
144-
}
129+
index = url.indexOf('/');
130+
String host = url.substring(0, index);
131+
url.remove(0, index); // remove host part
145132

133+
// get Authorization
134+
index = host.indexOf('@');
135+
if(index >= 0) {
136+
// auth info
137+
String auth = host.substring(0, index);
138+
host.remove(0, index + 1); // remove auth part including @
139+
_base64Authorization = base64::encode(auth);
146140
}
147141

148-
DEBUG_HTTPCLIENT("[HTTP-Client][begin] host: %s port: %d url: %s https: %d httpsFingerprint: %s\n", _host.c_str(), _port, _url.c_str(), _https, _httpsFingerprint.c_str());
142+
// get port
143+
index = host.indexOf(':');
144+
if(index >= 0) {
145+
_host = host.substring(0, index); // hostname
146+
host.remove(0, (index + 1)); // remove hostname + :
147+
_port = host.toInt(); // get port
148+
hasPort = true;
149+
} else {
150+
_host = host;
151+
}
152+
_uri = url;
149153

154+
if(_protocol.equalsIgnoreCase("http")) {
155+
if(!hasPort) {
156+
_port = 80;
157+
}
158+
} else if(_protocol.equalsIgnoreCase("https")) {
159+
if(!hasPort) {
160+
_port = 443;
161+
}
162+
} else {
163+
DEBUG_HTTPCLIENT("[HTTP-Client][begin] protocol: %s unknown?!\n", protocol.c_str());
164+
return false;
165+
}
166+
_transportTraits = TransportTraitsPtr(new TransportTraits());
167+
DEBUG_HTTPCLIENT("[HTTP-Client][begin] host: %s port: %d url: %s\n", _host.c_str(), _port, _uri.c_str());
168+
return true;
150169
}
151170

152-
void HTTPClient::begin(String host, uint16_t port, String url, bool https, String httpsFingerprint) {
171+
bool HTTPClient::begin(String host, uint16_t port, String uri)
172+
{
173+
clear();
153174
_host = host;
154175
_port = port;
155-
_url = url;
156-
_https = https;
157-
_httpsFingerprint = httpsFingerprint;
176+
_uri = uri;
177+
_transportTraits = TransportTraitsPtr(new TransportTraits());
178+
DEBUG_HTTPCLIENT("[HTTP-Client][begin] host: %s port: %d uri: %s\n", host.c_str(), port, uri.c_str());
179+
return true;
180+
}
158181

159-
_returnCode = 0;
160-
_size = -1;
182+
bool HTTPClient::begin(String host, uint16_t port, String uri, bool https, String httpsFingerprint)
183+
{
184+
if (https) {
185+
return begin(host, port, uri, httpsFingerprint);
186+
}
187+
else {
188+
return begin(host, port, uri);
189+
}
190+
}
161191

162-
_Headers = "";
192+
bool HTTPClient::begin(String host, uint16_t port, String uri, String httpsFingerprint)
193+
{
194+
clear();
195+
_host = host;
196+
_port = port;
197+
_uri = uri;
163198

164-
DEBUG_HTTPCLIENT("[HTTP-Client][begin] host: %s port:%d url: %s https: %d httpsFingerprint: %s\n", host, port, url, https, httpsFingerprint);
199+
if (httpsFingerprint.length() == 0) {
200+
return false;
201+
}
202+
_transportTraits = TransportTraitsPtr(new TLSTraits(httpsFingerprint));
203+
DEBUG_HTTPCLIENT("[HTTP-Client][begin] host: %s port: %d url: %s httpsFingerprint: %s\n", host.c_str(), port, uri.c_str(), httpsFingerprint.c_str());
204+
return true;
165205
}
166206

167207
/**
@@ -469,31 +509,30 @@ int HTTPClient::getSize(void) {
469509
}
470510

471511
/**
472-
* deprecated Note: this is not working with https!
473512
* returns the stream of the tcp connection
474513
* @return WiFiClient
475514
*/
476-
WiFiClient & HTTPClient::getStream(void) {
515+
WiFiClient& HTTPClient::getStream(void) {
477516
if(connected()) {
478517
return *_tcp;
479518
}
480519

481-
DEBUG_HTTPCLIENT("[HTTP-Client] no stream to return!?\n");
482-
483-
// todo return error?
520+
DEBUG_HTTPCLIENT("[HTTP-Client] getStream: not connected\n");
521+
static WiFiClient empty;
522+
return empty;
484523
}
485524

486525
/**
487526
* returns the stream of the tcp connection
488527
* @return WiFiClient *
489528
*/
490-
WiFiClient * HTTPClient::getStreamPtr(void) {
529+
WiFiClient* HTTPClient::getStreamPtr(void) {
491530
if(connected()) {
492-
return _tcp;
531+
return _tcp.get();
493532
}
494533

495-
DEBUG_HTTPCLIENT("[HTTP-Client] no stream to return!?\n");
496-
return NULL;
534+
DEBUG_HTTPCLIENT("[HTTP-Client] getStreamPtr: not connected\n");
535+
return nullptr;
497536
}
498537

499538
/**
@@ -642,9 +681,9 @@ void HTTPClient::addHeader(const String& name, const String& value, bool first)
642681
headerLine += "\r\n";
643682

644683
if(first) {
645-
_Headers = headerLine + _Headers;
684+
_headers = headerLine + _headers;
646685
} else {
647-
_Headers += headerLine;
686+
_headers += headerLine;
648687
}
649688
}
650689

@@ -706,39 +745,24 @@ bool HTTPClient::connect(void) {
706745
return true;
707746
}
708747

709-
if(_https) {
710-
DEBUG_HTTPCLIENT("[HTTP-Client] connect https...\n");
711-
if(_tcps) {
712-
delete _tcps;
713-
_tcps = NULL;
714-
_tcp = NULL;
715-
}
716-
_tcps = new WiFiClientSecure();
717-
_tcp = _tcps;
718-
} else {
719-
DEBUG_HTTPCLIENT("[HTTP-Client] connect http...\n");
720-
if(_tcp) {
721-
delete _tcp;
722-
_tcp = NULL;
723-
}
724-
_tcp = new WiFiClient();
748+
if (!_transportTraits) {
749+
DEBUG_HTTPCLIENT("[HTTP-Client] _transportTraits is null (HTTPClient::begin not called?)\n");
750+
return false;
725751
}
726752

753+
_tcp = _transportTraits->create();
754+
727755
if(!_tcp->connect(_host.c_str(), _port)) {
728756
DEBUG_HTTPCLIENT("[HTTP-Client] failed connect to %s:%u\n", _host.c_str(), _port);
729757
return false;
730758
}
731759

732760
DEBUG_HTTPCLIENT("[HTTP-Client] connected to %s:%u\n", _host.c_str(), _port);
733761

734-
if(_https && _httpsFingerprint.length() > 0) {
735-
if(_tcps->verify(_httpsFingerprint.c_str(), _host.c_str())) {
736-
DEBUG_HTTPCLIENT("[HTTP-Client] https certificate matches\n");
737-
} else {
738-
DEBUG_HTTPCLIENT("[HTTP-Client] https certificate doesn't match!\n");
739-
_tcp->stop();
740-
return false;
741-
}
762+
if (!_transportTraits->verify(*_tcp, _host.c_str())) {
763+
DEBUG_HTTPCLIENT("[HTTP-Client] transport level verify failed\n");
764+
_tcp->stop();
765+
return false;
742766
}
743767

744768
// set Timeout for readBytesUntil and readStringUntil
@@ -760,7 +784,7 @@ bool HTTPClient::sendHeader(const char * type) {
760784
return false;
761785
}
762786

763-
String header = String(type) + " " + _url + " HTTP/1.";
787+
String header = String(type) + " " + _uri + " HTTP/1.";
764788

765789
if(_useHTTP10) {
766790
header += "0";
@@ -788,7 +812,7 @@ bool HTTPClient::sendHeader(const char * type) {
788812
header += "Authorization: Basic " + _base64Authorization + "\r\n";
789813
}
790814

791-
header += _Headers + "\r\n";
815+
header += _headers + "\r\n";
792816

793817
return (_tcp->write(header.c_str(), header.length()) == header.length());
794818
}

0 commit comments

Comments
 (0)