fix(friends): five robustness bugs in the friends/control plane
Found in a bug audit of the just-merged friends-list feature. No crashes or security holes, but five real state/correctness bugs: - Host child dying on its own left the share campaign running, so it kept pushing a now-dead ticket to friends (retrying offline ones forever) and leaked share_status/met/share_code. The unexpected-exit path now captures the stderr error, then routes through the full stop_host() teardown (notably stop_share). (gui/mod.rs pump_host_events) - on_friend_request downgraded an already-Accepted friend back to PendingIncoming when they re-sent a request (e.g. after losing their store). It now stays Accepted and re-confirms. (friends.rs) - on_friend_accept advanced *any* known peer to Accepted, including a PendingIncoming one — a peer could mark itself accepted without the local user's consent. Now only a PendingOutgoing request we sent is honoured. (friends.rs) - A ShareCode redelivered by an ACK-loss retry fired a duplicate desktop notification. push_notice now reports whether the code is new/changed and only then toasts. (gui/mod.rs) - An inbound control message could be delayed up to IO_TIMEOUT on a degraded link because handle() awaited the sender's close before forwarding it. Forward to the UI first, then await close so the ACK still flushes. (control.rs) Adds two friends-store transition tests (accept ignores a pending-incoming peer; request doesn't downgrade an accepted friend). 47 gui / 8 headless tests pass, clippy + fmt clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -166,13 +166,16 @@ async fn handle(incoming: Incoming, tx: &mpsc::Sender<Inbound>) -> Result<()> {
|
||||
.await
|
||||
.context("timed out reading control message")??;
|
||||
|
||||
// Wait (briefly) for the sender's close so our ACK flushes before the
|
||||
// connection is dropped at the end of this scope.
|
||||
let _ = tokio::time::timeout(IO_TIMEOUT, conn.closed()).await;
|
||||
|
||||
// Hand the message up first, so it reaches the UI promptly even when the
|
||||
// sender is slow to close (a degraded link could otherwise delay a friend
|
||||
// request / pushed code by up to IO_TIMEOUT).
|
||||
tx.send(Inbound { from, msg })
|
||||
.await
|
||||
.map_err(|_| anyhow::anyhow!("control: receiver dropped"))?;
|
||||
|
||||
// Then wait (briefly) for the sender's close so our ACK has flushed before
|
||||
// the connection is dropped at the end of this scope.
|
||||
let _ = tokio::time::timeout(IO_TIMEOUT, conn.closed()).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user