diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index d5ded2a02c..827cdcd349 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -137,7 +137,16 @@ void DeferredUpdateEventSource::process_deferred_queue_() { if (this->send(message.c_str(), "state") != DISCARDED) { // O(n) but memory efficiency is more important than speed here which is why std::vector was chosen deferred_queue_.erase(deferred_queue_.begin()); + consecutive_send_failures_ = 0; // Reset failure count on successful send } else { + consecutive_send_failures_++; + if (consecutive_send_failures_ >= MAX_CONSECUTIVE_SEND_FAILURES) { + // Too many failures, connection is likely dead + ESP_LOGW(TAG, "Closing stuck EventSource connection after %" PRIu16 " failed sends", + consecutive_send_failures_); + this->close(); + deferred_queue_.clear(); + } break; } } @@ -176,6 +185,8 @@ void DeferredUpdateEventSource::deferrable_send_state(void *source, const char * std::string message = message_generator(web_server_, source); if (this->send(message.c_str(), "state") == DISCARDED) { deq_push_back_with_dedup_(source, message_generator); + } else { + consecutive_send_failures_ = 0; // Reset failure count on successful send } } } diff --git a/esphome/components/web_server/web_server.h b/esphome/components/web_server/web_server.h index 5f175b6bdd..5123b52921 100644 --- a/esphome/components/web_server/web_server.h +++ b/esphome/components/web_server/web_server.h @@ -126,6 +126,8 @@ class DeferredUpdateEventSource : public AsyncEventSource { // footprint is more important than speed here) std::vector deferred_queue_; WebServer *web_server_; + uint16_t consecutive_send_failures_{0}; + static constexpr uint16_t MAX_CONSECUTIVE_SEND_FAILURES = 2500; // ~20 seconds at 125Hz loop rate // helper for allowing only unique entries in the queue void deq_push_back_with_dedup_(void *source, message_generator_t *message_generator);