From c85b1b8609f1a7d4545b2ed40c74eb8dca7afacc Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 14 Dec 2025 13:31:19 -0600 Subject: [PATCH] [web_server_idf] Always enable LRU purge to prevent socket exhaustion (#12481) --- .../captive_portal/captive_portal.cpp | 6 ------ .../captive_portal/captive_portal.h | 4 ---- .../web_server_idf/web_server_idf.cpp | 19 +++++-------------- .../web_server_idf/web_server_idf.h | 2 -- 4 files changed, 5 insertions(+), 26 deletions(-) diff --git a/esphome/components/captive_portal/captive_portal.cpp b/esphome/components/captive_portal/captive_portal.cpp index 4eb00835b..e1f92d2d2 100644 --- a/esphome/components/captive_portal/captive_portal.cpp +++ b/esphome/components/captive_portal/captive_portal.cpp @@ -65,12 +65,6 @@ void CaptivePortal::start() { this->base_->init(); if (!this->initialized_) { this->base_->add_handler(this); -#ifdef USE_ESP32 - // Enable LRU socket purging to handle captive portal detection probe bursts - // OS captive portal detection makes many simultaneous HTTP requests which can - // exhaust sockets. LRU purging automatically closes oldest idle connections. - this->base_->get_server()->set_lru_purge_enable(true); -#endif } network::IPAddress ip = wifi::global_wifi_component->wifi_soft_ap_ip(); diff --git a/esphome/components/captive_portal/captive_portal.h b/esphome/components/captive_portal/captive_portal.h index ae9b9dfba..f48c286f0 100644 --- a/esphome/components/captive_portal/captive_portal.h +++ b/esphome/components/captive_portal/captive_portal.h @@ -40,10 +40,6 @@ class CaptivePortal : public AsyncWebHandler, public Component { void end() { this->active_ = false; this->disable_loop(); // Stop processing DNS requests -#ifdef USE_ESP32 - // Disable LRU socket purging now that captive portal is done - this->base_->get_server()->set_lru_purge_enable(false); -#endif this->base_->deinit(); if (this->dns_server_ != nullptr) { this->dns_server_->stop(); diff --git a/esphome/components/web_server_idf/web_server_idf.cpp b/esphome/components/web_server_idf/web_server_idf.cpp index af99b85e5..8c3ad288c 100644 --- a/esphome/components/web_server_idf/web_server_idf.cpp +++ b/esphome/components/web_server_idf/web_server_idf.cpp @@ -117,18 +117,6 @@ void AsyncWebServer::end() { } } -void AsyncWebServer::set_lru_purge_enable(bool enable) { - if (this->lru_purge_enable_ == enable) { - return; // No change needed - } - this->lru_purge_enable_ = enable; - // If server is already running, restart it with new config - if (this->server_) { - this->end(); - this->begin(); - } -} - void AsyncWebServer::begin() { if (this->server_) { this->end(); @@ -136,8 +124,11 @@ void AsyncWebServer::begin() { httpd_config_t config = HTTPD_DEFAULT_CONFIG(); config.server_port = this->port_; config.uri_match_fn = [](const char * /*unused*/, const char * /*unused*/, size_t /*unused*/) { return true; }; - // Enable LRU purging if requested (e.g., by captive portal to handle probe bursts) - config.lru_purge_enable = this->lru_purge_enable_; + // Always enable LRU purging to handle socket exhaustion gracefully. + // When max sockets is reached, the oldest connection is closed to make room for new ones. + // This prevents "httpd_accept_conn: error in accept (23)" errors. + // See: https://github.com/esphome/esphome/issues/12464 + config.lru_purge_enable = true; // Use custom close function that shuts down before closing to prevent lwIP race conditions config.close_fn = AsyncWebServer::safe_close_with_shutdown; if (httpd_start(&this->server_, &config) == ESP_OK) { diff --git a/esphome/components/web_server_idf/web_server_idf.h b/esphome/components/web_server_idf/web_server_idf.h index a139e9e4d..5f9f59838 100644 --- a/esphome/components/web_server_idf/web_server_idf.h +++ b/esphome/components/web_server_idf/web_server_idf.h @@ -199,13 +199,11 @@ class AsyncWebServer { return *handler; } - void set_lru_purge_enable(bool enable); httpd_handle_t get_server() { return this->server_; } protected: uint16_t port_{}; httpd_handle_t server_{}; - bool lru_purge_enable_{false}; static esp_err_t request_handler(httpd_req_t *r); static esp_err_t request_post_handler(httpd_req_t *r); esp_err_t request_handler_(AsyncWebServerRequest *request) const;