1
0
mirror of https://github.com/esp8266/Arduino.git synced 2025-06-13 13:01:55 +03:00

Add WiFiClient parameter to HTTPClient (#4980)

Make HTTPClient take a WiFiClient parameter, allowing you to pass in a
simple HTTP WiFiClient or a BearSSL or axTLS WiFiClientSecure with
any desired verification options.  Deprecate the older, TLSTraits methods.
Add basic HttpsClient example.

Add optional LED feedback to the Update class
This commit is contained in:
Jeroen88
2018-10-06 16:50:03 +02:00
committed by Earle F. Philhower, III
parent 9bc8ea1b58
commit 13f374666d
18 changed files with 765 additions and 168 deletions

View File

@ -21,15 +21,19 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <WiFiClientSecure.h>
#include <StreamString.h>
#include <base64.h>
#include "ESP8266HTTPClient.h"
#ifdef HTTPCLIENT_1_1_COMPATIBLE
#include <ESP8266WiFi.h>
#include <WiFiClientSecure.h>
#endif
#include <StreamString.h>
#include <base64.h>
#ifdef HTTPCLIENT_1_1_COMPATIBLE
class TransportTraits
{
public:
@ -100,12 +104,17 @@ public:
protected:
uint8_t _fingerprint[20];
};
#endif // HTTPCLIENT_1_1_COMPATIBLE
/**
* constructor
*/
HTTPClient::HTTPClient()
{
_client = nullptr;
#ifdef HTTPCLIENT_1_1_COMPATIBLE
_tcpDeprecated.reset(nullptr);
#endif
}
/**
@ -113,8 +122,8 @@ HTTPClient::HTTPClient()
*/
HTTPClient::~HTTPClient()
{
if(_tcp) {
_tcp->stop();
if(_client) {
_client->stop();
}
if(_currentHeaders) {
delete[] _currentHeaders;
@ -130,9 +139,66 @@ void HTTPClient::clear()
}
/**
* parsing the url for all needed parameters
* @param client Client&
* @param url String
* @param https bool
* @return success bool
*/
bool HTTPClient::begin(WiFiClient &client, String url) {
end();
_client = &client;
// check for : (http: or https:)
int index = url.indexOf(':');
if(index < 0) {
DEBUG_HTTPCLIENT("[HTTP-Client][begin] failed to parse protocol\n");
return false;
}
String protocol = url.substring(0, index);
if(protocol != "http" && protocol != "https") {
DEBUG_HTTPCLIENT("[HTTP-Client][begin] unknown protocol '%s'\n", protocol.c_str());
return false;
}
_port = (protocol == "https" ? 443 : 80);
return beginInternal(url, protocol.c_str());
}
/**
* directly supply all needed parameters
* @param client Client&
* @param host String
* @param port uint16_t
* @param uri String
* @param https bool
* @return success bool
*/
bool HTTPClient::begin(WiFiClient &client, String host, uint16_t port, String uri, bool https)
{
end();
_client = &client;
clear();
_host = host;
_port = port;
_uri = uri;
_protocol = (https ? "https" : "http");
return true;
}
#ifdef HTTPCLIENT_1_1_COMPATIBLE
bool HTTPClient::begin(String url, String httpsFingerprint)
{
_transportTraits.reset(nullptr);
if(_client) _canReuse = false;
end();
_port = 443;
if (httpsFingerprint.length() == 0) {
return false;
@ -148,7 +214,9 @@ bool HTTPClient::begin(String url, String httpsFingerprint)
bool HTTPClient::begin(String url, const uint8_t httpsFingerprint[20])
{
_transportTraits.reset(nullptr);
if(_client) _canReuse = false;
end();
_port = 443;
if (!beginInternal(url, "https")) {
return false;
@ -169,7 +237,9 @@ bool HTTPClient::begin(String url, const uint8_t httpsFingerprint[20])
*/
bool HTTPClient::begin(String url)
{
_transportTraits.reset(nullptr);
if(_client) _canReuse = false;
end();
_port = 80;
if (!beginInternal(url, "http")) {
return false;
@ -177,6 +247,7 @@ bool HTTPClient::begin(String url)
_transportTraits = TransportTraitsPtr(new TransportTraits());
return true;
}
#endif // HTTPCLIENT_1_1_COMPATIBLE
bool HTTPClient::beginInternal(String url, const char* expectedProtocol)
{
@ -225,8 +296,12 @@ bool HTTPClient::beginInternal(String url, const char* expectedProtocol)
return true;
}
#ifdef HTTPCLIENT_1_1_COMPATIBLE
bool HTTPClient::begin(String host, uint16_t port, String uri)
{
if(_client) _canReuse = false;
end();
clear();
_host = host;
_port = port;
@ -236,6 +311,8 @@ bool HTTPClient::begin(String host, uint16_t port, String uri)
return true;
}
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
bool HTTPClient::begin(String host, uint16_t port, String uri, bool https, String httpsFingerprint)
{
if (https) {
@ -244,9 +321,13 @@ bool HTTPClient::begin(String host, uint16_t port, String uri, bool https, Strin
return begin(host, port, uri);
}
}
#pragma GCC diagnostic pop
bool HTTPClient::begin(String host, uint16_t port, String uri, String httpsFingerprint)
{
if(_client) _canReuse = false;
end();
clear();
_host = host;
_port = port;
@ -262,6 +343,9 @@ bool HTTPClient::begin(String host, uint16_t port, String uri, String httpsFinge
bool HTTPClient::begin(String host, uint16_t port, String uri, const uint8_t httpsFingerprint[20])
{
if(_client) _canReuse = false;
end();
clear();
_host = host;
_port = port;
@ -275,7 +359,7 @@ bool HTTPClient::begin(String host, uint16_t port, String uri, const uint8_t htt
DEBUG_HTTPCLIENT("\n");
return true;
}
#endif // HTTPCLIENT_1_1_COMPATIBLE
/**
* end
@ -294,17 +378,29 @@ void HTTPClient::end(void)
void HTTPClient::disconnect()
{
if(connected()) {
if(_tcp->available() > 0) {
DEBUG_HTTPCLIENT("[HTTP-Client][end] still data in buffer (%d), clean up.\n", _tcp->available());
while(_tcp->available() > 0) {
_tcp->read();
if(_client) {
if(_client->available() > 0) {
DEBUG_HTTPCLIENT("[HTTP-Client][end] still data in buffer (%d), clean up.\n", _client->available());
while(_client->available() > 0) {
_client->read();
}
}
}
if(_reuse && _canReuse) {
DEBUG_HTTPCLIENT("[HTTP-Client][end] tcp keep open for reuse\n");
} else {
DEBUG_HTTPCLIENT("[HTTP-Client][end] tcp stop\n");
_tcp->stop();
if(_client) {
_client->stop();
_client = nullptr;
}
#ifdef HTTPCLIENT_1_1_COMPATIBLE
if(_tcpDeprecated) {
_transportTraits.reset(nullptr);
_tcpDeprecated.reset(nullptr);
}
#endif
}
} else {
DEBUG_HTTPCLIENT("[HTTP-Client][end] tcp is closed\n");
@ -317,8 +413,8 @@ void HTTPClient::disconnect()
*/
bool HTTPClient::connected()
{
if(_tcp) {
return (_tcp->connected() || (_tcp->available() > 0));
if(_client) {
return (_client->connected() || (_client->available() > 0));
}
return false;
}
@ -376,7 +472,7 @@ void HTTPClient::setTimeout(uint16_t timeout)
{
_tcpTimeout = timeout;
if(connected()) {
_tcp->setTimeout(timeout);
_client->setTimeout(timeout);
}
}
@ -478,7 +574,7 @@ int HTTPClient::sendRequest(const char * type, uint8_t * payload, size_t size)
// send Payload if needed
if(payload && size > 0) {
if(_tcp->write(&payload[0], size) != size) {
if(_client->write(&payload[0], size) != size) {
return returnError(HTTPC_ERROR_SEND_PAYLOAD_FAILED);
}
}
@ -557,7 +653,7 @@ int HTTPClient::sendRequest(const char * type, Stream * stream, size_t size)
int bytesRead = stream->readBytes(buff, readBytes);
// write it to Stream
int bytesWrite = _tcp->write((const uint8_t *) buff, bytesRead);
int bytesWrite = _client->write((const uint8_t *) buff, bytesRead);
bytesWritten += bytesWrite;
// are all Bytes a writen to stream ?
@ -565,11 +661,11 @@ int HTTPClient::sendRequest(const char * type, Stream * stream, size_t size)
DEBUG_HTTPCLIENT("[HTTP-Client][sendRequest] short write, asked for %d but got %d retry...\n", bytesRead, bytesWrite);
// check for write error
if(_tcp->getWriteError()) {
DEBUG_HTTPCLIENT("[HTTP-Client][sendRequest] stream write error %d\n", _tcp->getWriteError());
if(_client->getWriteError()) {
DEBUG_HTTPCLIENT("[HTTP-Client][sendRequest] stream write error %d\n", _client->getWriteError());
//reset write error for retry
_tcp->clearWriteError();
_client->clearWriteError();
}
// some time for the stream
@ -578,7 +674,7 @@ int HTTPClient::sendRequest(const char * type, Stream * stream, size_t size)
int leftBytes = (readBytes - bytesWrite);
// retry to send the missed bytes
bytesWrite = _tcp->write((const uint8_t *) (buff + bytesWrite), leftBytes);
bytesWrite = _client->write((const uint8_t *) (buff + bytesWrite), leftBytes);
bytesWritten += bytesWrite;
if(bytesWrite != leftBytes) {
@ -590,8 +686,8 @@ int HTTPClient::sendRequest(const char * type, Stream * stream, size_t size)
}
// check for write error
if(_tcp->getWriteError()) {
DEBUG_HTTPCLIENT("[HTTP-Client][sendRequest] stream write error %d\n", _tcp->getWriteError());
if(_client->getWriteError()) {
DEBUG_HTTPCLIENT("[HTTP-Client][sendRequest] stream write error %d\n", _client->getWriteError());
free(buff);
return returnError(HTTPC_ERROR_SEND_PAYLOAD_FAILED);
}
@ -642,7 +738,7 @@ int HTTPClient::getSize(void)
WiFiClient& HTTPClient::getStream(void)
{
if(connected()) {
return *_tcp;
return *_client;
}
DEBUG_HTTPCLIENT("[HTTP-Client] getStream: not connected\n");
@ -657,7 +753,7 @@ WiFiClient& HTTPClient::getStream(void)
WiFiClient* HTTPClient::getStreamPtr(void)
{
if(connected()) {
return _tcp.get();
return _client;
}
DEBUG_HTTPCLIENT("[HTTP-Client] getStreamPtr: not connected\n");
@ -697,7 +793,7 @@ int HTTPClient::writeToStream(Stream * stream)
if(!connected()) {
return returnError(HTTPC_ERROR_CONNECTION_LOST);
}
String chunkHeader = _tcp->readStringUntil('\n');
String chunkHeader = _client->readStringUntil('\n');
if(chunkHeader.length() <= 0) {
return returnError(HTTPC_ERROR_READ_TIMEOUT);
@ -734,7 +830,7 @@ int HTTPClient::writeToStream(Stream * stream)
// read trailing \r\n at the end of the chunk
char buf[2];
auto trailing_seq_len = _tcp->readBytes((uint8_t*)buf, 2);
auto trailing_seq_len = _client->readBytes((uint8_t*)buf, 2);
if (trailing_seq_len != 2 || buf[0] != '\r' || buf[1] != '\n') {
return returnError(HTTPC_ERROR_READ_TIMEOUT);
}
@ -903,39 +999,46 @@ bool HTTPClient::hasHeader(const char* name)
*/
bool HTTPClient::connect(void)
{
if(connected()) {
DEBUG_HTTPCLIENT("[HTTP-Client] connect. already connected, try reuse!\n");
while(_tcp->available() > 0) {
_tcp->read();
while(_client->available() > 0) {
_client->read();
}
return true;
}
if (!_transportTraits) {
#ifdef HTTPCLIENT_1_1_COMPATIBLE
if(!_client) {
_tcpDeprecated = _transportTraits->create();
_client = _tcpDeprecated.get();
}
#endif
if(!_client) {
DEBUG_HTTPCLIENT("[HTTP-Client] connect: HTTPClient::begin was not called or returned error\n");
return false;
}
_tcp = _transportTraits->create();
_tcp->setTimeout(_tcpTimeout);
_client->setTimeout(_tcpTimeout);
if(!_tcp->connect(_host.c_str(), _port)) {
if(!_client->connect(_host.c_str(), _port)) {
DEBUG_HTTPCLIENT("[HTTP-Client] failed connect to %s:%u\n", _host.c_str(), _port);
return false;
}
DEBUG_HTTPCLIENT("[HTTP-Client] connected to %s:%u\n", _host.c_str(), _port);
if (!_transportTraits->verify(*_tcp, _host.c_str())) {
#ifdef HTTPCLIENT_1_1_COMPATIBLE
if (_tcpDeprecated && !_transportTraits->verify(*_tcpDeprecated, _host.c_str())) {
DEBUG_HTTPCLIENT("[HTTP-Client] transport level verify failed\n");
_tcp->stop();
_client->stop();
return false;
}
#endif
#ifdef ESP8266
_tcp->setNoDelay(true);
_client->setNoDelay(true);
#endif
return connected();
}
@ -990,7 +1093,7 @@ bool HTTPClient::sendHeader(const char * type)
DEBUG_HTTPCLIENT("[HTTP-Client] sending request header\n-----\n%s-----\n", header.c_str());
return (_tcp->write((const uint8_t *) header.c_str(), header.length()) == header.length());
return (_client->write((const uint8_t *) header.c_str(), header.length()) == header.length());
}
/**
@ -1011,9 +1114,9 @@ int HTTPClient::handleHeaderResponse()
unsigned long lastDataTime = millis();
while(connected()) {
size_t len = _tcp->available();
size_t len = _client->available();
if(len > 0) {
String headerLine = _tcp->readStringUntil('\n');
String headerLine = _client->readStringUntil('\n');
headerLine.trim(); // remove \r
lastDataTime = millis();
@ -1114,7 +1217,7 @@ int HTTPClient::writeToStreamDataBlock(Stream * stream, int size)
while(connected() && (len > 0 || len == -1)) {
// get available data size
size_t sizeAvailable = _tcp->available();
size_t sizeAvailable = _client->available();
if(sizeAvailable) {
@ -1131,7 +1234,7 @@ int HTTPClient::writeToStreamDataBlock(Stream * stream, int size)
}
// read data
int bytesRead = _tcp->readBytes(buff, readBytes);
int bytesRead = _client->readBytes(buff, readBytes);
// write it to Stream
int bytesWrite = stream->write(buff, bytesRead);
@ -1212,7 +1315,7 @@ int HTTPClient::returnError(int error)
DEBUG_HTTPCLIENT("[HTTP-Client][returnError] error(%d): %s\n", error, errorToString(error).c_str());
if(connected()) {
DEBUG_HTTPCLIENT("[HTTP-Client][returnError] tcp stop\n");
_tcp->stop();
_client->stop();
}
}
return error;

View File

@ -20,13 +20,17 @@
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* Modified by Jeroen Döll, June 2018
*/
#ifndef ESP8266HTTPClient_H_
#define ESP8266HTTPClient_H_
#define HTTPCLIENT_1_1_COMPATIBLE
#include <memory>
#include <Arduino.h>
#include <WiFiClient.h>
#ifdef DEBUG_ESP_HTTP_CLIENT
@ -124,8 +128,10 @@ typedef enum {
HTTPC_TE_CHUNKED
} transferEncoding_t;
#ifdef HTTPCLIENT_1_1_COMPATIBLE
class TransportTraits;
typedef std::unique_ptr<TransportTraits> TransportTraitsPtr;
#endif
class StreamString;
@ -135,17 +141,26 @@ public:
HTTPClient();
~HTTPClient();
/*
* Since both begin() functions take a reference to client as a parameter, you need to
* ensure the client object lives the entire time of the HTTPClient
*/
bool begin(WiFiClient &client, String url);
bool begin(WiFiClient &client, String host, uint16_t port, String uri = "/", bool https = false);
#ifdef HTTPCLIENT_1_1_COMPATIBLE
// Plain HTTP connection, unencrypted
bool begin(String url);
bool begin(String host, uint16_t port, String uri = "/");
bool begin(String url) __attribute__ ((deprecated));
bool begin(String host, uint16_t port, String uri = "/") __attribute__ ((deprecated));
// Use axTLS for secure HTTPS connection
bool begin(String url, String httpsFingerprint);
bool begin(String host, uint16_t port, String uri, String httpsFingerprint);
bool begin(String url, String httpsFingerprint) __attribute__ ((deprecated));
bool begin(String host, uint16_t port, String uri, String httpsFingerprint) __attribute__ ((deprecated));
// Use BearSSL for secure HTTPS connection
bool begin(String url, const uint8_t httpsFingerprint[20]);
bool begin(String host, uint16_t port, String uri, const uint8_t httpsFingerprint[20]);
bool begin(String url, const uint8_t httpsFingerprint[20]) __attribute__ ((deprecated));
bool begin(String host, uint16_t port, String uri, const uint8_t httpsFingerprint[20]) __attribute__ ((deprecated));
// deprecated, use the overload above instead
bool begin(String host, uint16_t port, String uri, bool https, String httpsFingerprint) __attribute__ ((deprecated));
#endif
void end(void);
@ -207,8 +222,11 @@ protected:
int writeToStreamDataBlock(Stream * stream, int len);
#ifdef HTTPCLIENT_1_1_COMPATIBLE
TransportTraitsPtr _transportTraits;
std::unique_ptr<WiFiClient> _tcp;
std::unique_ptr<WiFiClient> _tcpDeprecated;
#endif
WiFiClient* _client;
/// request handling
String _host;