mirror of
https://github.com/esphome/esphome.git
synced 2026-09-13 16:18:41 +00:00
[ota] Add wall-clock timeout to OTA data transfer loop
The main `while (total < ota_size)` loop in `ESPHomeOTAComponent::handle_data_()` had a `// TODO: timeout check` and no wall-clock guard. If the uploader side dropped the TCP connection without a FIN/RST being delivered to the device (uploader process killed mid-transfer, NAT/router state dropped, packet loss eating the RST), `recv_fn` would never see a close and `s_err_fn` would never fire, so: - `pcb_` stays non-null - `rx_closed_` stays false - `waiting_for_data_()` stays true forever - the loop spins on `EWOULDBLOCK`, fed by `App.feed_wdt(); continue;`, indefinitely LwIP TCP keepalive is not enabled on the OTA socket, and even when enabled the default keepalive timer is on the order of hours, so the device is effectively unresponsive until power cycle. This matches the "device unresponsive until I do a hard power-reset" symptom in issue #15953. Track the timestamp of the last successful read and abort if no data arrives for `OTA_SOCKET_TIMEOUT_DATA` (90s, same constant the handshake, `readall_()`, and `writeall_()` already use). The timeout resets on every successful read so a slow but live link does not false-trigger.
This commit is contained in:
@@ -350,8 +350,17 @@ void ESPHomeOTAComponent::handle_data_() {
|
||||
// Acknowledge MD5 OK - 1 byte
|
||||
this->write_byte_(ota::OTA_RESPONSE_BIN_MD5_OK);
|
||||
|
||||
// Track when we last received data so a silently-vanished peer (no FIN/RST
|
||||
// delivered, e.g. uploader killed mid-transfer or NAT/router dropped state)
|
||||
// can't wedge the device indefinitely. Without this, the loop only exits
|
||||
// on actual data, EOF, or a non-EWOULDBLOCK error from read(), and lwIP
|
||||
// TCP keepalive isn't enabled here.
|
||||
uint32_t last_data_ms = millis();
|
||||
while (total < ota_size) {
|
||||
// TODO: timeout check
|
||||
if (millis() - last_data_ms > OTA_SOCKET_TIMEOUT_DATA) {
|
||||
ESP_LOGW(TAG, "No data received for %u ms", (unsigned) OTA_SOCKET_TIMEOUT_DATA);
|
||||
goto error; // NOLINT(cppcoreguidelines-avoid-goto)
|
||||
}
|
||||
size_t remaining = ota_size - total;
|
||||
size_t requested = remaining < OTA_BUFFER_SIZE ? remaining : OTA_BUFFER_SIZE;
|
||||
ssize_t read = this->client_->read(buf, requested);
|
||||
@@ -369,6 +378,7 @@ void ESPHomeOTAComponent::handle_data_() {
|
||||
goto error; // NOLINT(cppcoreguidelines-avoid-goto)
|
||||
}
|
||||
|
||||
last_data_ms = millis();
|
||||
error_code = this->backend_->write(buf, read);
|
||||
if (error_code != ota::OTA_RESPONSE_OK) {
|
||||
ESP_LOGW(TAG, "Flash write err %d", error_code);
|
||||
|
||||
Reference in New Issue
Block a user