Skip to content

Commit

Permalink
fix: add timeouts to UdpClient operations
Browse files Browse the repository at this point in the history
The generic UDP client does not have timeouts. When the server is down
it waits forever for responses. This client has been using only for
testing where the server was always up, but now it's using in production
code by the Tracker Checker.

For the time being, I keep the panicking behavior of the UdpClient when
something is wrong. However we should return an error when the operation
times out.

For the Tracker Checker, it means that when the checker can't connect to
a UDP server the checker is going to panic after 5 seconds. That is not
the intended behavior for the checker. It should always return a full
reprot of all checks. In order to implement that behavior we need to
change the UdpClient to return errors. Since that's a bug refactor I
open a new issue.
  • Loading branch information
josecelano committed Feb 1, 2024
1 parent 6b74c66 commit 592c0dd
Showing 1 changed file with 39 additions and 5 deletions.
44 changes: 39 additions & 5 deletions src/shared/bit_torrent/tracker/udp/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,18 @@ use tokio::time;

use crate::shared::bit_torrent::tracker::udp::{source_address, MAX_PACKET_SIZE};

/// Default timeout for sending and receiving packets. And waiting for sockets
/// to be readable and writable.
const DEFAULT_TIMEOUT: Duration = Duration::from_secs(5);

#[allow(clippy::module_name_repetitions)]
#[derive(Debug)]

Check warning on line 18 in src/shared/bit_torrent/tracker/udp/client.rs

View check run for this annotation

Codecov / codecov/patch

src/shared/bit_torrent/tracker/udp/client.rs#L18

Added line #L18 was not covered by tests
pub struct UdpClient {
/// The socket to connect to
pub socket: Arc<UdpSocket>,

/// Timeout for sending and receiving packets
pub timeout: Duration,

Check warning on line 24 in src/shared/bit_torrent/tracker/udp/client.rs

View check run for this annotation

Codecov / codecov/patch

src/shared/bit_torrent/tracker/udp/client.rs#L24

Added line #L24 was not covered by tests
}

impl UdpClient {
Expand All @@ -29,6 +37,7 @@ impl UdpClient {

Self {
socket: Arc::new(socket),
timeout: DEFAULT_TIMEOUT,
}
}

Expand All @@ -53,10 +62,23 @@ impl UdpClient {
/// - Can't write to the socket.
/// - Can't send data.
pub async fn send(&self, bytes: &[u8]) -> usize {
debug!(target: "UDP client", "send {bytes:?}");
debug!(target: "UDP client", "sending {bytes:?} ...");

match time::timeout(self.timeout, self.socket.writable()).await {
Ok(writable_result) => match writable_result {
Ok(()) => (),
Err(e) => panic!("{}", format!("IO error waiting for the socket to become readable: {e:?}")),

Check warning on line 70 in src/shared/bit_torrent/tracker/udp/client.rs

View check run for this annotation

Codecov / codecov/patch

src/shared/bit_torrent/tracker/udp/client.rs#L70

Added line #L70 was not covered by tests
},
Err(e) => panic!("{}", format!("Timeout waiting for the socket to become readable: {e:?}")),

Check warning on line 72 in src/shared/bit_torrent/tracker/udp/client.rs

View check run for this annotation

Codecov / codecov/patch

src/shared/bit_torrent/tracker/udp/client.rs#L72

Added line #L72 was not covered by tests
};

self.socket.writable().await.unwrap();
self.socket.send(bytes).await.unwrap()
match time::timeout(self.timeout, self.socket.send(bytes)).await {
Ok(send_result) => match send_result {
Ok(size) => size,
Err(e) => panic!("{}", format!("IO error during send: {e:?}")),

Check warning on line 78 in src/shared/bit_torrent/tracker/udp/client.rs

View check run for this annotation

Codecov / codecov/patch

src/shared/bit_torrent/tracker/udp/client.rs#L78

Added line #L78 was not covered by tests
},
Err(e) => panic!("{}", format!("Send operation timed out: {e:?}")),

Check warning on line 80 in src/shared/bit_torrent/tracker/udp/client.rs

View check run for this annotation

Codecov / codecov/patch

src/shared/bit_torrent/tracker/udp/client.rs#L80

Added line #L80 was not covered by tests
}
}

/// # Panics
Expand All @@ -68,9 +90,21 @@ impl UdpClient {
pub async fn receive(&self, bytes: &mut [u8]) -> usize {
debug!(target: "UDP client", "receiving ...");

self.socket.readable().await.unwrap();
match time::timeout(self.timeout, self.socket.readable()).await {
Ok(readable_result) => match readable_result {
Ok(()) => (),
Err(e) => panic!("{}", format!("IO error waiting for the socket to become readable: {e:?}")),

Check warning on line 96 in src/shared/bit_torrent/tracker/udp/client.rs

View check run for this annotation

Codecov / codecov/patch

src/shared/bit_torrent/tracker/udp/client.rs#L96

Added line #L96 was not covered by tests
},
Err(e) => panic!("{}", format!("Timeout waiting for the socket to become readable: {e:?}")),

Check warning on line 98 in src/shared/bit_torrent/tracker/udp/client.rs

View check run for this annotation

Codecov / codecov/patch

src/shared/bit_torrent/tracker/udp/client.rs#L98

Added line #L98 was not covered by tests
};

let size = self.socket.recv(bytes).await.unwrap();
let size = match time::timeout(self.timeout, self.socket.recv(bytes)).await {
Ok(recv_result) => match recv_result {
Ok(size) => size,
Err(e) => panic!("{}", format!("IO error during send: {e:?}")),

Check warning on line 104 in src/shared/bit_torrent/tracker/udp/client.rs

View check run for this annotation

Codecov / codecov/patch

src/shared/bit_torrent/tracker/udp/client.rs#L104

Added line #L104 was not covered by tests
},
Err(e) => panic!("{}", format!("Receive operation timed out: {e:?}")),

Check warning on line 106 in src/shared/bit_torrent/tracker/udp/client.rs

View check run for this annotation

Codecov / codecov/patch

src/shared/bit_torrent/tracker/udp/client.rs#L106

Added line #L106 was not covered by tests
};

debug!(target: "UDP client", "{size} bytes received {bytes:?}");

Expand Down

0 comments on commit 592c0dd

Please sign in to comment.