Skip to content

Commit

Permalink
Add API for deferring idle timeout
Browse files Browse the repository at this point in the history
  • Loading branch information
iyangsj committed Jul 25, 2024
1 parent 952767b commit 7ada2ff
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 1 deletion.
15 changes: 15 additions & 0 deletions include/tquic.h
Original file line number Diff line number Diff line change
Expand Up @@ -966,6 +966,21 @@ void quic_conn_session(struct quic_conn_t *conn, const uint8_t **out, size_t *ou
*/
int quic_conn_early_data_reason(struct quic_conn_t *conn, const uint8_t **out, size_t *out_len);

/**
* Send a Ping frame on the active path(s) for keep-alive.
*/
int quic_conn_ping(struct quic_conn_t *conn);

/**
* Send a Ping frame on the specified path for keep-alive.
* The API is only applicable to multipath quic connections.
*/
int quic_conn_ping_path(struct quic_conn_t *conn,
const struct sockaddr *local,
socklen_t local_len,
const struct sockaddr *remote,
socklen_t remote_len);

/**
* Add a new path on the client connection.
*/
Expand Down
58 changes: 57 additions & 1 deletion src/connection/connection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1843,11 +1843,15 @@ impl Connection {
self.try_write_new_token_frame(out, st, pkt_type, path_id)?;

// Write a PING frame
if st.ack_elicit_required && !st.ack_eliciting && !self.is_closing() {
if ((st.ack_elicit_required && !st.ack_eliciting)
|| self.paths.get_mut(path_id)?.need_send_ping)
&& !self.is_closing()
{
let frame = Frame::Ping { pmtu_probe: None };
Connection::write_frame_to_packet(frame, out, st)?;
st.ack_eliciting = true;
st.in_flight = true;
self.paths.get_mut(path_id)?.need_send_ping = false;
}

// No frames to be sent
Expand Down Expand Up @@ -2832,6 +2836,9 @@ impl Connection {
if space.need_send_buffered_frames() && path.recovery.can_send() {
return Ok(pid);
}
if path.need_send_ping {
return Ok(pid);
}
continue;
}
None => continue,
Expand Down Expand Up @@ -2924,6 +2931,7 @@ impl Connection {
|| self.local_error.as_ref().map_or(false, |e| e.is_app)
|| path.need_send_validation_frames(self.is_server)
|| path.dplpmtud.should_probe()
|| path.need_send_ping
|| self.cids.need_send_cid_control_frames()
|| self.streams.need_send_stream_frames()
|| self.spaces.need_send_buffered_frames())
Expand Down Expand Up @@ -3499,6 +3507,14 @@ impl Connection {
})
}

/// Send a Ping frame for keep-alive.
///
/// If `path_addr` is `None`, a Ping frame will be sent on each active path.
/// Otherwise, a Ping frame will be on the specified path.
pub fn ping(&mut self, path_addr: Option<FourTuple>) -> Result<()> {
self.paths.mark_ping(path_addr)
}

/// Client add a new path on the connection.
pub fn add_path(&mut self, local_addr: SocketAddr, remote_addr: SocketAddr) -> Result<u64> {
if self.is_server {
Expand Down Expand Up @@ -5940,6 +5956,46 @@ pub(crate) mod tests {
Ok(())
}

#[test]
fn ping() -> Result<()> {
let mut client_config = TestPair::new_test_config(false)?;
client_config.enable_dplpmtud(false);
client_config.local_transport_params = TransportParams {
max_idle_timeout: 15000,
..TransportParams::default()
};
let mut server_config = TestPair::new_test_config(true)?;
server_config.enable_dplpmtud(false);
server_config.local_transport_params = TransportParams {
max_idle_timeout: 15000,
..TransportParams::default()
};
let mut test_pair = TestPair::new(&mut client_config, &mut server_config)?;
test_pair.handshake()?;

// Move both connections to idle state
test_pair.move_forward()?;

// Enable qlog for Server
let slog = NamedTempFile::new().unwrap();
let mut sfile = slog.reopen().unwrap();
test_pair
.server
.set_qlog(Box::new(slog), "title".into(), "desc".into());

// Client send a Ping frame
test_pair.client.ping(None)?;
let packets = TestPair::conn_packets_out(&mut test_pair.client)?;
TestPair::conn_packets_in(&mut test_pair.server, packets.clone())?;

let mut slog_content = String::new();
sfile.read_to_string(&mut slog_content).unwrap();
assert_eq!(slog_content.contains("quic:packet_received"), true);
assert_eq!(slog_content.contains("frame_type\":\"ping"), true);

Ok(())
}

#[test]
fn conn_basic_operations() -> Result<()> {
let mut test_pair = TestPair::new_with_zero_cid()?;
Expand Down
32 changes: 32 additions & 0 deletions src/connection/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ use super::timer;
use crate::connection::SpaceId;
use crate::error::Error;
use crate::multipath_scheduler::MultipathScheduler;
use crate::FourTuple;
use crate::PathStats;
use crate::RecoveryConfig;
use crate::Result;
Expand Down Expand Up @@ -92,6 +93,9 @@ pub struct Path {
/// The current pmtu probing state of the path.
pub(super) dplpmtud: Dplpmtud,

/// Whether a Ping frame should be sent on the path.
pub(super) need_send_ping: bool,

/// Trace id.
trace_id: String,

Expand Down Expand Up @@ -140,6 +144,7 @@ impl Path {
peer_verified_local_address: false,
anti_ampl_limit: 0,
dplpmtud,
need_send_ping: false,
trace_id: trace_id.to_string(),
space_id: SpaceId::Data,
is_abandon: false,
Expand Down Expand Up @@ -607,6 +612,33 @@ impl PathMap {
left
}

/// Schedule a Ping frame on the specified path or all active paths.
pub fn mark_ping(&mut self, path_addr: Option<FourTuple>) -> Result<()> {
// If multipath is not enabled, schedule a Ping frame on the current
// active path.
if !self.is_multipath {
self.get_active_mut()?.need_send_ping = true;
return Ok(());
}

// If multipath is enabled, schedule a Ping frame on the specified path
// or all the active paths.
if let Some(a) = path_addr {
let pid = match self.get_path_id(&(a.local, a.remote)) {
Some(pid) => pid,
None => return Ok(()),
};
self.get_mut(pid)?.need_send_ping = true;
return Ok(());
}
for (_, path) in self.paths.iter_mut() {
if path.active() {
path.need_send_ping = true;
}
}
Ok(())
}

/// Promote to multipath mode.
pub fn enable_multipath(&mut self) {
self.is_multipath = true;
Expand Down
29 changes: 29 additions & 0 deletions src/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1048,6 +1048,35 @@ pub extern "C" fn quic_conn_early_data_reason(
}
}

/// Send a Ping frame on the active path(s) for keep-alive.
#[no_mangle]
pub extern "C" fn quic_conn_ping(conn: &mut Connection) -> c_int {
match conn.ping(None) {
Ok(_) => return 0,
Err(e) => return e.to_errno() as c_int,
}
}

/// Send a Ping frame on the specified path for keep-alive.
/// The API is only applicable to multipath quic connections.
#[no_mangle]
pub extern "C" fn quic_conn_ping_path(
conn: &mut Connection,
local: &sockaddr,
local_len: socklen_t,
remote: &sockaddr,
remote_len: socklen_t,
) -> c_int {
let addr = FourTuple {
local: sock_addr_from_c(local, local_len),
remote: sock_addr_from_c(remote, remote_len),
};
match conn.ping(Some(addr)) {
Ok(_) => return 0,
Err(e) => return e.to_errno() as c_int,
}
}

/// Add a new path on the client connection.
#[no_mangle]
pub extern "C" fn quic_conn_add_path(
Expand Down

0 comments on commit 7ada2ff

Please sign in to comment.