Skip to main content

darkbio_wire/protocol/
closer.rs

1// wire-rs: encrypted protocol between Ark and host
2// Copyright 2026 Dark Bio AG. All rights reserved.
3//
4// Use of this source code is governed by a BSD-style
5// license that can be found in the LICENSE file.
6
7//! Handles for closing a session or server from another thread.
8
9use super::{Error, server::ServerInner, session::SessionInner};
10use std::sync::Weak;
11
12/// Clonable handle for closing the session or server that created it.
13///
14/// From [`super::Session::closer`], it closes that session, with the semantics of
15/// [`super::Session::close`]. From [`super::Server::closer`], it closes the server
16/// and its active session, with the semantics of [`super::Server::close`].
17/// Its target never changes, so a session's closer cannot affect a successor
18/// session.
19///
20/// This handle does not keep its owner open. Dropping it does not close anything.
21#[derive(Clone, Debug)]
22pub struct Closer {
23    /// Session or server to close.
24    target: Target,
25}
26
27/// Session or server targeted by a [`Closer`].
28#[derive(Clone, Debug)]
29enum Target {
30    /// One session, even after another session connects to the same server.
31    Session(Weak<SessionInner>),
32    /// One persistent server and whichever session it has attached at closure.
33    Server(Weak<ServerInner>),
34}
35
36impl Closer {
37    /// Closes the session or server that created this handle.
38    ///
39    /// Repeated calls have no further effect.
40    pub fn close(&self) {
41        match &self.target {
42            Target::Session(target) => {
43                if let Some(session) = target.upgrade() {
44                    session.close(Error::Closed);
45                }
46            }
47            Target::Server(target) => {
48                if let Some(server) = target.upgrade() {
49                    server.close(Error::Closed);
50                }
51            }
52        }
53    }
54
55    /// Creates a closer targeting one session without keeping it alive.
56    pub(super) fn session(target: Weak<SessionInner>) -> Self {
57        Self {
58            target: Target::Session(target),
59        }
60    }
61
62    /// Creates a closer targeting one server without keeping it alive.
63    pub(super) fn server(target: Weak<ServerInner>) -> Self {
64        Self {
65            target: Target::Server(target),
66        }
67    }
68}
69
70/// Checks that the same closer type works for sessions and servers.
71#[cfg(test)]
72#[cfg_attr(coverage_nightly, coverage(off))]
73mod tests {
74    use crate::protocol::{Closer, Server, Session};
75    use std::fmt::Debug;
76
77    /// Compiles closing sessions and servers through cloned handles on other threads.
78    #[allow(dead_code)]
79    fn cross_thread_close(session: &Session, server: &Server) {
80        let session_closer: Closer = session.closer();
81        let server_closer: Closer = server.closer();
82        let session_copy = session_closer.clone();
83        let server_copy = server_closer.clone();
84        std::thread::spawn(move || session_copy.close());
85        std::thread::spawn(move || server_copy.close());
86        session.close();
87        server.close();
88    }
89
90    /// Checks that `Closer` implements `Clone`, `Debug`, `Send`, and `Sync`.
91    #[test]
92    fn test_thread_capabilities() {
93        /// Requires a handle to be clonable, printable and usable by multiple threads.
94        fn shared<T: Clone + Debug + Send + Sync + 'static>() {}
95        shared::<Closer>();
96    }
97}