Fix client stats: shared BandwidthState survives timeout cancellation
All checks were successful
CI / test (push) Successful in 1m24s
All checks were successful
CI / test (push) Successful in 1m24s
The state is now created in main.rs and passed into run_client, so when --duration timeout cancels the future, the stats are still accessible via shared_state.summary(). CSV and syslog now show real speeds and byte counts. Verified: TCP loopback shows 32 Gbps in CSV output. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -10,7 +10,6 @@ use crate::auth;
|
|||||||
use crate::bandwidth::{self, BandwidthState};
|
use crate::bandwidth::{self, BandwidthState};
|
||||||
use crate::protocol::*;
|
use crate::protocol::*;
|
||||||
|
|
||||||
/// Returns (total_tx_bytes, total_rx_bytes, total_lost_packets, duration_secs).
|
|
||||||
pub async fn run_client(
|
pub async fn run_client(
|
||||||
host: &str,
|
host: &str,
|
||||||
port: u16,
|
port: u16,
|
||||||
@@ -21,7 +20,8 @@ pub async fn run_client(
|
|||||||
auth_user: Option<String>,
|
auth_user: Option<String>,
|
||||||
auth_pass: Option<String>,
|
auth_pass: Option<String>,
|
||||||
nat_mode: bool,
|
nat_mode: bool,
|
||||||
) -> Result<(u64, u64, u64, u32)> {
|
shared_state: Arc<BandwidthState>,
|
||||||
|
) -> Result<()> {
|
||||||
let addr = format!("{}:{}", host, port);
|
let addr = format!("{}:{}", host, port);
|
||||||
tracing::info!("Connecting to {}...", addr);
|
tracing::info!("Connecting to {}...", addr);
|
||||||
let mut stream = TcpStream::connect(&addr).await?;
|
let mut stream = TcpStream::connect(&addr).await?;
|
||||||
@@ -91,16 +91,15 @@ pub async fn run_client(
|
|||||||
);
|
);
|
||||||
|
|
||||||
if use_udp {
|
if use_udp {
|
||||||
run_udp_test_client(&mut stream, host, &cmd, nat_mode).await
|
run_udp_test_client(&mut stream, host, &cmd, nat_mode, shared_state).await
|
||||||
} else {
|
} else {
|
||||||
run_tcp_test_client(stream, cmd).await
|
run_tcp_test_client(stream, cmd, shared_state).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- TCP Test Client ---
|
// --- TCP Test Client ---
|
||||||
|
|
||||||
async fn run_tcp_test_client(stream: TcpStream, cmd: Command) -> Result<(u64, u64, u64, u32)> {
|
async fn run_tcp_test_client(stream: TcpStream, cmd: Command, state: Arc<BandwidthState>) -> Result<()> {
|
||||||
let state = BandwidthState::new();
|
|
||||||
let tx_size = cmd.tx_size as usize;
|
let tx_size = cmd.tx_size as usize;
|
||||||
let client_should_tx = cmd.client_tx();
|
let client_should_tx = cmd.client_tx();
|
||||||
let client_should_rx = cmd.client_rx();
|
let client_should_rx = cmd.client_rx();
|
||||||
@@ -138,7 +137,7 @@ async fn run_tcp_test_client(stream: TcpStream, cmd: Command) -> Result<(u64, u6
|
|||||||
state.running.store(false, Ordering::SeqCst);
|
state.running.store(false, Ordering::SeqCst);
|
||||||
if let Some(h) = tx_handle { let _ = h.await; }
|
if let Some(h) = tx_handle { let _ = h.await; }
|
||||||
if let Some(h) = rx_handle { let _ = h.await; }
|
if let Some(h) = rx_handle { let _ = h.await; }
|
||||||
Ok(state.summary())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn tcp_client_tx_loop(
|
async fn tcp_client_tx_loop(
|
||||||
@@ -203,7 +202,8 @@ async fn run_udp_test_client(
|
|||||||
host: &str,
|
host: &str,
|
||||||
cmd: &Command,
|
cmd: &Command,
|
||||||
nat_mode: bool,
|
nat_mode: bool,
|
||||||
) -> Result<(u64, u64, u64, u32)> {
|
state: Arc<BandwidthState>,
|
||||||
|
) -> Result<()> {
|
||||||
let mut port_buf = [0u8; 2];
|
let mut port_buf = [0u8; 2];
|
||||||
stream.read_exact(&mut port_buf).await?;
|
stream.read_exact(&mut port_buf).await?;
|
||||||
let server_udp_port = u16::from_be_bytes(port_buf);
|
let server_udp_port = u16::from_be_bytes(port_buf);
|
||||||
@@ -234,7 +234,6 @@ async fn run_udp_test_client(
|
|||||||
udp.send(&[]).await?;
|
udp.send(&[]).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let state = BandwidthState::new();
|
|
||||||
let tx_size = cmd.tx_size as usize;
|
let tx_size = cmd.tx_size as usize;
|
||||||
let client_should_tx = cmd.client_tx();
|
let client_should_tx = cmd.client_tx();
|
||||||
let client_should_rx = cmd.client_rx();
|
let client_should_rx = cmd.client_rx();
|
||||||
@@ -266,7 +265,7 @@ async fn run_udp_test_client(
|
|||||||
state.running.store(false, Ordering::SeqCst);
|
state.running.store(false, Ordering::SeqCst);
|
||||||
if let Some(h) = tx_handle { let _ = h.await; }
|
if let Some(h) = tx_handle { let _ = h.await; }
|
||||||
if let Some(h) = rx_handle { let _ = h.await; }
|
if let Some(h) = rx_handle { let _ = h.await; }
|
||||||
Ok(state.summary())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn udp_client_tx_loop(
|
async fn udp_client_tx_loop(
|
||||||
|
|||||||
19
src/main.rs
19
src/main.rs
@@ -172,6 +172,9 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
};
|
};
|
||||||
let proto_str = if cli.udp { "UDP" } else { "TCP" };
|
let proto_str = if cli.udp { "UDP" } else { "TCP" };
|
||||||
|
|
||||||
|
// Create shared state that survives timeout cancellation
|
||||||
|
let shared_state = bandwidth::BandwidthState::new();
|
||||||
|
|
||||||
// Log test start
|
// Log test start
|
||||||
syslog_logger::test_start(&host, proto_str, dir_str, 0);
|
syslog_logger::test_start(&host, proto_str, dir_str, 0);
|
||||||
|
|
||||||
@@ -187,24 +190,28 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
cli.auth_user.clone(),
|
cli.auth_user.clone(),
|
||||||
cli.auth_pass.clone(),
|
cli.auth_pass.clone(),
|
||||||
cli.nat,
|
cli.nat,
|
||||||
|
shared_state.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let stats = if cli.duration > 0 {
|
if cli.duration > 0 {
|
||||||
match tokio::time::timeout(
|
match tokio::time::timeout(
|
||||||
std::time::Duration::from_secs(cli.duration),
|
std::time::Duration::from_secs(cli.duration),
|
||||||
client_fut,
|
client_fut,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(result) => Some(result?),
|
Ok(result) => { let _ = result?; },
|
||||||
Err(_) => None, // Timeout — stats not available from aborted future
|
Err(_) => {
|
||||||
|
// Timeout — signal stop
|
||||||
|
shared_state.running.store(false, std::sync::atomic::Ordering::SeqCst);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Some(client_fut.await?)
|
let _ = client_fut.await?;
|
||||||
};
|
}
|
||||||
|
|
||||||
let elapsed = start.elapsed().as_secs();
|
let elapsed = start.elapsed().as_secs();
|
||||||
let (total_tx, total_rx, total_lost, _intervals) = stats.unwrap_or((0, 0, 0, 0));
|
let (total_tx, total_rx, total_lost, _intervals) = shared_state.summary();
|
||||||
|
|
||||||
// Log test end to syslog
|
// Log test end to syslog
|
||||||
syslog_logger::test_end(
|
syslog_logger::test_end(
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ async fn test_ecsrp5_full_client_auth() {
|
|||||||
Some("testuser".into()),
|
Some("testuser".into()),
|
||||||
Some("testpass".into()),
|
Some("testpass".into()),
|
||||||
false,
|
false,
|
||||||
|
btest_rs::bandwidth::BandwidthState::new(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
});
|
});
|
||||||
@@ -109,6 +110,7 @@ async fn test_ecsrp5_wrong_password_fails() {
|
|||||||
Some("testuser".into()),
|
Some("testuser".into()),
|
||||||
Some("wrongpass".into()),
|
Some("wrongpass".into()),
|
||||||
false,
|
false,
|
||||||
|
btest_rs::bandwidth::BandwidthState::new(),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
@@ -131,6 +133,7 @@ async fn test_md5_auth_still_works() {
|
|||||||
Some("testuser".into()),
|
Some("testuser".into()),
|
||||||
Some("testpass".into()),
|
Some("testpass".into()),
|
||||||
false,
|
false,
|
||||||
|
btest_rs::bandwidth::BandwidthState::new(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
});
|
});
|
||||||
@@ -155,6 +158,7 @@ async fn test_noauth_still_works() {
|
|||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
false,
|
false,
|
||||||
|
btest_rs::bandwidth::BandwidthState::new(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
});
|
});
|
||||||
@@ -179,6 +183,7 @@ async fn test_ecsrp5_udp_bidirectional() {
|
|||||||
Some("testuser".into()),
|
Some("testuser".into()),
|
||||||
Some("testpass".into()),
|
Some("testpass".into()),
|
||||||
false,
|
false,
|
||||||
|
btest_rs::bandwidth::BandwidthState::new(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -153,6 +153,7 @@ async fn test_loopback_tcp_rx() {
|
|||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
false,
|
false,
|
||||||
|
btest_rs::bandwidth::BandwidthState::new(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
});
|
});
|
||||||
@@ -177,6 +178,7 @@ async fn test_loopback_tcp_tx() {
|
|||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
false,
|
false,
|
||||||
|
btest_rs::bandwidth::BandwidthState::new(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
});
|
});
|
||||||
@@ -201,6 +203,7 @@ async fn test_loopback_tcp_both() {
|
|||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
false,
|
false,
|
||||||
|
btest_rs::bandwidth::BandwidthState::new(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
});
|
});
|
||||||
@@ -225,6 +228,7 @@ async fn test_loopback_tcp_with_auth() {
|
|||||||
Some("admin".into()),
|
Some("admin".into()),
|
||||||
Some("secret".into()),
|
Some("secret".into()),
|
||||||
false,
|
false,
|
||||||
|
btest_rs::bandwidth::BandwidthState::new(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user