Skip to content

Commit

Permalink
Add examples and docstring improvements for mio/uds_recv_fd (rosenpas…
Browse files Browse the repository at this point in the history
  • Loading branch information
aparcar authored Dec 18, 2024
2 parents 4daf97b + a537eb3 commit d807a1b
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 4 deletions.
2 changes: 1 addition & 1 deletion util/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ pub mod io;
pub mod length_prefix_encoding;
/// Memory manipulation and allocation utilities.
pub mod mem;
/// MIO integration utilities.
/// [MIO (Metal I/O)](https://docs.rs/crate/mio/) integration utilities.
pub mod mio;
/// Extended Option type functionality.
pub mod option;
Expand Down
47 changes: 44 additions & 3 deletions util/src/mio/mio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::{
result::OkExt,
};

/// Module containing I/O interest flags for Unix operations
/// Module containing I/O interest flags for Unix operations (see also: [mio::Interest])
pub mod interest {
use mio::Interest;

Expand All @@ -20,9 +20,48 @@ pub mod interest {
pub const RW: Interest = R.add(W);
}

/// Extension trait providing additional functionality for Unix listener
/// Extension trait providing additional functionality for a Unix listener
///
/// # Example
///
/// ```rust
/// use mio::net::{UnixListener, UnixStream};
/// use rosenpass_util::mio::{UnixListenerExt, UnixStreamExt};
///
/// use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
/// use std::path::Path;
///
/// // This would be the UDS created by an external source
/// let socket_path = "/tmp/rp_mio_uds_test_socket";
/// if Path::new(socket_path).exists() {
/// std::fs::remove_file(socket_path).expect("Failed to remove existing socket");
/// }
///
/// // An extended MIO listener can then be created by claiming the existing socket
/// // Note that the original descriptor is not reused, but copied before claiming it here
/// let listener = UnixListener::bind(socket_path).unwrap();
/// let listener_fd: RawFd = listener.as_raw_fd();
/// let ext_listener = <UnixListener as UnixListenerExt>
/// ::claim_fd(listener_fd).expect("Failed to claim_fd for ext_listener socket");
///
/// // Similarly, "client" connections can be established by claiming existing sockets
/// // Note that in this case, the file descriptor will be reused (safety implications!)
/// let stream = UnixStream::connect(socket_path).unwrap();
/// let stream_fd = stream.into_raw_fd();
/// let ext_stream = <UnixStream as UnixStreamExt>
/// ::claim_fd_inplace(stream_fd).expect("Failed to claim_fd_inplace for ext_stream socket");
///
/// // Handle accepted connections...
/// ext_listener.accept().expect("Failed to accept incoming connection");
///
/// // Send or receive messages ...
///
/// // Cleanup, shutdown etc. goes here ...
/// std::fs::remove_file(socket_path).unwrap();
/// ```
pub trait UnixListenerExt: Sized {
/// Creates a new Unix listener by claiming ownership of a raw file descriptor
/// (see [fd::claim_fd](crate::fd::claim_fd))
fn claim_fd(fd: RawFd) -> anyhow::Result<Self>;
}

Expand All @@ -36,15 +75,17 @@ impl UnixListenerExt for UnixListener {
}
}

/// Extension trait providing additional functionality for Unix streams
/// Extension trait providing additional functionality for a Unix stream
pub trait UnixStreamExt: Sized {
/// Creates a new Unix stream from an owned file descriptor
fn from_fd(fd: OwnedFd) -> anyhow::Result<Self>;

/// Claims ownership of a raw file descriptor and creates a new Unix stream
/// (see [fd::claim_fd](crate::fd::claim_fd))
fn claim_fd(fd: RawFd) -> anyhow::Result<Self>;

/// Claims ownership of a raw file descriptor in place and creates a new Unix stream
/// (see [fd::claim_fd_inplace](crate::fd::claim_fd_inplace))
fn claim_fd_inplace(fd: RawFd) -> anyhow::Result<Self>;
}

Expand Down
52 changes: 52 additions & 0 deletions util/src/mio/uds_recv_fd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,58 @@ use crate::fd::{claim_fd_inplace, IntoStdioErr};
/// A wrapper around a socket that combines reading from the socket with tracking
/// received file descriptors. Limits the maximum number of file descriptors that
/// can be received in a single read operation via the `MAX_FDS` parameter.
///
/// # Example
///
/// ```rust
/// use std::collections::VecDeque;
/// use std::io::Cursor;
/// use std::io::Read;
/// use std::os::fd::AsRawFd;
/// use std::os::fd::OwnedFd;
///
/// use mio::net::UnixStream;
/// use rosenpass_util::mio::ReadWithFileDescriptors;
/// use rosenpass_util::io::TryIoResultKindHintExt;
///
/// const MAX_REQUEST_FDS : usize = 2; // Limit to 2 descriptors per read operation
/// let mut read_fd_buffer = VecDeque::<OwnedFd>::new(); // File descriptor queue
///
/// // In this case, the unused writable end of the connection can be ignored
/// let (io_stream, _) = UnixStream::pair().expect("failed to create socket pair");
///
/// // Wait until the output stream is writable...
///
/// // Wrap the socket to start tracking received file descriptors
/// let mut fd_passing_sock = ReadWithFileDescriptors::<MAX_REQUEST_FDS, UnixStream, _, _>::new(
/// &io_stream,
/// &mut read_fd_buffer,
/// );
////
/// // Simulated reads; the actual operations will depend on the protocol (implementation details)
/// let mut recv_buffer = Vec::<u8>::new();
/// let bytes_read = fd_passing_sock.read(&mut recv_buffer[..]).expect("error reading from socket");
/// assert_eq!(bytes_read, 0);
/// assert_eq!(&recv_buffer[..bytes_read], []);
///
/// // Alternatively, it's possible to use the try_io_err_kind_hint utility provided by this crate
/// match fd_passing_sock.read(&mut recv_buffer).try_io_err_kind_hint() {
/// Err(_) => {
/// // Handle errors here ...
/// }
/// Ok(result) => {
/// // Process messages here ...
/// assert_eq!(0, result); // Nothing to read in this example
/// }
/// };
///
/// // The wrapped components can still be accessed
/// assert_eq!(fd_passing_sock.socket().as_raw_fd(), io_stream.as_raw_fd());
/// let (socket, fd_queue) = fd_passing_sock.into_parts();
/// assert_eq!(socket.as_raw_fd(), io_stream.as_raw_fd());
///
/// // Shutdown, cleanup, etc. goes here ...
/// ```
pub struct ReadWithFileDescriptors<const MAX_FDS: usize, Sock, BorrowSock, BorrowFds>
where
Sock: FdPassingExt,
Expand Down

0 comments on commit d807a1b

Please sign in to comment.