@@ -50,11 +50,22 @@ struct guard_type {
5050#endif
5151
5252#ifndef ASYNCWEBSERVER_MINIMUM_ALLOC
53+ #ifdef ESP8266
5354#define ASYNCWEBSERVER_MINIMUM_ALLOC 1024
55+ #else
56+ #define ASYNCWEBSERVER_MINIMUM_ALLOC 4096
57+ #endif
5458#endif
5559
5660#ifndef ASYNCWEBSERVER_MINIMUM_HEAP
61+ #ifdef ESP8266
5762#define ASYNCWEBSERVER_MINIMUM_HEAP 2048
63+ #else /* ESP32 */
64+ // This is a *vastly* larger number. The ESP32 TCP stack does a *ton* of dynamic
65+ // allocation in the cricital path; and if we OOM the stack, it hangs up connections,
66+ // leaking bits and pieces and leaving the available memory fragmented.
67+ #define ASYNCWEBSERVER_MINIMUM_HEAP 8192
68+ #endif
5869#endif
5970
6071
@@ -79,23 +90,34 @@ static bool minimal_send_503(AsyncClient* c) {
7990 auto w = c->write (MSG_503, sizeof (MSG_503)-1 , ASYNC_WRITE_FLAG_COPY);
8091
8192 // assume any nonzero value is success
82- DEBUG_PRINTFP (" *** Sent 503 to %d (%d), result %d\n " , (intptr_t ) c, c->getRemotePort (), w);
93+ DEBUG_PRINTFP (" *** Sent 503 to %08X (%d), result %d\n " , (intptr_t ) c, c->getRemotePort (), w);
8394 if (w == 0 ) {
8495 c->close (true ); // sorry bud, we're really that strapped for ram
8596 }
8697 return (w != 0 );
8798}
8899
89100#ifdef ESP8266
90- #define GET_MAX_BLOCK_SIZE getMaxFreeBlockSize
101+ static inline size_t get_heap_available () {
102+ return ESP.getFreeHeap ();
103+ }
104+
105+ static inline size_t get_heap_alloc () {
106+ return ESP.getMaxFreeBlockSize ();
107+ }
91108#else
92- #define GET_MAX_BLOCK_SIZE getMaxAllocHeap
93- #endif
109+ // Platform functions don't correctly check for malloc()'s heap; at least on ESP32-WROVER
110+ // they incorrectly include some internal memory that is not accessible to malloc().
111+ // Reimplement using the correct MALLOC_CAPs.
112+ static inline size_t get_heap_available () {
113+ return heap_caps_get_free_size (MALLOC_CAP_INTERNAL | MALLOC_CAP_DEFAULT);
114+ }
94115
95- static bool heap_ok (size_t minHeap) {
96- return (ESP.getFreeHeap () > minHeap)
97- && (ESP.GET_MAX_BLOCK_SIZE () > ASYNCWEBSERVER_MINIMUM_ALLOC);
116+ static inline size_t get_heap_alloc () {
117+ return heap_caps_get_largest_free_block (MALLOC_CAP_INTERNAL | MALLOC_CAP_DEFAULT);
98118}
119+ #endif
120+
99121
100122AsyncWebServer::AsyncWebServer (uint16_t port)
101123 : AsyncWebServer(IPADDR_ANY, port)
@@ -128,33 +150,38 @@ AsyncWebServer::AsyncWebServer(IPAddress addr, uint16_t port, const AsyncWebServ
128150 if (c == NULL )
129151 return ;
130152
131- if (!heap_ok (ASYNCWEBSERVER_MINIMUM_HEAP)) {
153+ auto heap_avail = get_heap_available ();
154+ auto heap_alloc = get_heap_alloc ();
155+
156+ if ((heap_avail < ASYNCWEBSERVER_MINIMUM_HEAP)
157+ || (heap_alloc < ASYNCWEBSERVER_MINIMUM_ALLOC)) {
132158 // Protect ourselves from crashing - just abandon this request.
133- DEBUG_PRINTFP (" *** Dropping client %d (%d): %d, %d\n " , (intptr_t ) c, c->getRemotePort (), _requestQueue.length (), ESP. getFreeHeap () );
159+ DEBUG_PRINTFP (" *** Dropping client %08X (%d): %d, %d/%d \n " , (intptr_t ) c, c->getRemotePort (), _requestQueue.length (), heap_alloc, heap_avail );
134160 c->close (true );
135161 delete c;
136162 return ;
137163 }
138164
139165 guard ();
166+ auto queue_length = _requestQueue.length ();
140167
141- if (((_queueLimits.queueHeapRequired > 0 ) && ! heap_ok ( _queueLimits.queueHeapRequired ))
142- || ((_queueLimits.nMax > 0 ) && (_requestQueue. length () >= _queueLimits.nMax ))
168+ if (((_queueLimits.nMax > 0 ) && (queue_length >= _queueLimits.nMax ))
169+ || ((_queueLimits.queueHeapRequired > 0 ) && (heap_avail < _queueLimits.queueHeapRequired ))
143170 ) {
144171 // Don't even allocate anything we can avoid. Tell the client we're in trouble with a static response.
145- DEBUG_PRINTFP (" *** Rejecting client %d (%d): %d, %d\n " , (intptr_t ) c, c->getRemotePort (), _requestQueue.length (), ESP. getFreeHeap () );
172+ DEBUG_PRINTFP (" *** Rejecting client %08X (%d): %d, %d/%d \n " , (intptr_t ) c, c->getRemotePort (), _requestQueue.length (), heap_alloc, heap_avail );
146173 c->setNoDelay (true );
147174 c->onDisconnect ([](void *r, AsyncClient* rc){
148- DEBUG_PRINTFP (" *** Client %d (%d) disconnected\n " , (intptr_t )rc, rc->getRemotePort ());
175+ DEBUG_PRINTFP (" *** Client %08X (%d) disconnected\n " , (intptr_t )rc, rc->getRemotePort ());
149176 delete rc; // There is almost certainly something wrong with this - it's not OK to delete a function object while it's running
150177 });
151178 c->onAck ([](void *, AsyncClient* rc, size_t s, uint32_t ){
152- rc->close (true );
179+ if (s) rc->close (true );
153180 });
154181 c->onData ([](void *, AsyncClient* rc, void *, size_t ){
155182 rc->onData ({});
156- minimal_send_503 (rc);
157- });
183+ minimal_send_503 (rc);
184+ });
158185 return ;
159186 }
160187
@@ -351,7 +378,8 @@ void AsyncWebServer::processQueue(){
351378 DEBUG_PRINTFP (" Queue: %d entries, %d running, %d queued\n " , count, active, queued);
352379
353380 do {
354- auto heap_ok = ESP.getFreeHeap () >= (_queueLimits.requestHeapRequired + _queueLimits.queueHeapRequired );
381+ auto heap_ok = get_heap_available () > _queueLimits.requestHeapRequired ;
382+ auto alloc_ok = get_heap_alloc () > ASYNCWEBSERVER_MINIMUM_ALLOC;
355383 size_t active_entries = 0 ;
356384 AsyncWebServerRequest* next_queued_request = nullptr ;
357385
@@ -369,7 +397,10 @@ void AsyncWebServer::processQueue(){
369397
370398 if (!next_queued_request) break ; // all done
371399 if ((_queueLimits.nParallel > 0 ) && (active_entries >= _queueLimits.nParallel )) break ; // lots running
372- if ((active_entries > 0 ) && (!heap_ok)) break ; // heap not ok
400+ if ((active_entries > 0 ) && (!heap_ok || !alloc_ok)) {
401+ DEBUG_PRINTFP (" Can't queue more, heap %d alloc %d\n " , heap_ok, alloc_ok);
402+ break ;
403+ }
373404 next_queued_request->_handleRequest ();
374405 } while (1 ); // as long as we have memory and queued requests
375406
@@ -385,7 +416,7 @@ void AsyncWebServer::processQueue(){
385416
386417void AsyncWebServer::_dequeue (AsyncWebServerRequest *request){
387418 {
388- DEBUG_PRINTFP (" Removing %d from queue\n " , (intptr_t ) request);
419+ DEBUG_PRINTFP (" Removing %08X from queue\n " , (intptr_t ) request);
389420 guard ();
390421 _requestQueue.remove (request);
391422 }
0 commit comments