Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Movable HTTPClient and fixing WiFiClient copy #8237

Merged
merged 10 commits into from
Oct 13, 2021
44 changes: 15 additions & 29 deletions libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,16 @@
#include <StreamDev.h>
#include <base64.h>

// per https://github.com/esp8266/Arduino/issues/8231
// make sure HTTPClient can be utilized as a movable class member
static_assert(std::is_default_constructible_v<HTTPClient>, "");
static_assert(!std::is_copy_constructible_v<HTTPClient>, "");
static_assert(std::is_move_constructible_v<HTTPClient>, "");
static_assert(std::is_move_assignable_v<HTTPClient>, "");

static const char defaultUserAgentPstr[] PROGMEM = "ESP8266HTTPClient";
const String HTTPClient::defaultUserAgent = defaultUserAgentPstr;

static int StreamReportToHttpClientReport (Stream::Report streamSendError)
{
switch (streamSendError)
Expand All @@ -41,27 +51,6 @@ static int StreamReportToHttpClientReport (Stream::Report streamSendError)
return 0; // never reached, keep gcc quiet
}

/**
* constructor
*/
HTTPClient::HTTPClient()
: _client(nullptr), _userAgent(F("ESP8266HTTPClient"))
{
}

/**
* destructor
*/
HTTPClient::~HTTPClient()
{
if(_client) {
_client->stop();
}
if(_currentHeaders) {
delete[] _currentHeaders;
}
}

void HTTPClient::clear()
{
_returnCode = 0;
Expand Down Expand Up @@ -445,7 +434,7 @@ int HTTPClient::sendRequest(const char * type, const uint8_t * payload, size_t s
}

// transfer all of it, with send-timeout
if (size && StreamConstPtr(payload, size).sendAll(_client) != size)
if (size && StreamConstPtr(payload, size).sendAll(_client.get()) != size)
return returnError(HTTPC_ERROR_SEND_PAYLOAD_FAILED);

// handle Server Response (Header)
Expand Down Expand Up @@ -546,7 +535,7 @@ int HTTPClient::sendRequest(const char * type, Stream * stream, size_t size)
}

// transfer all of it, with timeout
size_t transferred = stream->sendSize(_client, size);
size_t transferred = stream->sendSize(_client.get(), size);
if (transferred != size)
{
DEBUG_HTTPCLIENT("[HTTP-Client][sendRequest] short write, asked for %d but got %d failed.\n", size, transferred);
Expand Down Expand Up @@ -596,7 +585,7 @@ WiFiClient& HTTPClient::getStream(void)
WiFiClient* HTTPClient::getStreamPtr(void)
{
if(connected()) {
return _client;
return _client.get();
}

DEBUG_HTTPCLIENT("[HTTP-Client] getStreamPtr: not connected\n");
Expand Down Expand Up @@ -791,10 +780,7 @@ void HTTPClient::addHeader(const String& name, const String& value, bool first,
void HTTPClient::collectHeaders(const char* headerKeys[], const size_t headerKeysCount)
{
_headerKeysCount = headerKeysCount;
if(_currentHeaders) {
delete[] _currentHeaders;
}
_currentHeaders = new RequestArgument[_headerKeysCount];
_currentHeaders = std::make_unique<RequestArgument[]>(_headerKeysCount);
for(size_t i = 0; i < _headerKeysCount; i++) {
_currentHeaders[i].key = headerKeys[i];
}
Expand Down Expand Up @@ -936,7 +922,7 @@ bool HTTPClient::sendHeader(const char * type)
DEBUG_HTTPCLIENT("[HTTP-Client] sending request header\n-----\n%s-----\n", header.c_str());

// transfer all of it, with timeout
return StreamConstPtr(header).sendAll(_client) == header.length();
return StreamConstPtr(header).sendAll(_client.get()) == header.length();
}

/**
Expand Down
79 changes: 70 additions & 9 deletions libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,12 @@
#ifndef ESP8266HTTPClient_H_
#define ESP8266HTTPClient_H_

#include <memory>
#include <Arduino.h>
#include <StreamString.h>
#include <WiFiClient.h>

#include <memory>
mcspr marked this conversation as resolved.
Show resolved Hide resolved

#ifdef DEBUG_ESP_HTTP_CLIENT
#ifdef DEBUG_ESP_PORT
#define DEBUG_HTTPCLIENT(fmt, ...) DEBUG_ESP_PORT.printf_P( (PGM_P)PSTR(fmt), ## __VA_ARGS__ )
Expand Down Expand Up @@ -151,8 +152,10 @@ typedef std::unique_ptr<TransportTraits> TransportTraitsPtr;
class HTTPClient
{
public:
HTTPClient();
~HTTPClient();
HTTPClient() = default;
~HTTPClient() = default;
HTTPClient(HTTPClient&&) = default;
HTTPClient& operator=(HTTPClient&&) = default;

/*
* Since both begin() functions take a reference to client as a parameter, you need to
Expand Down Expand Up @@ -223,6 +226,64 @@ class HTTPClient
String value;
};

// TODO: the common pattern to use the class is to
// {
// WiFiClient socket;
// HTTPClient http;
// http.begin(socket, "http://blahblah");
// }
// in case wificlient supports seamless ref() / unref() of the underlying connection
// for both wificlient and wificlientsecure, this may be removed in favour of that approach.

struct NonOwningClientPtr {
NonOwningClientPtr() = default;
NonOwningClientPtr(const NonOwningClientPtr&) = delete;
NonOwningClientPtr(NonOwningClientPtr&& other) noexcept {
*this = std::move(other);
}

~NonOwningClientPtr() noexcept {
if (_ptr) {
_ptr->stop();
}
}

explicit NonOwningClientPtr(WiFiClient* ptr) noexcept :
_ptr(ptr)
{}

explicit operator bool() const {
return _ptr != nullptr;
}

WiFiClient* get() const noexcept {
return _ptr;
}

WiFiClient& operator*() const {
return *_ptr;
}

WiFiClient* operator->() const noexcept {
return _ptr;
}

NonOwningClientPtr& operator=(WiFiClient* ptr) noexcept {
_ptr = ptr;
return *this;
}

NonOwningClientPtr& operator=(const NonOwningClientPtr&) = delete;
NonOwningClientPtr& operator=(NonOwningClientPtr&& other) noexcept {
_ptr = other._ptr;
other._ptr = nullptr;
return *this;
}

private:
WiFiClient* _ptr = nullptr;
};

bool beginInternal(const String& url, const char* expectedProtocol);
void disconnect(bool preserveClient = false);
void clear();
Expand All @@ -232,7 +293,7 @@ class HTTPClient
int handleHeaderResponse();
int writeToStreamDataBlock(Stream * stream, int len);

WiFiClient* _client;
NonOwningClientPtr _client;

/// request handling
String _host;
Expand All @@ -244,12 +305,14 @@ class HTTPClient
String _uri;
String _protocol;
String _headers;
String _userAgent;
String _base64Authorization;

static const String defaultUserAgent;
String _userAgent = defaultUserAgent;

/// Response handling
RequestArgument* _currentHeaders = nullptr;
size_t _headerKeysCount = 0;
std::unique_ptr<RequestArgument[]> _currentHeaders;
size_t _headerKeysCount = 0;

int _returnCode = 0;
int _size = -1;
Expand All @@ -261,6 +324,4 @@ class HTTPClient
std::unique_ptr<StreamString> _payload;
};



#endif /* ESP8266HTTPClient_H_ */