Skip to content

Conversation

gregor-cf
Copy link
Contributor

This fixes issues in quiche, where quiche does not properly update connection level flow control when sending STOP_SENDING or receiving RESET_STREAM frames.

On calling shutdown_stream(id, Shutdown::Read), quiche drops the receive buffer (correct) but it should also mark these bytes as "consumed" to the connection-level flow-control.

On receiving a RESET_STREAM frame, quiche drops the receive buffer but it should also mark these dropped bytes as "consumed" to the connection-level flow-control.

When Quiche updates "consumed" in the two above cases, it should also check if needs to send a MAX_DATA frame after updating "consumed".

Note, the two added quiche::h3 tests are not directly related but I started there because I first suspected streams not getting collected. I did add tests to quiche itself that test my changes though.

@gregor-cf gregor-cf requested a review from a team as a code owner September 17, 2025 02:32
@gregor-cf gregor-cf force-pushed the quiche-reset-send-max-data branch 2 times, most recently from 165159b to 5bdd02c Compare September 17, 2025 03:20
@LPardue
Copy link
Contributor

LPardue commented Sep 17, 2025

I'm not sure this sullfy satisfies RFC 9000 requirements when using stop_sending because, IIUC, you're accounting for bytes in the connection-level flow control before receiving the final size

An endpoint will know the final size for a stream when the receiving part of the stream enters the "Size Known" or "Reset Recvd" state (Section 3). The receiver MUST use the final size of the stream to account for all bytes sent on the stream in its connection-level flow controller.

I might be wrong but worth double checking. If im right, then data in-flight that increased the final size before the stopper received it, could result in the endpoints getting out of sync with connection-level FC

@gregor-cf
Copy link
Contributor Author

I'm not sure this sullfy satisfies RFC 9000 requirements when using stop_sending because, IIUC, you're accounting for bytes in the connection-level flow control before receiving the final size

I think my logic is correct. When sending the STOP_SENDING frame, we drop all currently received bytes and I return FC credit based on the highest offset received. This is correct IMHO, it's like the application had read the full buffer (the difference is that we don't about gaps in the STOP_SENDING case). Since whatever final size we later receive MUST be larger or equal than this offest, otherwise we close due to protocol violation. We also mark the send-buffer as "draining" (that is existing code): any additional stream data we receive with a higher offset we record in the buffer's max_off and in the connection level rx_data counter. And we also immediately mark it as read to the connection level FC too (since we are not buffering it, like the app had read it). Eventually we will receive a FIN or a RESET_STREAM for this stream. At this time we know the final size and verify it's >= than the highest received offset. If the final size is larger than highest received, we update the conn's rx_data and FC accordingly.
In other words: after sending STOP_SENDING, we essentially pretend that any newly arriving data is immediately read by the app (and we also pretend that we've received any gaps) and we still wait for a fin or RESET_FRAME to know the final size.

One thing to note: the current code doesn't do a sanity check with the final size. It keeps a running count of received data and just verifies that the final size is consistent with what has been received previously.

@LPardue
Copy link
Contributor

LPardue commented Sep 17, 2025

Thanks for the details. Can we add a test case for the scenario where data arrives after one of the endpoints has stopped reading to ensure that the connection FC counters are tracked appropriately (or confirm that one of the existing tests does it) please?

@gregor-cf gregor-cf force-pushed the quiche-reset-send-max-data branch from 5bdd02c to c838581 Compare September 17, 2025 16:46
@gregor-cf
Copy link
Contributor Author

I've updated the test flow_control_drain() (which wasn't really testing flow control) and added the test flow_control_reset_stream()

@gregor-cf gregor-cf force-pushed the quiche-reset-send-max-data branch 3 times, most recently from da7426d to 87fde90 Compare September 22, 2025 13:59
@gregor-cf
Copy link
Contributor Author

gentle ping @LPardue
any more thoughts on this PR?

…ET_STREAM

This fixes issues in quiche, where quiche does not properly update connection level flow control when sending STOP_SENDING or receiving RESET_STREAM frames.

On calling `shutdown_stream(id, Shutdown::Read)`, quiche drops the receive buffer (correct) but it should also mark these bytes as "consumed" to the connection-level flow-control.
On receiving a RESET_STREAM frame, quiche drops the receive buffer but it should also mark these dropped bytes as "consumed" to the connection-level flow-control.
Quiche should also check if needs to send a MAX_DATA frame after updating "consumed"
@gregor-cf gregor-cf force-pushed the quiche-reset-send-max-data branch from 87fde90 to c7aff6e Compare September 30, 2025 22:41
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.

2 participants