use std::sync::Arc;
use axum::extract::State;
use axum::extract::ws::{Message, WebSocket, WebSocketUpgrade};
use axum::response::Response;
use kernel::{AxCode, AxError, Seq};
use tokio::sync::broadcast;
use crate::frames::{Answered, Ask, AskOutcome, Sample, ServerFrame};
use crate::reception::inbound::Inbound;
use crate::reception::{SessionState, SessionStep, Stream, WelcomeFacts, decide_frame};
use crate::reply::{Delivered, Reply};
use super::config::{Answering, ShellState};
pub(crate) async fn upgrade(
State(state): State<Arc<ShellState>>,
ws: WebSocketUpgrade,
) -> Response {
ws.on_upgrade(move |socket| session(socket, state))
}
async fn session(mut socket: WebSocket, state: Arc<ShellState>) {
let mut phase = SessionState::AwaitingHello;
let mut inbound = Inbound::new();
let mut events = state.events.subscribe();
let mut deltas = state.deltas.subscribe();
let mut logs = state.logs.subscribe();
let mut outputs = state.outputs.subscribe();
let (refused, mut refusals) = tokio::sync::mpsc::unbounded_channel::<AxError>();
let mut stream = Stream::opening();
let mut watching: Option<Watching> = None;
loop {
tokio::select! {
incoming = socket.recv() => {
let Some(Ok(message)) = incoming else { return };
let Message::Text(text) = message else { continue };
let step = match inbound.read(&text) {
Ok(frame) => {
decide_frame(phase, frame, &state.face, WelcomeFacts { city: state.city.as_ref(), head: state.head.read(), epoch: state.epoch })
}
Err(unreadable) => unreadable,
};
match step {
SessionStep::Welcome(welcome) => {
phase = SessionState::Live;
if send(&mut socket, &ServerFrame::Welcome(*welcome)).await.is_err() {
return;
}
for written in (state.outputs_so_far)() {
if send(&mut socket, &ServerFrame::Output(written)).await.is_err() {
return;
}
}
}
SessionStep::Deliver(command) => {
let back = refused.clone();
let reply = Reply::to(move |error| match back.send(error) {
Ok(()) => Delivered::ToThePeer,
Err(_) => Delivered::PeerGone,
});
if let Err(error) = (state.commands)(*command, reply)
&& send(&mut socket, &ServerFrame::Refusal(Box::new(error))).await.is_err() {
return;
}
}
SessionStep::Answer(ask) => {
let frame = answered(Arc::clone(&state.queries), *ask).await;
if send(&mut socket, &frame).await.is_err() {
return;
}
}
SessionStep::Watch(watched) => {
let samples = watching
.take()
.map_or_else(|| state.monitor.samples.subscribe(), |(_, samples)| samples);
watching = Some(((state.monitor.watch)(watched), samples));
}
SessionStep::Release => watching = None,
SessionStep::Refuse { error, close } => {
if send(&mut socket, &ServerFrame::Refusal(error)).await.is_err() {
return;
}
if close {
return;
}
}
}
}
late = refusals.recv() => {
let Some(error) = late else { return };
if send(&mut socket, &ServerFrame::Refusal(Box::new(error))).await.is_err() {
return;
}
}
event = events.recv() => {
match event {
Ok(committed) => {
if phase == SessionState::Live {
let seq = committed.record().seq();
let (lag, next) = stream.before(seq);
if let Some(lag) = lag
&& send(&mut socket, &ServerFrame::Lagged(lag)).await.is_err()
{
return;
}
if socket.send(Message::Text(committed.frame())).await.is_err() {
return;
}
stream = next;
}
}
Err(broadcast::error::RecvError::Lagged(_)) => {
stream = stream.skipped(phase == SessionState::Live);
}
Err(broadcast::error::RecvError::Closed) => return,
}
}
said = deltas.recv() => {
match said {
Ok(delta) => {
if phase == SessionState::Live
&& send(&mut socket, &ServerFrame::Delta(delta)).await.is_err() {
return;
}
}
Err(broadcast::error::RecvError::Lagged(_)) => {}
Err(broadcast::error::RecvError::Closed) => return,
}
}
written = logs.recv() => {
match written {
Ok(line) => {
if phase == SessionState::Live
&& send(&mut socket, &ServerFrame::Log(line)).await.is_err() {
return;
}
}
Err(broadcast::error::RecvError::Lagged(_)) => {}
Err(broadcast::error::RecvError::Closed) => return,
}
}
written = outputs.recv() => {
match written {
Ok(piece) => {
if phase == SessionState::Live
&& send(&mut socket, &ServerFrame::Output(piece)).await.is_err() {
return;
}
}
Err(broadcast::error::RecvError::Lagged(_)) => {}
Err(broadcast::error::RecvError::Closed) => return,
}
}
read = next_sample(&mut watching) => {
match read {
Ok(sample) => {
if send(&mut socket, &ServerFrame::Monitor(sample)).await.is_err() {
return;
}
}
Err(broadcast::error::RecvError::Lagged(_)) => {}
Err(broadcast::error::RecvError::Closed) => watching = None,
}
}
}
}
}
async fn answered(answering: Answering, Ask { ask_id, query }: Ask) -> ServerFrame {
let (as_of, outcome) = match tokio::task::spawn_blocking(move || answering(query)).await {
Ok((as_of, Ok(answer))) => (as_of, AskOutcome::Answer(answer)),
Ok((as_of, Err(error))) => (as_of, AskOutcome::Refusal(error)),
Err(_) => (
Seq::FIRST,
AskOutcome::Refusal(
AxError::failure(
AxCode::StorageFatal,
"answer a query",
"the answering task did not finish",
)
.with_recovery("ask again; if it repeats, restart the server"),
),
),
};
ServerFrame::Answered(Box::new(Answered {
ask_id,
as_of,
outcome,
}))
}
type Watching = (Box<dyn Send>, broadcast::Receiver<Sample>);
async fn next_sample(
watching: &mut Option<Watching>,
) -> Result<Sample, broadcast::error::RecvError> {
match watching {
Some((_, samples)) => samples.recv().await,
None => std::future::pending().await,
}
}
async fn send(socket: &mut WebSocket, frame: &ServerFrame) -> Result<(), ()> {
let Ok(text) = serde_json::to_string(frame) else {
return Err(());
};
socket
.send(Message::Text(text.into()))
.await
.map_err(|_| ())
}