use std::collections::HashMap;
use std::sync::Arc;
use std::time::{Duration, Instant};
use http_body_util::Full;
use hyper::body::Bytes;
use hyper::client::conn::http2::SendRequest;
use tokio::sync::Mutex;
const IDLE_TIMEOUT: Duration = Duration::from_secs(30);
pub type PooledH2Sender = SendRequest<Full<Bytes>>;
#[derive(Hash, PartialEq, Eq, Clone, Debug)]
struct PoolKey {
host: String,
port: u16,
is_https: bool,
}
struct H2Entry {
sender: PooledH2Sender,
last_used: Instant,
}
#[derive(Clone, Debug, Default)]
pub struct ConnectionPool {
inner: Arc<Mutex<PoolInner>>,
}
#[derive(Default)]
struct PoolInner {
h2: HashMap<PoolKey, H2Entry>,
}
impl std::fmt::Debug for PoolInner {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PoolInner")
.field("h2_entries", &self.h2.len())
.finish()
}
}
impl ConnectionPool {
pub fn new() -> Self {
Self::default()
}
pub async fn get_h2(&self, host: &str, port: u16) -> Option<PooledH2Sender> {
let key = PoolKey {
host: host.to_string(),
port,
is_https: true,
};
let mut inner = self.inner.lock().await;
let entry = inner.h2.get(&key)?;
if entry.last_used.elapsed() > IDLE_TIMEOUT {
inner.h2.remove(&key);
return None;
}
let sender = entry.sender.clone();
if let Some(entry) = inner.h2.get_mut(&key) {
entry.last_used = Instant::now();
}
Some(sender)
}
pub async fn store_h2(&self, host: &str, port: u16, sender: PooledH2Sender) {
let key = PoolKey {
host: host.to_string(),
port,
is_https: true,
};
let mut inner = self.inner.lock().await;
inner.h2.insert(
key,
H2Entry {
sender,
last_used: Instant::now(),
},
);
}
pub async fn invalidate_h2(&self, host: &str, port: u16) {
let key = PoolKey {
host: host.to_string(),
port,
is_https: true,
};
let mut inner = self.inner.lock().await;
inner.h2.remove(&key);
}
}