Browse Source

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
pull/2001/head
Niek van der Maas 4 years ago
committed by Max Prokhorov
parent
commit
f588893373
6 changed files with 120 additions and 43 deletions
  1. +0
    -5
      code/espurna/config/dependencies.h
  2. +0
    -4
      code/espurna/config/general.h
  3. +9
    -24
      code/espurna/libs/HeapStats.h
  4. +70
    -0
      code/espurna/libs/TypeChecks.h
  5. +40
    -10
      code/espurna/ota_httpupdate.ino
  6. +1
    -0
      code/test/build/extra/secure_client.h

+ 0
- 5
code/espurna/config/dependencies.h View File

@ -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


+ 0
- 4
code/espurna/config/general.h View File

@ -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


+ 9
- 24
code/espurna/libs/HeapStats.h View File

@ -6,7 +6,7 @@ Show extended heap stats when EspClass::getHeapStats() is available
#pragma once
#include <type_traits>
#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<typename T, typename = decltype(
std::declval<T>().getHeapStats(0,0,0))>
static std::true_type detect(int);
template<typename>
static std::false_type detect(...);
};
namespace heap_stats {
template <typename T>
struct detector : public _detector {
using result = decltype(
std::declval<detector>().template detect<T>(0));
};
using has_getHeapStats_t = decltype(std::declval<T>().getHeapStats(0,0,0));
template <typename T>
struct typed_check : public detector<T>::result {
};
typed_check<EspClass> check{};
};
using has_getHeapStats = is_detected<has_getHeapStats_t, T>;
}
template <typename T>
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 <typename T>
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<decltype(ESP)>{}, 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<decltype(ESP)>{}) {
infoHeapStats("Heap", stats);
}
}

+ 70
- 0
code/espurna/libs/TypeChecks.h View File

@ -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 <type_traits>
// In case we do support c++17 just use the headers shipped with the GCC
#if __cplusplus >= 201703L
#include <type_traits>
#include <experimental/type_traits>
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 <typename... >
struct make_void {
using type = void;
};
template <typename... T>
using void_t = typename make_void<T...>::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<typename Default, typename AlwaysVoid,
template<typename...> class Op, typename... Args>
struct detector {
using value_t = std::false_type;
using type = Default;
};
// Implementation of the detection idiom (positive case).
template<typename Default, template<typename...> class Op,
typename... Args>
struct detector<Default, void_t<Op<Args...>>, Op, Args...> {
using value_t = std::true_type;
using type = Op<Args...>;
};
}
template<template<typename...> class Op, typename... Args>
using is_detected = typename implementation::detector<implementation::nonesuch, void, Op, Args...>::value_t;
template <template<class...> class Op, class... Args>
using detected_t = typename implementation::detector<implementation::nonesuch, void, Op, Args...>::type;
template <class Default, template<class...> class Op, class... Args>
using detected_or = implementation::detector<Default, void, Op, Args...>;
// ...
// implement the rest as needed. some things may not work though
}
using experimental_type_traits::is_detected;
#endif // __cplusplus >= 201703L

+ 40
- 10
code/espurna/ota_httpupdate.ino View File

@ -18,6 +18,7 @@ Copyright (C) 2019 by Maxim Prokhorov <prokhorov dot max at outlook dot com>
#include <ESP8266httpUpdate.h>
#include "libs/URL.h"
#include "libs/TypeChecks.h"
#if SECURE_CLIENT != SECURE_CLIENT_NONE
@ -30,6 +31,38 @@ Copyright (C) 2019 by Maxim Prokhorov <prokhorov dot max at outlook dot com>
#endif // SECURE_CLIENT != SECURE_CLIENT_NONE
template <typename T>
void _otaFollowRedirects(const std::true_type&, T& instance) {
instance.followRedirects(true);
}
template <typename T>
void _otaFollowRedirects(const std::false_type&, T& instance) {
}
template <typename T>
t_httpUpdate_return _otaClientUpdate(const std::true_type&, T& instance, WiFiClient* client, const String& url) {
return instance.update(*client, url);
}
template <typename T>
t_httpUpdate_return _otaClientUpdate(const std::false_type&, T& instance, WiFiClient*, const String& url) {
return instance.update(url);
}
namespace ota {
template <typename T>
using has_followRedirects_t = decltype(std::declval<T>().followRedirects(std::declval<bool>()));
template <typename T>
using has_followRedirects = is_detected<has_followRedirects_t, T>;
template <typename T>
using has_WiFiClient_argument_t = decltype(std::declval<T>().update(std::declval<WiFiClient&>(), std::declval<const String&>()));
template <typename T>
using has_WiFiClient_argument = is_detected<has_WiFiClient_argument_t, T>;
}
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<decltype(ESPhttpUpdate)>{}, 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<decltype(ESPhttpUpdate)>{}, 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<WiFiClient>();
std::unique_ptr<WiFiClient> client(nullptr);
if (ota::has_WiFiClient_argument<decltype(ESPhttpUpdate)>{}) {
client = std::make_unique<WiFiClient>();
}
_otaClientRunUpdater(client.get(), url, "");
}
#endif
#if SECURE_CLIENT == SECURE_CLIENT_BEARSSL


+ 1
- 0
code/test/build/extra/secure_client.h View File

@ -1,2 +1,3 @@
#define SECURE_CLIENT SECURE_CLIENT_BEARSSL
#define MQTT_LIBRARY MQTT_LIBRARY_ARDUINOMQTT
#define OTA_CLIENT OTA_CLIENT_HTTPUPDATE

Loading…
Cancel
Save