use super::call::Answer;
use super::punch::LINGER;
use super::say;
use crate::wire::server::serve;
use std::net::{IpAddr, TcpStream};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::{Receiver, SyncSender};
use std::time::{Duration, Instant};
const ACCEPT_POLL: Duration = Duration::from_millis(20);
pub(super) struct Toward {
pub(super) ips: Vec<IpAddr>,
pub(super) until: Instant,
pub(super) tx: SyncSender<TcpStream>,
}
impl Answer {
pub(super) fn accept(self, hints: &Receiver<Toward>, stop: &AtomicBool) {
let mut toward: Vec<Toward> = Vec::new();
let mut last: Option<Instant> = None;
while !stop.load(Ordering::Relaxed) {
let landed = self.punch.accept();
toward.extend(hints.try_iter());
toward.retain(|hint| Instant::now() < hint.until);
for stream in landed {
if let Some(stream) = hand(&toward, stream) {
let peer = stream.peer_addr().ok().map(|at| at.ip());
if last.is_none_or(|at| at.elapsed() > LINGER) {
(self.say)(&say::accepted(peer));
}
last = Some(Instant::now());
self.serve(stream, peer);
}
}
std::thread::sleep(ACCEPT_POLL);
}
}
fn serve(&self, stream: TcpStream, peer: Option<IpAddr>) {
self.stats.accepted.fetch_add(1, Ordering::Relaxed);
self.stats.served.fetch_add(1, Ordering::Relaxed);
let answer = self.clone();
std::thread::spawn(move || {
let (tls, quiet) = (&answer.tls, answer.quiet);
if !serve(
stream,
tls,
answer.answerer.as_ref(),
&answer.presence,
quiet,
) {
(answer.say)(&say::refused(peer));
}
});
}
}
fn hand(toward: &[Toward], stream: TcpStream) -> Option<TcpStream> {
let ip = stream.peer_addr().ok().map(|at| at.ip());
let mut stream = stream;
for hint in toward
.iter()
.filter(|h| ip.is_some_and(|ip| h.ips.contains(&ip)))
{
match hint.tx.send(stream) {
Ok(()) => return None,
Err(back) => stream = back.0,
}
}
Some(stream)
}