fix(http): probe the TLS heap only when a client is waiting for a slot

The probe holds ~21 kB across two callocs and ran whenever a slot was free,
which on a board with nobody connecting is every 50 ms while the web server is
active and every second when it is idle - on exactly the low-heap boards this
branch is for.

It now runs only when a zero-timeout select() on the listen socket says a
client is already in the backlog, which is the question loop() asks a few
microseconds later. With nothing knocking the server drives its open
connections and accepts nothing, so a connection is never accepted past an
unmeasured gate; one that lands between the two select()s waits a tick.
This commit is contained in:
Ben Meadors committed 2026-09-16 06:36:40 -05:00
1 parent a404fe44f1
commit 132d3f0859
1 file changed
+31 -14
+31 -14
View File
@@ -133,6 +133,19 @@ class MeshHTTPSServer : public HTTPSServer
_connections[i]->loop();
}
}
/// loop()'s own accept test, asked microseconds earlier: without it the probe holds 21 kB on
/// every tick of a server nobody is talking to.
bool hasPendingConnection() const
{
if (_socket < 0)
return false;
fd_set sockfds;
FD_ZERO(&sockfds);
FD_SET(_socket, &sockfds);
timeval immediate = {};
return select(_socket + 1, &sockfds, nullptr, nullptr, &immediate) > 0;
}
};
static SSLCert *cert;
@@ -149,21 +162,25 @@ static void handleWebResponse()
if (isWebServerReady) {
// Check heap before HTTPS processing - SSL requires significant memory
if (secureServer) {
// Reap first so the probe sees the heap a finished session just returned. With every slot
// busy loop() cannot accept, so the probe would buy nothing.
const TlsHeapVerdict verdict = secureServer->reapClosedConnections() ? judgeTlsSessionHeap() : TlsHeapVerdict::Ok;
if (verdict == TlsHeapVerdict::Ok) {
secureServer->loop();
} else {
// Low heap: accept nothing new, but keep servicing open connections so they can time out
// and free their contexts - skipping them pins the heap below the threshold for good.
// Reap first so the probe sees the heap a finished session just returned. Nowhere to put
// a connection, or nobody knocking: either way the probe would buy nothing.
if (!secureServer->reapClosedConnections() || !secureServer->hasPendingConnection()) {
secureServer->serviceExistingConnections();
static uint32_t lastHeapWarning = 0;
if (lastHeapWarning == 0 || !Throttle::isWithinTimespanMs(lastHeapWarning, 30000)) {
LOG_WARN("%s for a TLS session (%u free), not accepting HTTPS connections",
verdict == TlsHeapVerdict::TooLittleFree ? "Too little heap" : "No contiguous block",
(unsigned)heap_caps_get_free_size(TLS_HEAP_CAPS));
lastHeapWarning = Time::stampMillis();
} else {
const TlsHeapVerdict verdict = judgeTlsSessionHeap();
if (verdict == TlsHeapVerdict::Ok) {
secureServer->loop();
} else {
// Low heap: accept nothing new, but keep servicing open connections so they can time out
// and free their contexts - skipping them pins the heap below the threshold for good.
secureServer->serviceExistingConnections();
static uint32_t lastHeapWarning = 0;
if (lastHeapWarning == 0 || !Throttle::isWithinTimespanMs(lastHeapWarning, 30000)) {
LOG_WARN("%s for a TLS session (%u free), not accepting HTTPS connections",
verdict == TlsHeapVerdict::TooLittleFree ? "Too little heap" : "No contiguous block",
(unsigned)heap_caps_get_free_size(TLS_HEAP_CAPS));
lastHeapWarning = Time::stampMillis();
}
}
}
}