mod http;
mod map;
use std::time::Duration;
use crate::nq_core::Timestamp;
pub use self::http::{EstablishedConnection, set_insecure_tls};
pub use self::map::ConnectionManager;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ConnectionType {
H1 {
use_tls: bool,
},
H2,
H3,
}
impl ConnectionType {
pub fn h1() -> ConnectionType {
ConnectionType::H1 { use_tls: true }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct ConnectionTiming {
start: Timestamp,
time_lookup: Duration,
time_connect: Duration,
time_secure: Duration,
time_application: Duration,
dns_time: Duration,
tls_round_trips: u32,
}
impl ConnectionTiming {
pub fn new(start: Timestamp) -> Self {
Self {
start,
time_lookup: Duration::ZERO,
time_connect: Duration::ZERO,
time_secure: Duration::ZERO,
time_application: Duration::ZERO,
dns_time: Duration::ZERO,
tls_round_trips: 1,
}
}
pub fn set_lookup(&mut self, at: Timestamp) {
self.time_lookup = at.duration_since(self.start);
}
pub fn set_connect(&mut self, at: Timestamp) {
self.time_connect = at.duration_since(self.start);
}
pub fn set_secure(&mut self, at: Timestamp) {
self.time_secure = at.duration_since(self.start);
}
pub fn set_application(&mut self, at: Timestamp) {
self.time_application = at.duration_since(self.start);
}
pub fn start(&self) -> Timestamp {
self.start
}
pub fn time_lookup(&self) -> Duration {
self.time_lookup
}
pub fn time_connect(&self) -> Duration {
self.time_connect
}
pub fn set_dns_lookup(&mut self, duration: Duration) {
self.dns_time = duration;
}
pub fn dns_time(&self) -> Duration {
self.dns_time
}
pub fn time_secure(&self) -> Duration {
self.time_secure
}
pub fn time_application(&self) -> Duration {
self.time_application
}
pub fn set_tls_round_trips(&mut self, round_trips: u32) {
self.tls_round_trips = round_trips.max(1);
}
pub fn tls_round_trips(&self) -> u32 {
self.tls_round_trips.max(1)
}
pub fn tcp_handshake(&self) -> Duration {
self.time_connect.saturating_sub(self.time_lookup)
}
pub fn tls_handshake(&self) -> Duration {
self.time_secure.saturating_sub(self.time_connect)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn timing_at(
lookup_ms: u64,
connect_ms: u64,
secure_ms: u64,
application_ms: u64,
) -> ConnectionTiming {
let start = Timestamp::now();
let mut t = ConnectionTiming::new(start);
t.set_lookup(start + Duration::from_millis(lookup_ms));
t.set_connect(start + Duration::from_millis(connect_ms));
t.set_secure(start + Duration::from_millis(secure_ms));
t.set_application(start + Duration::from_millis(application_ms));
t
}
#[test]
fn independent_phase_deltas() {
let t = timing_at(0, 30, 60, 62);
assert_eq!(t.tcp_handshake(), Duration::from_millis(30));
assert_eq!(t.tls_handshake(), Duration::from_millis(30));
}
#[test]
fn tcp_handshake_excludes_dns_lookup() {
let t = timing_at(10, 40, 70, 72);
assert_eq!(t.tcp_handshake(), Duration::from_millis(30));
assert_eq!(t.tls_handshake(), Duration::from_millis(30));
}
#[test]
fn tls_handshake_saturates_for_quic_like_zero_secure() {
let start = Timestamp::now();
let mut t = ConnectionTiming::new(start);
t.set_connect(start + Duration::from_millis(30));
assert_eq!(t.tls_handshake(), Duration::ZERO);
}
#[test]
fn tls_round_trips_defaults_to_one_and_clamps() {
let t = ConnectionTiming::new(Timestamp::now());
assert_eq!(t.tls_round_trips(), 1);
let mut t = t;
t.set_tls_round_trips(2);
assert_eq!(t.tls_round_trips(), 2);
t.set_tls_round_trips(0);
assert_eq!(t.tls_round_trips(), 1);
}
}