feat(servo-shell): reconnect appd WebSocket with exponential backoff

run_listener() now loops forever instead of returning on first failure.
Connect errors retry starting at 500ms, doubling up to 16s.
On disconnect the inner loop breaks and the outer loop reconnects
after the current backoff delay, then resets backoff to 500ms.
QUERY_RUNNING is sent again on each reconnect to re-sync session state.
This commit is contained in:
Marco Allegretti 2026-03-11 18:24:54 +01:00
parent c181bc8015
commit dde4a1dffb

View file

@ -20,27 +20,39 @@ pub fn spawn_appd_listener(
fn run_listener(ws_port: u16, tx: mpsc::SyncSender<AppdCmd>, wake: Box<dyn Fn() + Send>) {
let url = format!("ws://127.0.0.1:{ws_port}");
let (mut ws, _) = match tungstenite::connect(url) {
Ok(p) => p,
Err(e) => {
tracing::warn!("appd WebSocket connect failed: {e}");
return;
}
};
let mut backoff = std::time::Duration::from_millis(500);
const MAX_BACKOFF: std::time::Duration = std::time::Duration::from_secs(16);
loop {
match tungstenite::connect(&url) {
Err(e) => {
tracing::debug!("appd WebSocket connect failed: {e}; retry in {backoff:?}");
std::thread::sleep(backoff);
backoff = (backoff * 2).min(MAX_BACKOFF);
continue;
}
Ok((mut ws, _)) => {
backoff = std::time::Duration::from_millis(500);
let _ = ws.send(tungstenite::Message::Text(
r#"{"type":"QUERY_RUNNING"}"#.into(),
));
loop {
match ws.read() {
Ok(tungstenite::Message::Text(text)) => {
process_message(&text, &tx, &*wake);
}
Ok(_) => {}
Err(_) => break,
Err(e) => {
tracing::debug!("appd WebSocket read error: {e}; reconnecting");
break;
}
}
}
}
}
std::thread::sleep(backoff);
backoff = (backoff * 2).min(MAX_BACKOFF);
}
}
fn process_message(text: &str, tx: &mpsc::SyncSender<AppdCmd>, wake: &dyn Fn()) {