use std::net::SocketAddr;
use ringline::ConnCtx;
use crate::{Client, Error};
pub struct PoolConfig {
pub addr: SocketAddr,
pub pool_size: usize,
pub connect_timeout_ms: u64,
pub tls_server_name: Option<String>,
}
enum Slot {
Connected(ConnCtx),
Disconnected,
}
pub struct Pool {
addr: SocketAddr,
slots: Vec<Slot>,
next: usize,
connect_timeout_ms: u64,
tls_server_name: Option<String>,
}
impl Pool {
pub fn new(config: PoolConfig) -> Self {
let mut slots = Vec::with_capacity(config.pool_size);
for _ in 0..config.pool_size {
slots.push(Slot::Disconnected);
}
Pool {
addr: config.addr,
slots,
next: 0,
connect_timeout_ms: config.connect_timeout_ms,
tls_server_name: config.tls_server_name,
}
}
pub async fn connect_all(&mut self) -> Result<(), Error> {
for i in 0..self.slots.len() {
let conn = self.do_connect().await?;
self.slots[i] = Slot::Connected(conn);
}
Ok(())
}
pub async fn client(&mut self) -> Result<Client, Error> {
let size = self.slots.len();
for _ in 0..size {
let idx = self.next;
self.next = (self.next + 1) % size;
match &self.slots[idx] {
Slot::Connected(conn) => return Ok(Client::new(*conn)),
Slot::Disconnected => {
if let Ok(conn) = self.do_connect().await {
self.slots[idx] = Slot::Connected(conn);
return Ok(Client::new(conn));
}
}
}
}
Err(Error::AllConnectionsFailed)
}
pub fn mark_disconnected(&mut self, conn: ConnCtx) {
let token = conn.token();
for slot in &mut self.slots {
if let Slot::Connected(conn) = slot
&& conn.token() == token
{
conn.close();
*slot = Slot::Disconnected;
return;
}
}
}
pub fn close_all(&mut self) {
for slot in &mut self.slots {
if let Slot::Connected(conn) = slot {
conn.close();
}
*slot = Slot::Disconnected;
}
}
pub fn connected_count(&self) -> usize {
self.slots
.iter()
.filter(|s| matches!(s, Slot::Connected(_)))
.count()
}
pub fn pool_size(&self) -> usize {
self.slots.len()
}
async fn do_connect(&self) -> Result<ConnCtx, Error> {
let conn = if let Some(sni) = &self.tls_server_name {
let fut = if self.connect_timeout_ms > 0 {
ringline::connect_tls_with_timeout(self.addr, sni, self.connect_timeout_ms)?
} else {
ringline::connect_tls(self.addr, sni)?
};
fut.await?
} else {
let fut = if self.connect_timeout_ms > 0 {
ringline::connect_with_timeout(self.addr, self.connect_timeout_ms)?
} else {
ringline::connect(self.addr)?
};
fut.await?
};
Ok(conn)
}
}