Skip to content

Commit

Permalink
make process_output be able to return keep_alive timeout
Browse files Browse the repository at this point in the history
  • Loading branch information
KershawChang committed Sep 26, 2024
1 parent 55e3a93 commit 961ab33
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 23 deletions.
4 changes: 2 additions & 2 deletions neqo-http3/src/connection_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2620,7 +2620,7 @@ mod tests {
force_idle(&mut client, &mut server);

let idle_timeout = ConnectionParameters::default().get_idle_timeout();
assert_eq!(client.process_output(now()).callback(), idle_timeout);
assert_eq!(client.process_output(now()).callback(), idle_timeout / 2);
}

// Helper function: read response when a server sends HTTP_RESPONSE_2.
Expand Down Expand Up @@ -5114,7 +5114,7 @@ mod tests {
assert!(!fin);

force_idle(&mut client, &mut server);
assert_eq!(client.process_output(now()).callback(), idle_timeout);
assert_eq!(client.process_output(now()).callback(), idle_timeout / 2);
}

#[test]
Expand Down
15 changes: 15 additions & 0 deletions neqo-transport/src/connection/idle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,21 @@ impl IdleTimeout {
self.start(now) + max(self.timeout / 2, pto)
}

pub fn maybe_keep_alive_timeout(&self, now: Instant, pto: Duration) -> Option<Instant> {
if self.keep_alive_outstanding {
return None;
}

let timeout = self.keep_alive_timeout(now, pto);
// This could happen when the state is AckElicitingPacketSent(t) and
// the t exceeds keep_alive_timeout.
if timeout <= now {
return None;
}

Some(timeout)
}

pub fn send_keep_alive(
&mut self,
now: Instant,
Expand Down
11 changes: 10 additions & 1 deletion neqo-transport/src/connection/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1061,9 +1061,18 @@ impl Connection {
let pto = rtt.pto(PacketNumberSpace::ApplicationData);

let idle_time = self.idle_timeout.expiry(now, pto);
qtrace!([self], "Idle/keepalive timer {:?}", idle_time);
qtrace!([self], "Idle timer {:?}", idle_time);
delays.push(idle_time);

let keep_alive = self.streams.need_keep_alive();
if keep_alive {
if let Some(keep_alive_time) = self.idle_timeout.maybe_keep_alive_timeout(now, pto)
{
qtrace!([self], "Keep alive timer {:?}", keep_alive_time);
delays.push(keep_alive_time);
}
}

if let Some(lr_time) = self.loss_recovery.next_timeout(&path) {
qtrace!([self], "Loss recovery timer {:?}", lr_time);
delays.push(lr_time);
Expand Down
45 changes: 25 additions & 20 deletions neqo-transport/src/connection/tests/idle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -412,8 +412,9 @@ fn keep_alive_initiator() {
let stream = create_stream_idle(&mut server, &mut client);
let mut now = now();

// Marking the stream for keep-alive changes the idle timeout.
server.stream_keep_alive(stream, true).unwrap();
assert_idle(&mut server, now, default_timeout());
assert_idle(&mut server, now, default_timeout() / 2);

// Wait that long and the server should send a PING frame.
now += default_timeout() / 2;
Expand All @@ -427,8 +428,8 @@ fn keep_alive_initiator() {
let out = server.process(out.as_ref(), now).dgram();
assert!(client.process(out.as_ref(), now).dgram().is_none());

// Check that there will be next keep-alive ping after default_timeout().
assert_idle(&mut server, now, default_timeout());
// Check that there will be next keep-alive ping after default_timeout() / 2.
assert_idle(&mut server, now, default_timeout() / 2);
now += default_timeout() / 2;
let pings_before2 = server.stats().frame_tx.ping;
let ping = server.process_output(now).dgram();
Expand All @@ -446,7 +447,7 @@ fn keep_alive_lost() {
let mut now = now();

server.stream_keep_alive(stream, true).unwrap();
assert_idle(&mut server, now, default_timeout());
assert_idle(&mut server, now, default_timeout() / 2);

// Wait that long and the server should send a PING frame.
now += default_timeout() / 2;
Expand Down Expand Up @@ -475,7 +476,7 @@ fn keep_alive_lost() {
// return some small timeout for the recovry although it does not have
// any outstanding data. Therefore we call it after AT_LEAST_PTO.
now += AT_LEAST_PTO;
assert_idle(&mut server, now, default_timeout() - AT_LEAST_PTO);
assert_idle(&mut server, now, default_timeout() / 2 - AT_LEAST_PTO);
}

/// The other peer can also keep it alive.
Expand All @@ -488,10 +489,11 @@ fn keep_alive_responder() {
let mut now = now();

client.stream_keep_alive(stream, true).unwrap();
assert_idle(&mut client, now, default_timeout());
assert_idle(&mut client, now, default_timeout() / 2);

// Wait that long and the client should send a PING frame.
now += default_timeout() / 2;
eprintln!("after wait");
let pings_before = client.stats().frame_tx.ping;
let ping = client.process_output(now).dgram();
assert!(ping.is_some());
Expand All @@ -507,7 +509,7 @@ fn keep_alive_unmark() {
let stream = create_stream_idle(&mut client, &mut server);

client.stream_keep_alive(stream, true).unwrap();
assert_idle(&mut client, now(), default_timeout());
assert_idle(&mut client, now(), default_timeout() / 2);

client.stream_keep_alive(stream, false).unwrap();
assert_idle(&mut client, now(), default_timeout());
Expand Down Expand Up @@ -537,11 +539,11 @@ fn keep_alive_close() {
let stream = create_stream_idle(&mut client, &mut server);

client.stream_keep_alive(stream, true).unwrap();
assert_idle(&mut client, now(), default_timeout());
assert_idle(&mut client, now(), default_timeout() / 2);

client.stream_close_send(stream).unwrap();
transfer_force_idle(&mut client, &mut server);
assert_idle(&mut client, now(), default_timeout());
assert_idle(&mut client, now(), default_timeout() / 2);

server.stream_close_send(stream).unwrap();
transfer_force_idle(&mut server, &mut client);
Expand All @@ -558,11 +560,11 @@ fn keep_alive_reset() {
let stream = create_stream_idle(&mut client, &mut server);

client.stream_keep_alive(stream, true).unwrap();
assert_idle(&mut client, now(), default_timeout());
assert_idle(&mut client, now(), default_timeout() / 2);

client.stream_close_send(stream).unwrap();
transfer_force_idle(&mut client, &mut server);
assert_idle(&mut client, now(), default_timeout());
assert_idle(&mut client, now(), default_timeout() / 2);

server.stream_reset_send(stream, 0).unwrap();
transfer_force_idle(&mut server, &mut client);
Expand All @@ -584,7 +586,7 @@ fn keep_alive_stop_sending() {
let stream = create_stream_idle(&mut client, &mut server);

client.stream_keep_alive(stream, true).unwrap();
assert_idle(&mut client, now(), default_timeout());
assert_idle(&mut client, now(), default_timeout() / 2);

client.stream_close_send(stream).unwrap();
client.stream_stop_sending(stream, 0).unwrap();
Expand All @@ -608,14 +610,14 @@ fn keep_alive_multiple_stop() {
let stream = create_stream_idle(&mut client, &mut server);

client.stream_keep_alive(stream, true).unwrap();
assert_idle(&mut client, now(), default_timeout());
assert_idle(&mut client, now(), default_timeout() / 2);

let other = client.stream_create(StreamType::BiDi).unwrap();
client.stream_keep_alive(other, true).unwrap();
assert_idle(&mut client, now(), default_timeout());
assert_idle(&mut client, now(), default_timeout() / 2);

client.stream_keep_alive(stream, false).unwrap();
assert_idle(&mut client, now(), default_timeout());
assert_idle(&mut client, now(), default_timeout() / 2);

client.stream_keep_alive(other, false).unwrap();
assert_idle(&mut client, now(), default_timeout());
Expand Down Expand Up @@ -686,8 +688,9 @@ fn keep_alive_with_ack_eliciting_packet_lost() {

// Create a stream.
let stream = client.stream_create(StreamType::BiDi).unwrap();
// Marking the stream for keep-alive changes the idle timeout.
client.stream_keep_alive(stream, true).unwrap();
assert_idle(&mut client, now, IDLE_TIMEOUT);
assert_idle(&mut client, now, IDLE_TIMEOUT / 2);

// Send data on the stream that will be lost.
_ = client.stream_send(stream, DEFAULT_STREAM_DATA).unwrap();
Expand All @@ -702,11 +705,13 @@ fn keep_alive_with_ack_eliciting_packet_lost() {
let retransmit = client.process_output(now).dgram();
assert!(retransmit.is_some());

// The timeout is the twice the PTO, because we've already sent one probe.
assert_eq!(client.process_output(now).callback(), pto * 2);
// The next callback should be for an idle PING.
assert_eq!(
client.process_output(now).callback(),
IDLE_TIMEOUT / 2 - pto
);

// Wait for half the idle timeout (less the PTO we've already waited)
// so that we get a keep-alive.
// Wait that long and the client should send a PING frame.
now += IDLE_TIMEOUT / 2 - pto;
let pings_before = client.stats().frame_tx.ping;
let ping = client.process_output(now).dgram();
Expand Down

0 comments on commit 961ab33

Please sign in to comment.