use crate::client_id::ClientId;
use crate::{core::to_pipenet, data::MessageDataInternal, prelude::*};
use pipenet::NonBlockStream;
use std::{
collections::HashMap,
net::{TcpListener, TcpStream},
sync::{Arc, Mutex},
time::{Duration, Instant},
};
type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
pub(crate) fn accept_loop<const MS: usize>(
server_uuid: ClientId,
config: Config,
clients: Arc<Mutex<HashMap<ClientId, NonBlockStream::<MS>>>>,
listener: TcpListener,
accept_timeout: Duration,
) {
while let Ok((stream, _)) = listener.accept() {
let Ok(mut map) = clients.lock() else {
return;
};
accept_one::<MS>(server_uuid, &config, &mut map, accept_timeout, stream);
}
}
fn accept_one<const MS: usize>(
server_uuid: ClientId,
config: &Config,
map: &mut HashMap<ClientId, NonBlockStream::<MS>>,
accept_timeout: Duration,
stream: TcpStream,
) {
if stream.set_nonblocking(true).is_err() {
eprintln!("[{}] accept: Cannot set client to nonblocking", server_uuid);
return;
}
let mut client = to_pipenet::<MS>(stream, config);
let Ok(Some(accepted_uuid)) = wait_for_client_joined_message(accept_timeout, &mut client)
else {
eprintln!(
"[{}] accept: Timed out while accepting unknown client",
server_uuid
);
return;
};
let Ok(server_uuid_msg) = (MessageDataInternal::ServerUuid(server_uuid)).try_into() else {
eprintln!("[{}] accept: Cannot build message", server_uuid);
return;
};
let Ok(accepted_client_joined_msg): Result<Vec<u8>> =
(MessageDataInternal::ClientJoined(accepted_uuid)).try_into()
else {
eprintln!("[{}] accept: Cannot build message", server_uuid);
return;
};
let Ok(_) = client.write(server_uuid_msg) else {
eprintln!(
"[{}] accept: Cannot send message to client {}",
server_uuid, accepted_uuid
);
return;
};
for (map_uuid, map_client) in map.iter_mut() {
let Ok(_) = map_client.write(accepted_client_joined_msg.clone()) else {
eprintln!(
"[{}] accept: Cannot send message to client {}",
server_uuid, map_uuid
);
return;
};
let Ok(map_client_joined_msg): Result<Vec<u8>> =
(MessageDataInternal::ClientJoined(*map_uuid)).try_into()
else {
eprintln!("[{}] accept: Cannot build message", server_uuid);
return;
};
let Ok(_) = client.write(map_client_joined_msg.clone()) else {
eprintln!(
"[{}] accept: Cannot send message to client {}",
server_uuid, accepted_uuid
);
return;
};
}
map.insert(accepted_uuid, client);
}
fn wait_for_client_joined_message<const MS: usize>(
timeout: Duration,
client: &mut NonBlockStream<MS>,
) -> Result<Option<ClientId>> {
let now = Instant::now();
loop {
let Some(msg) = client.read()? else {
continue;
};
let msg = MessageDataInternal::try_from(msg.as_slice())?;
if let MessageDataInternal::ClientJoined(uuid) = msg {
return Ok(Some(uuid));
}
if now.elapsed() > timeout {
return Ok(None);
}
}
}