use std::{
io,
net::{TcpListener, TcpStream, ToSocketAddrs},
sync::{Arc, Mutex},
thread,
};
use log::{debug, error, info, warn};
use crate::{mo::Message, storage::Storage};
#[derive(Debug)]
pub struct Server<A: ToSocketAddrs + Sync, S: Storage + Sync + Send> {
addr: A,
listener: Option<TcpListener>,
storage: Arc<Mutex<S>>,
}
impl<A, S> Server<A, S>
where
A: ToSocketAddrs + Sync,
S: 'static + Storage + Sync + Send,
{
pub fn new(addr: A, storage: S) -> Server<A, S> {
Server {
addr,
listener: None,
storage: Arc::new(Mutex::new(storage)),
}
}
pub fn bind(&mut self) -> io::Result<()> {
self.listener = Some(self.create_listener()?);
Ok(())
}
pub fn serve_forever(mut self) {
let listener = match self.listener {
Some(ref listener) => listener,
None => {
self.listener = Some(self.create_listener().unwrap());
self.listener.as_ref().unwrap()
}
};
for stream in listener.incoming() {
match stream {
Ok(stream) => {
let storage = Arc::clone(&self.storage);
thread::spawn(move || handle_stream(stream, storage));
}
Err(err) => {
thread::spawn(move || handle_error(&err));
}
}
}
}
fn create_listener(&self) -> io::Result<TcpListener> {
TcpListener::bind(&self.addr)
}
}
fn handle_stream(stream: TcpStream, storage: Arc<Mutex<dyn Storage>>) {
match stream.peer_addr() {
Ok(addr) => {
debug!("Handling TcpStream from {}", addr);
}
Err(err) => {
warn!(
"Problem when extracting peer address from TcpStream, but we'll press on: {:?}",
err
);
}
}
let message = match Message::read_from(stream) {
Ok(message) => {
info!(
"Received message from IMEI {} with MOMN {} and {} byte payload",
message.imei(),
message.momsn(),
message.payload().len(),
);
message
}
Err(err) => {
error!("Error when reading message: {:?}", err);
return;
}
};
match storage
.lock()
.expect("unable to lock storage mutex")
.store(message)
{
Ok(_) => info!("Stored message"),
Err(err) => error!("Problem storing message: {:?}", err),
}
}
fn handle_error(err: &io::Error) {
error!("Error when receiving tcp communication: {:?}", err);
}
#[cfg(test)]
mod tests {
use super::*;
use crate::storage::MemoryStorage;
use std::{fs, io::Cursor, path::Path};
#[test]
fn test_store_real_message_from_stream_file() {
let test_file = "data/2-location.mo.sbd";
if !Path::new(test_file).exists() {
panic!("Test file {} does not exist.", test_file);
}
let file_bytes = fs::read(test_file).expect("Failed to read test .mo.sbd file");
let mut cur = Cursor::new(&file_bytes);
let parsed_message = Message::read_from(&mut cur).unwrap();
assert_eq!(parsed_message.imei(), "301434061799480");
assert_eq!(parsed_message.momsn(), 7);
assert_eq!(parsed_message.payload().len(), 46);
let s = std::str::from_utf8(parsed_message.payload()).expect("payload not UTF-8");
assert!(s.contains("@gadomski"));
for ie in parsed_message.information_elements() {
if let Some(Ok(mo_location)) = ie.as_mo_location() {
assert!(!mo_location.north);
assert!(mo_location.east);
assert_eq!(mo_location.cep_km, 2);
} else {
panic!("Expected MO Location IE");
}
}
let storage = Arc::new(Mutex::new(MemoryStorage::new()));
let result = storage.lock().unwrap().store(parsed_message);
assert!(result.is_ok(), "Real message should be stored successfully");
}
}