From f1f122572d50aff37d2974f91b3bfbbf5ae8ddc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20F=C3=A4rnstrand?= Date: Tue, 2 Apr 2024 17:11:40 +0200 Subject: [PATCH 1/2] Hide implementation details of ApplyTcpOptionsError --- src/lib.rs | 2 +- src/tcp_options.rs | 69 ++++++++++++++++++++++++++++++++++------------ 2 files changed, 53 insertions(+), 18 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 6e95d3d..ab11191 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -91,7 +91,7 @@ mod forward_traffic; mod logging; mod tcp_options; -pub use tcp_options::{ApplyTcpOptionsError, TcpOptions}; +pub use tcp_options::{ApplyTcpOptionsError, ApplyTcpOptionsErrorKind, TcpOptions}; /// Helper trait for `Result` types. Allows getting the `E` value /// in a way that is guaranteed to not panic. diff --git a/src/tcp_options.rs b/src/tcp_options.rs index 8f91d5c..f58205c 100644 --- a/src/tcp_options.rs +++ b/src/tcp_options.rs @@ -35,27 +35,61 @@ pub struct TcpOptions { pub nodelay: bool, } +/// Represents a failure to apply socket options to the TCP socket. #[derive(Debug)] +pub struct ApplyTcpOptionsError(ApplyTcpOptionsErrorInternal); + +#[derive(Debug)] +enum ApplyTcpOptionsErrorInternal { + RecvBuffer(io::Error), + SendBuffer(io::Error), + #[cfg(target_os = "linux")] + Mark(nix::Error), + TcpNoDelay(io::Error), +} + +/// A list specifying what failed when applying the TCP options. +#[derive(Debug, Copy, Clone)] #[non_exhaustive] -pub enum ApplyTcpOptionsError { +pub enum ApplyTcpOptionsErrorKind { /// Failed to get/set TCP_RCVBUF - RecvBuffer(io::Error), + RecvBuffer, /// Failed to get/set TCP_SNDBUF - SendBuffer(io::Error), + SendBuffer, /// Failed to get/set SO_MARK #[cfg(target_os = "linux")] - Mark(nix::Error), + Mark, /// Failed to get/set TCP_NODELAY - TcpNoDelay(io::Error), + TcpNoDelay, +} + +impl ApplyTcpOptionsError { + /// Returns the kind of error that happened as an enum + pub fn kind(&self) -> ApplyTcpOptionsErrorKind { + use ApplyTcpOptionsErrorInternal::*; + match self.0 { + RecvBuffer(_) => ApplyTcpOptionsErrorKind::RecvBuffer, + SendBuffer(_) => ApplyTcpOptionsErrorKind::SendBuffer, + #[cfg(target_os = "linux")] + Mark(_) => ApplyTcpOptionsErrorKind::Mark, + TcpNoDelay(_) => ApplyTcpOptionsErrorKind::TcpNoDelay, + } + } +} + +impl From for ApplyTcpOptionsError { + fn from(value: ApplyTcpOptionsErrorInternal) -> Self { + Self(value) + } } impl fmt::Display for ApplyTcpOptionsError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use ApplyTcpOptionsError::*; - match self { + use ApplyTcpOptionsErrorInternal::*; + match self.0 { RecvBuffer(_) => "Failed to get/set TCP_RCVBUF", SendBuffer(_) => "Failed to get/set TCP_SNDBUF", #[cfg(target_os = "linux")] @@ -68,8 +102,8 @@ impl fmt::Display for ApplyTcpOptionsError { impl std::error::Error for ApplyTcpOptionsError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - use ApplyTcpOptionsError::*; - match self { + use ApplyTcpOptionsErrorInternal::*; + match &self.0 { RecvBuffer(e) => Some(e), SendBuffer(e) => Some(e), #[cfg(target_os = "linux")] @@ -90,33 +124,34 @@ pub fn apply(socket: &TcpSocket, options: &TcpOptions) -> Result<(), ApplyTcpOpt if let Some(recv_buffer_size) = options.recv_buffer_size { socket .set_recv_buffer_size(recv_buffer_size) - .map_err(ApplyTcpOptionsError::RecvBuffer)?; + .map_err(ApplyTcpOptionsErrorInternal::RecvBuffer)?; } log::debug!( "SO_RCVBUF: {}", socket .recv_buffer_size() - .map_err(ApplyTcpOptionsError::RecvBuffer)? + .map_err(ApplyTcpOptionsErrorInternal::RecvBuffer)? ); if let Some(send_buffer_size) = options.send_buffer_size { socket .set_send_buffer_size(send_buffer_size) - .map_err(ApplyTcpOptionsError::SendBuffer)?; + .map_err(ApplyTcpOptionsErrorInternal::SendBuffer)?; } log::debug!( "SO_SNDBUF: {}", socket .send_buffer_size() - .map_err(ApplyTcpOptionsError::SendBuffer)? + .map_err(ApplyTcpOptionsErrorInternal::SendBuffer)? ); #[cfg(target_os = "linux")] { if let Some(fwmark) = options.fwmark { - setsockopt(&socket, sockopt::Mark, &fwmark).map_err(ApplyTcpOptionsError::Mark)?; + setsockopt(&socket, sockopt::Mark, &fwmark) + .map_err(ApplyTcpOptionsErrorInternal::Mark)?; } log::debug!( "SO_MARK: {}", - getsockopt(&socket, sockopt::Mark).map_err(ApplyTcpOptionsError::Mark)? + getsockopt(&socket, sockopt::Mark).map_err(ApplyTcpOptionsErrorInternal::Mark)? ); } Ok(()) @@ -128,12 +163,12 @@ pub fn set_nodelay(tcp_stream: &TcpStream, nodelay: bool) -> Result<(), ApplyTcp // Configure TCP_NODELAY on the TCP stream tcp_stream .set_nodelay(nodelay) - .map_err(ApplyTcpOptionsError::TcpNoDelay)?; + .map_err(ApplyTcpOptionsErrorInternal::TcpNoDelay)?; log::debug!( "TCP_NODELAY: {}", tcp_stream .nodelay() - .map_err(ApplyTcpOptionsError::TcpNoDelay)? + .map_err(ApplyTcpOptionsErrorInternal::TcpNoDelay)? ); Ok(()) } From 77ee39d4776cffcf895aeca734646d8a33e8787b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20F=C3=A4rnstrand?= Date: Tue, 2 Apr 2024 17:37:03 +0200 Subject: [PATCH 2/2] Add error restructure to changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cf2148c..3396204 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,9 @@ Line wrap the file at 100 chars. Th ## [Unreleased] +### Changed +- Change the public API of `ApplyTcpOptionsError`. So this is a breaking change. This stops + exposing the internal details of the type which allows future changes to not be breaking. ## [0.4.0] - 2024-01-02