melib/connections.rs: disable Nagle's algorithm by default

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
pull/393/head
Manos Pitsidianakis 4 months ago
parent 671d35e21e
commit 4bdfb3a31b
No known key found for this signature in database
GPG Key ID: 7729C7707F7E09D0

@ -132,6 +132,66 @@ macro_rules! syscall {
}};
}
/// Hardcoded `setsockopt` arguments for type safety when calling
/// [`Connection::setsockopt`] in an `unsafe` block.
///
/// Add new variants when you need to call `setsockopt` with new arguments.
pub enum SockOpts {
/// Set TCP Keep Alive.
///
/// Following text is sourced from <https://tldp.org/HOWTO/html_single/TCP-Keepalive-HOWTO/>.
///
/// ```text
/// 4.2. The setsockopt function call
///
/// All you need to enable keepalive for a specific socket is to set the specific socket option
/// on the socket itself. The prototype of the function is as follows:
///
///
/// int setsockopt(int s, int level, int optname,
/// const void *optval, socklen_t optlen)
///
///
/// The first parameter is the socket, previously created with the socket(2); the second one
/// must be SOL_SOCKET, and the third must be SO_KEEPALIVE . The fourth parameter must be a
/// boolean integer value, indicating that we want to enable the option, while the last is the
/// size of the value passed before.
///
/// According to the manpage, 0 is returned upon success, and -1 is returned on error (and
/// errno is properly set).
///
/// There are also three other socket options you can set for keepalive when you write your
/// application. They all use the SOL_TCP level instead of SOL_SOCKET, and they override
/// system-wide variables only for the current socket. If you read without writing first, the
/// current system-wide parameters will be returned.
///
/// TCP_KEEPCNT: overrides tcp_keepalive_probes
///
/// TCP_KEEPIDLE: overrides tcp_keepalive_time
///
/// TCP_KEEPINTVL: overrides tcp_keepalive_intvl
/// ```
///
/// Field `duration` overrides `tcp_keepalive_time`:
///
/// ```text
/// tcp_keepalive_time
///
/// the interval between the last data packet sent (simple ACKs are not considered data) and the
/// first keepalive probe; after the connection is marked to need keepalive, this counter is not
/// used any further
/// ```
///
/// The default value in the Linux kernel is 7200 seconds (2 hours).
KeepAlive {
enable: bool,
duration: Option<Duration>,
},
TcpNoDelay {
enable: bool,
},
}
impl Connection {
pub const IO_BUF_SIZE: usize = 64 * 1024;
@ -160,11 +220,14 @@ impl Connection {
}
pub fn new_tcp(inner: std::net::TcpStream) -> Self {
Self::Tcp {
let ret = Self::Tcp {
inner,
id: None,
trace: false,
}
};
_ = ret.setsockopt(SockOpts::TcpNoDelay { enable: true });
ret
}
pub fn trace(mut self, val: bool) -> Self {
@ -288,11 +351,11 @@ impl Connection {
return Ok(None);
}
unsafe {
let raw: c_int = self.getsockopt(libc::SOL_SOCKET, libc::SO_KEEPALIVE)?;
let raw: c_int = self.__getsockopt(libc::SOL_SOCKET, libc::SO_KEEPALIVE)?;
if raw == 0 {
return Ok(None);
}
let secs: c_int = self.getsockopt(libc::IPPROTO_TCP, KEEPALIVE_OPTION)?;
let secs: c_int = self.__getsockopt(libc::IPPROTO_TCP, KEEPALIVE_OPTION)?;
Ok(Some(Duration::new(secs as u64, 0)))
}
}
@ -312,21 +375,13 @@ impl Connection {
if matches!(self, Fd { .. }) {
return Ok(());
}
unsafe {
self.setsockopt(
libc::SOL_SOCKET,
libc::SO_KEEPALIVE,
keepalive.is_some() as c_int,
)?;
if let Some(dur) = keepalive {
// [ref:TODO]: checked cast here
self.setsockopt(libc::IPPROTO_TCP, KEEPALIVE_OPTION, dur.as_secs() as c_int)?;
}
Ok(())
}
self.setsockopt(SockOpts::KeepAlive {
enable: keepalive.is_some(),
duration: keepalive,
})
}
unsafe fn setsockopt<T>(&self, opt: c_int, val: c_int, payload: T) -> std::io::Result<()>
unsafe fn inner_setsockopt<T>(&self, opt: c_int, val: c_int, payload: T) -> std::io::Result<()>
where
T: Copy,
{
@ -341,7 +396,44 @@ impl Connection {
Ok(())
}
unsafe fn getsockopt<T: Copy>(&self, opt: c_int, val: c_int) -> std::io::Result<T> {
fn setsockopt(&self, option: SockOpts) -> std::io::Result<()> {
match option {
SockOpts::KeepAlive {
enable: true,
duration,
} => {
unsafe {
self.inner_setsockopt(libc::SOL_SOCKET, libc::SO_KEEPALIVE, <c_int>::from(true))
}?;
if let Some(dur) = duration {
unsafe {
self.inner_setsockopt(
libc::IPPROTO_TCP,
KEEPALIVE_OPTION,
dur.as_secs() as c_int,
)
}?;
}
Ok(())
}
SockOpts::KeepAlive {
enable: false,
duration: _,
} => unsafe {
self.inner_setsockopt(libc::SOL_SOCKET, libc::SO_KEEPALIVE, <c_int>::from(false))
},
SockOpts::TcpNoDelay { enable } => unsafe {
self.inner_setsockopt(
libc::SOL_TCP,
libc::TCP_NODELAY,
if enable { c_int::from(1_u8) } else { 0 },
)
},
}
}
#[inline]
unsafe fn __getsockopt<T: Copy>(&self, opt: c_int, val: c_int) -> std::io::Result<T> {
let mut slot: T = std::mem::zeroed();
let mut len = std::mem::size_of::<T>() as libc::socklen_t;
syscall!(getsockopt(

Loading…
Cancel
Save