diff --git a/lazuli_core/src/client/client.rs b/lazuli_core/src/client/client.rs index ec1a9d7..443ee64 100644 --- a/lazuli_core/src/client/client.rs +++ b/lazuli_core/src/client/client.rs @@ -9,7 +9,10 @@ use log::trace; use crate::{hash_type_id, stream::Stream, ArcMutex, Result, Sendable}; -use super::{connector::StreamConnector, input, listener::SocketListener, StreamCollection}; +use super::{ + config::SocketConfig, connector::StreamConnector, input, listener::SocketListener, + StreamCollection, +}; /// A client for sending and receiving data. pub struct Client { socket: ArcMutex, @@ -55,6 +58,12 @@ impl Client { )) } + /// Applies the configuration to the socket. + pub fn with_config(self, config: &SocketConfig) -> Result { + config.apply_stream(&mut self.socket.lock().unwrap())?; + Ok(self) + } + /// Sends data to the socket. #[inline] pub fn send(&mut self, data: &T) -> Result<()> diff --git a/lazuli_core/src/client/config.rs b/lazuli_core/src/client/config.rs new file mode 100644 index 0000000..013a48a --- /dev/null +++ b/lazuli_core/src/client/config.rs @@ -0,0 +1,94 @@ +/// Config flags for the underlying socket of a client. +pub struct SocketConfig { + /// Whether the socket should be blocking. + pub blocking: Option, + /// The read timeout for the socket. + pub read_timeout: Option, + /// The write timeout for the socket. + pub write_timeout: Option, + /// The time-to-live for the socket. + pub ttl: Option, + /// Whether the socket should have the Nagle algorithm disabled + pub nodelay: Option, +} + +impl Default for SocketConfig { + fn default() -> Self { + Self { + blocking: None, + read_timeout: None, + write_timeout: None, + ttl: None, + nodelay: None, + } + } +} + +impl SocketConfig { + /// Creates a new `SocketConfig` with all fields set to `None`. + /// This is equivalent to `SocketConfig::default()`. + pub fn new() -> Self { + Self::default() + } + + /// Applies the configuration to the given socket. Any fields that are `None` are ignored. + pub fn apply_stream(&self, socket: &std::net::TcpStream) -> std::io::Result<()> { + if let Some(blocking) = self.blocking { + socket.set_nonblocking(!blocking)?; + } + if let Some(read_timeout) = self.read_timeout { + socket.set_read_timeout(Some(read_timeout))?; + } + if let Some(write_timeout) = self.write_timeout { + socket.set_write_timeout(Some(write_timeout))?; + } + if let Some(ttl) = self.ttl { + socket.set_ttl(ttl)?; + } + if let Some(nodelay) = self.nodelay { + socket.set_nodelay(nodelay)?; + } + Ok(()) + } + + /// Applies the configuration to the given listener. Any fields that are `None` are ignored. + pub fn apply_listener(&self, listener: &std::net::TcpListener) -> std::io::Result<()> { + if let Some(blocking) = self.blocking { + listener.set_nonblocking(!blocking)?; + } + if let Some(ttl) = self.ttl { + listener.set_ttl(ttl)?; + } + Ok(()) + } + + /// Sets the blocking flag for the socket. + pub fn blocking(mut self, blocking: bool) -> Self { + self.blocking = Some(blocking); + self + } + + /// Sets the read timeout for the socket. + pub fn read_timeout(mut self, read_timeout: std::time::Duration) -> Self { + self.read_timeout = Some(read_timeout); + self + } + + /// Sets the write timeout for the socket. + pub fn write_timeout(mut self, write_timeout: std::time::Duration) -> Self { + self.write_timeout = Some(write_timeout); + self + } + + /// Sets the time-to-live for the socket. + pub fn ttl(mut self, ttl: u32) -> Self { + self.ttl = Some(ttl); + self + } + + /// Sets the nodelay flag for the socket. + pub fn nodelay(mut self, nodelay: bool) -> Self { + self.nodelay = Some(nodelay); + self + } +} diff --git a/lazuli_core/src/client/mod.rs b/lazuli_core/src/client/mod.rs index b076ae8..de434c2 100644 --- a/lazuli_core/src/client/mod.rs +++ b/lazuli_core/src/client/mod.rs @@ -1,4 +1,5 @@ mod client; +mod config; mod connector; mod input; mod listener; diff --git a/lazuli_core/src/client/server.rs b/lazuli_core/src/client/server.rs index 7496395..afc6686 100644 --- a/lazuli_core/src/client/server.rs +++ b/lazuli_core/src/client/server.rs @@ -5,6 +5,8 @@ use std::{ use crate::{ArcMutex, Client, Result, Sendable}; +use super::config::{self, SocketConfig}; + pub struct Server { listener: TcpListener, streams: Vec>, @@ -19,14 +21,10 @@ impl Server { streams: vec![], }) } - /// Creates a new non-blocking server. - pub fn new_nonblocking(addrs: T) -> Result { - let listener = TcpListener::bind(addrs)?; - listener.set_nonblocking(true)?; - Ok(Server { - listener, - streams: vec![], - }) + /// Adds a configuration to the server. + pub fn with_config(self, config: SocketConfig) -> Result { + config.apply_listener(&self.listener)?; + Ok(self) } /// Accepts a connection. pub fn accept(&mut self) -> Result> { @@ -116,7 +114,8 @@ mod test { } #[test] fn test_nonblocking_server() -> Result<()> { - let mut server = Server::new_nonblocking((Ipv4Addr::LOCALHOST, 0))?; + let mut server = Server::new((Ipv4Addr::LOCALHOST, 0))? + .with_config(SocketConfig::new().blocking(false))?; assert!(server.accept().is_err()); if let Err(e) = server.accept() { assert_eq!(e.kind(), std::io::ErrorKind::WouldBlock);