[add] ping jitter, receiver backpressure, stream frame limits
Some checks failed
CI / checks (push) Failing after 1m50s
Some checks failed
CI / checks (push) Failing after 1m50s
This commit is contained in:
parent
c148314742
commit
a6dd73a41f
7 changed files with 449 additions and 189 deletions
|
|
@ -10,6 +10,12 @@ mtp-transport = { version = "0.1.0", path = "../transport" }
|
|||
mtp-crypto = { version = "0.1.0", path = "../crypto", optional = true }
|
||||
rand = "0.8"
|
||||
tokio = { version = "1", features = ["rt", "sync", "time"] }
|
||||
tracing = "0.1"
|
||||
|
||||
[dev-dependencies]
|
||||
mtp-host = { version = "0.1.0", path = "../host" }
|
||||
mtp-transport = { version = "0.1.0", path = "../transport", features = ["host"] }
|
||||
rcgen = "0.14"
|
||||
|
||||
[features]
|
||||
crypto = ["dep:mtp-crypto", "mtp-codec/crypto"]
|
||||
|
|
|
|||
|
|
@ -2,14 +2,14 @@ use mtp_codec::{CommunicationValue, DataType, DataValue, PROTOCOL_VERSION, Versi
|
|||
use mtp_common::CommunicationError;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::{mpsc, Mutex};
|
||||
use rand::Rng;
|
||||
use tokio::sync::{Mutex, mpsc};
|
||||
use tokio::time::{Duration, Instant};
|
||||
|
||||
pub use MTPClient as Client;
|
||||
pub use MTPConnection as Connection;
|
||||
pub use mtp_transport::Policy;
|
||||
pub use mtp_transport::Receiver;
|
||||
#[cfg(feature = "streaming")]
|
||||
pub use mtp_transport::SendMode;
|
||||
pub use mtp_transport::Sender;
|
||||
|
||||
|
|
@ -34,6 +34,7 @@ pub struct ClientConfig {
|
|||
pub description: Option<String>,
|
||||
pub policy: Policy,
|
||||
pub ping_interval: Duration,
|
||||
pub ping_jitter: Option<Duration>,
|
||||
pub max_missed_pings: usize,
|
||||
pub ping_timestamp: bool,
|
||||
#[cfg(feature = "crypto")]
|
||||
|
|
@ -55,6 +56,7 @@ impl ClientConfig {
|
|||
description: None,
|
||||
policy: Policy::default(),
|
||||
ping_interval: Duration::ZERO,
|
||||
ping_jitter: None,
|
||||
max_missed_pings: 3,
|
||||
ping_timestamp: true,
|
||||
#[cfg(feature = "crypto")]
|
||||
|
|
@ -91,6 +93,11 @@ impl ClientConfig {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn with_ping_jitter(mut self, jitter: Option<Duration>) -> Self {
|
||||
self.ping_jitter = jitter;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_max_missed_pings(mut self, max_missed_pings: usize) -> Self {
|
||||
self.max_missed_pings = max_missed_pings;
|
||||
self
|
||||
|
|
@ -209,6 +216,7 @@ fn start_ping_session(
|
|||
let last_ping = Arc::new(Mutex::new(None));
|
||||
let ping_state = last_ping.clone();
|
||||
let interval = config.ping_interval;
|
||||
let ping_jitter = config.ping_jitter;
|
||||
let max_missed_pings = config.max_missed_pings;
|
||||
let ping_timestamp = config.ping_timestamp;
|
||||
let mut close_rx = receiver.handle().subscribe_close();
|
||||
|
|
@ -231,6 +239,12 @@ fn start_ping_session(
|
|||
break;
|
||||
}
|
||||
|
||||
if let Some(jitter) = ping_jitter && !jitter.is_zero() {
|
||||
let max_ms = jitter.as_millis() as u64;
|
||||
let extra = rand::thread_rng().gen_range(0..=max_ms);
|
||||
tokio::time::sleep(Duration::from_millis(extra)).await;
|
||||
}
|
||||
|
||||
let mut ping = CommunicationValue::new(mtp_codec::CommunicationType::Ping);
|
||||
if ping_timestamp {
|
||||
let sent_at = std::time::SystemTime::now()
|
||||
|
|
@ -817,6 +831,7 @@ mod tests {
|
|||
.with_max_missed_pings(2)
|
||||
.with_ping_timestamp(false);
|
||||
assert_eq!(config.ping_interval, Duration::from_secs(5));
|
||||
assert_eq!(config.ping_jitter, None);
|
||||
assert_eq!(config.max_missed_pings, 2);
|
||||
assert!(!config.ping_timestamp);
|
||||
}
|
||||
|
|
|
|||
80
client/tests/ping.rs
Normal file
80
client/tests/ping.rs
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
use std::net::{IpAddr, Ipv4Addr};
|
||||
|
||||
use mtp_client::{ClientConfig, MTPClient};
|
||||
use mtp_host::{HostConfig, MTPHost};
|
||||
|
||||
fn generate_self_signed_cert() -> (Vec<u8>, Vec<u8>) {
|
||||
let key_pair = rcgen::KeyPair::generate().unwrap();
|
||||
let params =
|
||||
rcgen::CertificateParams::new(vec!["localhost".into(), "127.0.0.1".into()]).unwrap();
|
||||
let cert = params.self_signed(&key_pair).unwrap();
|
||||
let cert_pem = cert.pem();
|
||||
let key_pem = key_pair.serialize_pem();
|
||||
(cert_pem.into_bytes(), key_pem.into_bytes())
|
||||
}
|
||||
|
||||
async fn start_host(send_pongs: bool) -> (MTPHost, Vec<u8>) {
|
||||
let (cert_pem, key_pem) = generate_self_signed_cert();
|
||||
let host = MTPHost::new(
|
||||
HostConfig::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0, cert_pem.clone(), key_pem)
|
||||
.with_pongs(send_pongs),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
(host, cert_pem)
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_ping_rtt_and_missed_ping_teardown() {
|
||||
let (mut host, cert_pem) = start_host(true).await;
|
||||
let url = format!("https://127.0.0.1:{}", host.local_addr().port());
|
||||
|
||||
let client = MTPClient::connect(
|
||||
ClientConfig::new(url)
|
||||
.with_pinned_pem(cert_pem)
|
||||
.with_ping_interval(std::time::Duration::from_millis(25))
|
||||
.with_max_missed_pings(3),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let _accepted = host.accept().await.unwrap().unwrap();
|
||||
|
||||
let ping = tokio::time::timeout(std::time::Duration::from_secs(5), async {
|
||||
loop {
|
||||
if let Some(ping) = client.get_ping() {
|
||||
return ping;
|
||||
}
|
||||
tokio::time::sleep(std::time::Duration::from_millis(10)).await;
|
||||
}
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert!(ping > std::time::Duration::ZERO);
|
||||
|
||||
let (mut silent_host, silent_cert_pem) = start_host(false).await;
|
||||
let silent_url = format!("https://127.0.0.1:{}", silent_host.local_addr().port());
|
||||
let silent_client = MTPClient::connect(
|
||||
ClientConfig::new(silent_url)
|
||||
.with_pinned_pem(silent_cert_pem)
|
||||
.with_ping_interval(std::time::Duration::from_millis(25))
|
||||
.with_max_missed_pings(2),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let _accepted = silent_host.accept().await.unwrap().unwrap();
|
||||
|
||||
let closed = tokio::time::timeout(std::time::Duration::from_secs(5), async {
|
||||
loop {
|
||||
if silent_client.sender.is_closed() {
|
||||
return;
|
||||
}
|
||||
tokio::time::sleep(std::time::Duration::from_millis(10)).await;
|
||||
}
|
||||
})
|
||||
.await;
|
||||
|
||||
assert!(closed.is_ok(), "client should close after missed pings");
|
||||
}
|
||||
Loading…
Reference in a new issue