From 9e7456138cb0ee56ac70047202872e3c633b9e3a Mon Sep 17 00:00:00 2001 From: Maxim Prokhorov Date: Sat, 24 Jul 2021 04:03:36 +0300 Subject: [PATCH 1/5] HTTPClient: refactor constructors and operator= - =default for default ctor, move ctor and the assignment move - proxy wificlient through a helper class that handles the pointer - replace headers pointer array with unique_ptr to simplify the move operations - substitue userAgent with the default one when it is empty --- .../src/ESP8266HTTPClient.cpp | 49 +++++------- .../ESP8266HTTPClient/src/ESP8266HTTPClient.h | 75 +++++++++++++++++-- 2 files changed, 86 insertions(+), 38 deletions(-) diff --git a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp index c933b5c21f..754b1dced3 100644 --- a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp +++ b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp @@ -28,6 +28,15 @@ #include #include +// 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, ""); +static_assert(!std::is_copy_constructible_v, ""); +static_assert(std::is_move_constructible_v, ""); +static_assert(std::is_move_assignable_v, ""); + +static const char defaultUserAgent[] PROGMEM = "ESP8266HTTPClient"; + static int StreamReportToHttpClientReport (Stream::Report streamSendError) { switch (streamSendError) @@ -41,27 +50,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; @@ -445,7 +433,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) @@ -546,7 +534,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); @@ -596,7 +584,7 @@ WiFiClient& HTTPClient::getStream(void) WiFiClient* HTTPClient::getStreamPtr(void) { if(connected()) { - return _client; + return _client.get(); } DEBUG_HTTPCLIENT("[HTTP-Client] getStreamPtr: not connected\n"); @@ -791,10 +779,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(_headerKeysCount); for(size_t i = 0; i < _headerKeysCount; i++) { _currentHeaders[i].key = headerKeys[i]; } @@ -915,7 +900,11 @@ bool HTTPClient::sendHeader(const char * type) header += String(_port); } header += F("\r\nUser-Agent: "); - header += _userAgent; + if (!_userAgent.length()) { + header += defaultUserAgent; + } else { + header += _userAgent; + } if (!_useHTTP10) { header += F("\r\nAccept-Encoding: identity;q=1,chunked;q=0.1,*;q=0"); @@ -936,7 +925,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(); } /** diff --git a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h index 3605a807f3..c3ac2ed932 100644 --- a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h +++ b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h @@ -26,11 +26,12 @@ #ifndef ESP8266HTTPClient_H_ #define ESP8266HTTPClient_H_ -#include #include #include #include +#include + #ifdef DEBUG_ESP_HTTP_CLIENT #ifdef DEBUG_ESP_PORT #define DEBUG_HTTPCLIENT(fmt, ...) DEBUG_ESP_PORT.printf_P( (PGM_P)PSTR(fmt), ## __VA_ARGS__ ) @@ -151,8 +152,10 @@ typedef std::unique_ptr 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 @@ -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(); @@ -232,7 +293,7 @@ class HTTPClient int handleHeaderResponse(); int writeToStreamDataBlock(Stream * stream, int len); - WiFiClient* _client; + NonOwningClientPtr _client; /// request handling String _host; @@ -248,8 +309,8 @@ class HTTPClient String _base64Authorization; /// Response handling - RequestArgument* _currentHeaders = nullptr; - size_t _headerKeysCount = 0; + std::unique_ptr _currentHeaders; + size_t _headerKeysCount = 0; int _returnCode = 0; int _size = -1; @@ -261,6 +322,4 @@ class HTTPClient std::unique_ptr _payload; }; - - #endif /* ESP8266HTTPClient_H_ */ From 8e267554625c8892b69d52fcc49f261dea3f927e Mon Sep 17 00:00:00 2001 From: Maxim Prokhorov Date: Sun, 25 Jul 2021 00:25:34 +0300 Subject: [PATCH 2/5] initialize with default user agent but never reset it back --- libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp | 9 +++------ libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h | 4 +++- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp index 754b1dced3..5aca23a847 100644 --- a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp +++ b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp @@ -35,7 +35,8 @@ static_assert(!std::is_copy_constructible_v, ""); static_assert(std::is_move_constructible_v, ""); static_assert(std::is_move_assignable_v, ""); -static const char defaultUserAgent[] PROGMEM = "ESP8266HTTPClient"; +static const char defaultUserAgentPstr[] PROGMEM = "ESP8266HTTPClient"; +const String HTTPClient::defaultUserAgent = defaultUserAgentPstr; static int StreamReportToHttpClientReport (Stream::Report streamSendError) { @@ -900,11 +901,7 @@ bool HTTPClient::sendHeader(const char * type) header += String(_port); } header += F("\r\nUser-Agent: "); - if (!_userAgent.length()) { - header += defaultUserAgent; - } else { - header += _userAgent; - } + header += _userAgent; if (!_useHTTP10) { header += F("\r\nAccept-Encoding: identity;q=1,chunked;q=0.1,*;q=0"); diff --git a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h index c3ac2ed932..3f6ea01078 100644 --- a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h +++ b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h @@ -305,9 +305,11 @@ class HTTPClient String _uri; String _protocol; String _headers; - String _userAgent; String _base64Authorization; + static const String defaultUserAgent; + String _userAgent = defaultUserAgent; + /// Response handling std::unique_ptr _currentHeaders; size_t _headerKeysCount = 0; From bf8d804d14638e04c4c8fc0bcaf4ab32c26f2b36 Mon Sep 17 00:00:00 2001 From: Maxim Prokhorov Date: Mon, 6 Sep 2021 13:34:07 +0300 Subject: [PATCH 3/5] handle wificlient lifetime through the wificlient itself --- .../src/ESP8266HTTPClient.cpp | 6 +- .../ESP8266HTTPClient/src/ESP8266HTTPClient.h | 56 +------------------ 2 files changed, 5 insertions(+), 57 deletions(-) diff --git a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp index 5aca23a847..c327d1b99c 100644 --- a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp +++ b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp @@ -69,8 +69,6 @@ void HTTPClient::clear() * @return success bool */ bool HTTPClient::begin(WiFiClient &client, const String& url) { - _client = &client; - // check for : (http: or https:) int index = url.indexOf(':'); if(index < 0) { @@ -86,6 +84,8 @@ bool HTTPClient::begin(WiFiClient &client, const String& url) { } _port = (protocol == "https" ? 443 : 80); + _client = std::make_unique(client); + return beginInternal(url, protocol.c_str()); } @@ -101,7 +101,7 @@ bool HTTPClient::begin(WiFiClient &client, const String& url) { */ bool HTTPClient::begin(WiFiClient &client, const String& host, uint16_t port, const String& uri, bool https) { - _client = &client; + _client = std::make_unique(client); clear(); _host = host; diff --git a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h index 3f6ea01078..ed8d841435 100644 --- a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h +++ b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h @@ -157,10 +157,7 @@ class HTTPClient HTTPClient(HTTPClient&&) = default; HTTPClient& operator=(HTTPClient&&) = default; -/* - * 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 - */ + // Note that WiFiClient instance *will* be captured by the internal handler. bool begin(WiFiClient &client, const String& url); bool begin(WiFiClient &client, const String& host, uint16_t port, const String& uri = "/", bool https = false); @@ -235,55 +232,6 @@ class HTTPClient // 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(); @@ -293,7 +241,7 @@ class HTTPClient int handleHeaderResponse(); int writeToStreamDataBlock(Stream * stream, int len); - NonOwningClientPtr _client; + std::unique_ptr _client; /// request handling String _host; From f681e0b3b02cdab0376b355ba052c7c7767dca65 Mon Sep 17 00:00:00 2001 From: Maxim Prokhorov Date: Mon, 6 Sep 2021 16:01:58 +0300 Subject: [PATCH 4/5] ...but make sure to call the actual copy ctor of the object so wificlientsecure : public wificlient copy does not slice us back to a basic wificlient --- libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp | 4 ++-- libraries/ESP8266WiFi/src/WiFiClient.cpp | 4 ++++ libraries/ESP8266WiFi/src/WiFiClient.h | 6 ++++++ libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h | 8 ++++++++ 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp index c327d1b99c..837cfd4462 100644 --- a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp +++ b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp @@ -84,7 +84,7 @@ bool HTTPClient::begin(WiFiClient &client, const String& url) { } _port = (protocol == "https" ? 443 : 80); - _client = std::make_unique(client); + _client = client.clone(); return beginInternal(url, protocol.c_str()); } @@ -101,7 +101,7 @@ bool HTTPClient::begin(WiFiClient &client, const String& url) { */ bool HTTPClient::begin(WiFiClient &client, const String& host, uint16_t port, const String& uri, bool https) { - _client = std::make_unique(client); + _client = client.clone(); clear(); _host = host; diff --git a/libraries/ESP8266WiFi/src/WiFiClient.cpp b/libraries/ESP8266WiFi/src/WiFiClient.cpp index decc7d7ac0..74abc23655 100644 --- a/libraries/ESP8266WiFi/src/WiFiClient.cpp +++ b/libraries/ESP8266WiFi/src/WiFiClient.cpp @@ -101,6 +101,10 @@ WiFiClient::~WiFiClient() _client->unref(); } +std::unique_ptr WiFiClient::clone() const { + return std::make_unique(*this); +} + WiFiClient::WiFiClient(const WiFiClient& other) { _client = other._client; diff --git a/libraries/ESP8266WiFi/src/WiFiClient.h b/libraries/ESP8266WiFi/src/WiFiClient.h index 038e8032df..5b9baaa3ea 100644 --- a/libraries/ESP8266WiFi/src/WiFiClient.h +++ b/libraries/ESP8266WiFi/src/WiFiClient.h @@ -52,6 +52,12 @@ class WiFiClient : public Client, public SList { WiFiClient(const WiFiClient&); WiFiClient& operator=(const WiFiClient&); + // b/c wificlient is both a real class and the virtual base for secure client, + // make sure there's a safe way to clone the object without accidentally 'slicing' it + // (...which will happen when using normal copy ctor with the dereferenced pointer...) + // TODO: but, actually, implement secure handlers in the ClientContext instead, so normal copy works + virtual std::unique_ptr clone() const; + virtual uint8_t status(); virtual int connect(IPAddress ip, uint16_t port) override; virtual int connect(const char *host, uint16_t port) override; diff --git a/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h b/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h index 73dc9e7337..2ae534e365 100644 --- a/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h +++ b/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h @@ -39,6 +39,12 @@ class WiFiClientSecureCtx : public WiFiClient { WiFiClientSecureCtx& operator=(const WiFiClientSecureCtx&) = delete; + // TODO: usage is invalid invalid, but this will trigger an error only when + // it is actually used by something and `=delete`ed ctor above is accessed + std::unique_ptr clone() const override { + return std::unique_ptr(new WiFiClientSecureCtx(*this)); + } + int connect(IPAddress ip, uint16_t port) override; int connect(const String& host, uint16_t port) override; int connect(const char* name, uint16_t port) override; @@ -239,6 +245,8 @@ class WiFiClientSecure : public WiFiClient { WiFiClientSecure& operator=(const WiFiClientSecure&) = default; // The shared-ptrs handle themselves automatically + std::unique_ptr clone() const override { return std::unique_ptr(new WiFiClientSecure(*this)); } + uint8_t status() override { return _ctx->status(); } int connect(IPAddress ip, uint16_t port) override { return _ctx->connect(ip, port); } int connect(const String& host, uint16_t port) override { return _ctx->connect(host, port); } From 5cded435c966752743a6a40cb782b0fb98d37b7a Mon Sep 17 00:00:00 2001 From: Maxim Prokhorov Date: Sat, 18 Sep 2021 03:17:32 +0300 Subject: [PATCH 5/5] proofreading --- .../ESP8266HTTPClient/src/ESP8266HTTPClient.h | 19 +++++++++---------- libraries/ESP8266WiFi/src/WiFiClient.h | 14 ++++++++++---- .../ESP8266WiFi/src/WiFiClientSecureBearSSL.h | 15 ++++++++++++--- 3 files changed, 31 insertions(+), 17 deletions(-) diff --git a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h index 175e752d92..9e3aef9ecd 100644 --- a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h +++ b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h @@ -157,7 +157,7 @@ class HTTPClient HTTPClient(HTTPClient&&) = default; HTTPClient& operator=(HTTPClient&&) = default; - // Note that WiFiClient instance *will* be captured by the internal handler. + // Note that WiFiClient's underlying connection *will* be captured bool begin(WiFiClient &client, const String& url); bool begin(WiFiClient &client, const String& host, uint16_t port, const String& uri = "/", bool https = false); @@ -226,15 +226,6 @@ 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. - bool beginInternal(const String& url, const char* expectedProtocol); void disconnect(bool preserveClient = false); void clear(); @@ -244,6 +235,14 @@ class HTTPClient int handleHeaderResponse(); int writeToStreamDataBlock(Stream * stream, int len); + // The common pattern to use the class is to + // { + // WiFiClient socket; + // HTTPClient http; + // http.begin(socket, "http://blahblah"); + // } + // Make sure it's not possible to break things in an opposite direction + std::unique_ptr _client; /// request handling diff --git a/libraries/ESP8266WiFi/src/WiFiClient.h b/libraries/ESP8266WiFi/src/WiFiClient.h index 4867128688..4269dcbdf0 100644 --- a/libraries/ESP8266WiFi/src/WiFiClient.h +++ b/libraries/ESP8266WiFi/src/WiFiClient.h @@ -52,10 +52,16 @@ class WiFiClient : public Client, public SList { WiFiClient(const WiFiClient&); WiFiClient& operator=(const WiFiClient&); - // b/c wificlient is both a real class and the virtual base for secure client, - // make sure there's a safe way to clone the object without accidentally 'slicing' it - // (...which will happen when using normal copy ctor with the dereferenced pointer...) - // TODO: but, actually, implement secure handlers in the ClientContext instead, so normal copy works + // b/c this is both a real class and a virtual parent of the secure client, make sure + // there's a safe way to copy from the pointer without 'slicing' it; i.e. only the base + // portion of a derived object will be copied, and the polymorphic behavior will be corrupted. + // + // this class still implements the copy and assignment though, so this is not yet enforced + // (but, *should* be inside the Core itself, see httpclient & server) + // + // ref. + // - https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rc-copy-virtual + // - https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rh-copy virtual std::unique_ptr clone() const; virtual uint8_t status(); diff --git a/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h b/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h index 2ae534e365..8a77648d90 100644 --- a/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h +++ b/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h @@ -39,8 +39,9 @@ class WiFiClientSecureCtx : public WiFiClient { WiFiClientSecureCtx& operator=(const WiFiClientSecureCtx&) = delete; - // TODO: usage is invalid invalid, but this will trigger an error only when - // it is actually used by something and `=delete`ed ctor above is accessed + // TODO: usage is invalid b/c of deleted copy, but this will only trigger an error when it is actually used by something + // TODO: don't remove just yet to avoid including the WiFiClient default implementation and unintentionally causing + // a 'slice' that this method tries to avoid in the first place std::unique_ptr clone() const override { return std::unique_ptr(new WiFiClientSecureCtx(*this)); } @@ -237,13 +238,21 @@ class WiFiClientSecure : public WiFiClient { // Instead, all virtual functions call their counterpart in "WiFiClientecureCtx* _ctx" // which also derives from WiFiClient (this parent is the one which is eventually used) + // TODO: notice that this complicates the implementation by having two distinct ways the client connection is managed, consider: + // - implementing the secure connection details in the ClientContext + // (i.e. delegate the write & read functions there) + // - simplify the inheritance chain by implementing base wificlient class and inherit the original wificlient and wificlientsecure from it + // - abstract internals so it's possible to seamlessly =default copy and move with the instance *without* resorting to manual copy and initialization of each member + + // TODO: prefer implementing virtual overrides in the .cpp (or, at least one of them) + public: WiFiClientSecure():_ctx(new WiFiClientSecureCtx()) { _owned = _ctx.get(); } WiFiClientSecure(const WiFiClientSecure &rhs): WiFiClient(), _ctx(rhs._ctx) { if (_ctx) _owned = _ctx.get(); } ~WiFiClientSecure() override { _ctx = nullptr; } - WiFiClientSecure& operator=(const WiFiClientSecure&) = default; // The shared-ptrs handle themselves automatically + WiFiClientSecure& operator=(const WiFiClientSecure&) = default; std::unique_ptr clone() const override { return std::unique_ptr(new WiFiClientSecure(*this)); }