@@ -352,7 +352,21 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest* request, size_t len, u
352352 request->client ()->close ();
353353 return 0 ;
354354 }
355+ // return a credit for each chunk of acked data (polls does not give any credits)
356+ if (len)
357+ ++_in_flight_credit;
358+
359+ // for chunked responses ignore acks if there are no _in_flight_credits left
360+ if (_chunked && !_in_flight_credit) {
361+ #ifdef ESP32
362+ log_d (" (chunk) out of in-flight credits" );
363+ #endif
364+ return 0 ;
365+ }
366+
355367 _ackedLength += len;
368+ _in_flight -= (_in_flight > len) ? len : _in_flight;
369+ // get the size of available sock space
356370 size_t space = request->client ()->space ();
357371
358372 size_t headLen = _head.length ();
@@ -364,16 +378,31 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest* request, size_t len, u
364378 String out = _head.substring (0 , space);
365379 _head = _head.substring (space);
366380 _writtenLength += request->client ()->write (out.c_str (), out.length ());
381+ _in_flight += out.length ();
382+ --_in_flight_credit; // take a credit
367383 return out.length ();
368384 }
369385 }
370386
371387 if (_state == RESPONSE_CONTENT ) {
388+ // for response data we need to control the queue and in-flight fragmentation. Sending small chunks could give low latency,
389+ // but flood asynctcp's queue and fragment socket buffer space for large responses.
390+ // Let's ignore polled acks and acks in case when we have more in-flight data then the available socket buff space.
391+ // That way we could balance on having half the buffer in-flight while another half is filling up, while minimizing events in asynctcp q
392+ if (_in_flight > space) {
393+ // log_d("defer user call %u/%u", _in_flight, space);
394+ // take the credit back since we are ignoring this ack and rely on other inflight data
395+ if (len)
396+ --_in_flight_credit;
397+ return 0 ;
398+ }
399+
372400 size_t outLen;
373401 if (_chunked) {
374402 if (space <= 8 ) {
375403 return 0 ;
376404 }
405+
377406 outLen = space;
378407 } else if (!_sendContentLength) {
379408 outLen = space;
@@ -422,6 +451,8 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest* request, size_t len, u
422451
423452 if (outLen) {
424453 _writtenLength += request->client ()->write ((const char *)buf, outLen);
454+ _in_flight += outLen;
455+ --_in_flight_credit; // take a credit
425456 }
426457
427458 if (_chunked) {
0 commit comments