bitwarden_shared_unlock/
active_peers.rs1use std::{collections::HashMap, sync::Mutex, time::Duration};
4
5use bitwarden_ipc::{Endpoint, Source};
6use tracing::info;
7use web_time::Instant;
8
9#[derive(Clone, Debug, PartialEq, Eq)]
11pub(crate) struct SyncTarget {
12 pub(crate) endpoint: Endpoint,
14 pub(crate) origin: Option<String>,
16}
17
18impl SyncTarget {
19 pub(crate) fn from_source(source: &Source) -> Self {
21 Self {
22 endpoint: source.to_endpoint(),
23 origin: match source {
24 Source::Web { origin, .. } => Some(origin.clone()),
25 _ => None,
26 },
27 }
28 }
29
30 pub(crate) fn without_origin(endpoint: Endpoint) -> Self {
32 Self {
33 endpoint,
34 origin: None,
35 }
36 }
37}
38
39struct PeerRecord {
41 last_seen: Instant,
43 origin: Option<String>,
45}
46
47#[derive(Default)]
49pub(crate) struct ActivePeerTracker {
50 peers: Mutex<HashMap<Endpoint, PeerRecord>>,
51}
52
53impl ActivePeerTracker {
54 pub(crate) fn upsert(&self, target: &SyncTarget) -> bool {
57 let mut peers = self
58 .peers
59 .lock()
60 .unwrap_or_else(|poisoned| poisoned.into_inner());
61
62 let first_contact = !peers.contains_key(&target.endpoint);
63 if first_contact {
64 info!("Shared-Unlock peer connected {:?}", target.endpoint);
65 }
66 peers.insert(
67 target.endpoint.clone(),
68 PeerRecord {
69 last_seen: Instant::now(),
70 origin: target.origin.clone(),
71 },
72 );
73 first_contact
74 }
75
76 pub(crate) fn targets(&self) -> Vec<SyncTarget> {
77 self.peers
78 .lock()
79 .unwrap_or_else(|poisoned| poisoned.into_inner())
80 .iter()
81 .map(|(endpoint, record)| SyncTarget {
82 endpoint: endpoint.clone(),
83 origin: record.origin.clone(),
84 })
85 .collect()
86 }
87
88 pub(crate) fn prune_stale(&self, stale_after: Duration) {
91 let mut peers = self
92 .peers
93 .lock()
94 .unwrap_or_else(|poisoned| poisoned.into_inner());
95
96 let now = Instant::now();
97 peers.retain(|endpoint, record| {
98 let alive = now.duration_since(record.last_seen) <= stale_after;
99 if !alive {
100 info!("Shared-Unlock peer {:?} disconnected", endpoint);
101 }
102 alive
103 });
104 }
105}