From c95c241fa31ee8960919d79190eac2a6def60df5 Mon Sep 17 00:00:00 2001 From: Adam Matthews <52178922+adamsthws@users.noreply.github.com> Date: Wed, 14 Oct 2026 14:12:37 +0100 Subject: [PATCH] HttpClientLight: time out reading a body when the server goes silent (#25139) writeToStreamDataBlock() looped for as long as connected() held, waiting for the rest of the body with no time limit. A server that stops sending part way through a body without closing the connection - a computer that sleeps, a cable pulled, a network that drops between the two ends - leaves connected() true for ever, so the read never returned. Called from the main loop, as Berry's webclient get_string() is, it left the device answering nothing, not even its web page, until it was power-cycled. Give up after _tcpTimeout without data, returning HTTPC_ERROR_READ_TIMEOUT, as handleHeaderResponse() already does for the headers. A body that keeps arriving, however slowly, is unaffected: each piece resets the clock. --- lib/libesp32/HttpClientLight/src/HttpClientLight.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/libesp32/HttpClientLight/src/HttpClientLight.cpp b/lib/libesp32/HttpClientLight/src/HttpClientLight.cpp index 1430e6a6e..ff20e850f 100644 --- a/lib/libesp32/HttpClientLight/src/HttpClientLight.cpp +++ b/lib/libesp32/HttpClientLight/src/HttpClientLight.cpp @@ -1370,6 +1370,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)) { @@ -1442,8 +1446,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); } }