From 08d2ab05009732379bdd2abfc735f731defee159 Mon Sep 17 00:00:00 2001 From: Adam Matthews <52178922+adamsthws@users.noreply.github.com> Date: Thu, 15 Oct 2026 00:30:28 +0100 Subject: [PATCH 1/2] [HttpClientLight] Time out reading a body when the server goes silent writeToStreamDataBlock() waited for the rest of a body for as long as connected() held. A server that stops sending part way through a body without closing the connection leaves connected() true, so getString() never returned and the unit stopped responding. Give up after _tcpTimeout without data and return HTTPC_ERROR_READ_TIMEOUT, as handleHeaderResponse() already does. Same fix as arendst/Tasmota#25139. --- lib/HttpClientLight/src/HttpClientLight.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/HttpClientLight/src/HttpClientLight.cpp b/lib/HttpClientLight/src/HttpClientLight.cpp index efb8af6f1..3b9c7457a 100644 --- a/lib/HttpClientLight/src/HttpClientLight.cpp +++ b/lib/HttpClientLight/src/HttpClientLight.cpp @@ -1376,6 +1376,10 @@ int HTTPClientLight::writeToStreamDataBlock(Stream * stream, int size) uint8_t * buff = (uint8_t *) malloc(buff_size); if(buff) { + // a server gone silent without closing stays connected(): give up + // after _tcpTimeout without data, as handleHeaderResponse() does + unsigned long lastDataTime = millis(); + // read all data from server while(connected() && (len > 0 || len == -1)) { @@ -1448,8 +1452,14 @@ int HTTPClientLight::writeToStreamDataBlock(Stream * stream, int size) len -= readBytes; } + lastDataTime = millis(); delay(0); } else { + if((millis() - lastDataTime) > _tcpTimeout) { + log_w("read timeout after %d bytes", bytesWritten); + free(buff); + return HTTPC_ERROR_READ_TIMEOUT; + } delay(1); } } From d143a4b93df7ae0c1f5fd597fd20b516695507b5 Mon Sep 17 00:00:00 2001 From: Adam Matthews <52178922+adamsthws@users.noreply.github.com> Date: Fri, 16 Oct 2026 17:28:38 +0100 Subject: [PATCH 2/2] [HttpClientLight] Widen the TCP timeout to 32 bits --- lib/HttpClientLight/src/HttpClientLight.cpp | 2 +- lib/HttpClientLight/src/HttpClientLight.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/HttpClientLight/src/HttpClientLight.cpp b/lib/HttpClientLight/src/HttpClientLight.cpp index 3b9c7457a..d3a301a62 100644 --- a/lib/HttpClientLight/src/HttpClientLight.cpp +++ b/lib/HttpClientLight/src/HttpClientLight.cpp @@ -503,7 +503,7 @@ void HTTPClientLight::setConnectTimeout(int32_t connectTimeout) * set the timeout for the TCP connection * @param timeout unsigned int */ -void HTTPClientLight::setTimeout(uint16_t timeout) +void HTTPClientLight::setTimeout(uint32_t timeout) { _tcpTimeout = timeout; if(connected()) { diff --git a/lib/HttpClientLight/src/HttpClientLight.h b/lib/HttpClientLight/src/HttpClientLight.h index 4c2d9e5b0..5fbdc26f4 100644 --- a/lib/HttpClientLight/src/HttpClientLight.h +++ b/lib/HttpClientLight/src/HttpClientLight.h @@ -176,7 +176,7 @@ public: void setAuthorization(const char * user, const char * password); void setAuthorization(const char * auth); void setConnectTimeout(int32_t connectTimeout); - void setTimeout(uint16_t timeout); + void setTimeout(uint32_t timeout); // Redirections void setFollowRedirects(followRedirects_t follow); @@ -248,7 +248,7 @@ protected: uint16_t _port = 0; int32_t _connectTimeout = HTTPCLIENT_DEFAULT_TCP_TIMEOUT; // Do not set to -1 as it fails WiFiClient connect() bool _reuse = true; - uint16_t _tcpTimeout = HTTPCLIENT_DEFAULT_TCP_TIMEOUT; + uint32_t _tcpTimeout = HTTPCLIENT_DEFAULT_TCP_TIMEOUT; bool _useHTTP10 = false; bool _secure = false;