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: a session's closer cannot affect a successor session.
18///
19/// This handle does not keep its owner open. Dropping it does not close anything.
20#[derive(Clone, Debug)]
21pub struct Closer {
22    /// Session or server to close.
23    target: Target,
24}
25
26/// The session or server targeted by a `Closer`.
27#[derive(Clone, Debug)]
28enum Target {
29    /// One session, even after another session connects to the same server.
30    Session(Weak<SessionInner>),
31    /// One persistent server and whichever session it has attached at closure.
32    Server(Weak<ServerInner>),
33}
34
35impl Closer {
36    /// Closes the original owner. Repeated calls have no further effect.
37    pub fn close(&self) {
38        match &self.target {
39            Target::Session(target) => {
40                if let Some(session) = target.upgrade() {
41                    session.close(Error::Closed);
42                }
43            }
44            Target::Server(target) => {
45                if let Some(server) = target.upgrade() {
46                    server.close(Error::Closed);
47                }
48            }
49        }
50    }
51
52    /// Creates a closer for one session using a weak reference.
53    pub(super) fn session(target: Weak<SessionInner>) -> Self {
54        Self {
55            target: Target::Session(target),
56        }
57    }
58
59    /// Creates a closer for one server using a weak reference.
60    pub(super) fn server(target: Weak<ServerInner>) -> Self {
61        Self {
62            target: Target::Server(target),
63        }
64    }
65}
66
67/// Checks that the same closer type works for sessions and servers.
68#[cfg(test)]
69#[cfg_attr(coverage_nightly, coverage(off))]
70mod tests {
71    use crate::protocol::{Closer, Server, Session};
72    use std::fmt::Debug;
73
74    /// Compiles closing sessions and servers through cloned handles on other threads.
75    #[allow(dead_code)]
76    fn cross_thread_close(session: &Session, server: &Server) {
77        let session_closer: Closer = session.closer();
78        let server_closer: Closer = server.closer();
79        let session_copy = session_closer.clone();
80        let server_copy = server_closer.clone();
81        std::thread::spawn(move || session_copy.close());
82        std::thread::spawn(move || server_copy.close());
83        session.close();
84        server.close();
85    }
86
87    /// Checks that `Closer` implements `Clone`, `Debug`, `Send`, and `Sync`.
88    #[test]
89    fn test_thread_capabilities() {
90        /// Requires a handle to be clonable, printable and usable by multiple threads.
91        fn shared<T: Clone + Debug + Send + Sync + 'static>() {}
92        shared::<Closer>();
93    }
94}