@@ -368,15 +368,9 @@ impl State {
368368
369369 // if there was no error and we transmitted everything then just shut the
370370 // stream down
371- if close. error_code == VarInt :: ZERO
372- && close. frame_type . is_some ( )
373- && self . state . on_recv_all_acks ( ) . is_ok ( )
374- {
375- self . clean_up ( ) ;
376- // transmit one more PTO packet so we can ACK the peer's
377- // CONNECTION_CLOSE frame and they can shutdown quickly. Otherwise,
378- // they'll need to hang around to respond to potential loss.
379- self . pto . force_transmit ( ) ;
371+ if close. error_code == VarInt :: ZERO && close. frame_type . is_some ( ) {
372+ self . unacked_ranges . clear ( ) ;
373+ self . try_finish ( ) ;
380374 return Ok ( None ) ;
381375 }
382376
@@ -605,27 +599,25 @@ impl State {
605599 #[ allow( clippy:: redundant_closure_call) ]
606600 ( $on_packet) ( & packet) ;
607601
608- if let Some ( segment) = packet. info. retransmission {
602+ if let Some ( ( segment, retransmission) ) = packet. info. retransmit_copy( ) {
603+ // the stream segment was cleaned up so no need to retransmit
604+ if !self . stream_packet_buffers. contains_key( segment) {
605+ continue ;
606+ }
607+
609608 // update our local packet number to be at least 1 more than the largest lost
610609 // packet number
611610 let min_recovery_packet_number = num. as_u64( ) + 1 ;
612611 self . recovery_packet_number =
613612 self . recovery_packet_number. max( min_recovery_packet_number) ;
614613
615- let retransmission = retransmission:: Segment {
616- segment,
617- stream_offset: packet. info. stream_offset,
618- payload_len: packet. info. payload_len,
619- ty: TransmissionType :: Stream ,
620- included_fin: packet. info. included_fin,
621- } ;
622614 self . retransmissions. push( retransmission) ;
623615 } else {
624616 // we can only recover reliable streams
625617 is_unrecoverable |= packet. info. payload_len > 0 && !self . is_reliable;
626618 }
627- } }
628- }
619+ }
620+ } }
629621 }
630622
631623 match packet_space {
@@ -649,6 +641,48 @@ impl State {
649641 Ok ( ( ) )
650642 }
651643
644+ /// Takes the oldest stream packets and tries to make them into PTO packets
645+ ///
646+ /// This ensures that we're not wasting resources by sending empty payloads, especially
647+ /// when there's outstanding data waiting to be ACK'd.
648+ fn make_stream_packets_as_pto_probes ( & mut self ) {
649+ // check to see if we have in-flight stream segments
650+ ensure ! ( !self . sent_stream_packets. is_empty( ) ) ;
651+ // only reliable streams store segments
652+ ensure ! ( self . is_reliable) ;
653+
654+ let pto = self . pto . transmissions ( ) as usize ;
655+
656+ // check if we already have retransmissions scheduled
657+ let Some ( mut remaining) = pto. checked_sub ( self . retransmissions . len ( ) ) else {
658+ return ;
659+ } ;
660+
661+ // iterate until remaining is 0.
662+ //
663+ // This nested loop a bit weird but it's intentional - if we have `remaining == 2` but only have a single
664+ // in-flight stream segment then we want to transmit that segment `remaining` times.
665+ while remaining > 0 {
666+ for ( num, packet) in self . sent_stream_packets . iter ( ) . take ( remaining) {
667+ let Some ( ( _segment, retransmission) ) = packet. info . retransmit_copy ( ) else {
668+ // unreliable streams don't store segments so bail early - this was checked above
669+ return ;
670+ } ;
671+
672+ // update our local packet number to be at least 1 more than the largest lost
673+ // packet number
674+ let min_recovery_packet_number = num. as_u64 ( ) + 1 ;
675+ self . recovery_packet_number =
676+ self . recovery_packet_number . max ( min_recovery_packet_number) ;
677+
678+ self . retransmissions . push ( retransmission) ;
679+
680+ // consider this as a PTO
681+ remaining -= 1 ;
682+ }
683+ }
684+ }
685+
652686 #[ inline]
653687 fn on_peer_activity ( & mut self , newly_acked_packets : bool ) {
654688 if let Some ( prev) = self . peer_activity . as_mut ( ) {
@@ -894,6 +928,10 @@ impl State {
894928 // try to transition to start sending
895929 let _ = self . state . on_send_stream ( ) ;
896930 if info. included_fin {
931+ // clear out the unacked ranges that we're no longer tracking
932+ let final_offset = info. end_offset ( ) ;
933+ let _ = self . unacked_ranges . remove ( final_offset..) ;
934+
897935 // if the transmission included the final offset, then transition to that state
898936 let _ = self . state . on_send_fin ( ) ;
899937 }
@@ -960,6 +998,9 @@ impl State {
960998 // skip a packet number if we're probing
961999 if self . pto . transmissions ( ) > 0 {
9621000 self . recovery_packet_number += 1 ;
1001+
1002+ // Try making some existing stream packets as probes instead of transmitting empty ones
1003+ self . make_stream_packets_as_pto_probes ( ) ;
9631004 }
9641005
9651006 self . try_transmit_retransmissions ( control_key, credentials, clock) ?;
@@ -983,17 +1024,28 @@ impl State {
9831024 ensure ! ( self . is_reliable, Ok ( ( ) ) ) ;
9841025
9851026 while let Some ( retransmission) = self . retransmissions . peek ( ) {
986- // make sure we fit in the current congestion window
987- let remaining_cca_window = self
988- . cca
989- . congestion_window ( )
990- . saturating_sub ( self . cca . bytes_in_flight ( ) ) ;
991- ensure ! (
992- retransmission. payload_len as u32 <= remaining_cca_window,
993- break
994- ) ;
1027+ // If the CCA is requesting fast retransmission we can bypass the CWND check
1028+ if !self . cca . requires_fast_retransmission ( ) {
1029+ // make sure we fit in the current congestion window
1030+ let remaining_cca_window = self
1031+ . cca
1032+ . congestion_window ( )
1033+ . saturating_sub ( self . cca . bytes_in_flight ( ) ) ;
1034+ ensure ! (
1035+ retransmission. payload_len as u32 <= remaining_cca_window,
1036+ break
1037+ ) ;
1038+ }
9951039
996- let buffer = self . stream_packet_buffers [ retransmission. segment ] . make_mut ( ) ;
1040+ let Some ( buffer) = self . stream_packet_buffers . get_mut ( retransmission. segment ) else {
1041+ // the segment was acknowledged by another packet so remove it
1042+ let _ = self
1043+ . retransmissions
1044+ . pop ( )
1045+ . expect ( "retransmission should be available" ) ;
1046+ continue ;
1047+ } ;
1048+ let buffer = buffer. make_mut ( ) ;
9971049
9981050 debug_assert ! ( !buffer. is_empty( ) , "empty retransmission buffer submitted" ) ;
9991051
0 commit comments