@@ -278,95 +278,136 @@ void AsyncAbstractResponse::_respond(AsyncWebServerRequest *request){
278278 _ack (request, 0 , 0 );
279279}
280280
281+ template <typename T>
282+ static void dealloc_vector (T& vec) {
283+ vec = T{}; // move construct an empty vector; space will be freed when scope exits
284+ }
285+
281286size_t AsyncAbstractResponse::_ack (AsyncWebServerRequest *request, size_t len, uint32_t time){
282287 (void )time;
288+
283289 if (!_sourceValid ()){
284290 _state = RESPONSE_FAILED;
285291 request->client ()->close ();
286292 return 0 ;
287293 }
288294 _ackedLength += len;
289- size_t space = request->client ()->space ();
290295
296+ size_t space = request->client ()->space (); // TCP window space available; NOT a guarantee we can actually send this much
291297 size_t headLen = _head.length ();
298+ bool needs_send = false ;
299+
292300 if (_state == RESPONSE_HEADERS){
293- if (space >= headLen){
294- _state = RESPONSE_CONTENT;
295- space -= headLen;
296- } else {
297- String out = _head.substring (0 , space);
298- _head = _head.substring (space);
299- _writtenLength += request->client ()->write (out.c_str (), out.length ());
300- return out.length ();
301+ auto headWritten = request->client ()->add (_head.c_str (), std::min (space, headLen));
302+ _writtenLength += headWritten;
303+ if (headWritten < headLen) {
304+ _head = _head.substring (headWritten);
305+ request->client ()->send ();
306+ return headWritten;
301307 }
308+ _state = RESPONSE_CONTENT;
309+ space -= headWritten;
310+ _head = String (); // done
311+ needs_send = true ;
302312 }
303313
304314 if (_state == RESPONSE_CONTENT){
305- size_t outLen;
315+ if (_packet.size ()) {
316+ // Complete the cached data
317+ auto written = request->client ()->add ((const char *) _packet.data (), std::min (space, (size_t ) _packet.size ()));
318+ _writtenLength += written;
319+ _packet.erase (_packet.begin (), _packet.begin () + written);
320+ space -= written;
321+ if (_packet.size ()) {
322+ // Couldn't queue the full cache
323+ request->client ()->send ();
324+ return written;
325+ }
326+ needs_send = true ;
327+ }
328+
329+ size_t outLen, readLen;
306330 if (_chunked){
307331 if (space <= 8 ){
308- return 0 ;
332+ goto content_abort ;
309333 }
310334 outLen = space;
311335 } else if (!_sendContentLength){
312336 outLen = space;
313337 } else {
314- outLen = ((_contentLength - _sentLength) > space)?space:(_contentLength - _sentLength);
315- }
316-
317- uint8_t *buf = (uint8_t *)malloc (outLen+headLen);
318- if (!buf) {
319- // os_printf("_ack malloc %d failed\n", outLen+headLen);
320- return 0 ;
338+ outLen = std::min (space, _contentLength - _sentLength);
321339 }
322-
323- if (headLen){
324- memcpy (buf, _head.c_str (), _head.length ());
340+ #ifdef ESP8266
341+ // Limit outlen based on available memory
342+ // We require two packet buffers - one allocated here, and one belonging to the TCP stack
343+ {
344+ auto old_space = _packet.capacity ();
345+ auto max_block_size = ESP.getMaxFreeBlockSize () - 128 ;
346+ if ((old_space < outLen) || (outLen > max_block_size)) {
347+ Serial.printf_P (PSTR (" Space adjustment, have %d, want %d, avail %d\n " ), old_space, outLen, max_block_size);
348+ do {
349+ dealloc_vector (_packet);
350+ Serial.printf_P (PSTR (" Released buffer - capacity %d\n " ), _packet.capacity ());
351+ outLen = std::min (outLen, max_block_size);
352+ _packet.resize (outLen);
353+ max_block_size = ESP.getMaxFreeBlockSize () - 128 ;
354+ Serial.printf_P (PSTR (" Checking %d vs %d\n " ), outLen, max_block_size);
355+ } while (max_block_size < outLen);
356+ } else {
357+ _packet.resize (outLen);
358+ }
325359 }
326-
327- size_t readLen = 0 ;
328-
329- if (_chunked){
360+ #else
361+ _packet.resize (outLen);
362+ #endif
363+
364+ if (_chunked){
330365 // HTTP 1.1 allows leading zeros in chunk length. Or spaces may be added.
331366 // See RFC2616 sections 2, 3.6.1.
332- readLen = _fillBufferAndProcessTemplates (buf+headLen+ 6 , outLen - 8 );
367+ readLen = _fillBufferAndProcessTemplates (_packet. data () + 6 , outLen - 8 );
333368 if (readLen == RESPONSE_TRY_AGAIN){
334- free (buf );
335- return 0 ;
369+ _packet. clear ( );
370+ goto content_abort ;
336371 }
337- outLen = sprintf ((char *)buf+headLen , " %x" , readLen) + headLen ;
338- while (outLen < headLen + 4 ) buf [outLen++] = ' ' ;
339- buf [outLen++] = ' \r ' ;
340- buf [outLen++] = ' \n ' ;
372+ outLen = sprintf ((char *)_packet. data () , " %x" , readLen);
373+ while (outLen < 4 ) _packet [outLen++] = ' ' ;
374+ _packet [outLen++] = ' \r ' ;
375+ _packet [outLen++] = ' \n ' ;
341376 outLen += readLen;
342- buf[outLen++] = ' \r ' ;
343- buf[outLen++] = ' \n ' ;
377+ _packet[outLen++] = ' \r ' ;
378+ _packet[outLen++] = ' \n ' ;
379+
344380 } else {
345- readLen = _fillBufferAndProcessTemplates (buf+headLen, outLen );
381+ readLen = _fillBufferAndProcessTemplates (_packet. data (), _packet. size () );
346382 if (readLen == RESPONSE_TRY_AGAIN){
347- free (buf );
348- return 0 ;
383+ _packet. clear ( );
384+ goto content_abort ;
349385 }
350- outLen = readLen + headLen;
351- }
352-
353- if (headLen){
354- _head = String ();
386+ outLen = readLen;
355387 }
356-
357- if (outLen){
358- _writtenLength += request->client ()->write ((const char *)buf, outLen);
359- }
360-
361- if (_chunked){
362- _sentLength += readLen;
363- } else {
364- _sentLength += outLen - headLen;
388+ _packet.resize (outLen);
389+
390+ if (_packet.size ()){
391+ auto acceptedLen = request->client ()->write ((const char *)_packet.data (), _packet.size ());
392+ _writtenLength += acceptedLen;
393+ _packet.erase (_packet.begin (), _packet.begin () + acceptedLen); // TODO - does this realloc??
394+ if (acceptedLen < outLen) {
395+ // Save the unsent block in cache
396+ Serial.print (F (" Incomplete write, " )); Serial.print (acceptedLen); Serial.print (" /" ); Serial.println (outLen);
397+ Serial.print (F (" Heap: " )); Serial.print (ESP.getMaxFreeBlockSize ()); Serial.print (" /" ); Serial.println (ESP.getFreeHeap ());
398+ Serial.println (request->client ()->space ());
399+ // Try again, with less
400+ acceptedLen = request->client ()->write ((const char *)_packet.data (), _packet.size ()/2 );
401+ _writtenLength += acceptedLen;
402+ _packet.erase (_packet.begin (), _packet.begin () + acceptedLen); // TODO - does this realloc??
403+ Serial.println (acceptedLen);
404+ }
365405 }
366406
367- free (buf);
368-
369- if ((_chunked && readLen == 0 ) || (!_sendContentLength && outLen == 0 ) || (!_chunked && _sentLength == _contentLength)){
407+ if ( (_chunked && readLen == 0 ) // Chunked mode, no more data
408+ || (!_sendContentLength && outLen == 0 ) // No content length, no more data
409+ || (!_chunked && _writtenLength == (_headLength + _contentLength))) // non chunked mode, all data written
410+ {
370411 _state = RESPONSE_WAIT_ACK;
371412 }
372413 return outLen;
@@ -379,6 +420,12 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest *request, size_t len, u
379420 }
380421 }
381422 return 0 ;
423+
424+ content_abort:
425+ if (needs_send) {
426+ request->client ()->send ();
427+ }
428+ return 0 ;
382429}
383430
384431size_t AsyncAbstractResponse::_readDataFromCacheOrContent (uint8_t * data, const size_t len)
@@ -390,15 +437,22 @@ size_t AsyncAbstractResponse::_readDataFromCacheOrContent(uint8_t* data, const s
390437 _cache.erase (_cache.begin (), _cache.begin () + readFromCache);
391438 }
392439 // If we need to read more...
393- const size_t needFromFile = len - readFromCache;
394- const size_t readFromContent = _fillBuffer (data + readFromCache, needFromFile);
395- return readFromCache + readFromContent;
440+ if (len > readFromCache) {
441+ const size_t needFromFile = len - readFromCache;
442+ const size_t readFromContent = _fillBuffer (data + readFromCache, needFromFile);
443+ if (readFromContent != RESPONSE_TRY_AGAIN) {
444+ _sentLength += readFromContent;
445+ return readFromCache + readFromContent;
446+ }
447+ if (readFromCache == 0 ) return readFromContent;
448+ }
449+ return readFromCache;
396450}
397451
398452size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates (uint8_t * data, size_t len)
399453{
400454 if (!_callback)
401- return _fillBuffer (data, len);
455+ return _readDataFromCacheOrContent (data, len);
402456
403457 const size_t originalLen = len;
404458 len = _readDataFromCacheOrContent (data, len);
0 commit comments