fn update_pto_timer()

in quic/s2n-quic-transport/src/recovery/manager/tests.rs [2755:2891]


fn update_pto_timer() {
    let space = PacketNumberSpace::ApplicationData;
    let mut manager = ServerManager::new(space);
    let now = time::now() + Duration::from_secs(10);
    let is_handshake_confirmed = true;
    let mut path_manager = helper_generate_path_manager(Duration::from_millis(10));
    let ecn = ExplicitCongestionNotification::default();
    let mut context = MockContext::new(&mut path_manager);
    let mut publisher = Publisher::snapshot();

    context.path_mut().rtt_estimator.update_rtt(
        Duration::from_millis(0),
        Duration::from_millis(500),
        now,
        true,
        space,
    );
    context.path_mut().rtt_estimator.update_rtt(
        Duration::from_millis(0),
        Duration::from_millis(1000),
        now,
        true,
        space,
    );
    // The path will be at the anti-amplification limit
    let amplification_outcome = context.path_mut().on_bytes_received(1200);
    assert!(amplification_outcome.is_active_path_unblocked());
    context.path_mut().on_bytes_transmitted((1200 * 3) + 1);
    // Arm the PTO so we can verify it is cancelled
    manager.pto.update(now, Duration::from_secs(10));
    manager.pto_update_pending = true;
    manager.update_pto_timer(context.path(), now, is_handshake_confirmed);

    //= https://www.rfc-editor.org/rfc/rfc9002#section-6.2.2.1
    //= type=test
    //# If no additional data can be sent, the server's PTO timer MUST NOT be
    //# armed until datagrams have been received from the client, because
    //# packets sent on PTO count against the anti-amplification limit.
    assert!(!manager.pto.is_armed());
    assert!(!manager.pto_update_pending);

    // Arm the PTO so we can verify it is cancelled
    manager.pto.update(now, Duration::from_secs(10));
    manager.pto_update_pending = true;
    // Validate the path so it is not at the anti-amplification limit
    //
    // simulate receiving a handshake packet to force path validation
    context.path_mut().on_handshake_packet();
    context.path_mut().on_peer_validated();
    manager.update_pto_timer(context.path(), now, is_handshake_confirmed);

    // Since the path is peer validated and sent packets is empty, PTO is cancelled
    assert!(!manager.pto.is_armed());
    assert!(!manager.pto_update_pending);

    // Reset the path back to not peer validated
    let path_id = unsafe { path::Id::new(0) };
    let mut rtt_estimator = RttEstimator::default();
    rtt_estimator.on_max_ack_delay(Duration::from_millis(10).try_into().unwrap());
    context.path_manager[path_id] = Path::new(
        Default::default(),
        connection::PeerId::TEST_ID,
        connection::LocalId::TEST_ID,
        rtt_estimator,
        MockCongestionController::default(),
        false,
        mtu::Config::default(),
        ANTI_AMPLIFICATION_MULTIPLIER,
    );
    context.path_manager.activate_path_for_test(path_id);
    // simulate receiving a handshake packet to force path validation
    context.path_mut().on_handshake_packet();
    context.path_mut().pto_backoff = 2;
    manager.pto_update_pending = true;
    let is_handshake_confirmed = false;
    manager.update_pto_timer(context.path(), now, is_handshake_confirmed);

    //= https://www.rfc-editor.org/rfc/rfc9002#section-6.2.1
    //= type=test
    //# An endpoint MUST NOT set its PTO timer for the Application Data
    //# packet number space until the handshake is confirmed.
    assert!(!manager.pto.is_armed());
    assert!(!manager.pto_update_pending);

    // Set is handshake confirmed back to true
    let is_handshake_confirmed = true;
    manager.pto_update_pending = true;
    manager.update_pto_timer(context.path(), now, is_handshake_confirmed);

    // Now the PTO is armed
    assert!(manager.pto.is_armed());
    assert!(!manager.pto_update_pending);

    // Send a packet to validate behavior when sent_packets is not empty
    manager.on_packet_sent(
        space.new_packet_number(VarInt::from_u8(1)),
        transmission::Outcome {
            ack_elicitation: AckElicitation::Eliciting,
            is_congestion_controlled: true,
            bytes_sent: 1,
            bytes_progressed: 0,
        },
        now,
        ecn,
        transmission::Mode::Normal,
        None,
        &mut context,
        &mut publisher,
    );

    let expected_pto_base_timestamp = now - Duration::from_secs(5);
    manager.time_of_last_ack_eliciting_packet = Some(expected_pto_base_timestamp);
    // This will update the smoother_rtt to 2000, and rtt_var to 1000
    context.path_mut().rtt_estimator.update_rtt(
        Duration::from_millis(0),
        Duration::from_millis(2000),
        now,
        true,
        space,
    );
    manager.pto_update_pending = true;
    manager.update_pto_timer(context.path(), now, is_handshake_confirmed);

    //= https://www.rfc-editor.org/rfc/rfc9002#section-6.2.1
    //# When an ack-eliciting packet is transmitted, the sender schedules a
    //# timer for the PTO period as follows:
    //#
    //# PTO = smoothed_rtt + max(4*rttvar, kGranularity) + max_ack_delay
    // Including the pto backoff (2) =:
    // PTO = (2000 + max(4*1000, 1) + 10) * 2 = 12020
    assert!(manager.pto.is_armed());
    assert_eq!(
        manager.pto.next_expiration().unwrap(),
        expected_pto_base_timestamp + Duration::from_millis(12020)
    );
    assert!(!manager.pto_update_pending);
}