use std::time::{Duration, Instant};
#[derive(Debug)]
pub struct ManagedConnection<T> {
inner: T,
created_at: Instant,
last_used: Instant,
max_lifetime: Duration,
max_idle: Duration,
health_failures: usize,
}
impl<T> ManagedConnection<T> {
pub fn new(inner: T, max_lifetime: Duration, max_idle: Duration) -> Self {
let now = Instant::now();
Self { inner, created_at: now, last_used: now, max_lifetime, max_idle, health_failures: 0 }
}
#[must_use]
pub fn is_valid(&self) -> bool {
let now = Instant::now();
let not_expired = now.duration_since(self.created_at) < self.max_lifetime;
let not_idle = now.duration_since(self.last_used) < self.max_idle;
let healthy = self.health_failures < 3;
not_expired && not_idle && healthy
}
#[must_use]
pub fn is_expired(&self) -> bool {
self.created_at.elapsed() >= self.max_lifetime
}
#[must_use]
pub fn is_idle(&self) -> bool {
self.last_used.elapsed() >= self.max_idle
}
pub fn touch(&mut self) {
self.last_used = Instant::now();
}
pub fn record_health_failure(&mut self) {
self.health_failures += 1;
}
pub fn reset_health(&mut self) {
self.health_failures = 0;
}
pub fn inner(&self) -> &T {
&self.inner
}
pub fn inner_mut(&mut self) -> &mut T {
&mut self.inner
}
pub fn into_inner(self) -> T {
self.inner
}
#[must_use]
pub fn age(&self) -> Duration {
self.created_at.elapsed()
}
#[must_use]
pub fn idle_time(&self) -> Duration {
self.last_used.elapsed()
}
#[must_use]
pub fn health_failures(&self) -> usize {
self.health_failures
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct KeepAliveConfig {
pub enabled: bool,
pub timeout_secs: u32,
pub max_requests: u32,
}
impl KeepAliveConfig {
pub fn new() -> Self {
Self { enabled: true, timeout_secs: 60, max_requests: 100 }
}
pub fn disabled() -> Self {
Self { enabled: false, timeout_secs: 0, max_requests: 0 }
}
pub fn from_header(header: &str) -> Self {
let mut config = Self::new();
for part in header.split(',') {
let part = part.trim();
if let Some((key, val)) = part.split_once('=') {
let key = key.trim().to_lowercase();
let val = val.trim();
match key.as_str() {
"timeout" => {
if let Ok(t) = val.parse() {
config.timeout_secs = t;
}
}
"max" => {
if let Ok(m) = val.parse() {
config.max_requests = m;
}
}
_ => {} }
}
}
config
}
#[must_use]
pub fn should_keep_alive(&self, request_count: u32) -> bool {
self.enabled && request_count < self.max_requests
}
}
impl Default for KeepAliveConfig {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct ConnectionState(u8);
impl ConnectionState {
pub const OPEN: u8 = 0b0000_0001;
pub const READABLE: u8 = 0b0000_0010;
pub const WRITABLE: u8 = 0b0000_0100;
pub const HAS_PENDING: u8 = 0b0000_1000;
pub const KEEP_ALIVE: u8 = 0b0001_0000;
pub const UPGRADE: u8 = 0b0010_0000;
pub const CLOSING: u8 = 0b0100_0000;
pub const ERROR: u8 = 0b1000_0000;
#[must_use]
pub fn new() -> Self {
Self(0)
}
#[must_use]
pub fn open_connection() -> Self {
Self(Self::OPEN | Self::WRITABLE)
}
pub fn set(&mut self, flag: u8) {
self.0 |= flag;
}
pub fn clear(&mut self, flag: u8) {
self.0 &= !flag;
}
#[must_use]
pub fn is_set(&self, flag: u8) -> bool {
self.0 & flag != 0
}
#[must_use]
pub fn is_healthy(&self) -> bool {
self.is_set(Self::OPEN) && !self.is_set(Self::ERROR) && !self.is_set(Self::CLOSING)
}
#[must_use]
pub fn can_read(&self) -> bool {
self.is_set(Self::OPEN) && self.is_set(Self::READABLE)
}
#[must_use]
pub fn can_write(&self) -> bool {
self.is_set(Self::OPEN) && self.is_set(Self::WRITABLE) && !self.is_set(Self::CLOSING)
}
#[must_use]
pub fn bits(&self) -> u8 {
self.0
}
}
#[cfg(test)]
mod tests;