Repository navigation
Don't spend an in-flight credit when nothing was sent - #488
Open
MitchBradley wants to merge 1 commit into
Open
MitchBradley wants to merge 1 commit into
MitchBradley wants to merge 1 commit into
Conversation
With ASYNCWEBSERVER_USE_CHUNK_INFLIGHT, write_send_buffs() took an in-flight credit on every call that reached the content stage, even when the source had no data yet (RESPONSE_TRY_AGAIN) and nothing was written. Credits come back only with acks of sent data, never from polls, so a response whose source is empty on two such calls runs out of credits. From then on every call is ignored and the response stalls for good. Take a credit only when data was actually put in flight. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
🟢 Approval recommended
The focused change correctly preserves retry capability without altering accounting for transmitted data.
0 open findings
What changed in this PR
Prevents stalled chunked or unknown-length responses when their source temporarily has no data.
Changes:
- Consume in-flight credit only when bytes are actually sent.
- Document why zero-byte retries must retain credit.
| File | Description |
|---|---|
src/WebResponses.cpp |
Guards in-flight accounting against zero-byte sends. |
🧠 Review effort: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
mathieucarbou
approved these changes
Oct 9, 2026
mathieucarbou
requested review from
willmmiles
and
a balanced review from Copilot
October 9, 2026 21:45
Member
|
Thank you for this bug fix - I agree with the analysis we might have missed this use case! I will try to test this weekend. |
Member
|
@willmmiles : pinging you to approve this small fix also. thanks! |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
With
ASYNCWEBSERVER_USE_CHUNK_INFLIGHTenabled (the default), a chunked or unknown-length response can stall permanently if its source isn't ready on a couple of consecutive calls.AsyncAbstractResponse::write_send_buffs()takes an in-flight credit at the end of the content stage on every call:_in_flight += payloadlen; --_in_flight_credit; // take a creditIt does this even when
_fillBufferAndProcessTemplates()returnedRESPONSE_TRY_AGAINandpayloadlenis 0. Credits come back only with acks of sent data (len > 0); polls never return one. So:_in_flight_creditis 0. Every later poll hits the!_in_flight_creditearly return, the user callback is never called again, and the response hangs until the connection times out.This hits any response whose producer can be momentarily slower than the connection drains: a chunked response fed by another task, for example.
How it showed up
In FluidNC, HTTP commands run on another task and stream their output into a small buffer that a chunked response drains, returning
RESPONSE_TRY_AGAINwhile the buffer is empty. When the network side drained faster than the command produced output, the response stalled after its first couple of empty fills. The command task then gave up on the full, undrained buffer, and clients received truncated replies (the first 2 KB of a 9 KB JSON settings listing).It's easiest to reproduce on a host build with a loopback-fast transport where acks are immediate. On ESP32 the same sequence needs a producer that falls behind the TCP drain, so it's intermittent there.
Fix
Take a credit, and count
_in_flight, only whenpayloadlenis nonzero. An empty call puts nothing in flight, so it shouldn't consume flight credit. Retries from polls then keep working until the source has data, and normal ack pacing resumes from there.Testing
On a host (Emscripten) build of FluidNC, using a custom AsyncTCP transport with immediate acks and
ASYNCWEBSERVER_USE_CHUNK_INFLIGHT=1, fetching the ~9 KB settings listing over a chunked response:Not yet tested on ESP32 hardware with lwIP. The change only affects calls that write zero bytes, so it can't increase data in flight.
🤖 Generated with Claude Code