Repository navigation
AsyncHttpClientLight: end a body read when the server goes silent or closes - #25141
Conversation
|
Thanks @adamsthws, I ran the PR though Opus 5.5 and there are some additional changes suggested before merge: 1. Please don't call
|
…lent writeToStreamDataBlock() waited for the rest of a body for as long as connected() held. A server that stops sending part way through without closing leaves connected() true, so the worker never finishes and the caller, waiting in runJob(), never returns. Give up after _tcpTimeout without data and return HTTPC_ERROR_READ_TIMEOUT, as HttpClientLight now does (arendst#25139).
AsyncTcpAdapter only saw the peer close inside read(), and callers only read when available() > 0. A body without Content-Length that ends with the server closing left connected() true for ever, so writeToStreamDataBlock() never returned. connected() now peeks at the socket, as WiFiClient::connected() does: data still buffered keeps it connected, an orderly close after it does not.
- connected() only reports a peer close and no longer calls stop(): it can be called from another task than the worker using the socket. - writeToStreamDataBlock() closes the connection on a read timeout, as the chunked readers do, so a stalled connection is not reused. - readBodyToStreamString(): the identity branch used by async GET/POST now times out after _tcpTimeout without data, and notices a close: with no Content-Length it ends the body, with one it is a lost connection.
e88ddc3 to
72c67b5
Compare
|
Thanks, all three done in 72c67b5, and the branch is rebased on
Async results on an emulated ESP32-C3,
The sync cases are unchanged from before (stalls return -11 after 5 s, full and chunked bodies complete). I couldn't make the reuse in point 2 happen in a test: a stalled request with no One thing I noticed: |
|
For context, this came out of Sonoff-OTA, an over-the-air Sonoff S60 to Tasmota converter. The same stalled-download problem turned up in several other projects; the related pull requests, and how each fix was tested, are collected in docs/upstream-contributions.md. |
|
Thanks. |
Description:
Follow-up to #25139, as asked there: the same fix for
AsyncHttpClientLight::writeToStreamDataBlock(), used when Tasmota is built withUSE_BERRY_WEBCLIENT_ASYNC.The read runs on a worker task, but
runJob()waits for it with no limit, so a read that never returns leaves the device answering nothing, as with the sync client. There are two ways it never returned:_tcpTimeoutwithout data and returnsHTTPC_ERROR_READ_TIMEOUT. Each piece of data resets the clock, andset_timeouts()still lets a caller wait longer.Content-Lengthand closes.AsyncTcpAdapteronly noticed a close insideread(), and the loop only callsread()whenavailable() > 0, so after the last byteconnected()stayed true forever.connected()now peeks at the socket, asWiFiClient::connected()does. Data still waiting keeps it connected, so the body is read to the end first. Without this the timeout alone would turn these responses into an error after 5 s and drop the body.How to reproduce: build with
USE_BERRY_WEBCLIENT_ASYNC. Have a server send the headers (Content-Length: 8192) and the first 1000 bytes, then nothing while keeping the connection open (or send 3000 bytes with noContent-Lengthand close). Then:Testing: on an emulated ESP32-C3 (QEMU),
tasmota32c3fromdevelopmentwithUSE_BERRY_WEBCLIENT_ASYNC, with and without this change. Core 3.3.12.Content-Length, and no length)Content-Length, 3000 bytes, then the connection closesset_timeouts(10000)set_timeouts(10000)write_file(), body cut off part wayFree heap after 30 timeouts in a row fell less than after 30 normal downloads, and the normal downloads used the same as before this change.
The same cases on a real ESP32-C3 plug (Sonoff S60TPG), same builds:
Content-Length, and no length)Content-Length, 3000 bytes, then the connection closesset_timeouts(10000)set_timeouts(10000)write_file(), body cut off part waytasmota32,tasmota32s3andtasmota32c3withUSE_BERRY_WEBCLIENT_ASYNCbuild without warnings. The library is ESP32-only; the ESP8266tasmotabuild is unaffected and builds without warnings.Chunked bodies are not affected by this PR: they are read elsewhere and already time out. They also fail at the end of every body, for a separate reason; I'll send that fix in its own PR.
Related issue (if applicable): #25139
Checklist:
NOTE: The code change must pass CI tests. Your PR cannot be merged unless tests pass