Skip to content

Fix connection window credit loss in HTTP/2 backlog accounting - #1032

Open
note89 wants to merge 1 commit into
apache:mainfrom
note89:fix-http2-backlog-size-accounting
Open

Fix connection window credit loss in HTTP/2 backlog accounting#1032
note89 wants to merge 1 commit into
apache:mainfrom
note89:fix-http2-backlog-size-accounting

Conversation

@note89

@note89 note89 commented Jul 29, 2026

Copy link
Copy Markdown

backLogSize is incremented when streams join the backlog, but allocate() never decrements it when granting connection window capacity to a backlogged stream. Only the full-clear branch of releaseBackLog() resets it. After any partial allocation the aggregate is larger than the sum of the outstanding per-stream requests.

A later WINDOW_UPDATE that covers all outstanding requests is then compared against the stale aggregate, takes the partial-allocation branch, and the surplus is discarded instead of being returned to the connection flow control window. Subsequent streams stall waiting for credit the client has already sent, until the write timeout closes the connection with ENHANCE_YOUR_CALM.

In the extended TestRfc9218 scenario: 10,239 bytes of real backlog, a 16,384-byte update — the 6,145-byte surplus is lost and the next stream receives headers but no data.

This is a regression from 169b813 ("Switch HTTP/2 to use RFC 9218 priorities rather than RFC 7540", shipped in 10.1.10 / 9.0.76 / 11.0.0-M6). The previous RFC 7540 allocation path decremented the aggregate (backLogSize -= stream.getConnectionAllocationMade() / ...Requested()); the rewritten urgency-based path did not carry that over.

Fix: decrement backLogSize as allocations are made, keeping it equal to the sum of outstanding requests. The invariant is documented on the field. Also fixes a digit transposition in an existing test comment (5641 -> 5461).

Testing: the extended TestRfc9218 fails without the fix (next stream starves until read timeout) and passes with it, asserting the exact 6,145-byte surplus plus a 2,047-byte update completing the 8,192-byte body. Full org.apache.coyote.http2 package passes. The same issue exists on 9.0.x and 10.1.x.

backLogSize was incremented when a stream added a reservation to the
backlog, but never reduced when allocate() granted connection window
capacity to a backlogged stream. Only the full-clear branch of
releaseBackLog() reset it, so after any partial allocation the aggregate
stayed larger than the sum of the outstanding per-stream requests.

The stale aggregate forced later releaseBackLog() calls down the
partial-allocation path even when the WINDOW_UPDATE covered every
outstanding request. That path discards any surplus instead of returning
it to the connection flow control window, so the server's view of the
window ended up permanently smaller than the credit the client had
granted. Streams created afterwards could stall waiting for window
capacity the client believed it had already provided, until the write
timeout closed the connection with ENHANCE_YOUR_CALM.

Decrement backLogSize as allocations are made so it always equals the
sum of the outstanding connection allocation requests, and extend the
RFC 9218 priority test to assert that surplus window update credit
remains usable by a subsequent stream.

Also fix a digit transposition in an existing comment (5641 -> 5461).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant