Skip to content

Commit 90c7780

Browse files
committed
Merge #814: Refactor UdpClient to return errors instead of panicking
effca56 refactor: [#681] udp return errors instead of panicking (ngthhu) Pull request description: I replaced panics with error handling using `anyhow::Result<T>`, which is equivalent to `std::result::Result<T, anyhow::Error>`. Although I can run `cargo run` without errors, I'm not certain if there are any hidden bugs. ACKs for top commit: josecelano: ACK effca56 Tree-SHA512: 30653458d1f2b3155dbdddd0f5197cee6746819122db45d58c0aaa4a0b9cae0fccee76b769afb22b57749c2140d042580125ad2689fafa0b65ec6847866f659b
2 parents 92349d3 + effca56 commit 90c7780

File tree

3 files changed

+193
-109
lines changed

3 files changed

+193
-109
lines changed

src/console/clients/udp/checker.rs

+8-8
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ impl Client {
6464
let binding_address = local_bind_to.parse().context("binding local address")?;
6565

6666
debug!("Binding to: {local_bind_to}");
67-
let udp_client = UdpClient::bind(&local_bind_to).await;
67+
let udp_client = UdpClient::bind(&local_bind_to).await?;
6868

6969
let bound_to = udp_client.socket.local_addr().context("bound local address")?;
7070
debug!("Bound to: {bound_to}");
@@ -88,7 +88,7 @@ impl Client {
8888

8989
match &self.udp_tracker_client {
9090
Some(client) => {
91-
client.udp_client.connect(&tracker_socket_addr.to_string()).await;
91+
client.udp_client.connect(&tracker_socket_addr.to_string()).await?;
9292
self.remote_socket = Some(*tracker_socket_addr);
9393
Ok(())
9494
}
@@ -116,9 +116,9 @@ impl Client {
116116

117117
match &self.udp_tracker_client {
118118
Some(client) => {
119-
client.send(connect_request.into()).await;
119+
client.send(connect_request.into()).await?;
120120

121-
let response = client.receive().await;
121+
let response = client.receive().await?;
122122

123123
debug!("connection request response:\n{response:#?}");
124124

@@ -163,9 +163,9 @@ impl Client {
163163

164164
match &self.udp_tracker_client {
165165
Some(client) => {
166-
client.send(announce_request.into()).await;
166+
client.send(announce_request.into()).await?;
167167

168-
let response = client.receive().await;
168+
let response = client.receive().await?;
169169

170170
debug!("announce request response:\n{response:#?}");
171171

@@ -200,9 +200,9 @@ impl Client {
200200

201201
match &self.udp_tracker_client {
202202
Some(client) => {
203-
client.send(scrape_request.into()).await;
203+
client.send(scrape_request.into()).await?;
204204

205-
let response = client.receive().await;
205+
let response = client.receive().await?;
206206

207207
debug!("scrape request response:\n{response:#?}");
208208

+128-87
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
use core::result::Result::{Err, Ok};
12
use std::io::Cursor;
23
use std::net::SocketAddr;
34
use std::sync::Arc;
45
use std::time::Duration;
56

7+
use anyhow::{anyhow, Context, Result};
68
use aquatic_udp_protocol::{ConnectRequest, Request, Response, TransactionId};
79
use log::debug;
810
use tokio::net::UdpSocket;
@@ -25,99 +27,120 @@ pub struct UdpClient {
2527
}
2628

2729
impl UdpClient {
28-
/// # Panics
30+
/// # Errors
31+
///
32+
/// Will return error if the local address can't be bound.
2933
///
30-
/// Will panic if the local address can't be bound.
31-
pub async fn bind(local_address: &str) -> Self {
32-
let valid_socket_addr = local_address
34+
pub async fn bind(local_address: &str) -> Result<Self> {
35+
let socket_addr = local_address
3336
.parse::<SocketAddr>()
34-
.unwrap_or_else(|_| panic!("{local_address} is not a valid socket address"));
37+
.context(format!("{local_address} is not a valid socket address"))?;
3538

36-
let socket = UdpSocket::bind(valid_socket_addr).await.unwrap();
39+
let socket = UdpSocket::bind(socket_addr).await?;
3740

38-
Self {
41+
let udp_client = Self {
3942
socket: Arc::new(socket),
4043
timeout: DEFAULT_TIMEOUT,
41-
}
44+
};
45+
Ok(udp_client)
4246
}
4347

44-
/// # Panics
48+
/// # Errors
4549
///
46-
/// Will panic if can't connect to the socket.
47-
pub async fn connect(&self, remote_address: &str) {
48-
let valid_socket_addr = remote_address
50+
/// Will return error if can't connect to the socket.
51+
pub async fn connect(&self, remote_address: &str) -> Result<()> {
52+
let socket_addr = remote_address
4953
.parse::<SocketAddr>()
50-
.unwrap_or_else(|_| panic!("{remote_address} is not a valid socket address"));
54+
.context(format!("{remote_address} is not a valid socket address"))?;
5155

52-
match self.socket.connect(valid_socket_addr).await {
53-
Ok(()) => debug!("Connected successfully"),
54-
Err(e) => panic!("Failed to connect: {e:?}"),
56+
match self.socket.connect(socket_addr).await {
57+
Ok(()) => {
58+
debug!("Connected successfully");
59+
Ok(())
60+
}
61+
Err(e) => Err(anyhow!("Failed to connect: {e:?}")),
5562
}
5663
}
5764

58-
/// # Panics
65+
/// # Errors
5966
///
60-
/// Will panic if:
67+
/// Will return error if:
6168
///
6269
/// - Can't write to the socket.
6370
/// - Can't send data.
64-
pub async fn send(&self, bytes: &[u8]) -> usize {
71+
pub async fn send(&self, bytes: &[u8]) -> Result<usize> {
6572
debug!(target: "UDP client", "sending {bytes:?} ...");
6673

6774
match time::timeout(self.timeout, self.socket.writable()).await {
68-
Ok(writable_result) => match writable_result {
69-
Ok(()) => (),
70-
Err(e) => panic!("{}", format!("IO error waiting for the socket to become readable: {e:?}")),
71-
},
72-
Err(e) => panic!("{}", format!("Timeout waiting for the socket to become readable: {e:?}")),
75+
Ok(writable_result) => {
76+
match writable_result {
77+
Ok(()) => (),
78+
Err(e) => return Err(anyhow!("IO error waiting for the socket to become readable: {e:?}")),
79+
};
80+
}
81+
Err(e) => return Err(anyhow!("Timeout waiting for the socket to become readable: {e:?}")),
7382
};
7483

7584
match time::timeout(self.timeout, self.socket.send(bytes)).await {
7685
Ok(send_result) => match send_result {
77-
Ok(size) => size,
78-
Err(e) => panic!("{}", format!("IO error during send: {e:?}")),
86+
Ok(size) => Ok(size),
87+
Err(e) => Err(anyhow!("IO error during send: {e:?}")),
7988
},
80-
Err(e) => panic!("{}", format!("Send operation timed out: {e:?}")),
89+
Err(e) => Err(anyhow!("Send operation timed out: {e:?}")),
8190
}
8291
}
8392

84-
/// # Panics
93+
/// # Errors
8594
///
86-
/// Will panic if:
95+
/// Will return error if:
8796
///
8897
/// - Can't read from the socket.
8998
/// - Can't receive data.
90-
pub async fn receive(&self, bytes: &mut [u8]) -> usize {
99+
///
100+
/// # Panics
101+
///
102+
pub async fn receive(&self, bytes: &mut [u8]) -> Result<usize> {
91103
debug!(target: "UDP client", "receiving ...");
92104

93105
match time::timeout(self.timeout, self.socket.readable()).await {
94-
Ok(readable_result) => match readable_result {
95-
Ok(()) => (),
96-
Err(e) => panic!("{}", format!("IO error waiting for the socket to become readable: {e:?}")),
97-
},
98-
Err(e) => panic!("{}", format!("Timeout waiting for the socket to become readable: {e:?}")),
106+
Ok(readable_result) => {
107+
match readable_result {
108+
Ok(()) => (),
109+
Err(e) => return Err(anyhow!("IO error waiting for the socket to become readable: {e:?}")),
110+
};
111+
}
112+
Err(e) => return Err(anyhow!("Timeout waiting for the socket to become readable: {e:?}")),
99113
};
100114

101-
let size = match time::timeout(self.timeout, self.socket.recv(bytes)).await {
115+
let size_result = match time::timeout(self.timeout, self.socket.recv(bytes)).await {
102116
Ok(recv_result) => match recv_result {
103-
Ok(size) => size,
104-
Err(e) => panic!("{}", format!("IO error during send: {e:?}")),
117+
Ok(size) => Ok(size),
118+
Err(e) => Err(anyhow!("IO error during send: {e:?}")),
105119
},
106-
Err(e) => panic!("{}", format!("Receive operation timed out: {e:?}")),
120+
Err(e) => Err(anyhow!("Receive operation timed out: {e:?}")),
107121
};
108122

109-
debug!(target: "UDP client", "{size} bytes received {bytes:?}");
110-
111-
size
123+
if size_result.is_ok() {
124+
let size = size_result.as_ref().unwrap();
125+
debug!(target: "UDP client", "{size} bytes received {bytes:?}");
126+
size_result
127+
} else {
128+
size_result
129+
}
112130
}
113131
}
114132

115133
/// Creates a new `UdpClient` connected to a Udp server
116-
pub async fn new_udp_client_connected(remote_address: &str) -> UdpClient {
134+
///
135+
/// # Errors
136+
///
137+
/// Will return any errors present in the call stack
138+
///
139+
pub async fn new_udp_client_connected(remote_address: &str) -> Result<UdpClient> {
117140
let port = 0; // Let OS choose an unused port.
118-
let client = UdpClient::bind(&source_address(port)).await;
119-
client.connect(remote_address).await;
120-
client
141+
let client = UdpClient::bind(&source_address(port)).await?;
142+
client.connect(remote_address).await?;
143+
Ok(client)
121144
}
122145

123146
#[allow(clippy::module_name_repetitions)]
@@ -127,85 +150,103 @@ pub struct UdpTrackerClient {
127150
}
128151

129152
impl UdpTrackerClient {
130-
/// # Panics
153+
/// # Errors
131154
///
132-
/// Will panic if can't write request to bytes.
133-
pub async fn send(&self, request: Request) -> usize {
155+
/// Will return error if can't write request to bytes.
156+
pub async fn send(&self, request: Request) -> Result<usize> {
134157
debug!(target: "UDP tracker client", "send request {request:?}");
135158

136159
// Write request into a buffer
137160
let request_buffer = vec![0u8; MAX_PACKET_SIZE];
138161
let mut cursor = Cursor::new(request_buffer);
139162

140-
let request_data = match request.write(&mut cursor) {
163+
let request_data_result = match request.write(&mut cursor) {
141164
Ok(()) => {
142165
#[allow(clippy::cast_possible_truncation)]
143166
let position = cursor.position() as usize;
144167
let inner_request_buffer = cursor.get_ref();
145168
// Return slice which contains written request data
146-
&inner_request_buffer[..position]
169+
Ok(&inner_request_buffer[..position])
147170
}
148-
Err(e) => panic!("could not write request to bytes: {e}."),
171+
Err(e) => Err(anyhow!("could not write request to bytes: {e}.")),
149172
};
150173

174+
let request_data = request_data_result?;
175+
151176
self.udp_client.send(request_data).await
152177
}
153178

154-
/// # Panics
179+
/// # Errors
155180
///
156-
/// Will panic if can't create response from the received payload (bytes buffer).
157-
pub async fn receive(&self) -> Response {
181+
/// Will return error if can't create response from the received payload (bytes buffer).
182+
pub async fn receive(&self) -> Result<Response> {
158183
let mut response_buffer = [0u8; MAX_PACKET_SIZE];
159184

160-
let payload_size = self.udp_client.receive(&mut response_buffer).await;
185+
let payload_size = self.udp_client.receive(&mut response_buffer).await?;
161186

162187
debug!(target: "UDP tracker client", "received {payload_size} bytes. Response {response_buffer:?}");
163188

164-
Response::from_bytes(&response_buffer[..payload_size], true).unwrap()
189+
let response = Response::from_bytes(&response_buffer[..payload_size], true)?;
190+
191+
Ok(response)
165192
}
166193
}
167194

168195
/// Creates a new `UdpTrackerClient` connected to a Udp Tracker server
169-
pub async fn new_udp_tracker_client_connected(remote_address: &str) -> UdpTrackerClient {
170-
let udp_client = new_udp_client_connected(remote_address).await;
171-
UdpTrackerClient { udp_client }
196+
///
197+
/// # Errors
198+
///
199+
/// Will return any errors present in the call stack
200+
///
201+
pub async fn new_udp_tracker_client_connected(remote_address: &str) -> Result<UdpTrackerClient> {
202+
let udp_client = new_udp_client_connected(remote_address).await?;
203+
let udp_tracker_client = UdpTrackerClient { udp_client };
204+
Ok(udp_tracker_client)
172205
}
173206

174207
/// Helper Function to Check if a UDP Service is Connectable
175208
///
176-
/// # Errors
209+
/// # Panics
177210
///
178211
/// It will return an error if unable to connect to the UDP service.
179212
///
180-
/// # Panics
213+
/// # Errors
214+
///
181215
pub async fn check(binding: &SocketAddr) -> Result<String, String> {
182216
debug!("Checking Service (detail): {binding:?}.");
183217

184-
let client = new_udp_tracker_client_connected(binding.to_string().as_str()).await;
185-
186-
let connect_request = ConnectRequest {
187-
transaction_id: TransactionId(123),
188-
};
189-
190-
client.send(connect_request.into()).await;
191-
192-
let process = move |response| {
193-
if matches!(response, Response::Connect(_connect_response)) {
194-
Ok("Connected".to_string())
195-
} else {
196-
Err("Did not Connect".to_string())
197-
}
198-
};
199-
200-
let sleep = time::sleep(Duration::from_millis(2000));
201-
tokio::pin!(sleep);
202-
203-
tokio::select! {
204-
() = &mut sleep => {
205-
Err("Timed Out".to_string())
206-
}
207-
response = client.receive() => {
208-
process(response)
218+
match new_udp_tracker_client_connected(binding.to_string().as_str()).await {
219+
Ok(client) => {
220+
let connect_request = ConnectRequest {
221+
transaction_id: TransactionId(123),
222+
};
223+
224+
// client.send() return usize, but doesn't use here
225+
match client.send(connect_request.into()).await {
226+
Ok(_) => (),
227+
Err(e) => debug!("Error: {e:?}."),
228+
};
229+
230+
let process = move |response| {
231+
if matches!(response, Response::Connect(_connect_response)) {
232+
Ok("Connected".to_string())
233+
} else {
234+
Err("Did not Connect".to_string())
235+
}
236+
};
237+
238+
let sleep = time::sleep(Duration::from_millis(2000));
239+
tokio::pin!(sleep);
240+
241+
tokio::select! {
242+
() = &mut sleep => {
243+
Err("Timed Out".to_string())
244+
}
245+
response = client.receive() => {
246+
process(response.unwrap())
247+
}
248+
}
209249
}
250+
Err(e) => Err(format!("{e:?}")),
210251
}
211252
}

0 commit comments

Comments
 (0)