zlink_core/connection/
socket.rs

1//! The low-level Socket read and write traits.
2
3use core::future::Future;
4
5#[cfg(feature = "std")]
6use std::os::fd::{AsFd, OwnedFd};
7
8/// Result type for [`ReadHalf::read`] operations.
9///
10/// With `std` feature: returns `(usize, Vec<OwnedFd>)` - bytes read and file descriptors.
11/// Without `std` feature: returns `usize` - just bytes read.
12#[cfg(feature = "std")]
13pub type ReadResult = (usize, alloc::vec::Vec<OwnedFd>);
14
15/// Result type for [`ReadHalf::read`] operations.
16///
17/// With `std` feature: returns `(usize, Vec<OwnedFd>)` - bytes read and file descriptors.
18/// Without `std` feature: returns `usize` - just bytes read.
19#[cfg(not(feature = "std"))]
20pub type ReadResult = usize;
21
22/// The socket trait.
23///
24/// This is the trait that needs to be implemented for a type to be used as a socket/transport.
25pub trait Socket: core::fmt::Debug {
26    /// The read half of the socket.
27    type ReadHalf: ReadHalf;
28    /// The write half of the socket.
29    type WriteHalf: WriteHalf;
30
31    /// Whether this socket can transfer file descriptors.
32    ///
33    /// This is `true` for Unix domain sockets and `false` for other socket types.
34    const CAN_TRANSFER_FDS: bool = false;
35
36    /// Split the socket into read and write halves.
37    fn split(self) -> (Self::ReadHalf, Self::WriteHalf);
38}
39
40/// The read half of a socket.
41pub trait ReadHalf: core::fmt::Debug {
42    /// Read from a socket.
43    ///
44    /// On completion, the number of bytes read and any file descriptors received are returned.
45    ///
46    /// Notes for implementers:
47    ///
48    /// * The future returned by this method must be cancel safe.
49    /// * While there is no explicit `Unpin` bound on the future returned by this method, it is
50    ///   expected that it provides the same guarentees as `Unpin` would require. The reason `Unpin`
51    ///   is not explicitly requied is that it would force boxing (and therefore allocation) on the
52    ///   implemention that use `async fn`, which is undesirable for embedded use cases. See [this
53    ///   issue](https://github.com/rust-lang/rust/issues/82187) for details.
54    fn read(&mut self, buf: &mut [u8]) -> impl Future<Output = crate::Result<ReadResult>>;
55}
56
57/// The write half of a socket.
58pub trait WriteHalf: core::fmt::Debug {
59    /// Write to the socket.
60    ///
61    /// The `fds` parameter contains file descriptors to send along with the data (std only).
62    ///
63    /// The returned future has the same requirements as that of [`ReadHalf::read`].
64    fn write(
65        &mut self,
66        buf: &[u8],
67        #[cfg(feature = "std")] fds: &[impl AsFd],
68    ) -> impl Future<Output = crate::Result<()>>;
69}
70
71/// Trait for fetching peer credentials from a socket.
72///
73/// This trait provides the low-level capability to fetch credentials from a socket's underlying
74/// file descriptor. It is typically implemented by socket read halves that support credentials.
75#[cfg(feature = "std")]
76pub trait FetchPeerCredentials {
77    /// Fetch the peer credentials for this socket.
78    ///
79    /// This is the low-level method that socket implementations should override to provide peer
80    /// credentials. Higher-level APIs should use [`super::Connection::peer_credentials`] instead.
81    fn fetch_peer_credentials(&self) -> impl Future<Output = std::io::Result<super::Credentials>>;
82}
83
84/// Trait for Unix Domain Sockets.
85///
86/// Implementing this trait signals that the type is a Unix Domain Socket (UDS) where credentials
87/// fetching through a file descriptor will work correctly. [`FetchPeerCredentials`] is implemented
88/// for all types that implement this trait.
89#[cfg(feature = "std")]
90pub trait UnixSocket: AsFd {}
91
92#[cfg(feature = "std")]
93impl<T> FetchPeerCredentials for T
94where
95    T: UnixSocket,
96{
97    async fn fetch_peer_credentials(&self) -> std::io::Result<super::Credentials> {
98        // Assume peer credentials fetching never blocks so it's fine to call this synchronous
99        // method from an async context.
100        crate::unix_utils::get_peer_credentials(self)
101    }
102}
103
104/// Documentation-only socket implementations for doc tests.
105///
106/// These types exist only to make doc tests compile and should never be used in real code.
107#[doc(hidden)]
108pub mod impl_for_doc {
109
110    /// A mock socket for documentation examples.
111    #[derive(Debug)]
112    pub struct Socket;
113
114    impl super::Socket for Socket {
115        type ReadHalf = ReadHalf;
116        type WriteHalf = WriteHalf;
117
118        fn split(self) -> (Self::ReadHalf, Self::WriteHalf) {
119            (ReadHalf, WriteHalf)
120        }
121    }
122
123    /// A mock read half for documentation examples.
124    #[derive(Debug)]
125    pub struct ReadHalf;
126
127    impl super::ReadHalf for ReadHalf {
128        async fn read(&mut self, _buf: &mut [u8]) -> crate::Result<super::ReadResult> {
129            unreachable!("This is only for doc tests")
130        }
131    }
132
133    /// A mock write half for documentation examples.
134    #[derive(Debug)]
135    pub struct WriteHalf;
136
137    impl super::WriteHalf for WriteHalf {
138        async fn write(
139            &mut self,
140            _buf: &[u8],
141            #[cfg(feature = "std")] _fds: &[impl super::AsFd],
142        ) -> crate::Result<()> {
143            unreachable!("This is only for doc tests")
144        }
145    }
146}