@@ -278,95 +278,136 @@ void AsyncAbstractResponse::_respond(AsyncWebServerRequest *request){
278
278
_ack (request, 0 , 0 );
279
279
}
280
280
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
+
281
286
size_t AsyncAbstractResponse::_ack (AsyncWebServerRequest *request, size_t len, uint32_t time){
282
287
(void )time;
288
+
283
289
if (!_sourceValid ()){
284
290
_state = RESPONSE_FAILED;
285
291
request->client ()->close ();
286
292
return 0 ;
287
293
}
288
294
_ackedLength += len;
289
- size_t space = request->client ()->space ();
290
295
296
+ size_t space = request->client ()->space (); // TCP window space available; NOT a guarantee we can actually send this much
291
297
size_t headLen = _head.length ();
298
+ bool needs_send = false ;
299
+
292
300
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;
301
307
}
308
+ _state = RESPONSE_CONTENT;
309
+ space -= headWritten;
310
+ _head = String (); // done
311
+ needs_send = true ;
302
312
}
303
313
304
314
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;
306
330
if (_chunked){
307
331
if (space <= 8 ){
308
- return 0 ;
332
+ goto content_abort ;
309
333
}
310
334
outLen = space;
311
335
} else if (!_sendContentLength){
312
336
outLen = space;
313
337
} 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);
321
339
}
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
+ }
325
359
}
326
-
327
- size_t readLen = 0 ;
328
-
329
- if (_chunked){
360
+ #else
361
+ _packet.resize (outLen);
362
+ #endif
363
+
364
+ if (_chunked){
330
365
// HTTP 1.1 allows leading zeros in chunk length. Or spaces may be added.
331
366
// See RFC2616 sections 2, 3.6.1.
332
- readLen = _fillBufferAndProcessTemplates (buf+headLen+ 6 , outLen - 8 );
367
+ readLen = _fillBufferAndProcessTemplates (_packet. data () + 6 , outLen - 8 );
333
368
if (readLen == RESPONSE_TRY_AGAIN){
334
- free (buf );
335
- return 0 ;
369
+ _packet. clear ( );
370
+ goto content_abort ;
336
371
}
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 ' ;
341
376
outLen += readLen;
342
- buf[outLen++] = ' \r ' ;
343
- buf[outLen++] = ' \n ' ;
377
+ _packet[outLen++] = ' \r ' ;
378
+ _packet[outLen++] = ' \n ' ;
379
+
344
380
} else {
345
- readLen = _fillBufferAndProcessTemplates (buf+headLen, outLen );
381
+ readLen = _fillBufferAndProcessTemplates (_packet. data (), _packet. size () );
346
382
if (readLen == RESPONSE_TRY_AGAIN){
347
- free (buf );
348
- return 0 ;
383
+ _packet. clear ( );
384
+ goto content_abort ;
349
385
}
350
- outLen = readLen + headLen;
351
- }
352
-
353
- if (headLen){
354
- _head = String ();
386
+ outLen = readLen;
355
387
}
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
+ }
365
405
}
366
406
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
+ {
370
411
_state = RESPONSE_WAIT_ACK;
371
412
}
372
413
return outLen;
@@ -379,6 +420,12 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest *request, size_t len, u
379
420
}
380
421
}
381
422
return 0 ;
423
+
424
+ content_abort:
425
+ if (needs_send) {
426
+ request->client ()->send ();
427
+ }
428
+ return 0 ;
382
429
}
383
430
384
431
size_t AsyncAbstractResponse::_readDataFromCacheOrContent (uint8_t * data, const size_t len)
@@ -390,15 +437,22 @@ size_t AsyncAbstractResponse::_readDataFromCacheOrContent(uint8_t* data, const s
390
437
_cache.erase (_cache.begin (), _cache.begin () + readFromCache);
391
438
}
392
439
// 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;
396
450
}
397
451
398
452
size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates (uint8_t * data, size_t len)
399
453
{
400
454
if (!_callback)
401
- return _fillBuffer (data, len);
455
+ return _readDataFromCacheOrContent (data, len);
402
456
403
457
const size_t originalLen = len;
404
458
len = _readDataFromCacheOrContent (data, len);
0 commit comments