From f58889337342fe845e9370703ba62e6c0d6468e1 Mon Sep 17 00:00:00 2001 From: Niek van der Maas Date: Mon, 18 Nov 2019 00:45:49 +0100 Subject: [PATCH] Use generic typechecks, OTA add followRedirects (#1974) * Use generic typechecks, OTA add followRedirects * revamp using is_detected * relative include * mention ts draft instead, fix c++17 includes (still not buildable) * typo * Always create WiFiClient pointer * Remove old CREATE_CHECK macro * do not create wificlient obj when running old Core --- code/espurna/config/dependencies.h | 5 -- code/espurna/config/general.h | 4 -- code/espurna/libs/HeapStats.h | 33 ++++--------- code/espurna/libs/TypeChecks.h | 70 +++++++++++++++++++++++++++ code/espurna/ota_httpupdate.ino | 50 +++++++++++++++---- code/test/build/extra/secure_client.h | 1 + 6 files changed, 120 insertions(+), 43 deletions(-) create mode 100644 code/espurna/libs/TypeChecks.h diff --git a/code/espurna/config/dependencies.h b/code/espurna/config/dependencies.h index 9183e7c1..5e24d7c0 100644 --- a/code/espurna/config/dependencies.h +++ b/code/espurna/config/dependencies.h @@ -82,11 +82,6 @@ #define NTP_SUPPORT 1 // Scheduler needs NTP #endif -#if (SECURE_CLIENT == SECURE_CLIENT_BEARSSL) -#undef OTA_CLIENT_HTTPUPDATE_2_3_0_COMPATIBLE -#define OTA_CLIENT_HTTPUPDATE_2_3_0_COMPATIBLE 0 // Use new HTTPUpdate API with BearSSL -#endif - #if LWIP_VERSION_MAJOR != 1 #undef MDNS_CLIENT_SUPPORT #define MDNS_CLIENT_SUPPORT 0 // default resolver already handles this diff --git a/code/espurna/config/general.h b/code/espurna/config/general.h index 9658232c..4795303e 100644 --- a/code/espurna/config/general.h +++ b/code/espurna/config/general.h @@ -792,10 +792,6 @@ // OTA_CLIENT_HTTPUPDATE (Arduino Core library) #endif -#ifndef OTA_CLIENT_HTTPUPDATE_2_3_0_COMPATIBLE -#define OTA_CLIENT_HTTPUPDATE_2_3_0_COMPATIBLE 1 // Use old HTTPUpdate API by default -#endif - #define OTA_GITHUB_FP "CA:06:F5:6B:25:8B:7A:0D:4F:2B:05:47:09:39:47:86:51:15:19:84" #ifndef OTA_FINGERPRINT diff --git a/code/espurna/libs/HeapStats.h b/code/espurna/libs/HeapStats.h index 70f90f9a..a74752b4 100644 --- a/code/espurna/libs/HeapStats.h +++ b/code/espurna/libs/HeapStats.h @@ -6,7 +6,7 @@ Show extended heap stats when EspClass::getHeapStats() is available #pragma once -#include +#include "TypeChecks.h" struct heap_stats_t { uint32_t available; @@ -14,43 +14,28 @@ struct heap_stats_t { uint8_t frag_pct; }; -namespace EspClass_has_getHeapStats { - struct _detector { - template().getHeapStats(0,0,0))> - static std::true_type detect(int); - - template - static std::false_type detect(...); - }; - +namespace heap_stats { template - struct detector : public _detector { - using result = decltype( - std::declval().template detect(0)); - }; + using has_getHeapStats_t = decltype(std::declval().getHeapStats(0,0,0)); template - struct typed_check : public detector::result { - }; - - typed_check check{}; -}; + using has_getHeapStats = is_detected; +} template -void _getHeapStats(std::true_type&, T& instance, heap_stats_t& stats) { +void _getHeapStats(const std::true_type&, T& instance, heap_stats_t& stats) { instance.getHeapStats(&stats.available, &stats.usable, &stats.frag_pct); } template -void _getHeapStats(std::false_type&, T& instance, heap_stats_t& stats) { +void _getHeapStats(const std::false_type&, T& instance, heap_stats_t& stats) { stats.available = instance.getFreeHeap(); stats.usable = 0; stats.frag_pct = 0; } void getHeapStats(heap_stats_t& stats) { - _getHeapStats(EspClass_has_getHeapStats::check, ESP, stats); + _getHeapStats(heap_stats::has_getHeapStats{}, ESP, stats); } // WTF @@ -106,7 +91,7 @@ void infoHeapStats(const char* name, const heap_stats_t& stats) { void infoHeapStats(bool show_frag_stats = true) { const auto stats = getHeapStats(); infoMemory("Heap", stats); - if (show_frag_stats && EspClass_has_getHeapStats::check) { + if (show_frag_stats && heap_stats::has_getHeapStats{}) { infoHeapStats("Heap", stats); } } diff --git a/code/espurna/libs/TypeChecks.h b/code/espurna/libs/TypeChecks.h new file mode 100644 index 00000000..dc0b027f --- /dev/null +++ b/code/espurna/libs/TypeChecks.h @@ -0,0 +1,70 @@ +// ----------------------------------------------------------------------------- +// Detection idiom adapted from "Working Draft, C++ Extensions for Library Fundamentals, Version 3": +// https://cplusplus.github.io/fundamentals-ts/v3.html#meta.detect +// ----------------------------------------------------------------------------- + +#pragma once + +#include + +// In case we do support c++17 just use the headers shipped with the GCC +#if __cplusplus >= 201703L +#include +#include +using std::experimental::is_detected; +#else + +namespace experimental_type_traits { + namespace implementation { + // Small workaround for GCC-4.8.2 to *really* trigger substitution error with void_t + // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64395 + // https://stackoverflow.com/a/35754473 + template + struct make_void { + using type = void; + }; + + template + using void_t = typename make_void::type; + + // Return dummy type as the Default type + struct nonesuch { + nonesuch() = delete; + ~nonesuch() = delete; + nonesuch(nonesuch const&) = delete; + void operator=(nonesuch const&) = delete; + }; + + // Implementation of the detection idiom (negative case). + template class Op, typename... Args> + struct detector { + using value_t = std::false_type; + using type = Default; + }; + + // Implementation of the detection idiom (positive case). + template class Op, + typename... Args> + struct detector>, Op, Args...> { + using value_t = std::true_type; + using type = Op; + }; + } + + template class Op, typename... Args> + using is_detected = typename implementation::detector::value_t; + + template class Op, class... Args> + using detected_t = typename implementation::detector::type; + + template class Op, class... Args> + using detected_or = implementation::detector; + + // ... + // implement the rest as needed. some things may not work though +} + +using experimental_type_traits::is_detected; + +#endif // __cplusplus >= 201703L diff --git a/code/espurna/ota_httpupdate.ino b/code/espurna/ota_httpupdate.ino index 0588cfc4..95671952 100644 --- a/code/espurna/ota_httpupdate.ino +++ b/code/espurna/ota_httpupdate.ino @@ -18,6 +18,7 @@ Copyright (C) 2019 by Maxim Prokhorov #include #include "libs/URL.h" +#include "libs/TypeChecks.h" #if SECURE_CLIENT != SECURE_CLIENT_NONE @@ -30,6 +31,38 @@ Copyright (C) 2019 by Maxim Prokhorov #endif // SECURE_CLIENT != SECURE_CLIENT_NONE +template +void _otaFollowRedirects(const std::true_type&, T& instance) { + instance.followRedirects(true); +} + +template +void _otaFollowRedirects(const std::false_type&, T& instance) { +} + +template +t_httpUpdate_return _otaClientUpdate(const std::true_type&, T& instance, WiFiClient* client, const String& url) { + return instance.update(*client, url); +} + +template +t_httpUpdate_return _otaClientUpdate(const std::false_type&, T& instance, WiFiClient*, const String& url) { + return instance.update(url); +} + +namespace ota { + template + using has_followRedirects_t = decltype(std::declval().followRedirects(std::declval())); + + template + using has_followRedirects = is_detected; + + template + using has_WiFiClient_argument_t = decltype(std::declval().update(std::declval(), std::declval())); + + template + using has_WiFiClient_argument = is_detected; +} void _otaClientRunUpdater(WiFiClient* client, const String& url, const String& fp = "") { @@ -45,6 +78,8 @@ void _otaClientRunUpdater(WiFiClient* client, const String& url, const String& f // NOTE: ESPhttpUpdate.update(..., fp) will **always** fail with empty fingerprint // NOTE: It is possible to support BearSSL with 2.4.2 by using uint8_t[20] instead of String for fingerprint argument + _otaFollowRedirects(ota::has_followRedirects{}, ESPhttpUpdate); + ESPhttpUpdate.rebootOnUpdate(false); t_httpUpdate_return result = HTTP_UPDATE_NO_UPDATES; @@ -55,10 +90,8 @@ void _otaClientRunUpdater(WiFiClient* client, const String& url, const String& f } else { result = ESPhttpUpdate.update(url); } - #elif OTA_CLIENT_HTTPUPDATE_2_3_0_COMPATIBLE - result = ESPhttpUpdate.update(url); #else - result = ESPhttpUpdate.update(*client, url); + result = _otaClientUpdate(ota::has_WiFiClient_argument{}, ESPhttpUpdate, client, url); #endif switch (result) { @@ -78,16 +111,13 @@ void _otaClientRunUpdater(WiFiClient* client, const String& url, const String& f } -#if OTA_CLIENT_HTTPUPDATE_2_3_0_COMPATIBLE -void _otaClientFromHttp(const String& url) { - _otaClientRunUpdater(nullptr, url, ""); -} -#else void _otaClientFromHttp(const String& url) { - auto client = std::make_unique(); + std::unique_ptr client(nullptr); + if (ota::has_WiFiClient_argument{}) { + client = std::make_unique(); + } _otaClientRunUpdater(client.get(), url, ""); } -#endif #if SECURE_CLIENT == SECURE_CLIENT_BEARSSL diff --git a/code/test/build/extra/secure_client.h b/code/test/build/extra/secure_client.h index 56879073..11a34e9e 100644 --- a/code/test/build/extra/secure_client.h +++ b/code/test/build/extra/secure_client.h @@ -1,2 +1,3 @@ #define SECURE_CLIENT SECURE_CLIENT_BEARSSL #define MQTT_LIBRARY MQTT_LIBRARY_ARDUINOMQTT +#define OTA_CLIENT OTA_CLIENT_HTTPUPDATE