diff --git a/cores/esp8266/Stream.h b/cores/esp8266/Stream.h index f39bb423f2..322ada96d5 100644 --- a/cores/esp8266/Stream.h +++ b/cores/esp8266/Stream.h @@ -65,7 +65,7 @@ class Stream: public Print { // parsing methods - void setTimeout(unsigned long timeout); // sets maximum milliseconds to wait for stream data, default is 1 second + virtual void setTimeout(unsigned long timeout); // sets maximum milliseconds to wait for stream data, default is 1 second unsigned long getTimeout () const { return _timeout; } bool find(const char *target); // reads data from the stream until the target string is found diff --git a/libraries/ESP8266HTTPClient/examples/BasicHttpsClient/BasicHttpsClient.ino b/libraries/ESP8266HTTPClient/examples/BasicHttpsClient/BasicHttpsClient.ino index 62bfc93a9a..3eb3c59acc 100644 --- a/libraries/ESP8266HTTPClient/examples/BasicHttpsClient/BasicHttpsClient.ino +++ b/libraries/ESP8266HTTPClient/examples/BasicHttpsClient/BasicHttpsClient.ino @@ -41,11 +41,33 @@ void loop() { std::unique_ptr client(new BearSSL::WiFiClientSecure); +#if 1 // 1=secure, 0=insecure + client->setFingerprint(fingerprint_sni_cloudflaressl_com); + + // date needs to be setup + configTime("UTC", "pool.ntp.org"); + time_t now; + while (time(&now) < 24 * 3600) { + Serial.println("waiting for NTP time to be set..."); + delay(1000); + } + Serial.printf("date: %s", ctime(&now)); + +#else // Or, if you happy to ignore the SSL certificate, then use the following line instead: - // client->setInsecure(); + client->setInsecure(); +#endif HTTPClient https; + https.setWallTime(10000); // do not exceed 10s while getting data + client->setWallTime(20000); // do not exceed 20s during handshake + + // Try to reduce RAM footprint when SSL server allows it + constexpr int sslbufsize = 1024; + bool mfln = client->probeMaxFragmentLength(jigsaw_host, jigsaw_port, sslbufsize); + Serial.printf("Can reduce SSL footprint to %d bytes in RAM: %s\n", sslbufsize, mfln ? "yes" : "no"); + if (mfln) { client->setBufferSizes(sslbufsize, sslbufsize); } Serial.print("[HTTPS] begin...\n"); if (https.begin(*client, jigsaw_host, jigsaw_port)) { // HTTPS diff --git a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp index 480699e453..3e1a345e70 100644 --- a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp +++ b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp @@ -216,7 +216,7 @@ void HTTPClient::disconnect(bool preserveClient) } if(_reuse && _canReuse) { - DEBUG_HTTPCLIENT("[HTTP-Client][end] tcp keep open for reuse\n"); + DEBUG_HTTPCLIENT("[HTTP-Client][end] tcp kept open for reuse\n"); } else { DEBUG_HTTPCLIENT("[HTTP-Client][end] tcp stop\n"); if(_client) { @@ -303,15 +303,12 @@ void HTTPClient::setAuthorization(String auth) } /** - * set the timeout for the TCP connection + * set the wall time for operations * @param timeout unsigned int */ -void HTTPClient::setTimeout(uint16_t timeout) +void HTTPClient::setWallTime(unsigned long wallTime) { - _tcpTimeout = timeout; - if(connected()) { - _client->setTimeout(timeout); - } + _wallTime.reset(wallTime?: esp8266::polledTimeout::oneShotMs::neverExpires); } /** @@ -794,8 +791,6 @@ bool HTTPClient::connect(void) return false; } - _client->setTimeout(_tcpTimeout); - if(!_client->connect(_host.c_str(), _port)) { DEBUG_HTTPCLIENT("[HTTP-Client] failed connect to %s:%u\n", _host.c_str(), _port); return false; @@ -879,7 +874,6 @@ bool HTTPClient::sendHeader(const char * type) */ int HTTPClient::handleHeaderResponse() { - if(!connected()) { return HTTPC_ERROR_NOT_CONNECTED; } @@ -891,7 +885,7 @@ int HTTPClient::handleHeaderResponse() String transferEncoding; _transferEncoding = HTTPC_TE_IDENTITY; - unsigned long lastDataTime = millis(); + _wallTime.reset(); while(connected()) { size_t len = _client->available(); @@ -899,8 +893,6 @@ int HTTPClient::handleHeaderResponse() int headerSeparator = -1; String headerLine = _client->readStringUntil('\n'); - lastDataTime = millis(); - DEBUG_HTTPCLIENT("[HTTP-Client][handleHeaderResponse] RX: '%s'\n", headerLine.c_str()); if (headerLine.startsWith(F("HTTP/1."))) { @@ -978,7 +970,7 @@ int HTTPClient::handleHeaderResponse() } } else { - if((millis() - lastDataTime) > _tcpTimeout) { + if(_wallTime) { return HTTPC_ERROR_READ_TIMEOUT; } esp_yield(); diff --git a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h index bc8a42d33e..97a34d44d0 100644 --- a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h +++ b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h @@ -29,6 +29,7 @@ #include #include #include +#include #include @@ -152,7 +153,7 @@ typedef std::unique_ptr TransportTraitsPtr; class HTTPClient { public: - HTTPClient() = default; + HTTPClient(): _wallTime(HTTPCLIENT_DEFAULT_TCP_TIMEOUT) {}; ~HTTPClient() = default; HTTPClient(HTTPClient&&) = default; HTTPClient& operator=(HTTPClient&&) = default; @@ -177,7 +178,8 @@ class HTTPClient void setAuthorization(const char * user, const char * password); void setAuthorization(const char * auth); void setAuthorization(String auth); - void setTimeout(uint16_t timeout); + void setWallTime(unsigned long wallTime); + [[deprecated("use setWallTime() instead")]] void setTimeout(unsigned long wallTime) { setWallTime(wallTime); } // Redirections void setFollowRedirects(followRedirects_t follow); @@ -256,7 +258,7 @@ class HTTPClient String _host; uint16_t _port = 0; bool _reuse = true; - uint16_t _tcpTimeout = HTTPCLIENT_DEFAULT_TCP_TIMEOUT; + esp8266::polledTimeout::oneShotMs _wallTime; bool _useHTTP10 = false; String _uri; @@ -314,6 +316,7 @@ int HTTPClient::writeToStream(S * output) } } else if(_transferEncoding == HTTPC_TE_CHUNKED) { int size = 0; + _wallTime.reset(); while(1) { if(!connected()) { return returnError(HTTPC_ERROR_CONNECTION_LOST); @@ -360,6 +363,10 @@ int HTTPClient::writeToStream(S * output) return returnError(HTTPC_ERROR_READ_TIMEOUT); } + if (_wallTime) { + return returnError(HTTPC_ERROR_READ_TIMEOUT); + } + esp_yield(); } } else { diff --git a/libraries/ESP8266WiFi/src/WiFiClient.cpp b/libraries/ESP8266WiFi/src/WiFiClient.cpp index fb10209ec0..42120e99ad 100644 --- a/libraries/ESP8266WiFi/src/WiFiClient.cpp +++ b/libraries/ESP8266WiFi/src/WiFiClient.cpp @@ -216,7 +216,7 @@ size_t WiFiClient::write(const uint8_t *buf, size_t size) { return 0; } - _client->setTimeout(_timeout); + _client->setTimeout(_timeout); // context write uses timeout return _client->write((const char*)buf, size); } @@ -238,7 +238,6 @@ size_t WiFiClient::write_P(PGM_P buf, size_t size) { return 0; } - _client->setTimeout(_timeout); StreamConstPtr nopeek(buf, size); return nopeek.sendAll(this); } @@ -463,3 +462,10 @@ void WiFiClient::peekConsume (size_t consume) if (_client) _client->peekConsume(consume); } + +void WiFiClient::setTimeout (unsigned long timeout) +{ + Client::setTimeout(timeout); + if (_client) + _client->setTimeout(timeout); +} diff --git a/libraries/ESP8266WiFi/src/WiFiClient.h b/libraries/ESP8266WiFi/src/WiFiClient.h index 711adb6204..c9a8a30338 100644 --- a/libraries/ESP8266WiFi/src/WiFiClient.h +++ b/libraries/ESP8266WiFi/src/WiFiClient.h @@ -64,6 +64,7 @@ class WiFiClient : public Client, public SList { // - https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rh-copy virtual std::unique_ptr clone() const; + virtual void setTimeout (unsigned long timeout) override; 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.cpp b/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp index 7bc0c70df6..84737ef283 100644 --- a/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp +++ b/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp @@ -31,7 +31,6 @@ extern "C" { } #include "debug.h" #include "ESP8266WiFi.h" -#include "PolledTimeout.h" #include "WiFiClient.h" #include "WiFiClientSecureBearSSL.h" #include "StackThunk.h" @@ -68,10 +67,9 @@ extern "C" { namespace BearSSL { -void WiFiClientSecureCtx::_clear() { - // TLS handshake may take more than the 5 second default timeout - _timeout = 15000; +constexpr auto defaultWallTime = 10000UL; +void WiFiClientSecureCtx::_clear() { _sc = nullptr; _sc_svr = nullptr; _eng = nullptr; @@ -103,7 +101,7 @@ void WiFiClientSecureCtx::_clearAuthenticationSettings() { } -WiFiClientSecureCtx::WiFiClientSecureCtx() : WiFiClient() { +WiFiClientSecureCtx::WiFiClientSecureCtx() : WiFiClient(), _wallTime(defaultWallTime) { _clear(); _clearAuthenticationSettings(); _certStore = nullptr; // Don't want to remove cert store on a clear, should be long lived @@ -124,7 +122,7 @@ WiFiClientSecureCtx::~WiFiClientSecureCtx() { WiFiClientSecureCtx::WiFiClientSecureCtx(ClientContext* client, const X509List *chain, const PrivateKey *sk, int iobuf_in_size, int iobuf_out_size, ServerSessions *cache, - const X509List *client_CA_ta, int tls_min, int tls_max) { + const X509List *client_CA_ta, int tls_min, int tls_max): _wallTime(defaultWallTime) { _clear(); _clearAuthenticationSettings(); stack_thunk_add_ref(); @@ -145,7 +143,7 @@ WiFiClientSecureCtx::WiFiClientSecureCtx(ClientContext *client, const X509List *chain, unsigned cert_issuer_key_type, const PrivateKey *sk, int iobuf_in_size, int iobuf_out_size, ServerSessions *cache, - const X509List *client_CA_ta, int tls_min, int tls_max) { + const X509List *client_CA_ta, int tls_min, int tls_max): _wallTime(defaultWallTime) { _clear(); _clearAuthenticationSettings(); stack_thunk_add_ref(); @@ -204,7 +202,10 @@ bool WiFiClientSecureCtx::stop(unsigned int maxWaitMs) { } bool WiFiClientSecureCtx::flush(unsigned int maxWaitMs) { + auto savedTimeout = _timeout; + _timeout = std::max(1U, maxWaitMs); (void) _run_until(BR_SSL_SENDAPP); + _timeout = savedTimeout; return WiFiClient::flush(maxWaitMs); } @@ -246,7 +247,6 @@ void WiFiClientSecureCtx::_freeSSL() { _recvapp_len = 0; // This connection is toast _handshake_done = false; - _timeout = 15000; } bool WiFiClientSecureCtx::_clientConnected() { @@ -462,7 +462,7 @@ size_t WiFiClientSecureCtx::peekBytes(uint8_t *buffer, size_t length) { } _startMillis = millis(); - while ((_pollRecvBuffer() < (int) length) && ((millis() - _startMillis) < 5000)) { + while ((_pollRecvBuffer() < (int)length) && ((millis() - _startMillis) < _timeout)) { yield(); } @@ -539,6 +539,7 @@ int WiFiClientSecureCtx::_run_until(unsigned target, bool blocking) { br_ssl_engine_sendrec_ack(_eng, wlen); } no_work = 0; + loopTimeout.reset(); continue; } @@ -582,6 +583,7 @@ int WiFiClientSecureCtx::_run_until(unsigned target, bool blocking) { br_ssl_engine_recvrec_ack(_eng, rlen); } no_work = 0; + loopTimeout.reset(); continue; } } @@ -602,6 +604,14 @@ int WiFiClientSecureCtx::_run_until(unsigned target, bool blocking) { } bool WiFiClientSecureCtx::_wait_for_handshake() { + +#if defined(DEBUG_ESP_PORT) + if constexpr (F_CPU != 160000000L) { + DEBUG_ESP_PORT.printf_P((PGM_P)PSTR("BSSL: Please enable 160MHz build\n")); + } +#endif + + _wallTime.reset(); _handshake_done = false; while (!_handshake_done && _clientConnected()) { int ret = _run_until(BR_SSL_SENDAPP); @@ -612,6 +622,10 @@ bool WiFiClientSecureCtx::_wait_for_handshake() { if (br_ssl_engine_current_state(_eng) & BR_SSL_SENDAPP) { _handshake_done = true; } + if (_wallTime) { + DEBUG_BSSL("handshake too long\n"); + break; + } optimistic_yield(1000); } return _handshake_done; @@ -1206,9 +1220,6 @@ bool WiFiClientSecureCtx::_connectSSL(const char* hostName) { _x509_insecure = nullptr; _x509_knownkey = nullptr; - // reduce timeout after successful handshake to fail fast if server stop accepting our data for whathever reason - if (ret) _timeout = 5000; - return ret; } @@ -1673,4 +1684,11 @@ bool WiFiClientSecure::probeMaxFragmentLength(IPAddress ip, uint16_t port, uint1 return _SendAbort(probe, supportsLen); } -}; +void WiFiClientSecure::setTimeout (unsigned long timeout) +{ + WiFiClient::setTimeout(timeout); + if (_ctx) + _ctx->setTimeout(timeout); +} + +}; // namespace BearSSL diff --git a/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h b/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h index e0cb44928f..ca27bca936 100644 --- a/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h +++ b/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h @@ -28,6 +28,7 @@ #include #include "BearSSLHelpers.h" #include "CertStoreBearSSL.h" +#include "PolledTimeout.h" namespace BearSSL { @@ -147,6 +148,9 @@ class WiFiClientSecureCtx : public WiFiClient { // consume bytes after use (see peekBuffer) virtual void peekConsume (size_t consume) override; + // install a wall-time used during handshake + void setWallTime (unsigned long wallTime) { _wallTime.reset(wallTime?: esp8266::polledTimeout::oneShotMs::neverExpires); } + protected: bool _connectSSL(const char *hostName); // Do initial SSL handshake @@ -235,6 +239,8 @@ class WiFiClientSecureCtx : public WiFiClient { bool _installServerX509Validator(const X509List *client_CA_ta); // Setup X509 client cert validation, if supplied uint8_t *_streamLoad(Stream& stream, size_t size); + + esp8266::polledTimeout::oneShotMs _wallTime; }; // class WiFiClientSecureCtx @@ -373,6 +379,12 @@ class WiFiClientSecure : public WiFiClient { void disableKeepAlive() override { _ctx->disableKeepAlive(); }; + // override setTimeout and forward to context + virtual void setTimeout (unsigned long timeout) override; + + // install a wall-time used during handshake + void setWallTime (unsigned long wallTime) { _ctx->setWallTime(wallTime); } + private: std::shared_ptr _ctx; diff --git a/libraries/ESP8266httpUpdate/examples/httpUpdate/httpUpdate.ino b/libraries/ESP8266httpUpdate/examples/httpUpdate/httpUpdate.ino index b9da7f8cb4..ed7f38f61b 100644 --- a/libraries/ESP8266httpUpdate/examples/httpUpdate/httpUpdate.ino +++ b/libraries/ESP8266httpUpdate/examples/httpUpdate/httpUpdate.ino @@ -27,7 +27,7 @@ void setup() { Serial.println(); - ESPhttpUpdate.setClientTimeout(2000); // default was 8000 + ESPhttpUpdate.setWallTime(2000); // default was 8000 WiFi.mode(WIFI_STA); WiFiMulti.addAP(APSSID, APPSK); diff --git a/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.cpp b/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.cpp index 9cd5d62801..256d971560 100755 --- a/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.cpp +++ b/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.cpp @@ -28,12 +28,12 @@ #include ESP8266HTTPUpdate::ESP8266HTTPUpdate(void) - : _httpClientTimeout(8000) + : _httpWallTime(8000) { } -ESP8266HTTPUpdate::ESP8266HTTPUpdate(int httpClientTimeout) - : _httpClientTimeout(httpClientTimeout) +ESP8266HTTPUpdate::ESP8266HTTPUpdate(int httpWallTime) + : _httpWallTime(httpWallTime) { } @@ -164,7 +164,7 @@ HTTPUpdateResult ESP8266HTTPUpdate::handleUpdate(HTTPClient& http, const String& // use HTTP/1.0 for update since the update handler not support any transfer Encoding http.useHTTP10(true); - http.setTimeout(_httpClientTimeout); + http.setWallTime(_httpWallTime); http.setFollowRedirects(_followRedirects); http.setUserAgent(F("ESP8266-http-Update")); http.addHeader(F("x-ESP8266-Chip-ID"), String(ESP.getChipId())); diff --git a/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.h b/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.h index 803988d3be..45ae03349f 100755 --- a/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.h +++ b/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.h @@ -132,9 +132,8 @@ class ESP8266HTTPUpdate int getLastError(void); String getLastErrorString(void); - void setClientTimeout(int timeout) { - _httpClientTimeout = timeout; - } + [[deprecated("use setWallTime()")]] void setClientTimeout(int wallTime) { setWallTime(wallTime); } + void setWallTime(int wallTime) { _httpWallTime = wallTime; } protected: t_httpUpdate_return handleUpdate(HTTPClient& http, const String& currentVersion, bool spiffs = false); bool runUpdate(Stream& in, uint32_t size, const String& md5, int command = U_FLASH); @@ -154,7 +153,7 @@ class ESP8266HTTPUpdate String _auth; String _md5Sum; private: - int _httpClientTimeout; + unsigned long _httpWallTime; followRedirects_t _followRedirects = HTTPC_DISABLE_FOLLOW_REDIRECTS; // Callbacks