feat: Add connect timeout to TimeoutSettings (#158)

Because TcpSocket connects in Socket::new TimeoutSettings are now
required for Socket::new. Since we already have TimeoutSettings there
Sockets are now expected to apply timeout settings in Socket::new.
This commit is contained in:
Tom 2023-11-22 10:40:22 +00:00 committed by GitHub
parent 7416d54b14
commit e3bdbc2a41
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 97 additions and 42 deletions

View file

@ -24,8 +24,7 @@ fn get_server_values(
address: &SocketAddr,
timeout_settings: &Option<TimeoutSettings>,
) -> GDResult<HashMap<String, String>> {
let mut socket = UdpSocket::new(address)?;
socket.apply_timeout(timeout_settings)?;
let mut socket = UdpSocket::new(address, timeout_settings)?;
retry_on_timeout(
TimeoutSettings::get_retries_or_default(timeout_settings),
move || get_server_values_impl(&mut socket),

View file

@ -52,9 +52,8 @@ const DEFAULT_PAYLOAD: [u8; 4] = [0xFF, 0xFF, 0xFF, 0x01];
impl GameSpy3 {
fn new(address: &SocketAddr, timeout_settings: Option<TimeoutSettings>) -> GDResult<Self> {
let socket = UdpSocket::new(address)?;
let socket = UdpSocket::new(address, &timeout_settings)?;
let retry_count = TimeoutSettings::get_retries_or_default(&timeout_settings);
socket.apply_timeout(&timeout_settings)?;
Ok(Self {
socket,
@ -70,9 +69,8 @@ impl GameSpy3 {
payload: [u8; 4],
single_packets: bool,
) -> GDResult<Self> {
let socket = UdpSocket::new(address)?;
let socket = UdpSocket::new(address, &timeout_settings)?;
let retry_count = TimeoutSettings::get_retries_or_default(&timeout_settings);
socket.apply_timeout(&timeout_settings)?;
Ok(Self {
socket,

View file

@ -79,9 +79,8 @@ fn data_as_table(data: &mut Buffer<BigEndian>) -> GDResult<(HashMap<String, Vec<
impl GameSpy2 {
fn new(address: &SocketAddr, timeout_settings: Option<TimeoutSettings>) -> GDResult<Self> {
let socket = UdpSocket::new(address)?;
let socket = UdpSocket::new(address, &timeout_settings)?;
let retry_count = TimeoutSettings::get_retries_or_default(&timeout_settings);
socket.apply_timeout(&timeout_settings)?;
Ok(Self {
socket,

View file

@ -25,8 +25,7 @@ fn get_data<Client: QuakeClient>(
address: &SocketAddr,
timeout_settings: &Option<TimeoutSettings>,
) -> GDResult<Vec<u8>> {
let mut socket = UdpSocket::new(address)?;
socket.apply_timeout(timeout_settings)?;
let mut socket = UdpSocket::new(address, timeout_settings)?;
retry_on_timeout(
TimeoutSettings::get_retries_or_default(timeout_settings),
move || get_data_impl::<Client>(&mut socket),

View file

@ -150,6 +150,7 @@ pub struct CommonPlayerJson<'a> {
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct TimeoutSettings {
connect: Option<Duration>,
read: Option<Duration>,
write: Option<Duration>,
retries: usize,
@ -164,22 +165,34 @@ impl TimeoutSettings {
/// "1" will try the request again once if it fails.
/// The retry count is per-request so for multi-request queries (valve) if a
/// single part fails that part can be retried up to `retries` times.
pub fn new(read: Option<Duration>, write: Option<Duration>, retries: usize) -> GDResult<Self> {
pub fn new(
read: Option<Duration>,
write: Option<Duration>,
connect: Option<Duration>,
retries: usize,
) -> GDResult<Self> {
if let Some(read_duration) = read {
if read_duration == Duration::new(0, 0) {
if read_duration.is_zero() {
return Err(InvalidInput.context("Read duration must not be 0"));
}
}
if let Some(write_duration) = write {
if write_duration == Duration::new(0, 0) {
if write_duration.is_zero() {
return Err(InvalidInput.context("Write duration must not be 0"));
}
}
if let Some(connect_duration) = connect {
if connect_duration.is_zero() {
return Err(InvalidInput.context("Connect duration must not be 0"));
}
}
Ok(Self {
read,
write,
connect,
retries,
})
}
@ -190,6 +203,9 @@ impl TimeoutSettings {
/// Get the write timeout.
pub const fn get_write(&self) -> Option<Duration> { self.write }
/// Get the connect timeout.
pub const fn get_connect(&self) -> Option<Duration> { self.connect }
/// Get number of retries
pub const fn get_retries(&self) -> usize { self.retries }
@ -216,11 +232,21 @@ impl TimeoutSettings {
}
}
/// Get the connect duration given timeout settings or get the default.
pub const fn get_connect_or_default(timeout_settings: &Option<TimeoutSettings>) -> Option<Duration> {
if let Some(timeout_settings) = timeout_settings {
timeout_settings.get_connect()
} else {
TimeoutSettings::const_default().get_connect()
}
}
/// Default values are 4 seconds for both read and write, no retries.
pub const fn const_default() -> Self {
Self {
read: Some(Duration::from_secs(4)),
write: Some(Duration::from_secs(4)),
connect: Some(Duration::from_secs(4)),
retries: 0,
}
}
@ -320,9 +346,15 @@ mod tests {
// Define valid read and write durations
let read_duration = Duration::from_secs(1);
let write_duration = Duration::from_secs(2);
let connect_duration = Duration::from_secs(3);
// Create new TimeoutSettings with the valid durations
let timeout_settings = TimeoutSettings::new(Some(read_duration), Some(write_duration), 0)?;
let timeout_settings = TimeoutSettings::new(
Some(read_duration),
Some(write_duration),
Some(connect_duration),
0,
)?;
// Verify that the get_read and get_write methods return the expected values
assert_eq!(timeout_settings.get_read(), Some(read_duration));
@ -337,10 +369,16 @@ mod tests {
// Define a zero read duration and a valid write duration
let read_duration = Duration::new(0, 0);
let write_duration = Duration::from_secs(2);
let connect_duration = Duration::from_secs(3);
// Try to create new TimeoutSettings with the zero read duration (this should
// fail)
let result = TimeoutSettings::new(Some(read_duration), Some(write_duration), 0);
let result = TimeoutSettings::new(
Some(read_duration),
Some(write_duration),
Some(connect_duration),
0,
);
// Verify that the function returned an error and that the error type is
// InvalidInput

View file

@ -32,12 +32,11 @@ pub(crate) struct Unreal2Protocol {
impl Unreal2Protocol {
pub fn new(address: &SocketAddr, timeout_settings: Option<TimeoutSettings>) -> GDResult<Self> {
let socket = UdpSocket::new(address)?;
let socket = UdpSocket::new(address, &timeout_settings)?;
let retry_count = timeout_settings
.as_ref()
.map(|t| t.get_retries())
.unwrap_or_else(|| TimeoutSettings::default().get_retries());
socket.apply_timeout(&timeout_settings)?;
Ok(Self {
socket,

View file

@ -126,12 +126,11 @@ static PACKET_SIZE: usize = 6144;
impl ValveProtocol {
pub fn new(address: &SocketAddr, timeout_settings: Option<TimeoutSettings>) -> GDResult<Self> {
let socket = UdpSocket::new(address)?;
let socket = UdpSocket::new(address, &timeout_settings)?;
let retry_count = timeout_settings
.as_ref()
.map(|t| t.get_retries())
.unwrap_or_else(|| TimeoutSettings::default().get_retries());
socket.apply_timeout(&timeout_settings)?;
Ok(Self {
socket,