use super::frame;
use super::material::Material;
use crate::registry::Client;
use crate::registry::presence::Presence;
use rustls::pki_types::CertificateDer;
use rustls::{ServerConfig, ServerConnection, StreamOwned};
use serde_json::Value;
use std::net::{TcpListener, TcpStream};
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::thread::JoinHandle;
use std::time::Duration;
const ACCEPT_POLL: Duration = Duration::from_millis(20);
pub trait Answerer: Send + Sync {
fn answer(&self, client: &Client, request: Value) -> Box<dyn Iterator<Item = Value>>;
}
pub struct Listener {
address: String,
stop: Arc<AtomicBool>,
handle: Option<JoinHandle<()>>,
}
impl Listener {
pub fn bind(
m: &Material,
answerer: Arc<dyn Answerer>,
presence: Presence,
) -> Result<Self, String> {
let config = super::tls::server_config(m)?;
let tcp = TcpListener::bind(&m.address).map_err(|e| format!("bind {}: {e}", m.address))?;
let address = tcp
.local_addr()
.map_err(|e| format!("bind {}: {e}", m.address))?
.to_string();
tcp.set_nonblocking(true)
.map_err(|e| format!("bind {address}: {e}"))?;
let stop = Arc::new(AtomicBool::new(false));
let flag = Arc::clone(&stop);
let handle =
std::thread::spawn(move || accept_loop(&tcp, &config, &answerer, &presence, &flag));
Ok(Self {
address,
stop,
handle: Some(handle),
})
}
pub fn address(&self) -> String {
self.address.clone()
}
}
impl Drop for Listener {
fn drop(&mut self) {
self.stop.store(true, Ordering::Relaxed);
if let Some(handle) = self.handle.take() {
let _ = handle.join();
}
}
}
fn accept_loop(
tcp: &TcpListener,
config: &Arc<ServerConfig>,
answerer: &Arc<dyn Answerer>,
presence: &Presence,
stop: &Arc<AtomicBool>,
) {
while !stop.load(Ordering::Relaxed) {
match tcp.accept() {
Ok((stream, _)) => {
let config = Arc::clone(config);
let answerer = Arc::clone(answerer);
let presence = presence.clone();
std::thread::spawn(move || {
let _ = stream.set_nonblocking(false);
serve(stream, &config, answerer.as_ref(), &presence);
});
}
Err(_) => std::thread::sleep(ACCEPT_POLL),
}
}
}
pub(crate) fn serve(
tcp: TcpStream,
config: &Arc<ServerConfig>,
answerer: &dyn Answerer,
presence: &Presence,
) {
let Ok(conn) = ServerConnection::new(Arc::clone(config)) else {
return;
};
let mut tls = StreamOwned::new(conn, tcp);
let mut live = None;
while let Ok(Some(request)) = frame::read_value(&mut tls) {
let Some(client) = peer_client(tls.conn.peer_certificates()) else {
return;
};
let _ = live.get_or_insert_with(|| presence.enter(&client));
for chunk in answerer.answer(&client, request) {
if frame::write_value(&mut tls, &chunk).is_err() {
return;
}
}
if frame::write_end(&mut tls).is_err() {
return;
}
}
}
pub(crate) fn peer_client(chain: Option<&[CertificateDer<'_>]>) -> Option<Client> {
let name = crate::registry::leaf::common_name(chain?.first()?)?;
Client::parse(&name).ok()
}
#[cfg(test)]
mod tests;