From f615990c0067a4ca5156ed08ab80bfe53ccf3da0 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Sat, 21 Feb 2026 09:06:17 -0600 Subject: [PATCH] Limit http connections and add free heap check before allocating for SSL (#9693) * Reduce maximum concurrent HTTPS connections to save memory * Add heap check and limit connections to prevent HTTPS connection related crashes * Use Throttle::isWithinTimespanMs for overflow-safe heap warning throttle * Update src/mesh/http/WebServer.cpp Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Fix double-free heap corruption and parser leak in web server handlers - Remove double-free in handleFsBrowseStatic, handleNodes, handleScanNetworks: JSONValue(const JSONArray&) shallow-copies pointers, so delete value already recursively frees all elements. The explicit cleanup loops were deleting the same pointers a second time, corrupting the ESP32 heap allocator metadata. This is the likely root cause of #8827 (SSL setup failures after uptime). - Fix memory leak in handleFormUpload: two early-return paths were missing delete parser. - Remove unused global HTTPClient httpClient and its includes. --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/mesh/http/ContentHandler.cpp | 21 ++------------------- src/mesh/http/WebServer.cpp | 25 ++++++++++++++++++++++--- 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/src/mesh/http/ContentHandler.cpp b/src/mesh/http/ContentHandler.cpp index 281ece464..6b3aa4859 100644 --- a/src/mesh/http/ContentHandler.cpp +++ b/src/mesh/http/ContentHandler.cpp @@ -46,10 +46,6 @@ using namespace httpsserver; #include "mesh/http/ContentHandler.h" -#include -#include -HTTPClient httpClient; - #define DEST_FS_USES_LITTLEFS // We need to specify some content-type mapping, so the resources get delivered with the @@ -344,11 +340,6 @@ void handleFsBrowseStatic(HTTPRequest *req, HTTPResponse *res) res->print(jsonString.c_str()); delete value; - - // Clean up the fileList to prevent memory leak - for (auto *val : fileList) { - delete val; - } } void handleFsDeleteStatic(HTTPRequest *req, HTTPResponse *res) @@ -543,6 +534,7 @@ void handleFormUpload(HTTPRequest *req, HTTPResponse *res) if (name != "file") { LOG_DEBUG("Skip unexpected field"); res->println("

No file found.

"); + delete parser; return; } @@ -550,6 +542,7 @@ void handleFormUpload(HTTPRequest *req, HTTPResponse *res) if (filename == "") { LOG_DEBUG("Skip unexpected field"); res->println("

No file found.

"); + delete parser; return; } @@ -783,11 +776,6 @@ void handleNodes(HTTPRequest *req, HTTPResponse *res) std::string jsonString = value->Stringify(); res->print(jsonString.c_str()); delete value; - - // Clean up the nodesArray to prevent memory leak - for (auto *val : nodesArray) { - delete val; - } } /* @@ -941,10 +929,5 @@ void handleScanNetworks(HTTPRequest *req, HTTPResponse *res) std::string jsonString = value->Stringify(); res->print(jsonString.c_str()); delete value; - - // Clean up the networkObjs to prevent memory leak - for (auto *val : networkObjs) { - delete val; - } } #endif \ No newline at end of file diff --git a/src/mesh/http/WebServer.cpp b/src/mesh/http/WebServer.cpp index 3a264fa5a..90fd8b084 100644 --- a/src/mesh/http/WebServer.cpp +++ b/src/mesh/http/WebServer.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -55,6 +56,12 @@ static const int32_t ACTIVE_INTERVAL_MS = 50; static const int32_t MEDIUM_INTERVAL_MS = 200; static const int32_t IDLE_INTERVAL_MS = 1000; +// Maximum concurrent HTTPS connections (reduced from default 4 to save memory) +static const uint8_t MAX_HTTPS_CONNECTIONS = 2; + +// Minimum free heap required for SSL handshake (~40KB for mbedTLS contexts) +static const uint32_t MIN_HEAP_FOR_SSL = 40000; + static SSLCert *cert; static HTTPSServer *secureServer; static HTTPServer *insecureServer; @@ -67,8 +74,20 @@ static void handleWebResponse() if (isWifiAvailable()) { if (isWebServerReady) { - if (secureServer) - secureServer->loop(); + // Check heap before HTTPS processing - SSL requires significant memory + if (secureServer) { + uint32_t freeHeap = ESP.getFreeHeap(); + if (freeHeap >= MIN_HEAP_FOR_SSL) { + secureServer->loop(); + } else { + // Skip HTTPS when memory is low to prevent SSL setup failures + static uint32_t lastHeapWarning = 0; + if (lastHeapWarning == 0 || !Throttle::isWithinTimespanMs(lastHeapWarning, 30000)) { + LOG_WARN("Low heap (%u bytes), skipping HTTPS processing", freeHeap); + lastHeapWarning = millis(); + } + } + } insecureServer->loop(); } } @@ -229,7 +248,7 @@ void initWebServer() LOG_DEBUG("Init Web Server"); // We can now use the new certificate to setup our server as usual. - secureServer = new HTTPSServer(cert); + secureServer = new HTTPSServer(cert, 443, MAX_HTTPS_CONNECTIONS); insecureServer = new HTTPServer(); registerHandlers(insecureServer, secureServer);