use crate::socket::BlockingSocket;
use crate::sync::Mutex;
use alloc::string::String;
use alloc::vec::Vec;
use bytes::BytesMut;
use core::time::Duration;
use hashbrown::HashMap;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct PoolKey {
scheme: String,
host: String,
port: u16,
}
impl PoolKey {
#[must_use]
pub fn new(
scheme: String,
host: &str,
port: u16,
) -> Self {
Self {
scheme,
host: host.to_ascii_lowercase(),
port,
}
}
}
#[derive(Debug, Default)]
pub struct PooledBuffers {
pub buf: BytesMut,
}
struct IdleConn<S> {
socket: S,
idle_since: u64,
buffers: PooledBuffers,
}
pub struct ConnectionPool<S> {
connections: Mutex<HashMap<PoolKey, Vec<IdleConn<S>>>>,
max_idle_per_host: usize,
max_idle_age: Duration,
}
impl<S: BlockingSocket> ConnectionPool<S> {
#[must_use]
pub fn new(
max_idle_per_host: usize,
max_idle_age: Duration,
) -> Self {
Self {
connections: Mutex::new(HashMap::new()),
max_idle_per_host,
max_idle_age,
}
}
pub fn get(
&self,
key: &PoolKey,
) -> Option<(S, PooledBuffers)> {
let mut connections = self.connections.lock();
let sockets = connections.get_mut(key)?;
let now = crate::util::now_unix_secs();
let max_age = self.max_idle_age.as_secs();
while let Some(idle) = sockets.pop() {
if now.saturating_sub(idle.idle_since) <= max_age {
return Some((idle.socket, idle.buffers));
}
}
None
}
pub fn return_connection(
&self,
key: PoolKey,
socket: S,
mut buffers: PooledBuffers,
) {
buffers.buf.clear();
let mut connections = self.connections.lock();
let sockets = connections.entry(key).or_default();
if sockets.len() >= self.max_idle_per_host {
return;
}
sockets.push(IdleConn {
socket,
idle_since: crate::util::now_unix_secs(),
buffers,
});
}
}