summaryrefslogtreecommitdiffstats
path: root/third_party/rust/neqo-transport/src/connection/tests/ackrate.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /third_party/rust/neqo-transport/src/connection/tests/ackrate.rs
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/neqo-transport/src/connection/tests/ackrate.rs')
-rw-r--r--third_party/rust/neqo-transport/src/connection/tests/ackrate.rs194
1 files changed, 194 insertions, 0 deletions
diff --git a/third_party/rust/neqo-transport/src/connection/tests/ackrate.rs b/third_party/rust/neqo-transport/src/connection/tests/ackrate.rs
new file mode 100644
index 0000000000..1b83d42acd
--- /dev/null
+++ b/third_party/rust/neqo-transport/src/connection/tests/ackrate.rs
@@ -0,0 +1,194 @@
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::{mem, time::Duration};
+
+use test_fixture::{addr_v4, assertions};
+
+use super::{
+ super::{ConnectionParameters, ACK_RATIO_SCALE},
+ ack_bytes, connect_rtt_idle, default_client, default_server, fill_cwnd, increase_cwnd,
+ induce_persistent_congestion, new_client, new_server, send_something, DEFAULT_RTT,
+};
+use crate::stream_id::StreamType;
+
+/// With the default RTT here (100ms) and default ratio (4), endpoints won't send
+/// `ACK_FREQUENCY` as the ACK delay isn't different enough from the default.
+#[test]
+fn ack_rate_default() {
+ let mut client = default_client();
+ let mut server = default_server();
+ _ = connect_rtt_idle(&mut client, &mut server, DEFAULT_RTT);
+
+ assert_eq!(client.stats().frame_tx.ack_frequency, 0);
+ assert_eq!(server.stats().frame_tx.ack_frequency, 0);
+}
+
+/// When the congestion window increases, the rate doesn't change.
+#[test]
+fn ack_rate_slow_start() {
+ let mut client = default_client();
+ let mut server = default_server();
+ let now = connect_rtt_idle(&mut client, &mut server, DEFAULT_RTT);
+
+ // Increase the congestion window a few times.
+ let stream = client.stream_create(StreamType::UniDi).unwrap();
+ let now = increase_cwnd(&mut client, &mut server, stream, now);
+ let now = increase_cwnd(&mut client, &mut server, stream, now);
+ _ = increase_cwnd(&mut client, &mut server, stream, now);
+
+ // The client should not have sent an ACK_FREQUENCY frame, even
+ // though the value would have updated.
+ assert_eq!(client.stats().frame_tx.ack_frequency, 0);
+ assert_eq!(server.stats().frame_rx.ack_frequency, 0);
+}
+
+/// When the congestion window decreases, a frame is sent.
+#[test]
+fn ack_rate_exit_slow_start() {
+ let mut client = default_client();
+ let mut server = default_server();
+ let now = connect_rtt_idle(&mut client, &mut server, DEFAULT_RTT);
+
+ // Increase the congestion window a few times, enough that after a loss,
+ // there are enough packets in the window to increase the packet
+ // count in ACK_FREQUENCY frames.
+ let stream = client.stream_create(StreamType::UniDi).unwrap();
+ let now = increase_cwnd(&mut client, &mut server, stream, now);
+ let now = increase_cwnd(&mut client, &mut server, stream, now);
+
+ // Now fill the congestion window and drop the first packet.
+ let (mut pkts, mut now) = fill_cwnd(&mut client, stream, now);
+ pkts.remove(0);
+
+ // After acknowledging the other packets the client will notice the loss.
+ now += DEFAULT_RTT / 2;
+ let ack = ack_bytes(&mut server, stream, pkts, now);
+
+ // Receiving the ACK will cause the client to reduce its congestion window
+ // and to send ACK_FREQUENCY.
+ now += DEFAULT_RTT / 2;
+ assert_eq!(client.stats().frame_tx.ack_frequency, 0);
+ let af = client.process(Some(&ack), now).dgram();
+ assert!(af.is_some());
+ assert_eq!(client.stats().frame_tx.ack_frequency, 1);
+}
+
+/// When the congestion window collapses, `ACK_FREQUENCY` is updated.
+#[test]
+fn ack_rate_persistent_congestion() {
+ // Use a configuration that results in the value being set after exiting
+ // the handshake.
+ const RTT: Duration = Duration::from_millis(3);
+ let mut client = new_client(ConnectionParameters::default().ack_ratio(ACK_RATIO_SCALE));
+ let mut server = default_server();
+ let now = connect_rtt_idle(&mut client, &mut server, RTT);
+
+ // The client should have sent a frame.
+ assert_eq!(client.stats().frame_tx.ack_frequency, 1);
+
+ // Now crash the congestion window.
+ let stream = client.stream_create(StreamType::UniDi).unwrap();
+ let (dgrams, mut now) = fill_cwnd(&mut client, stream, now);
+ now += RTT / 2;
+ mem::drop(ack_bytes(&mut server, stream, dgrams, now));
+
+ let now = induce_persistent_congestion(&mut client, &mut server, stream, now);
+
+ // The client sends a second ACK_FREQUENCY frame with an increased rate.
+ let af = client.process_output(now).dgram();
+ assert!(af.is_some());
+ assert_eq!(client.stats().frame_tx.ack_frequency, 2);
+}
+
+/// Validate that the configuration works for the client.
+#[test]
+fn ack_rate_client_one_rtt() {
+ // This has to be chosen so that the resulting ACK delay is between 1ms and 50ms.
+ // We also have to avoid values between 20..30ms (approximately). The default
+ // maximum ACK delay is 25ms and an ACK_FREQUENCY frame won't be sent when the
+ // change to the maximum ACK delay is too small.
+ const RTT: Duration = Duration::from_millis(3);
+ let mut client = new_client(ConnectionParameters::default().ack_ratio(ACK_RATIO_SCALE));
+ let mut server = default_server();
+ let mut now = connect_rtt_idle(&mut client, &mut server, RTT);
+
+ // A single packet from the client will cause the server to engage its delayed
+ // acknowledgment timer, which should now be equal to RTT.
+ // The first packet will elicit an immediate ACK however, so do this twice.
+ let d = send_something(&mut client, now);
+ now += RTT / 2;
+ let ack = server.process(Some(&d), now).dgram();
+ assert!(ack.is_some());
+ let d = send_something(&mut client, now);
+ now += RTT / 2;
+ let delay = server.process(Some(&d), now).callback();
+ assert_eq!(delay, RTT);
+
+ assert_eq!(client.stats().frame_tx.ack_frequency, 1);
+}
+
+/// Validate that the configuration works for the server.
+#[test]
+fn ack_rate_server_half_rtt() {
+ const RTT: Duration = Duration::from_millis(10);
+ let mut client = default_client();
+ let mut server = new_server(ConnectionParameters::default().ack_ratio(ACK_RATIO_SCALE * 2));
+ let mut now = connect_rtt_idle(&mut client, &mut server, RTT);
+
+ // The server now sends something.
+ let d = send_something(&mut server, now);
+ now += RTT / 2;
+ // The client now will acknowledge immediately because it has been more than
+ // an RTT since it last sent an acknowledgment.
+ let ack = client.process(Some(&d), now);
+ assert!(ack.as_dgram_ref().is_some());
+ let d = send_something(&mut server, now);
+ now += RTT / 2;
+ let delay = client.process(Some(&d), now).callback();
+ assert_eq!(delay, RTT / 2);
+
+ assert_eq!(server.stats().frame_tx.ack_frequency, 1);
+}
+
+/// ACK delay calculations are path-specific,
+/// so check that they can be sent on new paths.
+#[test]
+fn migrate_ack_delay() {
+ // Have the client send ACK_FREQUENCY frames at a normal-ish rate.
+ let mut client = new_client(ConnectionParameters::default().ack_ratio(ACK_RATIO_SCALE));
+ let mut server = default_server();
+ let mut now = connect_rtt_idle(&mut client, &mut server, DEFAULT_RTT);
+
+ client
+ .migrate(Some(addr_v4()), Some(addr_v4()), true, now)
+ .unwrap();
+
+ let client1 = send_something(&mut client, now);
+ assertions::assert_v4_path(&client1, true); // Contains PATH_CHALLENGE.
+ let client2 = send_something(&mut client, now);
+ assertions::assert_v4_path(&client2, false); // Doesn't. Is dropped.
+ now += DEFAULT_RTT / 2;
+ server.process_input(&client1, now);
+
+ let stream = client.stream_create(StreamType::UniDi).unwrap();
+ let now = increase_cwnd(&mut client, &mut server, stream, now);
+ let now = increase_cwnd(&mut client, &mut server, stream, now);
+ let now = increase_cwnd(&mut client, &mut server, stream, now);
+
+ // Now lose a packet and force the client to update
+ let (mut pkts, mut now) = fill_cwnd(&mut client, stream, now);
+ pkts.remove(0);
+ now += DEFAULT_RTT / 2;
+ let ack = ack_bytes(&mut server, stream, pkts, now);
+
+ // After noticing this new loss, the client sends ACK_FREQUENCY.
+ // It has sent a few before (as we dropped `client2`), so ignore those.
+ let ad_before = client.stats().frame_tx.ack_frequency;
+ let af = client.process(Some(&ack), now).dgram();
+ assert!(af.is_some());
+ assert_eq!(client.stats().frame_tx.ack_frequency, ad_before + 1);
+}