1
0
mirror of https://github.com/esp8266/Arduino.git synced 2025-04-19 23:22:16 +03:00

HTTPClient: decouple transport layer handling

This commit is contained in:
Ivan Grokhotkov 2016-04-05 00:25:13 +03:00
parent 93d57fabe2
commit cae4039225
2 changed files with 218 additions and 185 deletions

View File

@ -30,76 +30,100 @@
#include "ESP8266HTTPClient.h"
class TransportTraits {
public:
virtual std::unique_ptr<WiFiClient> create()
{
return std::unique_ptr<WiFiClient>(new WiFiClient());
}
virtual bool verify(WiFiClient& client, const char* host)
{
return true;
}
};
class TLSTraits : public TransportTraits {
public:
TLSTraits(const String& fingerprint) :
_fingerprint(fingerprint)
{
}
std::unique_ptr<WiFiClient> create() override
{
return std::unique_ptr<WiFiClient>(new WiFiClientSecure());
}
bool verify(WiFiClient& client, const char* host) override
{
auto wcs = reinterpret_cast<WiFiClientSecure&>(client);
return wcs.verify(_fingerprint.c_str(), host);
}
protected:
String _fingerprint;
};
/**
* constractor
* constructor
*/
HTTPClient::HTTPClient() {
_tcp = NULL;
_tcps = NULL;
_port = 0;
_reuse = false;
_tcpTimeout = HTTPCLIENT_DEFAULT_TCP_TIMEOUT;
_useHTTP10 = false;
_https = false;
_userAgent = "ESP8266HTTPClient";
_headerKeysCount = 0;
_currentHeaders = NULL;
_returnCode = 0;
_size = -1;
_canReuse = false;
_transferEncoding = HTTPC_TE_IDENTITY;
HTTPClient::HTTPClient()
{
}
/**
* deconstractor
* destructor
*/
HTTPClient::~HTTPClient() {
if(_tcps) {
_tcps->stop();
delete _tcps;
_tcps = NULL;
_tcp = NULL;
} else if(_tcp) {
HTTPClient::~HTTPClient()
{
if(_tcp) {
_tcp->stop();
delete _tcp;
_tcp = NULL;
}
if(_currentHeaders) {
delete[] _currentHeaders;
}
}
/**
* phasing the url for all needed informations
* @param url String
* @param httpsFingerprint String
*/
void HTTPClient::begin(String url, String httpsFingerprint) {
DEBUG_HTTPCLIENT("[HTTP-Client][begin] url: %s\n", url.c_str());
bool HTTPClient::begin(String url, String httpsFingerprint) {
if (httpsFingerprint.length() == 0) {
return false;
}
if (!begin(url)) {
return false;
}
_transportTraits = TransportTraitsPtr(new TLSTraits(httpsFingerprint));
DEBUG_HTTPCLIENT("[HTTP-Client][begin] httpsFingerprint: %s\n", httpsFingerprint.c_str());
return true;
}
_httpsFingerprint = httpsFingerprint;
void HTTPClient::clear()
{
_returnCode = 0;
_size = -1;
_headers = "";
}
_Headers = "";
String protocol;
/**
* parsing the url for all needed parameters
* @param url String
*/
bool HTTPClient::begin(String url)
{
DEBUG_HTTPCLIENT("[HTTP-Client][begin] url: %s\n", url.c_str());
bool hasPort = false;
clear();
// check for : (http: or https:
int index = url.indexOf(':');
//int index2;
bool hasPort = false;
if(index >= 0) {
protocol = url.substring(0, index);
if(index < 0) {
DEBUG_HTTPCLIENT("[HTTP-Client][begin] failed to parse protocol\n");
return false;
}
_protocol = url.substring(0, index);
url.remove(0, (index + 3)); // remove http:// or https://
index = url.indexOf('/');
@ -125,43 +149,59 @@ void HTTPClient::begin(String url, String httpsFingerprint) {
} else {
_host = host;
}
_uri = url;
_url = url;
if(protocol.equalsIgnoreCase("http")) {
_https = false;
if(_protocol.equalsIgnoreCase("http")) {
if(!hasPort) {
_port = 80;
}
} else if(protocol.equalsIgnoreCase("https")) {
_https = true;
} else if(_protocol.equalsIgnoreCase("https")) {
if(!hasPort) {
_port = 443;
}
} else {
DEBUG_HTTPCLIENT("[HTTP-Client][begin] protocol: %s unknown?!\n", protocol.c_str());
return;
return false;
}
_transportTraits = TransportTraitsPtr(new TransportTraits());
DEBUG_HTTPCLIENT("[HTTP-Client][begin] host: %s port: %d url: %s\n", _host.c_str(), _port, _uri.c_str());
return true;
}
}
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());
}
void HTTPClient::begin(String host, uint16_t port, String url, bool https, String httpsFingerprint) {
bool HTTPClient::begin(String host, uint16_t port, String uri)
{
clear();
_host = host;
_port = port;
_url = url;
_https = https;
_httpsFingerprint = httpsFingerprint;
_uri = uri;
_transportTraits = TransportTraitsPtr(new TransportTraits());
DEBUG_HTTPCLIENT("[HTTP-Client][begin] host: %s port: %d uri: %s\n", host.c_str(), port, uri.c_str());
return true;
}
_returnCode = 0;
_size = -1;
bool HTTPClient::begin(String host, uint16_t port, String uri, bool https, String httpsFingerprint)
{
if (https) {
return begin(host, port, uri, httpsFingerprint);
}
else {
return begin(host, port, uri);
}
}
_Headers = "";
bool HTTPClient::begin(String host, uint16_t port, String uri, String httpsFingerprint)
{
clear();
_host = host;
_port = port;
_uri = uri;
DEBUG_HTTPCLIENT("[HTTP-Client][begin] host: %s port:%d url: %s https: %d httpsFingerprint: %s\n", host, port, url, https, httpsFingerprint);
if (httpsFingerprint.length() == 0) {
return false;
}
_transportTraits = TransportTraitsPtr(new TLSTraits(httpsFingerprint));
DEBUG_HTTPCLIENT("[HTTP-Client][begin] host: %s port: %d url: %s httpsFingerprint: %s\n", host.c_str(), port, uri.c_str(), httpsFingerprint.c_str());
return true;
}
/**
@ -469,7 +509,6 @@ int HTTPClient::getSize(void) {
}
/**
* deprecated Note: this is not working with https!
* returns the stream of the tcp connection
* @return WiFiClient
*/
@ -478,9 +517,9 @@ WiFiClient & HTTPClient::getStream(void) {
return *_tcp;
}
DEBUG_HTTPCLIENT("[HTTP-Client] no stream to return!?\n");
// todo return error?
DEBUG_HTTPCLIENT("[HTTP-Client] getStream: not connected\n");
static WiFiClient empty;
return empty;
}
/**
@ -489,11 +528,11 @@ WiFiClient & HTTPClient::getStream(void) {
*/
WiFiClient* HTTPClient::getStreamPtr(void) {
if(connected()) {
return _tcp;
return _tcp.get();
}
DEBUG_HTTPCLIENT("[HTTP-Client] no stream to return!?\n");
return NULL;
DEBUG_HTTPCLIENT("[HTTP-Client] getStreamPtr: not connected\n");
return nullptr;
}
/**
@ -642,9 +681,9 @@ void HTTPClient::addHeader(const String& name, const String& value, bool first)
headerLine += "\r\n";
if(first) {
_Headers = headerLine + _Headers;
_headers = headerLine + _headers;
} else {
_Headers += headerLine;
_headers += headerLine;
}
}
@ -706,24 +745,13 @@ bool HTTPClient::connect(void) {
return true;
}
if(_https) {
DEBUG_HTTPCLIENT("[HTTP-Client] connect https...\n");
if(_tcps) {
delete _tcps;
_tcps = NULL;
_tcp = NULL;
}
_tcps = new WiFiClientSecure();
_tcp = _tcps;
} else {
DEBUG_HTTPCLIENT("[HTTP-Client] connect http...\n");
if(_tcp) {
delete _tcp;
_tcp = NULL;
}
_tcp = new WiFiClient();
if (!_transportTraits) {
DEBUG_HTTPCLIENT("[HTTP-Client] _transportTraits is null (HTTPClient::begin not called?)\n");
return false;
}
_tcp = _transportTraits->create();
if(!_tcp->connect(_host.c_str(), _port)) {
DEBUG_HTTPCLIENT("[HTTP-Client] failed connect to %s:%u\n", _host.c_str(), _port);
return false;
@ -731,15 +759,11 @@ bool HTTPClient::connect(void) {
DEBUG_HTTPCLIENT("[HTTP-Client] connected to %s:%u\n", _host.c_str(), _port);
if(_https && _httpsFingerprint.length() > 0) {
if(_tcps->verify(_httpsFingerprint.c_str(), _host.c_str())) {
DEBUG_HTTPCLIENT("[HTTP-Client] https certificate matches\n");
} else {
DEBUG_HTTPCLIENT("[HTTP-Client] https certificate doesn't match!\n");
if (!_transportTraits->verify(*_tcp, _host.c_str())) {
DEBUG_HTTPCLIENT("[HTTP-Client] transport level verify failed\n");
_tcp->stop();
return false;
}
}
// set Timeout for readBytesUntil and readStringUntil
_tcp->setTimeout(_tcpTimeout);
@ -760,7 +784,7 @@ bool HTTPClient::sendHeader(const char * type) {
return false;
}
String header = String(type) + " " + _url + " HTTP/1.";
String header = String(type) + " " + _uri + " HTTP/1.";
if(_useHTTP10) {
header += "0";
@ -788,7 +812,7 @@ bool HTTPClient::sendHeader(const char * type) {
header += "Authorization: Basic " + _base64Authorization + "\r\n";
}
header += _Headers + "\r\n";
header += _headers + "\r\n";
return (_tcp->write(header.c_str(), header.length()) == header.length());
}

View File

@ -25,6 +25,10 @@
#ifndef ESP8266HTTPClient_H_
#define ESP8266HTTPClient_H_
#include <memory>
#include <Arduino.h>
#include <WiFiClient.h>
#ifdef DEBUG_ESP_HTTP_CLIENT
#ifdef DEBUG_ESP_PORT
#define DEBUG_HTTPCLIENT(...) DEBUG_ESP_PORT.printf( __VA_ARGS__ )
@ -120,13 +124,20 @@ typedef enum {
HTTPC_TE_CHUNKED
} transferEncoding_t;
class TransportTraits;
typedef std::unique_ptr<TransportTraits> TransportTraitsPtr;
class HTTPClient {
public:
HTTPClient();
~HTTPClient();
void begin(String url, String httpsFingerprint = "");
void begin(String host, uint16_t port, String url = "/", bool https = false, String httpsFingerprint = "");
bool begin(String url);
bool begin(String url, String httpsFingerprint);
bool begin(String host, uint16_t port, String uri = "/");
bool begin(String host, uint16_t port, String uri, String httpsFingerprint);
// deprecated, use the overload above instead
bool begin(String host, uint16_t port, String uri, bool https, String httpsFingerprint) __attribute__ ((deprecated));
void end(void);
@ -161,7 +172,7 @@ class HTTPClient {
int getSize(void);
WiFiClient & getStream(void) __attribute__ ((deprecated)) ;
WiFiClient& getStream(void);
WiFiClient* getStreamPtr(void);
int writeToStream(Stream* stream);
String getString(void);
@ -169,45 +180,43 @@ class HTTPClient {
static String errorToString(int error);
protected:
struct RequestArgument {
String key;
String value;
};
WiFiClient * _tcp;
WiFiClientSecure * _tcps;
/// request handling
String _host;
uint16_t _port;
bool _reuse;
uint16_t _tcpTimeout;
bool _useHTTP10;
String _url;
bool _https;
String _httpsFingerprint;
String _Headers;
String _userAgent;
String _base64Authorization;
/// Response handling
RequestArgument* _currentHeaders;
size_t _headerKeysCount;
int _returnCode;
int _size;
bool _canReuse;
transferEncoding_t _transferEncoding;
void clear();
int returnError(int error);
bool connect(void);
bool sendHeader(const char * type);
int handleHeaderResponse();
int writeToStreamDataBlock(Stream * stream, int len);
TransportTraitsPtr _transportTraits;
std::unique_ptr<WiFiClient> _tcp;
/// request handling
String _host;
uint16_t _port = 0;
bool _reuse = false;
uint16_t _tcpTimeout = HTTPCLIENT_DEFAULT_TCP_TIMEOUT;
bool _useHTTP10 = false;
String _uri;
String _protocol;
String _headers;
String _userAgent = "ESP8266HTTPClient";
String _base64Authorization;
/// Response handling
RequestArgument* _currentHeaders = nullptr;
size_t _headerKeysCount = 0;
int _returnCode = 0;
int _size = -1;
bool _canReuse = false;
transferEncoding_t _transferEncoding = HTTPC_TE_IDENTITY;
};