Skip to content
This repository was archived by the owner on Jan 21, 2025. It is now read-only.

Commit 3d3456e

Browse files
committed
implement in-flight buffer credits and event moderation for large/chunked responses
Referer to #165 Relates to #169 in-flight buffer credits are intended to moderate buffer fill callbacks in AsyncAbstractResponse it could prevent bad designed slow user-callbacks to flood the queue in chunked responces. for response data we need to control the queue and in-flight fragmentation. Sending small chunks could give low latency, but flood asynctcp's queue and fragment socket buffer space for large responses. Let's ignore polled acks and acks in case when we have more in-flight data then the available socket buff space. That way we could balance on having half the buffer in-flight while another half is filling up, while minimizing events in asynctcp q
1 parent 359cc68 commit 3d3456e

2 files changed

Lines changed: 35 additions & 0 deletions

File tree

src/WebResponseImpl.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ class AsyncBasicResponse : public AsyncWebServerResponse {
4747

4848
class AsyncAbstractResponse : public AsyncWebServerResponse {
4949
private:
50+
// amount of responce data in-flight, i.e. sent, but not acked yet
51+
size_t _in_flight{0};
52+
// in-flight queue credits
53+
size_t _in_flight_credit{2};
5054
String _head;
5155
// Data is inserted into cache at begin().
5256
// This is inefficient with vector, but if we use some other container,

src/WebResponses.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)