use std::net::{SocketAddr, UdpSocket};
use std::sync::mpsc::{Receiver, Sender};
use std::thread;
use std::time::Duration;
use crate::server_mocker::MockerOptions;
use crate::Instruction::{
self, ReceiveMessageWithMaxSize, SendMessage, SendMessageDependingOnLastReceivedMessage,
};
use crate::ServerMockerError::{
self, FailedToSendUdpMessage, GotSendMessageBeforeReceiveMessage, UnableToBindListener,
UnableToGetLocalAddress, UnableToReadUdpStream, UnableToSetReadTimeout,
};
#[derive(Debug, Clone)]
pub struct UdpMocker {
pub socket_addr: SocketAddr,
pub net_timeout: Duration,
pub rx_timeout: Duration,
pub max_packet_size: usize,
}
impl Default for UdpMocker {
fn default() -> Self {
Self {
socket_addr: SocketAddr::from(([127, 0, 0, 1], 0)),
net_timeout: Duration::from_millis(100),
rx_timeout: Duration::from_millis(100),
max_packet_size: 65507,
}
}
}
impl MockerOptions for UdpMocker {
fn socket_address(&self) -> SocketAddr {
self.socket_addr
}
fn net_timeout(&self) -> Duration {
self.net_timeout
}
fn run(
self,
instruction_rx: Receiver<Vec<Instruction>>,
message_tx: Sender<Vec<u8>>,
error_tx: Sender<ServerMockerError>,
) -> Result<SocketAddr, ServerMockerError> {
let connection = UdpSocket::bind(self.socket_addr)
.map_err(|e| UnableToBindListener(self.socket_addr, e))?;
let socket_addr = connection.local_addr().map_err(UnableToGetLocalAddress)?;
thread::spawn(move || {
UdpServerImpl {
options: self,
connection,
instruction_rx,
message_tx,
error_tx,
}
.run();
});
Ok(socket_addr)
}
}
struct UdpServerImpl {
options: UdpMocker,
connection: UdpSocket,
instruction_rx: Receiver<Vec<Instruction>>,
message_tx: Sender<Vec<u8>>,
error_tx: Sender<ServerMockerError>,
}
impl UdpServerImpl {
fn run(&self) {
let timeout = Some(self.options.net_timeout);
if let Err(e) = self.connection.set_read_timeout(timeout) {
self.error_tx.send(UnableToSetReadTimeout(e)).unwrap();
return;
}
let mut last_received_packed_with_addr: Option<(SocketAddr, Vec<u8>)> = None;
while let Ok(instructions) = self.instruction_rx.recv_timeout(self.options.rx_timeout) {
for instruction in instructions {
match instruction {
SendMessage(binary_message) => {
if let Err(e) = self.send_packet_to_last_client(
&binary_message,
&last_received_packed_with_addr,
) {
self.error_tx.send(e).unwrap();
}
}
SendMessageDependingOnLastReceivedMessage(sent_message_calculator) => {
let message_to_send =
sent_message_calculator(match last_received_packed_with_addr {
Some((_, ref message)) => Some(message.clone()),
None => None,
});
if let Some(message_to_send) = message_to_send {
if let Err(e) = self.send_packet_to_last_client(
&message_to_send,
&last_received_packed_with_addr,
) {
self.error_tx.send(e).unwrap();
}
}
}
Instruction::ReceiveMessage => {
let received_packet_with_addr =
match self.receive_packet(self.options.max_packet_size) {
Ok(received) => received,
Err(e) => {
self.error_tx.send(e).unwrap();
continue;
}
};
last_received_packed_with_addr = Some((
received_packet_with_addr.0,
received_packet_with_addr.1.clone(),
));
self.message_tx.send(received_packet_with_addr.1).unwrap();
}
ReceiveMessageWithMaxSize(max_message_size) => {
match self.receive_packet(max_message_size) {
Ok(received) => {
last_received_packed_with_addr =
Some((received.0, received.1.clone()));
self.message_tx.send(received.1).unwrap();
}
Err(e) => self.error_tx.send(e).unwrap(),
};
}
Instruction::StopExchange => {
return;
}
}
}
}
}
fn receive_packet(
&self,
max_packet_size: usize,
) -> Result<(SocketAddr, Vec<u8>), ServerMockerError> {
let mut whole_received_packet: Vec<u8> = vec![0; max_packet_size];
let (bytes_read, packet_sender_addr) = self
.connection
.recv_from(&mut whole_received_packet)
.map_err(UnableToReadUdpStream)?;
whole_received_packet.truncate(bytes_read);
Ok((packet_sender_addr, whole_received_packet))
}
fn send_packet_to_last_client(
&self,
message_to_send: &[u8],
last_received_packed_with_addr: &Option<(SocketAddr, Vec<u8>)>,
) -> Result<(), ServerMockerError> {
last_received_packed_with_addr
.as_ref()
.ok_or(GotSendMessageBeforeReceiveMessage)?;
self.connection
.send_to(
message_to_send,
last_received_packed_with_addr.as_ref().unwrap().0,
)
.map_err(FailedToSendUdpMessage)?;
Ok(())
}
}