Context
Follow-up from PR #23 review (merged in 5d9c626).
Both PTO-driven retransmit-queueing sites in src/quic/connection.zig (~line 2989 crypto/loss reset path, ~line 3372 PTO scan) iterate only self.streams.streams (bidirectional streams) when looking for unacked data to queue for retransmission. Locally-initiated unidirectional send streams live in self.streams.send_streams and are never visited by these scans.
Issue
If a uni send stream loses its tail packet and no further ACK activity declares that packet lost, PTO will not rescue it — it sends a PING instead of the missing data, and the stream can sit unprogressed until ACK-based loss detection eventually fires (if it does).
This is pre-existing behavior (the old send_offset = ack_offset rewind in these same sites also only ran on bidi). PR #23 did not introduce it, but it also did not close the gap, and PR #23's new ACK-driven loss detection (scan in-flight on every ACK range) mostly compensates in practice — which is why this is a follow-up, not a blocker.
Severity
Important but low-frequency. Bare-QUIC / WebTransport workloads that push bulk data over uni streams are the exposure (HTTP/3 request/response is bidi and unaffected).
Fix
In both PTO sites, additionally iterate self.streams.send_streams.valueIterator() and apply the same queueRetransmit(start, end - start, fin_queued) logic used for bidi s.send. Add a regression test: open a uni stream, send data, drop the tail, fire PTO, assert the data is queued for retransmit (not a bare PING).
Context
Follow-up from PR #23 review (merged in 5d9c626).
Both PTO-driven retransmit-queueing sites in
src/quic/connection.zig(~line 2989 crypto/loss reset path, ~line 3372 PTO scan) iterate onlyself.streams.streams(bidirectional streams) when looking for unacked data to queue for retransmission. Locally-initiated unidirectional send streams live inself.streams.send_streamsand are never visited by these scans.Issue
If a uni send stream loses its tail packet and no further ACK activity declares that packet lost, PTO will not rescue it — it sends a PING instead of the missing data, and the stream can sit unprogressed until ACK-based loss detection eventually fires (if it does).
This is pre-existing behavior (the old
send_offset = ack_offsetrewind in these same sites also only ran on bidi). PR #23 did not introduce it, but it also did not close the gap, and PR #23's new ACK-driven loss detection (scan in-flight on every ACK range) mostly compensates in practice — which is why this is a follow-up, not a blocker.Severity
Important but low-frequency. Bare-QUIC / WebTransport workloads that push bulk data over uni streams are the exposure (HTTP/3 request/response is bidi and unaffected).
Fix
In both PTO sites, additionally iterate
self.streams.send_streams.valueIterator()and apply the samequeueRetransmit(start, end - start, fin_queued)logic used for bidis.send. Add a regression test: open a uni stream, send data, drop the tail, fire PTO, assert the data is queued for retransmit (not a bare PING).