3030#define DEBUG_PRINTFP (...)
3131#endif
3232
33- #ifdef ESP8266
34- #define GET_MAX_BLOCK_SIZE getMaxFreeBlockSize
35- #else
36- #define GET_MAX_BLOCK_SIZE getMaxAllocHeap
37- #endif
38- // When looking up available memory, leave some slack
39- #define BLOCK_SIZE_SLACK 128
40-
4133
4234/*
4335 * Abstract Response
@@ -291,14 +283,40 @@ void AsyncAbstractResponse::_respond(AsyncWebServerRequest *request){
291283 _ack (request, 0 , 0 );
292284}
293285
294- template <typename T>
295- static void dealloc_vector (T& vec) {
296- vec = T{}; // move construct an empty vector; space will be freed when scope exits
286+
287+ static size_t _max_heap_alloc () {
288+ auto result =
289+ #ifdef ESP8266
290+ ESP.getMaxFreeBlockSize ()
291+ #else
292+ // ESP.getMaxAllocHeap() does not accurately reflect the behavior of malloc()
293+ // as it is missing the 'MALLOC_CAP_DEFAULT' flag.
294+ heap_caps_get_largest_free_block (MALLOC_CAP_INTERNAL | MALLOC_CAP_DEFAULT)
295+ #endif
296+ - 128 ; // fudge factor
297+
298+ return result;
299+ }
300+
301+ static DynamicBuffer _safe_allocate_buffer (size_t outLen) {
302+ // Espressif lwip configuration always copies in to the TCP stack, so
303+ // we have to have enough room to allocate the copy buffer. It's too bad we
304+ // can't re-use our assembly buffer, but it is what it is.
305+ auto rv = DynamicBuffer (outLen);
306+ if (outLen > TCP_MSS) {
307+ // Validate that there's enough space to allocate the copy buffer
308+ if (!rv || (_max_heap_alloc () < outLen)) {
309+ // Try allocating a single packet's worth instead
310+ rv.clear ();
311+ rv.resize (TCP_MSS);
312+ }
313+ }
314+ return rv;
297315}
298316
299317size_t AsyncAbstractResponse::_ack (AsyncWebServerRequest *request, size_t len, uint32_t time){
300318 (void )time;
301- DEBUG_PRINTFP (" (%d ) ack %d\n " , (intptr_t ) this , len);
319+ DEBUG_PRINTFP (" (%08x ) ack %d\n " , (intptr_t ) this , len);
302320
303321 if (!_sourceValid ()){
304322 _state = RESPONSE_FAILED;
@@ -307,16 +325,17 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest *request, size_t len, u
307325 }
308326 _ackedLength += len;
309327
310- size_t space = request->client ()->space (); // TCP window space available; NOT a guarantee we can actually send this much
311- size_t headLen = _head.length ();
328+ size_t space = request->client ()->space (); // TCP window space available; NOT a guarantee we can actually send this much
312329 bool needs_send = false ;
313330 if ((space == 0 ) && ((_state == RESPONSE_HEADERS) || (_state == RESPONSE_CONTENT))) {
314331 // Cannot accept more data now, wait for next event
315- DEBUG_PRINTFP (" (%d) No space to write \n " , (intptr_t )this );
332+ DEBUG_PRINTFP (" (%08x)NS \n " , (intptr_t )this );
316333 return 0 ;
317334 }
318335
319336 if (_state == RESPONSE_HEADERS){
337+ // FUTURE: we could replace _head with _packet
338+ size_t headLen = _head.length ();
320339 auto headWritten = request->client ()->add (_head.c_str (), std::min (space, headLen));
321340 _writtenLength += headWritten;
322341 if (headWritten < headLen) {
@@ -339,14 +358,15 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest *request, size_t len, u
339358 space -= written;
340359 if (_packet.size ()) {
341360 // Couldn't queue the full cache
342- DEBUG_PRINTFP (" (%d) Partial buffered write: wrote %d, remaining %d\n " , (intptr_t )this , written, _packet.size ());
361+ DEBUG_PRINTFP (" (%08x)PBW %d,%d\n " , (intptr_t )this , written, _packet.size ());
343362 if (written) request->client ()->send ();
344363 return written;
345364 }
365+ _packet = {};
346366 needs_send = true ;
347367 }
348- _packet.reset ();
349-
368+ assert ( _packet.capacity () == 0 ); // no buffer is allocated
369+
350370 size_t outLen, readLen;
351371 if (_chunked){
352372 if (space <= 8 ){
@@ -361,27 +381,16 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest *request, size_t len, u
361381
362382 // Limit outlen based on available memory
363383 // We require two packet buffers - one allocated here, and one belonging to the TCP stack
364- {
365- auto old_space = _packet.capacity ();
366- auto max_block_size = ESP.GET_MAX_BLOCK_SIZE () - BLOCK_SIZE_SLACK;
367- if ((old_space < outLen) || (outLen > max_block_size)) {
368- DEBUG_PRINTFP (" (%d) Space adjustment, have %d, want %d, avail %d\n " , (intptr_t )this , old_space, outLen, max_block_size);
369- do {
370- dealloc_vector (_packet);
371- outLen = std::min (outLen, max_block_size);
372- _packet.reallocate (outLen);
373- max_block_size = ESP.GET_MAX_BLOCK_SIZE () - BLOCK_SIZE_SLACK;
374- DEBUG_PRINTFP (" (%d) Checking %d vs %d\n " , (intptr_t )this , outLen, max_block_size);
375- } while (max_block_size < outLen);
376- } else {
377- _packet.reallocate (outLen);
378- }
379- }
384+ _packet = _safe_allocate_buffer (outLen);
380385
381- if (_chunked){
386+ if (_chunked){
387+ if (_packet.size () < 8 ) {
388+ _packet.clear ();
389+ goto content_abort;
390+ }
382391 // HTTP 1.1 allows leading zeros in chunk length. Or spaces may be added.
383392 // See RFC2616 sections 2, 3.6.1.
384- readLen = _fillBufferAndProcessTemplates ((uint8_t *) (_packet.data () + 6 ), outLen - 8 );
393+ readLen = _fillBufferAndProcessTemplates ((uint8_t *) (_packet.data () + 6 ), _packet. size () - 8 );
385394 if (readLen == RESPONSE_TRY_AGAIN){
386395 _packet.clear ();
387396 goto content_abort;
@@ -408,15 +417,15 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest *request, size_t len, u
408417 _writtenLength += acceptedLen;
409418 _packet.advance (acceptedLen);
410419 if (acceptedLen < outLen) {
411- // Save the unsent block in cache
412- DEBUG_PRINTFP (" (%d) Incomplete write, %d/%d\n Heap: %d/%d\n Space:%d\n " , (intptr_t ) this , acceptedLen, outLen, ESP.GET_MAX_BLOCK_SIZE (), ESP.getFreeHeap (), request->client ()->space ());
413- // Try again, with less
414- acceptedLen = request->client ()->write ((const char *)_packet.data (), _packet.size ()/2 );
420+ DEBUG_PRINTFP (" (%08x)IW%d/%d\n H:%d/%d\n S:%d\n " , (intptr_t ) this , acceptedLen, outLen, _max_heap_alloc (), ESP.getFreeHeap (), request->client ()->space ());
421+ // Try again, with less.
422+ acceptedLen = request->client ()->write ((const char *)_packet.data (), std::min (outLen/2 , (size_t )TCP_MSS));
415423 _writtenLength += acceptedLen;
416424 _packet.advance (acceptedLen);
417425 }
418- DEBUG_PRINTFP (" (%d) Accepted: %d\n " , (intptr_t ) this , acceptedLen);
419- if (_packet.size () == 0 ) dealloc_vector (_packet);
426+ // Data we couldn't send is held in _packet
427+ DEBUG_PRINTFP (" (%08x)AL%d %d\n " , (intptr_t ) this , acceptedLen, _packet.size ());
428+ if (_packet.size () == 0 ) _packet = {}; // release buffer
420429 }
421430
422431 if ( (_chunked && readLen == 0 ) // Chunked mode, no more data
0 commit comments