use std::{collections::HashSet, net::SocketAddr};
use quiche::Config;
use rand::seq::SliceRandom;
use crate::{Error, Group, Result, Token, utils::random_conn_id};
impl Group {
pub fn connect(
&self,
server_name: Option<&str>,
local: SocketAddr,
peer: SocketAddr,
config: &mut Config,
) -> Result<Token> {
let conn = quiche::connect(server_name, &random_conn_id(), local, peer, config)?;
let token = self.register(conn)?;
Ok(token)
}
}
pub struct Connector {
local_addr: SocketAddr,
targets: Vec<SocketAddr>,
max_pool_size: usize,
conns: HashSet<Token>,
handshaking_conns: HashSet<Token>,
config: quiche::Config,
}
impl Connector {
pub fn new(
local_addr: SocketAddr,
targets: Vec<SocketAddr>,
config: quiche::Config,
max_pool_size: usize,
) -> Self {
assert!(max_pool_size > 0, "Invalid `max_pool_size` value.");
Self {
max_pool_size,
local_addr,
targets,
conns: Default::default(),
handshaking_conns: Default::default(),
config,
}
}
#[inline]
pub fn local_addr(&self) -> SocketAddr {
self.local_addr
}
pub fn connected(&mut self, token: Token) {
log::info!("put `QUIC` connection {:?}.", token);
assert!(self.handshaking_conns.remove(&token));
assert!(self.conns.insert(token));
}
pub fn closed(&mut self, token: Token) {
log::info!("remove `QUIC` connection {:?}.", token);
if !self.handshaking_conns.remove(&token) {
self.conns.remove(&token);
}
}
pub fn stream_open(&mut self, group: &Group) -> Result<(Token, u64)> {
let mut conns = self.conns.iter().cloned().collect::<Vec<_>>();
conns.shuffle(&mut rand::rng());
for conn_id in conns {
match group.stream_open(conn_id) {
Ok(stream_id) => return Ok((conn_id, stream_id)),
Err(Error::Busy) | Err(Error::Retry) => {}
Err(_) => {
log::info!("Remove quic connection {:?}", conn_id);
self.conns.remove(&conn_id);
}
}
}
if self.conns.len() + self.handshaking_conns.len() < self.max_pool_size {
self.targets.shuffle(&mut rand::rng());
let token = group.connect(None, self.local_addr, self.targets[0], &mut self.config)?;
log::info!("`QUIC` connect to {}, token={:?}", self.targets[0], token);
self.handshaking_conns.insert(token);
}
Err(Error::Retry)
}
}
#[cfg(test)]
mod tests {
use crate::{Event, EventKind, Group};
#[test]
fn test_connect_events() {
let group = Group::new();
let token = group
.connect(
None,
"127.0.0.1:1".parse().unwrap(),
"127.0.0.1:2".parse().unwrap(),
&mut quiche::Config::new(quiche::PROTOCOL_VERSION).unwrap(),
)
.unwrap();
let mut events = vec![];
group.poll(&mut events, None).unwrap();
assert_eq!(
events,
vec![Event {
kind: EventKind::Send,
is_server: false,
is_error: false,
token,
stream_id: 0
}]
);
}
}