use std::collections::VecDeque;
use std::io::Write;
use std::net::{Shutdown, TcpListener, TcpStream};
use std::path::Path;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::{Arc, Mutex, PoisonError};
use std::time::Duration;
use rustls::{ServerConfig, ServerConnection, StreamOwned};
use serde_json::{Value, json};
use crate::channel::frame;
use crate::channel::line;
use crate::channel::rendezvous::Pairing;
use crate::channel::rendezvous::item::Call;
use crate::dht::Mutable;
#[derive(Clone, Copy, Debug)]
pub(crate) enum Fate {
Stay,
Pinged(usize),
Fin,
Farewell,
Reset,
Garbage,
}
#[derive(Clone)]
pub(crate) struct Reply {
pub(crate) pings: usize,
pub(crate) frames: Vec<Value>,
pub(crate) fate: Fate,
}
impl Reply {
pub(crate) fn yes() -> Reply {
Reply {
pings: 0,
frames: vec![json!({"ok": true, "kind": "workspaces"})],
fate: Fate::Stay,
}
}
pub(crate) fn then(self, fate: Fate) -> Reply {
Reply { fate, ..self }
}
pub(crate) fn pinged(self, pings: usize) -> Reply {
Reply { pings, ..self }
}
}
struct Shared {
script: Mutex<VecDeque<Reply>>,
seen: Mutex<Vec<Value>>,
connections: AtomicUsize,
pings: AtomicUsize,
taken: AtomicBool,
}
pub(crate) struct Roving {
shared: Arc<Shared>,
}
impl Roving {
pub(crate) fn listen(dir: &Path, listener: TcpListener, script: Vec<Reply>) -> Roving {
let config = super::engine::server_config(dir);
let roving = Roving::new(script);
let shared = Arc::clone(&roving.shared);
std::thread::spawn(move || {
while let Ok((tcp, _)) = listener.accept() {
shared.connections.fetch_add(1, Ordering::Relaxed);
let (config, shared) = (Arc::clone(&config), Arc::clone(&shared));
std::thread::spawn(move || serve(&config, tcp, &shared));
}
});
roving
}
pub(crate) fn call_back(
dir: &Path,
store: Arc<Mutex<Vec<Mutable>>>,
pairing: Pairing,
script: Vec<Reply>,
) -> Roving {
let config = super::engine::server_config(dir);
let roving = Roving::new(script);
let shared = Arc::clone(&roving.shared);
std::thread::spawn(move || {
let inbox = pairing.inbox_keypair().unwrap().public();
let call = loop {
std::thread::sleep(Duration::from_millis(20));
let found = store
.lock()
.unwrap_or_else(PoisonError::into_inner)
.iter()
.find(|item| item.key == inbox)
.and_then(|item| Call::open(&pairing.seal_key(), &item.value));
if let Some(call) = found {
break call;
}
};
for endpoint in call.endpoints {
if let Ok(tcp) = TcpStream::connect(endpoint) {
shared.connections.fetch_add(1, Ordering::Relaxed);
serve(&config, tcp, &shared);
return;
}
}
});
roving
}
fn new(script: Vec<Reply>) -> Roving {
Roving {
shared: Arc::new(Shared {
script: Mutex::new(script.into()),
seen: Mutex::new(Vec::new()),
connections: AtomicUsize::new(0),
pings: AtomicUsize::new(0),
taken: AtomicBool::new(false),
}),
}
}
pub(crate) fn heard(&self) -> Vec<Value> {
self.shared
.seen
.lock()
.unwrap_or_else(PoisonError::into_inner)
.clone()
}
pub(crate) fn connections(&self) -> usize {
self.shared.connections.load(Ordering::Relaxed)
}
pub(crate) fn pinged(&self) -> usize {
self.shared.pings.load(Ordering::Relaxed)
}
pub(crate) fn taken(&self) {
self.shared.taken.store(true, Ordering::Relaxed);
}
}
fn serve(config: &Arc<ServerConfig>, tcp: TcpStream, shared: &Shared) {
let conn = ServerConnection::new(Arc::clone(config)).expect("the stand-in's own config");
let mut tls = StreamOwned::new(conn, tcp);
let _ = frame::write_value(
&mut tls,
&json!({ "protocol": crate::channel::hello::PROTOCOL }),
);
let mut preface_owed = true;
while let Ok(Some(said)) = frame::read_value(&mut tls) {
shared
.seen
.lock()
.unwrap_or_else(PoisonError::into_inner)
.push(said);
if preface_owed {
preface_owed = false;
continue;
}
let Some(reply) = shared
.script
.lock()
.unwrap_or_else(PoisonError::into_inner)
.pop_front()
else {
return;
};
ping(&mut tls, reply.pings, shared);
for value in &reply.frames {
let _ = frame::write_value(&mut tls, value);
}
let _ = frame::write_end(&mut tls);
match reply.fate {
Fate::Stay => {}
Fate::Pinged(n) => ping(&mut tls, n, shared),
Fate::Fin => return,
Fate::Farewell => {
tls.conn.send_close_notify();
let _ = tls.flush();
let _ = tls.sock.shutdown(Shutdown::Both);
return;
}
Fate::Reset => {
taken(shared);
let _ = socket2::SockRef::from(&tls.sock).set_linger(Some(Duration::ZERO));
return;
}
Fate::Garbage => {
taken(shared);
let _ = tls.sock.write_all(b"this is not a TLS record");
std::thread::sleep(Duration::from_secs(1));
return;
}
}
}
}
fn ping(tls: &mut StreamOwned<ServerConnection, TcpStream>, n: usize, shared: &Shared) {
for _ in 0..n {
let _ = frame::write_value(tls, &line::ping());
shared.pings.fetch_add(1, Ordering::Relaxed);
}
}
fn taken(shared: &Shared) {
loop {
std::thread::sleep(Duration::from_millis(5));
if shared.taken.load(Ordering::Relaxed) {
break;
}
}
}