Skip to main content

oasis_core_runtime/enclave_rpc/
sessions.rs

1//! Session demultiplexer.
2use std::{
3    collections::{BTreeSet, HashMap, HashSet},
4    hash::Hash,
5    io::Write,
6    mem,
7    sync::Arc,
8};
9
10use anyhow::Result;
11use rand::{rand_core::UnwrapErr, rngs::SysRng, RngExt};
12use tokio::sync::OwnedMutexGuard;
13
14use super::{
15    session::{Builder, Session, SessionInfo},
16    types::{Message, SessionID},
17};
18use crate::common::{
19    crypto::signature,
20    namespace::Namespace,
21    sgx::{EnclaveIdentity, QuotePolicy},
22    time::insecure_posix_time,
23};
24
25/// Shared pointer to a multiplexed session.
26pub type SharedSession<PeerID> = Arc<tokio::sync::Mutex<MultiplexedSession<PeerID>>>;
27
28/// Key for use in the by-idle-time index.
29pub type SessionByTimeKey<PeerID> = (i64, PeerID, SessionID);
30
31/// Sessions error.
32#[derive(Debug, thiserror::Error)]
33pub enum Error {
34    #[error("max concurrent sessions reached")]
35    MaxConcurrentSessions,
36}
37
38/// A multiplexed session.
39pub struct MultiplexedSession<PeerID> {
40    /// Peer identifier (needed for resolution when only given the shared pointer).
41    peer_id: PeerID,
42    /// Session identifier (needed for resolution when only given the shared pointer).
43    session_id: SessionID,
44    /// The actual session.
45    inner: Session,
46}
47
48impl<PeerID> MultiplexedSession<PeerID> {
49    /// Return the session's peer ID.
50    pub fn get_peer_id(&self) -> &PeerID {
51        &self.peer_id
52    }
53
54    /// Set the session's peer ID.
55    pub fn set_peer_id(&mut self, peer_id: PeerID) {
56        self.peer_id = peer_id;
57    }
58
59    /// Return the session ID.
60    pub fn get_session_id(&self) -> &SessionID {
61        &self.session_id
62    }
63
64    /// Session information.
65    pub fn info(&self) -> Option<Arc<SessionInfo>> {
66        self.inner.session_info()
67    }
68
69    /// Whether the session is in closed state.
70    pub fn is_closed(&self) -> bool {
71        self.inner.is_closed()
72    }
73
74    /// Process incoming session data.
75    pub async fn process_data<W: Write>(
76        &mut self,
77        data: &[u8],
78        writer: W,
79    ) -> Result<Option<Message>> {
80        self.inner.process_data(data, writer).await
81    }
82
83    /// Write message to session and generate a response.
84    pub fn write_message<W: Write>(&mut self, msg: Message, mut writer: W) -> Result<()> {
85        self.inner.write_message(msg, &mut writer)
86    }
87
88    /// Return remote node identifier.
89    pub fn get_remote_node(&self) -> Result<signature::PublicKey> {
90        self.inner.get_remote_node()
91    }
92
93    /// Set the remote node identifier.
94    pub fn set_remote_node(&mut self, node: signature::PublicKey) -> Result<()> {
95        self.inner.set_remote_node(node)
96    }
97
98    /// Whether the session handshake has completed and the session
99    /// is in transport mode.
100    pub fn is_connected(&self) -> bool {
101        self.inner.is_connected()
102    }
103
104    /// Whether the session is in unauthenticated transport state. In this state the session can
105    /// only be used to transmit a close notification.
106    pub fn is_unauthenticated(&self) -> bool {
107        self.inner.is_unauthenticated()
108    }
109
110    /// Mark the session as closed.
111    ///
112    /// After the session is closed it can no longer be used to transmit
113    /// or receive messages and any such use will result in an error.
114    pub fn close(&mut self) {
115        self.inner.close()
116    }
117}
118
119/// Structure used for session accounting.
120pub struct SessionMeta<PeerID: Clone + Ord + Hash> {
121    /// Peer identifier.
122    peer_id: PeerID,
123    /// Session identifier.
124    session_id: SessionID,
125    /// Timestamp when the session was last accessed.
126    last_access_time: i64,
127    /// The shared session pointer that needs to be locked for access.
128    inner: SharedSession<PeerID>,
129}
130
131impl<PeerID> SessionMeta<PeerID>
132where
133    PeerID: Clone + Ord + Hash,
134{
135    /// Key for ordering in the by-idle-time index.
136    fn by_time_key(&self) -> SessionByTimeKey<PeerID> {
137        (self.last_access_time, self.peer_id.clone(), self.session_id)
138    }
139}
140
141/// Session indices and management operations.
142pub struct Sessions<PeerID: Clone + Ord + Hash> {
143    /// Session builder.
144    builder: Builder,
145    /// Maximum number of sessions.
146    max_sessions: usize,
147    /// Maximum number of sessions per peer.
148    max_sessions_per_peer: usize,
149    /// Stale session timeout (in seconds).
150    stale_session_timeout: i64,
151
152    /// A map of sessions for each peer.
153    by_peer: HashMap<PeerID, HashMap<SessionID, SessionMeta<PeerID>>>,
154    /// A set of all sessions, ordered by idle time.
155    by_idle_time: BTreeSet<SessionByTimeKey<PeerID>>,
156}
157
158impl<PeerID> Sessions<PeerID>
159where
160    PeerID: Clone + Ord + Hash,
161{
162    /// Create a new session management instance.
163    pub fn new(
164        builder: Builder,
165        max_sessions: usize,
166        max_sessions_per_peer: usize,
167        stale_session_timeout: i64,
168    ) -> Self {
169        Self {
170            builder,
171            max_sessions,
172            max_sessions_per_peer,
173            stale_session_timeout,
174            by_peer: HashMap::new(),
175            by_idle_time: BTreeSet::new(),
176        }
177    }
178
179    /// Set the session builder to use.
180    pub fn set_builder(&mut self, builder: Builder) {
181        self.builder = builder;
182    }
183
184    /// Update remote enclave identity verification in the session builder
185    /// and clear all sessions if the identity has changed.
186    pub fn update_enclaves(
187        &mut self,
188        enclaves: Option<HashSet<EnclaveIdentity>>,
189    ) -> Vec<SharedSession<PeerID>> {
190        if self.builder.get_remote_enclaves() == &enclaves {
191            return vec![];
192        }
193
194        self.builder = mem::take(&mut self.builder).remote_enclaves(enclaves);
195        self.drain()
196    }
197
198    /// Update quote policy used for remote quote verification in the session builder
199    /// and clear all sessions if the policy has changed.
200    pub fn update_quote_policy(&mut self, policy: QuotePolicy) -> Vec<SharedSession<PeerID>> {
201        let policy = Some(Arc::new(policy));
202        if self.builder.get_quote_policy() == &policy {
203            return vec![];
204        }
205
206        self.builder = mem::take(&mut self.builder).quote_policy(policy);
207        self.drain()
208    }
209
210    /// Update remote runtime ID for node identity verification in the session builder
211    /// and clear all sessions if the runtime ID has changed.
212    pub fn update_runtime_id(&mut self, id: Option<Namespace>) -> Vec<SharedSession<PeerID>> {
213        if self.builder.get_remote_runtime_id() == &id {
214            return vec![];
215        }
216
217        self.builder = mem::take(&mut self.builder).remote_runtime_id(id);
218        self.drain()
219    }
220
221    /// Create a new multiplexed responder session.
222    pub fn create_responder(
223        &mut self,
224        peer_id: PeerID,
225        session_id: SessionID,
226    ) -> Result<MultiplexedSession<PeerID>> {
227        // If no quote policy is set, use the local one.
228        if self.builder.get_quote_policy().is_none() {
229            let policy = self
230                .builder
231                .get_local_identity()
232                .as_ref()
233                .and_then(|id| id.quote_policy());
234
235            self.builder = mem::take(&mut self.builder).quote_policy(policy);
236        }
237
238        Ok(MultiplexedSession {
239            peer_id: peer_id.clone(),
240            session_id,
241            inner: self.builder.clone().build_responder()?,
242        })
243    }
244
245    /// Create a new multiplexed initiator session.
246    pub fn create_initiator(&self, peer_id: PeerID) -> Result<MultiplexedSession<PeerID>> {
247        let session_id = SessionID::random();
248
249        Ok(MultiplexedSession {
250            peer_id,
251            session_id,
252            inner: self.builder.clone().build_initiator()?,
253        })
254    }
255
256    /// Fetch an existing session given its identifier.
257    pub fn get(
258        &mut self,
259        peer_id: &PeerID,
260        session_id: &SessionID,
261    ) -> Option<SharedSession<PeerID>> {
262        // Check if peer exists.
263        let sessions = self.by_peer.get_mut(peer_id)?;
264
265        // Check if the session exists. If so, return it.
266        let session = sessions.get_mut(session_id)?;
267
268        Self::update_access_time(session, &mut self.by_idle_time);
269
270        Some(session.inner.clone())
271    }
272
273    /// Fetch an existing session from one of the given peers. If no peers
274    /// are provided, a session from any peer will be returned.
275    pub fn find(&mut self, peer_ids: &[PeerID]) -> Option<SharedSession<PeerID>> {
276        match peer_ids.is_empty() {
277            true => self.find_any(),
278            false => self.find_one(peer_ids),
279        }
280    }
281
282    /// Fetch an existing session from any peer.
283    pub fn find_any(&mut self) -> Option<SharedSession<PeerID>> {
284        if self.by_idle_time.is_empty() {
285            return None;
286        }
287
288        // Check if there is a session that is not currently in use.
289        for (_, peer_id, session_id) in self.by_idle_time.iter() {
290            let session = self
291                .by_peer
292                .get_mut(peer_id)
293                .unwrap()
294                .get_mut(session_id)
295                .unwrap();
296
297            if session.inner.clone().try_lock_owned().is_ok() {
298                Self::update_access_time(session, &mut self.by_idle_time);
299                return Some(session.inner.clone());
300            }
301        }
302
303        // If all sessions are in use, return a random one.
304        let mut rng = UnwrapErr(SysRng);
305        let n = rng.random_range(0..self.by_idle_time.len());
306        let (_, peer_id, session_id) = self.by_idle_time.iter().nth(n).unwrap();
307        let session = self
308            .by_peer
309            .get_mut(peer_id)
310            .unwrap()
311            .get_mut(session_id)
312            .unwrap();
313
314        Self::update_access_time(session, &mut self.by_idle_time);
315
316        Some(session.inner.clone())
317    }
318
319    /// Fetch an existing session from one of the given peers.
320    pub fn find_one(&mut self, peer_ids: &[PeerID]) -> Option<SharedSession<PeerID>> {
321        let mut all_sessions = vec![];
322
323        for peer_id in peer_ids.iter() {
324            let sessions = self.by_peer.get_mut(peer_id)?;
325
326            // Check if peer has a session that is not currently in use.
327            let session = sessions
328                .values_mut()
329                .filter(|s| s.inner.clone().try_lock_owned().is_ok())
330                .min_by_key(|s| s.last_access_time);
331
332            if let Some(session) = session {
333                Self::update_access_time(session, &mut self.by_idle_time);
334                return Some(session.inner.clone());
335            }
336
337            for session in sessions.values() {
338                all_sessions.push((session.peer_id.clone(), session.session_id));
339            }
340        }
341
342        if all_sessions.is_empty() {
343            return None;
344        }
345
346        // If all sessions are in use, return a random one.
347        let mut rng = UnwrapErr(SysRng);
348        let n = rng.random_range(0..all_sessions.len());
349        let (peer_id, session_id) = all_sessions.get(n).unwrap();
350        let session = self
351            .by_peer
352            .get_mut(peer_id)
353            .unwrap()
354            .get_mut(session_id)
355            .unwrap();
356
357        Self::update_access_time(session, &mut self.by_idle_time);
358
359        Some(session.inner.clone())
360    }
361
362    /// Remove one session to free up a slot for the given peer.
363    pub fn remove_for(
364        &mut self,
365        peer_id: &PeerID,
366        now: i64,
367    ) -> Result<Option<OwnedMutexGuard<MultiplexedSession<PeerID>>>, Error> {
368        if let Some(session) = self.remove_from(peer_id)? {
369            return Ok(Some(session));
370        }
371        self.remove_one(now)
372    }
373
374    /// Remove one existing session from the given peer if the peer has reached
375    /// the maximum number of sessions or if the total number of sessions exceeds
376    /// the global session limit.
377    pub fn remove_from(
378        &mut self,
379        peer_id: &PeerID,
380    ) -> Result<Option<OwnedMutexGuard<MultiplexedSession<PeerID>>>, Error> {
381        // Check if peer exists.
382        let sessions = match self.by_peer.get_mut(peer_id) {
383            Some(sessions) => sessions,
384            None => return Ok(None),
385        };
386
387        // Check if the peer has max sessions or if no more sessions are available globally.
388        // If so, remove the oldest or return an error.
389        if sessions.len() < self.max_sessions_per_peer
390            && self.by_idle_time.len() < self.max_sessions
391        {
392            return Ok(None);
393        }
394
395        // Force close the oldest idle session.
396        let remove_session = sessions
397            .iter()
398            .min_by_key(|(_, s)| {
399                if let Ok(_inner) = s.inner.try_lock() {
400                    s.last_access_time
401                } else {
402                    i64::MAX // Session is currently in use.
403                }
404            })
405            .map(|(_, s)| s.inner.clone())
406            .ok_or(Error::MaxConcurrentSessions)?;
407
408        let session = match remove_session.try_lock_owned() {
409            Ok(inner) => inner,
410            Err(_) => return Err(Error::MaxConcurrentSessions), // All sessions are in use.
411        };
412
413        self.remove(&session);
414
415        Ok(Some(session))
416    }
417
418    /// Remove one stale session if the total number of sessions exceeds
419    /// the global session limit.
420    pub fn remove_one(
421        &mut self,
422        now: i64,
423    ) -> Result<Option<OwnedMutexGuard<MultiplexedSession<PeerID>>>, Error> {
424        // Check if there are too many sessions. If so, remove one or return an error.
425        if self.by_idle_time.len() < self.max_sessions {
426            return Ok(None);
427        }
428
429        // Attempt to prune stale sessions, starting with the oldest ones.
430        let mut remove_session: Option<OwnedMutexGuard<MultiplexedSession<PeerID>>> = None;
431
432        for (last_process_frame_time, peer_id, session_id) in self.by_idle_time.iter() {
433            if now.saturating_sub(*last_process_frame_time) < self.stale_session_timeout {
434                // This is the oldest session, all next ones will be more fresh.
435                return Err(Error::MaxConcurrentSessions);
436            }
437
438            // Fetch session and attempt to lock it.
439            if let Some(sessions) = self.by_peer.get(peer_id) {
440                if let Some(session) = sessions.get(session_id) {
441                    if let Ok(session) = session.inner.clone().try_lock_owned() {
442                        remove_session = Some(session);
443                        break;
444                    }
445                }
446            }
447        }
448
449        // Check if we found a session that can be removed.
450        let session = match remove_session {
451            Some(session) => session,
452            None => return Err(Error::MaxConcurrentSessions), // All stale sessions are in use.
453        };
454
455        self.remove(&session);
456
457        Ok(Some(session))
458    }
459
460    /// Add a session if there is an available spot.
461    pub fn add(
462        &mut self,
463        session: MultiplexedSession<PeerID>,
464        now: i64,
465    ) -> Result<SharedSession<PeerID>, Error> {
466        if self.by_idle_time.len() >= self.max_sessions {
467            return Err(Error::MaxConcurrentSessions);
468        }
469
470        let sessions = self.by_peer.entry(session.peer_id.clone()).or_default();
471        if sessions.len() >= self.max_sessions_per_peer {
472            return Err(Error::MaxConcurrentSessions);
473        }
474
475        let peer_id = session.peer_id.clone();
476        let session_id = session.session_id;
477
478        let session = SessionMeta {
479            inner: Arc::new(tokio::sync::Mutex::new(session)),
480            peer_id,
481            session_id,
482            last_access_time: now,
483        };
484        let inner = session.inner.clone();
485
486        self.by_idle_time.insert(session.by_time_key());
487        sessions.insert(session.session_id, session);
488
489        Ok(inner)
490    }
491
492    /// Remove a session that must be currently owned by the caller.
493    pub fn remove(&mut self, session: &OwnedMutexGuard<MultiplexedSession<PeerID>>) {
494        let sessions = match self.by_peer.get_mut(&session.peer_id) {
495            Some(sessions) => sessions,
496            None => return,
497        };
498        let session_meta = match sessions.get(&session.session_id) {
499            Some(session_meta) => session_meta,
500            None => return,
501        };
502        let key = session_meta.by_time_key();
503        sessions.remove(&session.session_id);
504        self.by_idle_time.remove(&key);
505
506        // If peer doesn't have any more sessions, remove the peer.
507        if sessions.is_empty() {
508            self.by_peer.remove(&session.peer_id);
509        }
510    }
511
512    /// Removes and returns all sessions.
513    pub fn drain(&mut self) -> Vec<SharedSession<PeerID>> {
514        self.by_idle_time.clear();
515
516        let mut all_sessions = vec![];
517        for (_, mut sessions) in self.by_peer.drain() {
518            for (_, session) in sessions.drain() {
519                all_sessions.push(session.inner);
520            }
521        }
522
523        all_sessions
524    }
525
526    fn update_access_time(
527        session: &mut SessionMeta<PeerID>,
528        by_idle_time: &mut BTreeSet<SessionByTimeKey<PeerID>>,
529    ) {
530        // Remove old idle time.
531        by_idle_time.remove(&session.by_time_key());
532
533        // Update idle time.
534        session.last_access_time = insecure_posix_time();
535        by_idle_time.insert(session.by_time_key());
536    }
537
538    /// Number of all sessions.
539    #[cfg(test)]
540    fn session_count(&self) -> usize {
541        self.by_idle_time.len()
542    }
543
544    /// Number of all peers.
545    #[cfg(test)]
546    fn peer_count(&self) -> usize {
547        self.by_peer.len()
548    }
549}
550
551#[cfg(test)]
552mod test {
553    use crate::enclave_rpc::{session::Builder, types::SessionID};
554
555    use super::{Error, Sessions};
556
557    fn ids() -> (Vec<Vec<u8>>, Vec<SessionID>) {
558        let peer_ids: Vec<Vec<u8>> = (1..8).map(|x| vec![x]).collect();
559        let session_ids: Vec<SessionID> = (1..8).map(|_| SessionID::random()).collect();
560
561        (peer_ids, session_ids)
562    }
563
564    #[test]
565    fn test_add() {
566        let (peer_ids, session_ids) = ids();
567        let mut sessions = Sessions::new(Builder::default(), 4, 2, 60);
568
569        let test_vector = vec![
570            (&peer_ids[0], &session_ids[0], 1, 1, true),
571            (&peer_ids[0], &session_ids[1], 2, 1, true), // Different session ID.
572            (&peer_ids[0], &session_ids[2], 2, 1, false), // Too many sessions per peer.
573            (&peer_ids[1], &session_ids[0], 3, 2, true), // Different peer ID.
574            (&peer_ids[2], &session_ids[2], 4, 3, true), // Different peer ID and session ID.
575            (&peer_ids[3], &session_ids[3], 4, 3, false), // Too many sessions.
576        ];
577
578        let now = 0;
579        for (peer_id, session_id, num_sessions, num_peers, created) in test_vector {
580            let session = sessions
581                .create_responder(peer_id.clone(), session_id.clone())
582                .unwrap();
583            let res = sessions.add(session, now);
584            match created {
585                true => {
586                    assert!(res.is_ok(), "session should be created");
587                    let s = res.unwrap();
588                    let s_owned = s.try_lock().unwrap();
589                    assert_eq!(&s_owned.peer_id, peer_id);
590                    assert_eq!(&s_owned.session_id, session_id);
591                }
592                false => {
593                    assert!(res.is_err(), "session should not be created");
594                    assert!(matches!(res, Err(Error::MaxConcurrentSessions)));
595                }
596            };
597            assert_eq!(sessions.session_count(), num_sessions);
598            assert_eq!(sessions.peer_count(), num_peers);
599        }
600    }
601
602    #[test]
603    fn test_get() {
604        let (peer_ids, session_ids) = ids();
605        let mut sessions = Sessions::new(Builder::default(), 8, 2, 60);
606
607        let test_vector = vec![
608            (&peer_ids[0], &session_ids[0], true),
609            (&peer_ids[0], &session_ids[1], false), // Different peer ID.
610            (&peer_ids[1], &session_ids[0], false), // Different session ID.
611            (&peer_ids[1], &session_ids[1], false), // Different peer ID and session ID.
612        ];
613
614        let now = 0;
615        for (peer_id, session_id, create) in test_vector {
616            if create {
617                let session = sessions
618                    .create_responder(peer_id.clone(), session_id.clone())
619                    .unwrap();
620                let _ = sessions.add(session, now);
621            }
622
623            let maybe_s = sessions.get(peer_id, session_id);
624            match create {
625                true => {
626                    assert!(maybe_s.is_some(), "session should exist");
627                    let s = maybe_s.unwrap();
628                    let s_owned = s.try_lock_owned().unwrap();
629                    assert_eq!(&s_owned.peer_id, peer_id);
630                    assert_eq!(&s_owned.session_id, session_id);
631                }
632                false => assert!(maybe_s.is_none(), "session should not exist"),
633            }
634        }
635    }
636
637    #[test]
638    fn test_find_any() {
639        let (peer_ids, session_ids) = ids();
640        let mut sessions = Sessions::new(Builder::default(), 8, 2, 60);
641
642        let test_vector = vec![
643            (&peer_ids[0], &session_ids[0]),
644            (&peer_ids[0], &session_ids[1]),
645            (&peer_ids[1], &session_ids[2]),
646        ];
647
648        // No sessions.
649        let maybe_s = sessions.find_any();
650        assert!(maybe_s.is_none(), "session should not be found");
651
652        let mut now = 0;
653        for (peer_id, session_id) in test_vector {
654            let session = sessions
655                .create_responder(peer_id.clone(), session_id.clone())
656                .unwrap();
657            let _ = sessions.add(session, now);
658            now += 1
659        }
660
661        // No sessions in use.
662        let maybe_s = sessions.find_any();
663        assert!(maybe_s.is_some(), "session should be found");
664        let s = maybe_s.unwrap();
665        let s1_owned = s.try_lock_owned().unwrap(); // Session now in use.
666        assert_eq!(&s1_owned.peer_id, &peer_ids[0]);
667        assert_eq!(&s1_owned.session_id, &session_ids[0]);
668
669        // One session in use.
670        let maybe_s = sessions.find_any();
671        assert!(maybe_s.is_some(), "session should be found");
672        let s = maybe_s.unwrap();
673        let s2_owned = s.try_lock_owned().unwrap(); // Session now in use.
674        assert_eq!(&s2_owned.peer_id, &peer_ids[0]);
675        assert_eq!(&s2_owned.session_id, &session_ids[1]); // Different session found.
676
677        // Two sessions in use.
678        let maybe_s = sessions.find_any();
679        assert!(maybe_s.is_some(), "session should be found");
680        let s = maybe_s.unwrap();
681        let s3_owned = s.try_lock_owned().unwrap(); // Session now in use.
682        assert_eq!(&s3_owned.peer_id, &peer_ids[1]);
683        assert_eq!(&s3_owned.session_id, &session_ids[2]); // Different session found.
684
685        // All sessions in use.
686        let maybe_s = sessions.find_any();
687        assert!(maybe_s.is_some(), "session should be found");
688        let s = maybe_s.unwrap();
689        let res = s.try_lock_owned(); // Session now in use.
690        assert!(res.is_err(), "session should be in use");
691
692        // Free one session.
693        drop(s2_owned);
694
695        // Two sessions in use.
696        let maybe_s = sessions.find_any();
697        assert!(maybe_s.is_some(), "session should be found");
698        let s = maybe_s.unwrap();
699        let s_owned = s.try_lock_owned().unwrap(); // Session now in use.
700        assert_eq!(&s_owned.peer_id, &peer_ids[0]);
701        assert_eq!(&s_owned.session_id, &session_ids[1]);
702    }
703
704    #[test]
705    fn test_find_one() {
706        let (peer_ids, session_ids) = ids();
707        let mut sessions = Sessions::new(Builder::default(), 8, 2, 60);
708
709        let test_vector = vec![
710            (&peer_ids[2], &session_ids[0]), // Incorrect peer.
711            (&peer_ids[0], &session_ids[0]),
712            (&peer_ids[3], &session_ids[1]), // Incorrect peer.
713            (&peer_ids[0], &session_ids[1]),
714            (&peer_ids[3], &session_ids[2]), // Incorrect peer.
715            (&peer_ids[1], &session_ids[2]),
716            (&peer_ids[2], &session_ids[2]), // Incorrect peer.
717        ];
718
719        // No sessions.
720        let maybe_s = sessions.find_one(&peer_ids[0..2]);
721        assert!(maybe_s.is_none(), "session should not be found");
722
723        let mut now = 0;
724        for (peer_id, session_id) in test_vector {
725            let session = sessions
726                .create_responder(peer_id.clone(), session_id.clone())
727                .unwrap();
728            let _ = sessions.add(session, now);
729            now += 1
730        }
731
732        // Peers without sessions.
733        let maybe_s = sessions.find_one(&peer_ids[4..]);
734        assert!(maybe_s.is_none(), "session should not be found");
735
736        // No sessions in use.
737        let maybe_s = sessions.find_one(&peer_ids[0..2]);
738        assert!(maybe_s.is_some(), "session should be found");
739        let s = maybe_s.unwrap();
740        let s1_owned = s.try_lock_owned().unwrap(); // Session now in use.
741        assert_eq!(&s1_owned.peer_id, &peer_ids[0]);
742        assert_eq!(&s1_owned.session_id, &session_ids[0]);
743
744        // One session in use.
745        let maybe_s = sessions.find_one(&peer_ids[0..2]);
746        assert!(maybe_s.is_some(), "session should be found");
747        let s = maybe_s.unwrap();
748        let s2_owned = s.try_lock_owned().unwrap(); // Session now in use.
749        assert_eq!(&s2_owned.peer_id, &peer_ids[0]);
750        assert_eq!(&s2_owned.session_id, &session_ids[1]); // Different session found.
751
752        // Two sessions in use.
753        let maybe_s = sessions.find_one(&peer_ids[0..2]);
754        assert!(maybe_s.is_some(), "session should be found");
755        let s = maybe_s.unwrap();
756        let s3_owned = s.try_lock_owned().unwrap(); // Session now in use.
757        assert_eq!(&s3_owned.peer_id, &peer_ids[1]);
758        assert_eq!(&s3_owned.session_id, &session_ids[2]); // Different session found.
759
760        // All sessions in use.
761        let maybe_s = sessions.find_one(&peer_ids[0..2]);
762        assert!(maybe_s.is_some(), "session should be found");
763        let s = maybe_s.unwrap();
764        let res = s.try_lock_owned(); // Session now in use.
765        assert!(res.is_err(), "session should be in use");
766
767        // Free one session.
768        drop(s2_owned);
769
770        // Two sessions in use.
771        let maybe_s = sessions.find_one(&peer_ids[0..2]);
772        assert!(maybe_s.is_some(), "session should be found");
773        let s = maybe_s.unwrap();
774        let s_owned = s.try_lock_owned().unwrap(); // Session now in use.
775        assert_eq!(&s_owned.peer_id, &peer_ids[0]);
776        assert_eq!(&s_owned.session_id, &session_ids[1]);
777    }
778
779    #[test]
780    fn test_remove_from() {
781        let (peer_ids, session_ids) = ids();
782        let mut sessions = Sessions::new(Builder::default(), 4, 2, 60);
783
784        let test_vector = vec![
785            (&peer_ids[0], &session_ids[0]),
786            (&peer_ids[1], &session_ids[1]),
787            (&peer_ids[2], &session_ids[2]),
788            (&peer_ids[2], &session_ids[3]), // Max sessions per peer reached.
789                                             // Max sessions reached.
790        ];
791
792        let mut now = 0;
793        for (peer_id, session_id) in test_vector.clone() {
794            let session = sessions
795                .create_responder(peer_id.clone(), session_id.clone())
796                .unwrap();
797            let _ = sessions.add(session, now);
798            now += 1;
799        }
800
801        // Removing one session from an unknown peer should have no effect,
802        // even if all global session slots are occupied.
803        let res = sessions.remove_from(&peer_ids[3]);
804        assert!(res.is_ok(), "remove_from should succeed");
805        let maybe_s_owned = res.unwrap();
806        assert!(maybe_s_owned.is_none(), "no sessions should be removed");
807        assert_eq!(sessions.session_count(), 4);
808        assert_eq!(sessions.peer_count(), 3);
809
810        // Removing one session for one of the existing peers should work
811        // as it should force evict an old session.
812        // Note that each peer has 2 available slots, but globally there are
813        // only 4 slots so if global slots are full this should trigger peer
814        // session eviction.
815        let res = sessions.remove_from(&peer_ids[0]);
816        assert!(res.is_ok(), "remove_from should succeed");
817        let maybe_s_owned = res.unwrap();
818        assert!(maybe_s_owned.is_some(), "one session should be removed");
819        let s_owned = maybe_s_owned.unwrap();
820        assert_eq!(&s_owned.peer_id, &peer_ids[0]);
821        assert_eq!(&s_owned.session_id, &session_ids[0]);
822        assert_eq!(sessions.session_count(), 3);
823        assert_eq!(sessions.peer_count(), 2);
824
825        // Removing another session should fail as one global session slot
826        // is available.
827        for peer_id in vec![&peer_ids[0], &peer_ids[1]] {
828            let res = sessions.remove_from(peer_id);
829            assert!(res.is_ok(), "remove_from should succeed");
830            let maybe_s_owned = res.unwrap();
831            assert!(maybe_s_owned.is_none(), "no sessions should be removed");
832            assert_eq!(sessions.session_count(), 3);
833            assert_eq!(sessions.peer_count(), 2);
834        }
835
836        // Removing one session from a peer with max sessions should succeed
837        // even if one global slot is available.
838        let res = sessions.remove_from(&peer_ids[2]);
839        assert!(res.is_ok(), "remove_from should succeed");
840        let maybe_s_owned = res.unwrap();
841        assert!(maybe_s_owned.is_some(), "one session should be removed");
842        let s_owned = maybe_s_owned.unwrap();
843        assert_eq!(&s_owned.peer_id, &peer_ids[2]);
844        assert_eq!(&s_owned.session_id, &session_ids[2]);
845        assert_eq!(sessions.session_count(), 2);
846        assert_eq!(sessions.peer_count(), 2);
847    }
848
849    #[test]
850    fn test_remove_one() {
851        let (peer_ids, session_ids) = ids();
852        let mut sessions = Sessions::new(Builder::default(), 4, 2, 60);
853
854        let test_vector = vec![
855            (&peer_ids[0], &session_ids[0]),
856            (&peer_ids[1], &session_ids[1]),
857            (&peer_ids[2], &session_ids[2]),
858            (&peer_ids[2], &session_ids[3]), // Max sessions reached.
859        ];
860
861        let mut now = 0;
862        for (peer_id, session_id) in test_vector.clone() {
863            let session = sessions
864                .create_responder(peer_id.clone(), session_id.clone())
865                .unwrap();
866            let _ = sessions.add(session, now);
867            now += 1;
868        }
869
870        // Forward time (stale_session_timeout - test_vector.len() - 1).
871        now += 60 - 4 - 1;
872
873        // Removing one session should fail as there are none stale sessions.
874        let res = sessions.remove_one(now);
875        assert!(res.is_err(), "remove_one should fail");
876        assert!(matches!(res, Err(Error::MaxConcurrentSessions)));
877        assert_eq!(sessions.session_count(), 4);
878        assert_eq!(sessions.peer_count(), 3);
879
880        // Forward time.
881        now += 1;
882
883        // Removing one session should succeed as no session slots
884        // are available and there is one stale session.
885        let res = sessions.remove_one(now);
886        assert!(res.is_ok(), "remove_one should succeed");
887        let maybe_s_owned = res.unwrap();
888        assert!(maybe_s_owned.is_some(), "one session should be removed");
889        let s_owned = maybe_s_owned.unwrap();
890        assert_eq!(&s_owned.peer_id, &peer_ids[0]);
891        assert_eq!(&s_owned.session_id, &session_ids[0]);
892        assert_eq!(sessions.session_count(), 3);
893        assert_eq!(sessions.peer_count(), 2);
894
895        // Forward time.
896        now += 100;
897
898        // Removing one session should fail even though there are stale sessions
899        // because there is one session slot available.
900        let res = sessions.remove_one(now);
901        assert!(res.is_ok(), "remove_one should succeed");
902        let maybe_s_owned = res.unwrap();
903        assert!(maybe_s_owned.is_none(), "no sessions should be removed");
904        assert_eq!(sessions.session_count(), 3);
905        assert_eq!(sessions.peer_count(), 2);
906    }
907
908    #[test]
909    fn test_remove() {
910        let (peer_ids, session_ids) = ids();
911        let mut sessions = Sessions::new(Builder::default(), 8, 2, 60);
912
913        let test_vector = vec![
914            (&peer_ids[0], &session_ids[0], 3, 2),
915            (&peer_ids[1], &session_ids[1], 2, 1),
916            (&peer_ids[2], &session_ids[2], 1, 1),
917            (&peer_ids[2], &session_ids[3], 0, 0),
918        ];
919
920        let now = 0;
921        for (peer_id, session_id, _, _) in test_vector.clone() {
922            let session = sessions
923                .create_responder(peer_id.clone(), session_id.clone())
924                .unwrap();
925            let _ = sessions.add(session, now);
926        }
927
928        for (peer_id, session_id, num_sessions, num_peers) in test_vector {
929            let maybe_s = sessions.get(peer_id, session_id);
930            assert!(maybe_s.is_some(), "session should exist");
931            let s = maybe_s.unwrap();
932            let s_owned = s.try_lock_owned().unwrap();
933
934            sessions.remove(&s_owned);
935            assert_eq!(sessions.session_count(), num_sessions);
936            assert_eq!(sessions.peer_count(), num_peers);
937        }
938    }
939
940    #[test]
941    fn test_clear() {
942        let (peer_ids, session_ids) = ids();
943        let mut sessions = Sessions::new(Builder::default(), 8, 2, 60);
944
945        let test_vector = vec![
946            (&peer_ids[0], &session_ids[0]),
947            (&peer_ids[1], &session_ids[1]),
948            (&peer_ids[2], &session_ids[2]),
949            (&peer_ids[2], &session_ids[3]),
950        ];
951
952        let now = 0;
953        for (peer_id, session_id) in test_vector.clone() {
954            let session = sessions
955                .create_responder(peer_id.clone(), session_id.clone())
956                .unwrap();
957            let _ = sessions.add(session, now);
958        }
959
960        let removed_sessions = sessions.drain();
961        assert_eq!(removed_sessions.len(), 4);
962        assert_eq!(sessions.session_count(), 0);
963        assert_eq!(sessions.peer_count(), 0);
964    }
965}