Loading src/quic.cpp +11 −3 Original line number Diff line number Diff line Loading @@ -5522,11 +5522,19 @@ void quic::processAckFrame(const uint8_t* data, size_t len, size_t& offset, bool auto now = std::chrono::steady_clock::now(); // RFC 9002 §6.2.1: reset PTO backoff only when this ACK newly // acknowledges an ack-eliciting packet. Merely running loss detection // while some older ACK exists must not erase the backoff. bool newly_acked_ack_eliciting = false; // Remove acknowledged packets from _sent_packets and update RTT for (size_t ri = 0; ri < acked_count; ++ri) { auto& ar = acked_ranges[ri]; for (auto it = _sent_packets.lower_bound(ar.lo); it != _sent_packets.end() && it->first <= ar.hi; ) { if (it->second.ack_eliciting) { newly_acked_ack_eliciting = true; } // Update RTT from the largest newly-acked packet if (it->first == largest_ack && it->second.ack_eliciting) { double rtt_sample = std::chrono::duration<double>(now - it->second.sent_time).count(); Loading Loading @@ -5563,6 +5571,9 @@ void quic::processAckFrame(const uint8_t* data, size_t len, size_t& offset, bool if (_largest_acked_pn == UINT64_MAX || largest_ack > _largest_acked_pn) { _largest_acked_pn = largest_ack; } if (newly_acked_ack_eliciting) { _pto_count = 0; } // Trigger loss detection after processing ACK checkLossAndRetransmit(); Loading Loading @@ -5695,9 +5706,6 @@ void quic::checkLossAndRetransmit() { // Normal loss detection requires at least one ACK if (_largest_acked_pn == UINT64_MAX) return; // Reset PTO back-off counter once ACKs are flowing _pto_count = 0; // Time-based loss threshold (RFC 9002 §6.1.2). Floor raised from 1ms to // 5ms: on loopback/same-host connections srtt/rttvar are tiny, but two // independently scheduled userspace threads (sender + receiver event Loading test/quic_rfc9000_test.cpp +19 −0 Original line number Diff line number Diff line Loading @@ -546,6 +546,24 @@ static void test_flow_control_truncated_fin() { "Payload remains intact across the flow-control boundary"); } // ============================================================================ // RFC 9002 §6.2.1: PTO backoff persists until new data is acknowledged // ============================================================================ static void test_pto_backoff_persists_without_new_ack() { std::cout << "\n=== RFC 9002 §6.2.1: PTO backoff persistence ===" << std::endl; quic endpoint; uint8_t payload = 0x42; endpoint.recordSentPacket(100, 0, 0, &payload, 1, false, 1); endpoint._largest_acked_pn = 0; // An ACK existed before this loss scan. endpoint._pto_count = 3; endpoint.checkLossAndRetransmit(); check(endpoint._pto_count == 3, "Loss scan without a new ACK preserves PTO exponential backoff"); } // ============================================================================ // RFC 9000 §4.6: MAX_STREAMS — Rapid stream creation // ============================================================================ Loading Loading @@ -730,6 +748,7 @@ int main() { test_fin_handling(); test_empty_fin(); test_flow_control_truncated_fin(); test_pto_backoff_persists_without_new_ack(); test_data_integrity_small(); test_data_integrity_large(); test_connection_flow_control(); Loading Loading
src/quic.cpp +11 −3 Original line number Diff line number Diff line Loading @@ -5522,11 +5522,19 @@ void quic::processAckFrame(const uint8_t* data, size_t len, size_t& offset, bool auto now = std::chrono::steady_clock::now(); // RFC 9002 §6.2.1: reset PTO backoff only when this ACK newly // acknowledges an ack-eliciting packet. Merely running loss detection // while some older ACK exists must not erase the backoff. bool newly_acked_ack_eliciting = false; // Remove acknowledged packets from _sent_packets and update RTT for (size_t ri = 0; ri < acked_count; ++ri) { auto& ar = acked_ranges[ri]; for (auto it = _sent_packets.lower_bound(ar.lo); it != _sent_packets.end() && it->first <= ar.hi; ) { if (it->second.ack_eliciting) { newly_acked_ack_eliciting = true; } // Update RTT from the largest newly-acked packet if (it->first == largest_ack && it->second.ack_eliciting) { double rtt_sample = std::chrono::duration<double>(now - it->second.sent_time).count(); Loading Loading @@ -5563,6 +5571,9 @@ void quic::processAckFrame(const uint8_t* data, size_t len, size_t& offset, bool if (_largest_acked_pn == UINT64_MAX || largest_ack > _largest_acked_pn) { _largest_acked_pn = largest_ack; } if (newly_acked_ack_eliciting) { _pto_count = 0; } // Trigger loss detection after processing ACK checkLossAndRetransmit(); Loading Loading @@ -5695,9 +5706,6 @@ void quic::checkLossAndRetransmit() { // Normal loss detection requires at least one ACK if (_largest_acked_pn == UINT64_MAX) return; // Reset PTO back-off counter once ACKs are flowing _pto_count = 0; // Time-based loss threshold (RFC 9002 §6.1.2). Floor raised from 1ms to // 5ms: on loopback/same-host connections srtt/rttvar are tiny, but two // independently scheduled userspace threads (sender + receiver event Loading
test/quic_rfc9000_test.cpp +19 −0 Original line number Diff line number Diff line Loading @@ -546,6 +546,24 @@ static void test_flow_control_truncated_fin() { "Payload remains intact across the flow-control boundary"); } // ============================================================================ // RFC 9002 §6.2.1: PTO backoff persists until new data is acknowledged // ============================================================================ static void test_pto_backoff_persists_without_new_ack() { std::cout << "\n=== RFC 9002 §6.2.1: PTO backoff persistence ===" << std::endl; quic endpoint; uint8_t payload = 0x42; endpoint.recordSentPacket(100, 0, 0, &payload, 1, false, 1); endpoint._largest_acked_pn = 0; // An ACK existed before this loss scan. endpoint._pto_count = 3; endpoint.checkLossAndRetransmit(); check(endpoint._pto_count == 3, "Loss scan without a new ACK preserves PTO exponential backoff"); } // ============================================================================ // RFC 9000 §4.6: MAX_STREAMS — Rapid stream creation // ============================================================================ Loading Loading @@ -730,6 +748,7 @@ int main() { test_fin_handling(); test_empty_fin(); test_flow_control_truncated_fin(); test_pto_backoff_persists_without_new_ack(); test_data_integrity_small(); test_data_integrity_large(); test_connection_flow_control(); Loading