diff --git a/Cargo.toml b/Cargo.toml index d430020..5227718 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ edition = "2018" [dependencies] regex = "1" +uuid = "0.8" crossbeam = "0.7" crossbeam-channel = "0.4" crossbeam-utils = "0.7" @@ -19,8 +20,8 @@ zeroize = "1.1.0" crossterm = "0.17.7" clap = "3.0.0-beta.1" log = "0.4" -cursive = { version = "0.15.0", default-features = false, features = ["crossterm-backend"]} -openssl = { version = "0.10", features = ["vendored"] } +serde = { version = "1.0", features = ["derive"] } +url = "2.2.0" [profile.dev] diff --git a/LICENSE b/LICENSE index f288702..43fab77 100644 --- a/LICENSE +++ b/LICENSE @@ -42,7 +42,7 @@ know their rights. giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and +that there is No warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. @@ -98,14 +98,14 @@ public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. +a computer network, with No transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the +tells the user that there is No warranty for the work (except to the extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If +work under this License, and how to View a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. @@ -202,7 +202,7 @@ non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. - You may charge any price or no price for each copy that you convey, + You may charge any price or No price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. @@ -223,7 +223,7 @@ terms of section 4, provided that you also meet all of these conditions: License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no + regardless of how they are packaged. This License gives No permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. @@ -258,13 +258,13 @@ in one of these ways: (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a + Model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no + medium customarily used for software interchange, for a price No more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. + Corresponding Source from a network server at No charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This @@ -274,7 +274,7 @@ in one of these ways: d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no + Corresponding Source in the same way through the same place at No further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source @@ -287,7 +287,7 @@ in one of these ways: e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no + Source of the work are being offered to the general public at No charge under subsection 6d. A separable portion of the object code, whose source code is excluded @@ -312,7 +312,7 @@ procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because +code is in No case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or @@ -337,7 +337,7 @@ protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in -source code form), and must require no special password or key for +source code form), and must require No special password or key for unpacking, reading or copying. 7. Additional Terms. @@ -582,7 +582,7 @@ public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any +permissions. However, No additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. diff --git a/src/app/Traits.rs b/src/app/Traits.rs new file mode 100644 index 0000000..c4235cc --- /dev/null +++ b/src/app/Traits.rs @@ -0,0 +1,12 @@ +use url::Url + +pub trait TBundle { + fn main() -> Result; + + fn initWithURL(url: Url) -> Result; + fn initWithPath(path: String) -> Result; + + fn urlForResource(name: String, extention: String, subDirectory: Option) -> Result<[u8]>; + + +} \ No newline at end of file diff --git a/src/app/bundle.rs b/src/app/bundle.rs new file mode 100644 index 0000000..a4300d6 --- /dev/null +++ b/src/app/bundle.rs @@ -0,0 +1,11 @@ + +/** + * Bundle: inspired from NSBundle on macOS + */ +struct Bundle { + location: +} + +impl Bundle { + +} \ No newline at end of file diff --git a/src/app/mod.rs b/src/app/mod.rs new file mode 100644 index 0000000..2282244 --- /dev/null +++ b/src/app/mod.rs @@ -0,0 +1,2 @@ +pub mod bundle; +pub mod Traits \ No newline at end of file diff --git a/src/client_api/mod.rs b/src/client_api/mod.rs deleted file mode 100644 index 1af8627..0000000 --- a/src/client_api/mod.rs +++ /dev/null @@ -1,71 +0,0 @@ -use std::{net::TcpStream, io::{Write, Read}, io}; -use crate::{ - server::client::client_profile::Client, - commands::Commands, -}; -use std::time::Duration; -use zeroize::Zeroize; - -pub struct ClientApi { - socket: TcpStream, - addr: String, - - pub on_client_add_handle: fn(Client) -> (), - pub on_client_remove_handle: fn(String) -> (), -} - -impl ClientApi { - pub fn new(addr: &str) -> Result { - let socket = TcpStream::connect(addr)?; - - let on_add = |_client: Client| {println!("Client_api: Client added {:?}", _client)}; - let on_remove = |_uuid: String| {println!("Client_api: Client removed {}", _uuid)}; - let a = Self { - socket, - addr: addr.to_string(), - on_client_add_handle: on_add, - on_client_remove_handle: on_remove, - }; - Ok(a) - } - - pub fn set_on_client_add(&mut self, func: fn(Client) -> ()) { - self.on_client_add_handle = func; - } - - pub fn set_on_client_removed(&mut self, func: fn(String) -> ()) { - self.on_client_remove_handle = func; - } - - pub fn get_info(host: &str) -> Result { - let mut buffer: [u8; 1024] = [0; 1024]; - let addr = host.parse().unwrap(); - let mut stream = TcpStream::connect_timeout(&addr, Duration::from_millis(1000))?; - - let _ = stream.read(&mut buffer)?; - println!("data recieved: {:?}", &buffer[0..20]); - match Commands::from(&mut buffer) { - Commands::Request(None) => { - println!("zeroing"); - buffer.zeroize(); - println!("writing"); - let sending_command = Commands::Info(None).to_string(); - println!("sending string: {:?} as_bytes: {:?}", &sending_command, &sending_command.as_bytes()); - stream.write_all(sending_command.as_bytes())?; - stream.flush()?; - println!("reading"); - let bytes = stream.read(&mut buffer)?; - println!("new buffer size: {:?} contents: {:?}", bytes, &buffer[0..20]); - println!("commanding"); - Ok(Commands::from(String::from(String::from_utf8_lossy(&buffer)))) - }, - _ => { - Err(io::Error::new(io::ErrorKind::InvalidData, "the data was not expected")) - } - } - } - - pub fn get_clients(&self) { - - } -} diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 60d4605..a4a50a2 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -1,13 +1,12 @@ -use std::string::ToString; -use std::collections::HashMap; -use std::str::FromStr; - use std::borrow::Borrow; -use regex::Regex; +use std::collections::HashMap; use std::ops::Index; +use std::str::FromStr; +use std::string::ToString; + use log::info; +use regex::Regex; use zeroize::Zeroize; -//use dashmap::DashMap; #[derive(Clone, Debug)] pub enum Commands { @@ -52,6 +51,7 @@ pub enum Commands { Error(Option>), } +#[allow(dead_code)] #[derive(Debug)] pub enum CommandParseError { UnknownCommand, diff --git a/src/lib.rs b/src/lib.rs deleted file mode 100644 index 4e15e7d..0000000 --- a/src/lib.rs +++ /dev/null @@ -1,104 +0,0 @@ -use std::thread; -use crossbeam::{unbounded , Sender, Receiver}; -use std::sync::Arc; -use std::sync::Mutex; - -enum Message { - NewJob(Job), - Terminate, -} - -#[derive(Debug)] -pub struct ThreadPool{ - workers: Vec, - sender: Sender, -} - -type Job = Box; - -impl ThreadPool{ - /// Create a new ThreadPool. - /// - /// The size is the number of threads in the pool. - /// - /// # Panics - /// - /// The `new` function will panic if the size is zero. - pub fn new(size: usize) -> ThreadPool { - assert!(size > 0); - - let (sender, receiver) = unbounded(); - - let receiver = Arc::new(Mutex::new(receiver)); - - let mut workers = Vec::with_capacity(size); - - for id in 0..size { - // create some threads and store them in the vector - workers.push(Worker::new(id, Arc::clone(&receiver))); - } - - ThreadPool { - workers, - sender, - } - } - - pub fn execute(&self, f: F) where F: FnOnce() + Send + 'static { - let job = Box::new(f); - - self.sender.send(Message::NewJob(job)).unwrap(); - } -} - -#[derive(Debug)] -struct Worker { - id: usize, - thread: Option>, -} - -impl Worker { - fn new(id: usize, receiver: Arc>>) -> Worker { - let thread = thread::spawn(move || { - loop{ - let message = receiver.lock().unwrap().recv().unwrap(); - - match message { - Message::NewJob(job) => { - println!("Worker {} got a job; executing.", id); - job(); - }, - Message::Terminate => { - println!("Worker {} was told to terminate.", id); - break; - }, - } - } - }); - - Worker { - id, - thread: Some(thread), - } - } -} - -impl Drop for ThreadPool { - fn drop(&mut self) { - println!("Sending terminate message to all workers."); - - for _ in &mut self.workers { - self.sender.send(Message::Terminate).unwrap(); - } - - println!("Shutting down all workers."); - - for worker in &mut self.workers { - println!("Shutting down worker {}", worker.id); - - if let Some(thread) = worker.thread.take() { - thread.join().unwrap(); - } - } - } -} diff --git a/src/lib/mod.rs b/src/lib/mod.rs new file mode 100644 index 0000000..3dbcdd0 --- /dev/null +++ b/src/lib/mod.rs @@ -0,0 +1,108 @@ +// pub mod commands; +pub mod prelude; +pub mod server; + +use std::sync::Arc; +use std::sync::Mutex; +use std::thread; + +use crossbeam::{unbounded, Receiver, Sender}; + +enum Message { + NewJob(Job), + Terminate, +} + +#[derive(Debug)] +pub struct ThreadPool { + workers: Vec, + sender: Sender, +} + +type Job = Box; + +#[allow(dead_code)] +impl ThreadPool { + /// Create a new ThreadPool. + /// + /// The size is the number of threads in the pool. + /// + /// # Panics + /// + /// The `new` function will panic if the size is zero. + pub fn new(size: usize) -> ThreadPool { + assert!(size > 0); + + let (sender, receiver) = unbounded(); + + let receiver = Arc::new(Mutex::new(receiver)); + + let mut workers = Vec::with_capacity(size); + + for id in 0..size { + // create some threads and store them in the vector + workers.push(Worker::new(id, Arc::clone(&receiver))); + } + + ThreadPool { workers, sender } + } + + pub fn execute(&self, f: F) + where + F: FnOnce() + Send + 'static, + { + let job = Box::new(f); + + self.sender.send(Message::NewJob(job)).unwrap(); + } +} + +#[derive(Debug)] +struct Worker { + id: usize, + thread: Option>, +} + +impl Worker { + fn new(id: usize, receiver: Arc>>) -> Worker { + let thread = thread::spawn(move || loop { + let message = receiver.lock().unwrap().recv().unwrap(); + + match message { + Message::NewJob(job) => { + println!("Worker {} got a job; executing.", id); + job(); + } + Message::Terminate => { + println!("Worker {} was told to terminate.", id); + break; + } + } + }); + + Worker { + id, + thread: Some(thread), + } + } +} + +impl Drop for ThreadPool { + fn drop(&mut self) { + println!("Sending terminate message to all workers."); + + for _ in &mut self.workers { + self.sender.send(Message::Terminate).unwrap(); + } + + println!("Shutting down all workers."); + + for worker in &mut self.workers { + println!("Shutting down worker {}", worker.id); + + if let Some(thread) = worker.thread.take() { + thread.join().unwrap(); + } + } + } +} diff --git a/src/lib/prelude.rs b/src/lib/prelude.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/server/client/client_profile.rs b/src/lib/server/client_management/client/client_profile.rs similarity index 97% rename from src/server/client/client_profile.rs rename to src/lib/server/client_management/client/client_profile.rs index e71db70..1d5ce47 100644 --- a/src/server/client/client_profile.rs +++ b/src/lib/server/client_management/client/client_profile.rs @@ -1,32 +1,29 @@ extern crate regex; use std::{ + io, + io::Error, + io::prelude::*, + net::{Shutdown, TcpStream}, sync::Arc, sync::Mutex, - net::{Shutdown, TcpStream}, - io::prelude::*, - io::Error, - //collections::HashMap, - time::{Instant, Duration}, - io, + time::{Duration, Instant}, }; -use crossbeam::{ - Sender, +use crossbeam_channel::{ Receiver, + Sender, TryRecvError, unbounded }; - -use openssl::rsa::Rsa; use log::info; use crate::{ + commands::Commands, server::{ //server_profile::Server, server_profile::ServerMessages, - }, - commands::Commands + } }; @@ -129,7 +126,7 @@ impl Client { } }, Err(_) => { - // no data was read + // No data was read }, } @@ -172,7 +169,7 @@ impl Client { } }, - /*no data available yet*/ + /*No data available yet*/ Err(TryRecvError::Empty) => {}, _ => {}, } diff --git a/src/lib/server/client_management/client/client_v3.rs b/src/lib/server/client_management/client/client_v3.rs new file mode 100644 index 0000000..4a7ac26 --- /dev/null +++ b/src/lib/server/client_management/client/client_v3.rs @@ -0,0 +1,223 @@ +extern crate regex; + +use std::{ + io, + io::Error, + io::prelude::*, + net::{Shutdown, TcpStream}, + sync::Mutex, + time::{Duration, Instant}, +}; + +use crossbeam_channel::{ + Receiver, + Sender, + TryRecvError, + unbounded +}; +use log::info; + +use crate::{ + commands::Commands, + server::server_v3::ServerMessages, +}; + +#[derive(Debug)] +pub struct Client { + + parent: Option<&ClientManager> + + uuid: String, + username: String, + address: String, + + last_heartbeat: Option, + + stream: Option>, + + sender: Sender, + receiver: Receiver, + + server_sender: Sender, +} +/// # client Struct +impl Client { + + #[allow(dead_code)] + pub fn new(stream: TcpStream, server_sender: Sender, uuid: &str, username: &str, address: &str) -> Self { + let (sender, receiver): (Sender, Receiver) = unbounded(); + stream.set_read_timeout(Some(Duration::from_secs(1))).unwrap(); + + Client { + stream: Some(Mutex::new(stream)), + uuid: uuid.to_string(), + username: username.to_string(), + address: address.to_string(), + + sender, + receiver, + + server_sender, + + last_heartbeat: Some(Instant::now()), + } + } + + #[allow(dead_code)] + pub fn get_sender(&self) -> &Sender { + &self.sender + } + + #[allow(dead_code)] + pub fn get_uuid(&self) -> String { + self.uuid.clone() + } + + #[allow(dead_code)] + pub fn get_username(&self) -> String { + self.username.clone() + } + + #[allow(dead_code)] + pub fn get_address(&self) -> String { + self.address.clone() + } + + // TODO: - add heartbeat timer. + #[allow(dead_code)] + pub fn handle_connection(&mut self) { + let mut buffer = [0; 1024]; + + // TODO: - Check heartbeat + { + //info!("heartbeat") + } + + info!("{}: handling connection", self.uuid); + match self.read_data(&mut buffer) { + + + Ok(Commands::Disconnect(None)) => { + self.server_sender.send(ServerMessages::Disconnect(self.uuid.clone())).expect("sending message to server failed"); + self.stream.lock().unwrap().shutdown(Shutdown::Both).expect("shutdown call failed"); + }, + + Ok(Commands::HeartBeat(None)) => { + self.last_heartbeat = Instant::now(); + self.send_data(Commands::Success(None).to_string().as_str()); + }, + + Ok(Commands::ClientUpdate(None)) => { + self.send_data(Commands::Success(None).to_string().as_str()); + let _ = self.server_sender.send(ServerMessages::RequestUpdate(self.stream.clone())); + }, + + Ok(Commands::ClientInfo(Some(params))) => { + let uuid = params.get("uuid").unwrap(); + let _ = self.server_sender.send(ServerMessages::RequestInfo(uuid.clone(), self.stream.clone())); + }, + + Ok(Commands::Error(None)) => { + self.send_data(Commands::Error(None).to_string().as_str()); + }, + + _ => { + self.send_data(Commands::Error(None).to_string().as_str()); + }, + + + } + + println!("buffer"); + // test to see if there is anything for the client to receive from its channel + match self.receiver.try_recv() { + /*command is on the channel*/ + Ok(Commands::ClientRemove(Some(params))) => { + let mut retry: u8 = 3; + 'retry_loop1: loop { + if retry < 1 { + self.send_data(Commands::Error(None).to_string().as_str()); + break 'retry_loop1 + } else { + self.send_data(Commands::ClientRemove(Some(params.clone())).to_string().as_str()); + + if self.read_data(&mut buffer).unwrap_or(Commands::Error(None)) == Commands::Success(None) { + break 'retry_loop1; + } else { + retry -= 1; + } + } + } + }, + Ok(Commands::Client(Some(params))) => { + let mut retry: u8 = 3; + 'retry_loop2: loop { + if retry < 1 { + self.send_data(Commands::Error(None).to_string().as_str()); + break 'retry_loop2; + } else { + self.send_data(Commands::Client(Some(params.clone())).to_string().as_str()); + + if self.read_data(&mut buffer).unwrap_or(Commands::Error(None)) == Commands::Success(None) { + break 'retry_loop2; + } else { + retry -= 1; + } + } + } + + }, + /*No data available yet*/ + Err(TryRecvError::Empty) => {}, + _ => {}, + } + println!("---Client Thread Exit---"); + } + + // move into a drop perhaps + #[allow(dead_code)] + pub fn disconnect(&mut self){ + self.stream.lock().unwrap().shutdown(Shutdown::Both).expect("shutdown call failed"); + } + + #[allow(dead_code)] + pub fn send_data(&self, data: &str) { + println!("Transmitting data: {}", data); + + let error_result = self.stream.lock().unwrap().write_all(data.to_string().as_bytes()); + if let Some(error) = error_result.err(){ + match error.kind() { + // handle disconnections + io::ErrorKind::NotConnected => { + let _ = self.server_sender.send(ServerMessages::Disconnect(self.uuid.clone())); + }, + _ => { }, + } + } + } + + #[allow(dead_code)] + fn read_data(&mut self, buffer: &mut [u8; 1024]) -> Result { + let _ = self.stream.lock().unwrap().read(buffer)?; + let command = Commands::from(buffer); + + Ok(command) + } + +} + +impl ToString for Client { + fn to_string(&self) -> std::string::String { todo!() } +} + +impl Drop for Client { + fn drop(&mut self) { + let _ = self.stream.lock().unwrap().write_all(Commands::Disconnect(None).to_string().as_bytes()); + let _ = self.stream.lock().unwrap().shutdown(Shutdown::Both); + } +} + +#[cfg(test)] +mod test { + +} \ No newline at end of file diff --git a/src/lib/server/client_management/client/mod.rs b/src/lib/server/client_management/client/mod.rs new file mode 100644 index 0000000..68c21ef --- /dev/null +++ b/src/lib/server/client_management/client/mod.rs @@ -0,0 +1,54 @@ +// pub mod client_profile; +// pub mod client_v3; +pub mod traits; + +use serde::{Serialize, Deserialize}; +use std::net::TcpStream; +use std::sync::Weak; +use std::sync::Arc; +use uuid::Uuid; + +use super::traits::TClientManager; +use super::ClientManager; +use traits::TClient; + + +pub enum ClientMessage { + a, + b, +} + +/// # Client +/// This struct represents a connected user. +/// +/// ## Attrubutes +/// - uuid: The id of the connected user. +/// - username: The username of the connected user. +/// - address: The the address of the connected client. +/// +/// - stream: The socket for the connected client. +/// - owner: An optional reference to the owning object. +#[derive(Serialize, Deserialize)] +pub struct Client { + uuid: String, + username: String, + address: String, + + #[serde(skip)] + stream: Option, + + #[serde(skip)] + owner: Option> +} + +impl TClient for Client { + fn new(uuid: Uuid, name: String, addr: String) -> Arc { todo!() } + + fn send(&self, bytes: Vec) -> Result<(), &str> { todo!() } + fn recv(&self) -> Option> { todo!() } + + fn send_msg(&self, msg: ClientMessage) -> Result<(), &str> { todo!() } + fn recv_msg(&self) -> Option { todo!() } + + fn tick(&self) { } +} \ No newline at end of file diff --git a/src/lib/server/client_management/client/traits.rs b/src/lib/server/client_management/client/traits.rs new file mode 100644 index 0000000..9129eb6 --- /dev/null +++ b/src/lib/server/client_management/client/traits.rs @@ -0,0 +1,25 @@ +use std::sync::Arc; + +use uuid::Uuid; + +/// # TClient +/// This trait represents the methods that a client must implement +/// in order to be used with a client manager +/// +/// # Methods +/// - new: creates a new client from an id, username and a address. +/// - send: send a message to the client. +/// - recv: if there is a message in the queue, returns the message +/// - send_msg: sends a event message to the client +/// - recv_msg: used by the client to receive and process event messages +pub trait TClient { + fn new(uuid: Uuid, name: String, addr: String) -> Arc; + + fn send(&self, bytes: Vec) -> Result<(), &str>; + fn recv(&self) -> Option>; + + fn send_msg(&self, msg: TClientMessage) -> Result<(), &str>; + fn recv_msg(&self) -> Option; + + fn tick(&self); +} \ No newline at end of file diff --git a/src/lib/server/client_management/mod.rs b/src/lib/server/client_management/mod.rs new file mode 100644 index 0000000..5a81880 --- /dev/null +++ b/src/lib/server/client_management/mod.rs @@ -0,0 +1,83 @@ +mod traits; +pub mod client; + +use std::sync::Weak; +use std::sync::Arc; +use std::sync::Mutex; + +use crossbeam_channel::{Sender, Receiver, unbounded}; + +use uuid::Uuid; + +use self::client::Client; +use self::client::ClientMessage; +// use client::client_v3::Client; +use self::traits::TClientManager; + +enum ClientManagerMessages { + +} + +/// # ClientManager +/// This struct manages all connected users +pub struct ClientManager { + clients: Vec>, + + weak_self: Mutex>>, + + sender: Sender, + receiver: Receiver, +} + +impl ClientManager { + pub fn new() -> Arc { + let channels = unbounded(); + + + let mut manager_ref: Arc = Arc::new(ClientManager { + clients: Vec::default(), + + weak_self: Mutex::default(), + + sender: channels.0, + receiver: channels.1, + }); + + manager_ref.set_ref(manager_ref.clone()); + + manager_ref + } + + pub fn get_ref(&self) -> Arc{ + let new_ref: Weak = self.weak_self.lock().unwrap().clone().unwrap(); + new_ref.upgrade().unwrap() + } + + fn set_ref(&self, reference: Arc) { + let mut lock = self.weak_self.lock().unwrap(); + *lock = Some(Arc::downgrade(&reference)); + } +} + +impl TClientManager for ClientManager { + fn addClient(&self, Client: std::sync::Arc) { todo!() } + + fn removeClient(&self, uuid: Uuid) { todo!() } + + fn messageClient(&self, id: Uuid, msg: ClientMessage) { todo!() } + fn tick(&self) { todo!() } +} + + +#[cfg(test)] +mod test { + + #[test] + fn test_add_client() { todo!() } + + #[test] + fn test_remove_client() { todo!() } + + #[test] + fn test_remove_all_clients() { todo!() } +} \ No newline at end of file diff --git a/src/lib/server/client_management/traits.rs b/src/lib/server/client_management/traits.rs new file mode 100644 index 0000000..e1a733e --- /dev/null +++ b/src/lib/server/client_management/traits.rs @@ -0,0 +1,15 @@ +use std::sync::Arc; + +use uuid::Uuid; + +use super::client::traits; + +/** + * @michael-bailey + */ +pub trait TClientManager { + fn addClient(&self, client: Arc); + fn removeClient(&self, id: Uuid); + fn messageClient(&self, id: Uuid, msg: TClientMessage); + fn tick(&self, ); +} \ No newline at end of file diff --git a/src/lib/server/config.rs b/src/lib/server/config.rs new file mode 100644 index 0000000..cb4e403 --- /dev/null +++ b/src/lib/server/config.rs @@ -0,0 +1,9 @@ + +pub struct ServerConfig { + pub name: String, + pub address: String, + pub owner: String, + + pub host: String, + pub port: u16, +} \ No newline at end of file diff --git a/src/lib/server/mod.rs b/src/lib/server/mod.rs new file mode 100644 index 0000000..2d1cca4 --- /dev/null +++ b/src/lib/server/mod.rs @@ -0,0 +1,5 @@ +pub mod client_management; +// pub mod server; +// pub mod server_v3; + +pub struct Server {} diff --git a/src/lib/server/server.rs b/src/lib/server/server.rs new file mode 100644 index 0000000..9d8009d --- /dev/null +++ b/src/lib/server/server.rs @@ -0,0 +1,633 @@ +// extern crate regex; +// extern crate rayon; + +// use super::client_management::client::client_profile::Client; + +// use crate::commands::Commands; +// use std::{ +// sync::{Arc, Mutex}, +// net::{TcpStream, TcpListener}, +// collections::HashMap, +// io::prelude::*, +// time::Duration, +// io::Error, +// thread, +// io +// }; + +// use log::info; + +// use crossbeam_channel::{Sender, Receiver, unbounded}; + +// #[deprecated( +// since = "0.1", +// note = "Please use server v3" +// )] +// #[derive(Debug)] +// pub enum ServerMessages { +// RequestUpdate(Arc>), +// RequestInfo(String, Arc>), +// Disconnect(String), +// Shutdown, +// } + +// // MARK: - server struct +// #[deprecated( +// since = "0.1", +// note = "Please use server v3" +// )] +// pub struct Server { +// name: String, +// host: String, +// port: String, +// author: Option, + +// //connected_clients: Arc>>, + + + +// sender: Sender, +// receiver: Receiver, + +// pub running: bool, + +// client_list_changed_handle: Box, +// } + +// // MARK: - server implemetation +// #[deprecated( +// since = "0.1", +// note = "Please use server v3" +// )] +// impl Server { +// pub fn new(name: &str, host: &str, port: &str) -> Self { +// let (sender, receiver) = unbounded(); + +// Self { +// name: name.to_string(), +// host: host.to_string(), +// port: port.to_string() +// author: author.to_string(), +// //connected_clients: Arc::new(Mutex::new(HashMap::new())), + +// sender, +// receiver, + +// running: false, + +// client_list_changed_handle: Box::new(|_s| println!("help")) +// } +// } + +// #[allow(dead_code)] +// pub fn get_name(&self) -> String { +// self.name.to_string() +// } + +// pub fn set_host() { + +// } + +// pub fn set_port() { + +// } + +// #[allow(dead_code)] +// pub fn get_author(&self) -> String { +// self.author.to_string() +// } + +// pub fn set_client_update_handle(function: Box) { + +// } + +// pub fn start(&mut self) -> Result<(), io::Error> { +// println!("server: starting server..."); + +// self.running = true; + + + +// // MARK: - creating clones of the server property references +// let name = self.name.clone(); +// #[allow(dead_code)] +// let address = self.address.clone(); +// let author = self.author.clone(); +// let connected_clients = self.connected_clients.clone(); +// let sender = self.sender.clone(); +// let receiver = self.receiver.clone(); + +// // set up listener and buffer +// let mut buffer = [0; 1024]; +// let listener = TcpListener::bind(self.get_address())?; +// listener.set_nonblocking(true)?; + +// println!("server: spawning threads"); +// let _ = thread::Builder::new().name("Server Thread".to_string()).spawn(move || { + +// 'outer: loop { +// std::thread::sleep(Duration::from_millis(100)); + +// // get messages from the servers channel. +// println!("server: getting messages"); +// for i in receiver.try_iter() { +// match i { +// ServerMessages::Shutdown => { +// // TODO: implement disconnecting all clients and shutting down the server. +// println!("server: shutting down..."); +// break 'outer; +// }, +// ServerMessages::RequestUpdate(stream_arc) => { +// for (_k, v) in connected_clients.lock().unwrap().iter() { +// let mut stream = stream_arc.lock().unwrap(); +// let _ = Server::transmit_data(&mut stream, v.to_string().as_str()); + +// if Server::read_data(&mut stream, &mut buffer).unwrap_or(Commands::Error(None)) == Commands::Success(None) { +// println!("Success Confirmed"); +// } else { +// println!("no success read"); +// let error = Commands::Error(None); +// let _ = Server::transmit_data(&mut stream, error.to_string().as_str()); +// } +// } +// }, +// ServerMessages::RequestInfo(uuid, stream_arc) => { +// let mut stream = stream_arc.lock().unwrap(); + +// if let Some(client) = connected_clients.lock().unwrap().get(&uuid) { +// let params: HashMap = [(String::from("uuid"), client.get_uuid()), (String::from("name"), client.get_username()), (String::from("host"), client.get_address())].iter().cloned().collect(); +// let command = Commands::Success(Some(params)); +// let _ = Server::transmit_data(&mut stream, command.to_string().as_str()); +// } else { +// let command = Commands::Success(None); +// let _ = Server::transmit_data(&mut stream, command.to_string().as_str()); +// } +// }, +// ServerMessages::Disconnect(uuid) => { +// let mut clients = connected_clients.lock().unwrap(); +// clients.remove(&uuid.to_string()); +// let params: HashMap = [(String::from("uuid"), uuid)].iter().cloned().collect(); +// let command = Commands::ClientRemove(Some(params)); +// let _ = connected_clients.lock().unwrap().iter().map(move |(_k, v)| {v.get_sender().send(command.clone())}); +// }, +// } +// } + +// println!("server: checking for new connections"); +// if let Ok((mut stream, _addr)) = listener.accept() { +// stream.set_read_timeout(Some(Duration::from_millis(1000))).unwrap(); +// let _ = stream.set_nonblocking(false); + +// let request = Commands::Request(None); +// let _ = Server::transmit_data(&mut stream, &request.to_string().as_str()); + +// match Server::read_data(&mut stream, &mut buffer) { +// Ok(command) => { +// println!("Server: new connection sent - {:?}", command); +// match command { +// Commands::Connect(Some(data)) => { +// let uuid = data.get("uuid").unwrap(); +// let username = data.get("name").unwrap(); +// let address = data.get("host").unwrap(); + +// println!("{}", format!("Server: new Client connection: _addr = {}", address )); + +// let client = Client::new(stream, sender.clone(), &uuid, &username, &address); + +// connected_clients.lock().unwrap().insert(uuid.to_string(), client); + +// let params: HashMap = [(String::from("name"), username.clone()), (String::from("host"), address.clone()), (String::from("uuid"), uuid.clone())].iter().cloned().collect(); +// let new_client = Commands::Client(Some(params)); + +// let _ = connected_clients.lock().unwrap().iter().map(|(_k, v)| v.sender.send(new_client.clone())); +// }, +// // TODO: - correct connection reset error when getting info. +// Commands::Info(None) => { +// println!("Server: info requested"); +// let params: HashMap = [(String::from("name"), name.to_string().clone()), (String::from("owner"), author.to_string().clone())].iter().cloned().collect(); +// let command = Commands::Info(Some(params)); + +// let _ = Server::transmit_data(&mut stream, command.to_string().as_str()); +// }, +// _ => { +// println!("Server: Invalid command sent"); +// let _ = Server::transmit_data(&mut stream, Commands::Error(None).to_string().as_str()); +// }, +// } +// }, +// Err(_) => println!("ERROR: stream closed"), +// } +// } +// // TODO: end - + +// // handle each client for messages +// println!("server: handing control to clients"); +// for (_k, client) in connected_clients.lock().unwrap().iter_mut() { +// client.handle_connection(); +// } +// } +// info!("server: stopped"); +// }); +// info!("server: started"); +// Ok(()) +// } + +// pub fn stop(&mut self) { +// info!("server: sending stop message"); +// let _ = self.sender.send(ServerMessages::Shutdown); +// self.running = false; +// } + +// fn transmit_data(stream: &mut TcpStream, data: &str) -> Result<(), Error>{ +// println!("Transmitting..."); +// println!("data: {}", data); + +// /* +// * This will throw an error and crash any thread, including the main thread, if +// * the connection is lost before transmitting. Maybe change to handle any exceptions +// * that may occur. +// */ +// let _ = stream.write(data.to_string().as_bytes())?; +// stream.flush()?; +// Ok(()) +// } + +// fn read_data(stream: &mut TcpStream, buffer: &mut [u8; 1024]) -> Result { +// let _ = stream.read(buffer)?; +// let command = Commands::from(buffer); + +// Ok(command) +// } +// } + +// impl ToString for Server { +// fn to_string(&self) -> std::string::String { todo!() } +// } + +// impl Drop for Server { +// fn drop(&mut self) { +// println!("server dropped"); +// let _ = self.sender.send(ServerMessages::Shutdown); +// } +// } + + +// /* The new version of the server no long works with these unit +// * tests. +// * They will be fixed soon! +// * TODO: fix unit tests +// */ + + + +// /*#[cfg(test)] +// #[deprecated( +// since = "0.1", +// note = "Please use server v3" +// )] +// mod tests{ +// use super::*; +// use std::{thread, time}; +// use std::sync::Once; +// use std::time::Duration; + +// lazy_static!{ +// static ref SERVER_NAME: &'static str = "test"; +// static ref SERVER_ADDRESS: &'static str = "0.0.0.0:6000"; +// static ref SERVER_AUTHOR: &'static str = "test"; +// static ref SERVER: Server<'static> = Server::new(&SERVER_NAME, &SERVER_ADDRESS, &SERVER_AUTHOR); +// } + +// static START: Once = Once::new(); + +// /* +// * These tests must be executed individually to ensure that no errors +// * occur, this is due to the fact that the server is created everytime. +// * Setup a system for the server to close after every test. +// */ +// fn setup_server(){ +// unsafe{ +// START.call_once(|| { +// thread::spawn(|| { +// SERVER.start(); +// }); +// }); + +// let millis = time::Duration::from_millis(1000); +// thread::sleep(millis); +// } +// } + +// fn establish_client_connection(uuid: &str) -> TcpStream { +// let mut buffer = [0; 1024]; + +// let mut stream = TcpStream::connect("0.0.0.0:6000").unwrap(); + +// let mut command = read_data(&stream, &mut buffer); + +// assert_eq!(command, Commands::Request(None)); + +// let msg: String = format!("!connect: uuid:{uuid} name:\"{name}\" host:\"{host}\"", uuid=uuid, name="alice", host="127.0.0.1"); +// transmit_data(&stream, msg.as_str()); + +// command = read_data(&stream, &mut buffer); + +// assert_eq!(command, Commands::Success(None)); + +// stream +// } + +// fn transmit_data(mut stream: &TcpStream, data: &str){ +// stream.write(data.to_string().as_bytes()).unwrap(); +// stream.flush().unwrap(); +// } + +// fn read_data(mut stream: &TcpStream, buffer: &mut [u8; 1024]) -> Commands { +// match stream.read(buffer) { +// Ok(_) => Commands::from(buffer), +// Err(_) => Commands::Error(None), +// } +// } + +// fn force_disconnect(mut stream: &TcpStream){ +// let msg = "!disconnect:"; +// transmit_data(&stream, msg); +// } + +// #[test] +// fn test_server_connect(){ +// let mut buffer = [0; 1024]; + +// setup_server(); + +// let mut stream = TcpStream::connect("0.0.0.0:6000").unwrap(); + +// stream.read(&mut buffer).unwrap(); +// let mut command = Commands::from(&mut buffer); + +// assert_eq!(command, Commands::Request(None)); + +// let msg = b"!connect: uuid:123456-1234-1234-123456 name:\"alice\" host:\"127.0.0.1\""; +// stream.write(msg).unwrap(); + +// stream.read(&mut buffer).unwrap(); +// command = Commands::from(&mut buffer); + +// assert_eq!(command, Commands::Success(None)); + +// let msg = b"!disconnect:"; +// stream.write(msg).unwrap(); + +// let dur = time::Duration::from_millis(500); +// thread::sleep(dur); +// } + +// #[test] +// fn test_server_info(){ +// let mut buffer = [0; 1024]; + +// setup_server(); + +// let mut stream = TcpStream::connect("0.0.0.0:6000").unwrap(); + +// let command = read_data(&stream, &mut buffer); + +// assert_eq!(command, Commands::Request(None)); + +// let msg = "!info:"; +// transmit_data(&stream, msg); + +// let command = read_data(&stream, &mut buffer); + +// let params: HashMap = [(String::from("name"), String::from("test")), (String::from("owner"), String::from("test"))].iter().cloned().collect(); +// assert_eq!(command, Commands::Success(Some(params))); +// } + +// #[test] +// fn test_client_info(){ +// let mut buffer = [0; 1024]; + +// setup_server(); + +// let mut stream = establish_client_connection("1234-5542-2124-155"); + +// let msg = "!info:"; +// transmit_data(&stream, msg); + +// let command = read_data(&stream, &mut buffer); + +// let params: HashMap = [(String::from("name"), String::from("test")), (String::from("owner"), String::from("test"))].iter().cloned().collect(); +// assert_eq!(command, Commands::Success(Some(params))); + +// let msg = "!disconnect:"; +// transmit_data(&stream, msg); + +// let dur = time::Duration::from_millis(500); +// thread::sleep(dur); +// } + +// #[test] +// fn test_clientUpdate_solo(){ +// let mut buffer = [0; 1024]; + +// setup_server(); + +// let mut stream = establish_client_connection("1222-555-6-7"); + +// let msg = "!clientUpdate:"; +// transmit_data(&stream, msg); + +// let command = read_data(&stream, &mut buffer); + +// assert_eq!(command, Commands::Success(None)); + +// let msg = "!disconnect:"; +// transmit_data(&stream, msg); + +// let dur = time::Duration::from_millis(500); +// thread::sleep(dur); +// } + + +// #[test] +// fn test_clientUpdate_multi(){ +// let mut buffer = [0; 1024]; + +// setup_server(); + +// let mut stream_one = establish_client_connection("0001-776-6-5"); +// let mut stream_two = establish_client_connection("0010-776-6-5"); +// let mut stream_three = establish_client_connection("0011-776-6-5"); +// let mut stream_four = establish_client_connection("0100-776-6-5"); + +// let client_uuids: [String; 3] = [String::from("0010-776-6-5"), String::from("0011-776-6-5"), String::from("0100-776-6-5")]; +// let mut user_1 = true; +// let mut user_2 = true; +// let mut user_3 = true; + +// for uuid in client_uuids.iter() { +// let command = read_data(&stream_one, &mut buffer); + +// if *uuid == String::from("0010-776-6-5") && user_1 { +// let params: HashMap = [(String::from("uuid"), String::from("0010-776-6-5")), (String::from("name"), String::from("\"alice\"")), (String::from("host"), String::from("\"127.0.0.1\""))].iter().cloned().collect(); +// assert_eq!(command, Commands::Client(Some(params))); + +// user_1 = false; +// } else if *uuid == String::from("0011-776-6-5") && user_2 { +// let params: HashMap = [(String::from("uuid"), String::from("0011-776-6-5")), (String::from("name"), String::from("\"alice\"")), (String::from("host"), String::from("\"127.0.0.1\""))].iter().cloned().collect(); +// assert_eq!(command, Commands::Client(Some(params))); + +// user_2 = false; +// } else if *uuid == String::from("0100-776-6-5") && user_3 { +// let params: HashMap = [(String::from("uuid"), String::from("0100-776-6-5")), (String::from("name"), String::from("\"alice\"")), (String::from("host"), String::from("\"127.0.0.1\""))].iter().cloned().collect(); +// assert_eq!(command, Commands::Client(Some(params))); + +// user_3 = false; +// } else { +// assert!(false); +// } +// let msg = "!success:"; +// transmit_data(&stream_one, msg); +// } + +// stream_one.set_read_timeout(Some(Duration::from_millis(3000))).unwrap(); +// let mut unsuccessful = true; +// while unsuccessful { +// let msg = "!clientUpdate:"; +// transmit_data(&stream_one, msg); + +// let command = read_data(&stream_one, &mut buffer); +// match command.clone() { +// Commands::Error(None) => println!("resending..."), +// _ => { +// assert_eq!(command, Commands::Success(None)); +// unsuccessful = false; +// }, +// } +// } +// stream_one.set_read_timeout(None).unwrap(); + +// for x in 0..3 { +// let command = read_data(&stream_one, &mut buffer); + +// let command_clone = command.clone(); +// match command{ +// Commands::Client(Some(params)) => { +// let uuid = params.get("uuid").unwrap(); + +// if *uuid == String::from("0010-776-6-5") { +// let params: HashMap = [(String::from("uuid"), String::from("0010-776-6-5")), (String::from("name"), String::from("\"alice\"")), (String::from("host"), String::from("\"127.0.0.1\""))].iter().cloned().collect(); +// assert_eq!(command_clone, Commands::Client(Some(params))); +// } else if *uuid == String::from("0011-776-6-5") { +// let params: HashMap = [(String::from("uuid"), String::from("0011-776-6-5")), (String::from("name"), String::from("\"alice\"")), (String::from("host"), String::from("\"127.0.0.1\""))].iter().cloned().collect(); +// assert_eq!(command_clone, Commands::Client(Some(params))); +// } else if *uuid == String::from("0100-776-6-5") { +// let params: HashMap = [(String::from("uuid"), String::from("0100-776-6-5")), (String::from("name"), String::from("\"alice\"")), (String::from("host"), String::from("\"127.0.0.1\""))].iter().cloned().collect(); +// assert_eq!(command_clone, Commands::Client(Some(params))); +// } else { +// assert!(false); +// } +// }, +// _ => assert!(false), +// } + +// let msg = "!success:"; +// transmit_data(&stream_one, msg); +// } + +// let dur = time::Duration::from_millis(500); +// thread::sleep(dur); + +// let msg = "!disconnect:"; +// transmit_data(&stream_one, msg); +// transmit_data(&stream_two, msg); +// transmit_data(&stream_three, msg); +// transmit_data(&stream_four, msg); + +// let dur = time::Duration::from_millis(500); +// thread::sleep(dur); +// } + +// #[test] +// fn test_clientInfo(){ +// let mut buffer = [0; 1024]; + +// setup_server(); + +// let mut stream_one = establish_client_connection("0001-776-6-5"); +// let mut stream_two = establish_client_connection("\"0010-776-6-5\""); + +// let command = read_data(&stream_one, &mut buffer); +// let params: HashMap = [(String::from("uuid"), String::from("\"0010-776-6-5\"")), (String::from("name"), String::from("\"alice\"")), (String::from("host"), String::from("\"127.0.0.1\""))].iter().cloned().collect(); +// assert_eq!(command, Commands::Client(Some(params))); + +// let msg = "!success:"; +// transmit_data(&stream_one, msg); + + +// stream_one.set_read_timeout(Some(Duration::from_millis(3000))).unwrap(); +// let mut unsuccessful = true; +// while unsuccessful { +// let msg = "!clientInfo: uuid:\"0010-776-6-5\""; +// transmit_data(&stream_one, msg); + +// let command = read_data(&stream_one, &mut buffer); +// match command.clone() { +// Commands::Error(None) => println!("resending..."), +// _ => { +// let params: HashMap = [(String::from("uuid"), String::from("\"0010-776-6-5\"")), (String::from("name"), String::from("\"alice\"")), (String::from("host"), String::from("\"127.0.0.1\""))].iter().cloned().collect(); +// assert_eq!(command, Commands::Success(Some(params))); +// unsuccessful = false; +// }, +// } +// } +// stream_one.set_read_timeout(None).unwrap(); + +// let msg = "!disconnect:"; +// transmit_data(&stream_one, msg); +// transmit_data(&stream_two, msg); + +// let dur = time::Duration::from_millis(500); +// thread::sleep(dur); +// } + +// #[test] +// fn test_client_disconnect(){ +// let mut buffer = [0; 1024]; + +// setup_server(); + +// let mut stream_one = establish_client_connection("0001-776-6-5"); +// let mut stream_two = establish_client_connection("0010-776-6-5"); + +// let command = read_data(&stream_one, &mut buffer); +// let params: HashMap = [(String::from("uuid"), String::from("0010-776-6-5")), (String::from("name"), String::from("\"alice\"")), (String::from("host"), String::from("\"127.0.0.1\""))].iter().cloned().collect(); +// assert_eq!(command, Commands::Client(Some(params))); + +// let msg = "!success:"; +// transmit_data(&stream_one, msg); + +// let msg = "!disconnect:"; +// transmit_data(&stream_two, msg); + +// let command = read_data(&stream_one, &mut buffer); +// let params: HashMap = [(String::from("uuid"), String::from("0010-776-6-5"))].iter().cloned().collect(); +// assert_eq!(command, Commands::Client(Some(params))); + +// let msg = "!success:"; +// transmit_data(&stream_one, msg); + +// stream_one.set_read_timeout(Some(Duration::from_millis(2000))).unwrap(); +// match stream_one.peek(&mut buffer) { +// Ok(_) => assert!(false), +// Err(_) => assert!(true), +// } +// stream_one.set_read_timeout(None).unwrap(); + +// let msg = "!disconnect:"; +// transmit_data(&stream_one, msg); + +// let dur = time::Duration::from_millis(500); +// thread::sleep(dur); +// } +// }*/ diff --git a/src/lib/server/server_v3.rs b/src/lib/server/server_v3.rs new file mode 100644 index 0000000..1328f17 --- /dev/null +++ b/src/lib/server/server_v3.rs @@ -0,0 +1,372 @@ +// use std::time::Duration; +// use std::{ +// collections::HashMap, +// io, +// io::{Read, Write}, +// net::{TcpListener, TcpStream}, +// sync::{Arc, Mutex}, +// }; + +// use crossbeam_channel::{unbounded, Receiver, SendError, Sender}; +// use log::info; + +// use crate::commands::Commands; +// use super::client_management; + +// #[derive(Debug)] +// pub enum ServerMessages { +// RequestUpdate(Arc>), +// RequestInfo(String, Arc>), +// Disconnect(String), +// Shutdown, +// } + +// pub enum ServerEvent { +// Stopped, +// Started, +// addedClient(Arc>), +// } + +// #[allow(dead_code)] +// #[derive(Eq, PartialEq, Debug)] +// pub enum ServerState { +// Starting, +// Started, +// Stopping, +// Stopped, +// } + +// // MARK: - server struct +// #[allow(dead_code)] +// pub struct Server { +// pub config: , + +// pub state: ServerState, + +// // to be seperated into a different struct +// connected_clients: HashMap, + +// server_event_sink: Sender, +// server_message_source: Receiver, + +// message_source_handler: fn(&Self, event: T) -> (), + +// buffer: [u8; 1024], + +// // metrics +// pub o2s_rqst: usize, +// pub c2s_msgs: usize, +// pub s2s_msgs: usize, +// pub s2c_msgs: usize, +// } + +// // MARK: - server implemetation +// impl Server { +// pub fn new(name: &str, address: &str, author: &str) -> Result { +// // creating server channels +// let (sender, receiver) = unbounded(); + +// Ok(Self { +// // server data +// name: name.to_string(), +// address: address.to_string(), +// owner: author.to_string(), +// connected_clients: HashMap::new(), +// state: ServerState::Stopped, + +// // messages & connections +// sender, +// receiver, +// listener: None, + +// buffer: [0; 1024], + +// // metrics +// o2s_rqst: 0, +// c2s_msgs: 0, +// s2s_msgs: 0, +// s2c_msgs: 0, +// }) +// } + +// pub fn get_name(&self) -> String { +// self.name.clone() +// } + +// pub fn get_address(&self) -> String { +// self.address.clone() +// } + +// pub fn get_owner(&self) -> String { +// self.owner.clone() +// } + +// fn handle_server_messages(&mut self) -> Result<(), Vec>> { +// // check for any server messages in the channel +// println!("server: getting messages"); +// self.receiver.try_iter().map(|msg| { +// let _ = match msg { +// // request the server to shutdown +// // TODO: - move this into the stop method +// ServerMessages::Shutdown => { +// println!("server: shutting down..."); + +// let results = self +// .connected_clients +// .iter() +// .map(|(_k, v)| v.sender.send(Commands::Disconnect(None))) +// .cloned() +// .collect(); + +// self.state = ServerState::Stopping; +// } + +// // a client requests an updated list of clients +// ServerMessages::RequestUpdate(stream_arc) => { +// self.c2s_msgs += 1; + +// self.connected_clients.iter().map(|(_k, v)| { +// let mut stream = stream_arc.lock().unwrap(); +// let _ = Server::send_data(&mut stream, v.to_string().as_str()); +// let data = +// Server::recv_data(&mut stream, &mut self.buffer).unwrap_or(Commands::Error(None)); + +// if data == Commands::Success(None) { +// println!("Success Confirmed"); +// } else { +// println!("No success read"); +// let error = Commands::Error(None); +// let _ = Server::send_data(&mut stream, error.to_string().as_str()); +// } +// }) +// } + +// // a client requests for the servers info +// ServerMessages::RequestInfo(uuid, stream_arc) => { +// self.c2s_msgs += 1; + +// let mut stream = stream_arc.lock().unwrap(); + +// if let Some(client) = self.connected_clients.get(&uuid) { +// let params: HashMap = [ +// (String::from("uuid"), client.get_uuid()), +// (String::from("name"), client.get_username()), +// (String::from("host"), client.get_address()), +// ] +// .iter() +// .cloned() +// .collect(); + +// let command = Commands::Success(Some(params)); +// let _ = Server::send_data(&mut stream, command.to_string().as_str()); +// } else { +// let command = Commands::Success(None); +// let _ = Server::send_data(&mut stream, command.to_string().as_str()); +// } +// } + +// // a client requests to disconnect +// ServerMessages::Disconnect(uuid) => { +// self.c2s_msgs += 1; + +// self.connected_clients.remove(&uuid.to_string()); + +// let params: HashMap = +// [(String::from("uuid"), uuid)].iter().cloned().collect(); + +// let command = Commands::ClientRemove(Some(params)); +// let _ = self +// .connected_clients +// .iter() +// .map(move |(_k, v)| v.get_sender().send(command.clone())); +// } +// }; +// }); +// Ok(()) +// } + +// #[allow(dead_code)] +// pub fn tick(&mut self) -> Result<(), ServerError> { +// // check to see if this server is ready to execute things. +// if self.state == ServerState::Stopped { +// Err(ServerIsStopped) +// } + +// self.handle_server_messages(); + +// println!("server: checking for new connections"); +// if let Ok((mut stream, _addr)) = self +// .listener +// .as_ref() +// .expect("tcpListener not here") +// .accept() +// { +// let _ = stream.set_read_timeout(Some(Duration::from_millis(1000))); +// let _ = stream.set_nonblocking(false); + +// let request = Commands::Request(None); +// let _ = Server::send_data(&mut stream, &request.to_string().as_str()); + +// match Server::recv_data(&mut stream, &mut self.buffer) { +// Ok(Commands::Connect(Some(data))) => { +// self.o2s_rqst += 1; + +// let uuid = data.get("uuid").unwrap(); +// let username = data.get("name").unwrap(); +// let address = data.get("host").unwrap(); + +// info!("{}", format!("Server: new client from {}", address)); + +// let client = Client::new(stream, self.sender.clone(), &uuid, &username, &address); + +// self.connected_clients.insert(uuid.to_string(), client); + +// let params: HashMap = [ +// (String::from("name"), username.clone()), +// (String::from("host"), address.clone()), +// (String::from("uuid"), uuid.clone()), +// ] +// .iter() +// .cloned() +// .collect(); +// let new_client = Commands::Client(Some(params)); + +// let _ = self +// .connected_clients +// .iter() +// .map(|(_k, v)| v.sender.send(new_client.clone())); +// } + +// Ok(Commands::Info(None)) => { +// self.o2s_rqst += 1; + +// println!("Server: info requested"); +// let params: HashMap = [ +// (String::from("name"), self.name.to_string().clone()), +// (String::from("owner"), self.owner.to_string().clone()), +// ] +// .iter() +// .cloned() +// .collect(); +// let command = Commands::Info(Some(params)); + +// let _ = Server::send_data(&mut stream, command.to_string().as_str()); +// } + +// Err(_) => println!("ERROR: stream closed"), + +// // TODO: - correct connection reset error when getting info. +// _ => { +// println!("Server: Invalid command sent"); +// let _ = Server::send_data(&mut stream, Commands::Error(None).to_string().as_str()); +// } +// } +// } + +// println!("server: handing control to clients"); +// for (_k, client) in self.connected_clients.iter_mut() { +// client.handle_connection(); +// } + +// Ok(()) +// } + +// #[allow(dead_code)] +// pub fn start(&mut self) -> Result<(), io::Error> { +// let listener = TcpListener::bind(&self.address)?; +// listener.set_nonblocking(true)?; + +// self.listener = Some(listener); +// self.state = ServerState::Started; + +// Ok(()) +// } + +// #[allow(dead_code)] +// pub fn stop(&mut self) -> Result<(), SendError> { +// info!("server: sending stop message"); +// self.sender.send(ServerMessages::Shutdown)?; +// self.state = ServerState::Stopping; +// Ok(()) +// } + +// #[allow(dead_code)] +// fn send_data(stream: &mut TcpStream, data: &str) -> Result<(), io::Error> { +// println!("Transmitting..."); +// println!("data: {}", data); + +// /* +// * This will throw an error and crash any thread, including the main thread, if +// * the connection is lost before transmitting. Maybe change to handle any exceptions +// * that may occur. +// */ +// let _ = stream.write(data.to_string().as_bytes())?; +// stream.flush()?; +// Ok(()) +// } + +// #[allow(dead_code)] +// fn recv_data(stream: &mut TcpStream, buffer: &mut [u8; 1024]) -> Result { +// let _ = stream.read(buffer)?; +// let command = Commands::from(buffer); + +// Ok(command) +// } +// } + +// impl Drop for Server { +// // TODO: - implement the drop logic +// // this includes signaling all clients to disconnect +// fn drop(&mut self) {} +// } + +// #[cfg(test)] +// mod server_v3_tests { +// use crate::server::server_v3::{Server, ServerState}; + +// #[test] +// fn test_creation_and_drop() { +// let server = +// Server::new("test server", "0.0.0.0:6000", "michael").expect("server creation failed"); + +// assert_eq!(server.name, "test server"); +// assert_eq!(server.address, "0.0.0.0:6000"); +// assert_eq!(server.owner, "michael"); +// } + +// #[test] +// fn test_server_start() { +// let mut server = +// Server::new("test server", "0.0.0.0:6000", "michael").expect("server creation failed"); + +// let result = server.start(); + +// assert!(result.is_ok()); +// assert_eq!(server.state, ServerState::Started); +// } + +// #[test] +// fn test_server_stop() { +// let mut server = +// Server::new("test server", "0.0.0.0:6000", "michael").expect("server creation failed"); + +// let _ = server.start(); +// let result = server.stop(); + +// assert!(result.is_ok()); +// assert_eq!(server.state, ServerState::Stopping); +// } + +// #[test] +// fn test_server_start_stop_and_one_tick() { +// let mut server = +// Server::new("test server", "0.0.0.0:6000", "michael").expect("server creation failed"); + +// let _ = server.start(); +// let result = server.stop(); +// server.tick(); + +// assert!(result.is_ok()); +// assert_eq!(server.state, ServerState::Stopped); +// } +// } diff --git a/src/main.rs b/src/main.rs index 7f651d7..2c81892 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,252 +1,79 @@ -mod client_api; -mod commands; -mod server; mod lib; -use cursive::{ - Cursive, - menu::*, - event::Key, - views::{ Dialog, TextView, LinearLayout, ListView, ResizedView, Panel }, - CursiveExt, - align::Align, - view::SizeConstraint, -}; -//use std::sync::Arc; -use std::time::Duration; -use std::sync::Arc; -use crossterm::ErrorKind; -use log::info; use clap::{App, Arg}; -use crate::server::server_profile::Server; +use lib::server::Server; + +fn main() { + let args = App::new("--rust chat server--") + .version("0.1.5") + .author("Mitchel Hardie , Michael Bailey ") + .about("this is a chat server developed in rust, depending on the version one of two implementations will be used") + .arg( + Arg::with_name("config") + .short('p') + .long("port") + .value_name("PORT") + .about("sets the port the server listens on.") + .takes_value(true)) + .get_matches(); -fn main() -> Result<(), ErrorKind> { - let args = App::new("--rust chat server--") - .version("0.1.5") - .author("Mitchel Hardie , Michael Bailey ") - .about("this is a chat server developed in rust, depending on the version one of two implementations will be used") - .arg(Arg::with_name("graphical") - .short('g') - .takes_value(false) - .about("Enables graphical mode")) - .get_matches(); - - if args.is_present("graphical") { - let server = Server::new("Server-01", "0.0.0.0:6000", "noreply@email.com"); - let server_arc = Arc::new(server); - let s1 = server_arc.clone(); - let s2 = s1.clone(); - - cursive::logger::init(); - - info!("Main: init display"); - let mut display = Cursive::default(); - - info!("Main: setting up callbacks"); - display.add_global_callback(Key::Backspace, |s| s.quit()); - display.add_global_callback(Key::Tab, |s| s.toggle_debug_console()); - display.add_global_callback(Key::Esc, |s| s.select_menubar()); - - info!("Main: setting up menu bar"); - let _ = display.menubar() - .add_subtree("Server", - MenuTree::new() - .leaf("about", - |s| s.add_layer(about())) - .delimiter() - .leaf("quit", |s| s.quit())) - .add_subtree("File", - MenuTree::new() - .leaf("Start", move |_s| {let _ = s1.start();}) - .leaf("Stop", move |_s| {let _ = s2.stop();}) - .delimiter() - .leaf("Debug", |s| {s.toggle_debug_console();})); - info!("Main: entering loop"); - display.add_layer(control_panel()); - display.run(); - Ok(()) - } else { - let server = Server::new("Server-01", "0.0.0.0:6000", "noreply@email.com"); - - server.start()?; - loop {std::thread::sleep(Duration::from_secs(1));} - } + // creating the server object } -fn about() -> Dialog { - Dialog::new() - .content(TextView::new("Rust-Chat-Server\nmade by\n Mitchell Hardie\nMichael Bailey\nMit Licence") - ).button("Close", |s| {let _ = s.pop_layer(); s.add_layer(control_panel())} ) -} - -#[allow(dead_code)] -fn launch_screen() -> Dialog { - Dialog::new() - .content(TextView::new("\ - Server. - * press for menu bar - * press for debug (FIXME) - * press to exit. - ").align(Align::center())) - .button("ok", |s| {s.pop_layer();}) -} - -fn control_panel() -> ResizedView> { - - let mut root = LinearLayout::horizontal(); - let mut left = LinearLayout::vertical(); - let mut right = ListView::new(); - right.add_child("test", TextView::new("")); - right.add_child("test", TextView::new("")); - right.add_delimiter(); - right.add_child("test", TextView::new("")); - right.add_child("test", TextView::new("")); - - left.add_child(TextView::new("Hello world")); - - root.add_child(ResizedView::new(SizeConstraint::Full, SizeConstraint::Full, Panel::new(left))); - root.add_child(ResizedView::new(SizeConstraint::Full, SizeConstraint::Full, Panel::new(right))); - ResizedView::new(SizeConstraint::Fixed(60), SizeConstraint::Fixed(18), Panel::new(root)) -} // MARK: - general testing zone -#[cfg(test)] -mod tests { - use crate::server::server_profile::Server; - use crate::client_api::ClientApi; - use std::collections::HashMap; - use crate::commands::Commands; - use std::{thread, time}; - use std::time::Duration; +// #[cfg(test)] +// mod tests { +// use crate::server::server_profile::Server; +// use crate::client_api::ClientApi; +// use std::collections::HashMap; +// use crate::commands::Commands; +// use std::{thread, time}; - #[test] - fn test_server_info() { - // setup the server - let name = "Server-01"; - let address = "0.0.0.0:6000"; - let owner = "noreply@email.com"; +// #[test] +// fn test_server_info() { +// // setup the server +// let name = "Server-01"; +// let address = "0.0.0.0:6000"; +// let owner = "noreply@email.com"; - let server = Server::new(name, address, owner); - let result = server.start(); +// let mut server = Server::new(name, address, owner); +// let result = server.start(); - assert_eq!(result.is_ok(), true); +// assert_eq!(result.is_ok(), true); - let dur = time::Duration::from_millis(1000); - thread::sleep(dur); +// let dur = time::Duration::from_millis(1000); +// thread::sleep(dur); - let api = ClientApi::get_info("127.0.0.1:6000"); - assert_eq!(api.is_ok(), true); - if let Ok(api) = api { - println!("received: {:?}", api); - let mut map = HashMap::new(); - map.insert("name".to_string(), name.to_string()); - map.insert("owner".to_string(), owner.to_string()); +// let api = ClientApi::get_info("127.0.0.1:6000"); +// assert_eq!(api.is_ok(), true); +// if let Ok(api) = api { +// println!("received: {:?}", api); +// let mut map = HashMap::new(); +// map.insert("name".to_string(), name.to_string()); +// map.insert("owner".to_string(), owner.to_string()); - let expected = Commands::Info(Some(map)); - println!("expected: {:?}", expected); - assert_eq!(api, expected); - } - } +// let expected = Commands::Info(Some(map)); +// println!("expected: {:?}", expected); +// assert_eq!(api, expected); +// } +// } - #[test] - fn test_server_connect() { - let name = "Server-01"; - let address = "0.0.0.0:6001"; - let owner = "noreply@email.com"; +// #[test] +// fn test_server_connect() { +// let name = "Server-01"; +// let address = "0.0.0.0:6001"; +// let owner = "noreply@email.com"; - let server = Server::new(name, address, owner); - let _ = server.start().unwrap(); +// let mut server = Server::new(name, address, owner); +// let _ = server.start().unwrap(); - let api_result = ClientApi::new(address); - assert_eq!(api_result.is_ok(), true); - if let Ok(api) = api_result { - std::thread::sleep(std::time::Duration::from_secs(2)); - } - } -} +// let api_result = ClientApi::new(address); +// assert_eq!(api_result.is_ok(), true); +// if api_result.is_ok() { +// std::thread::sleep(std::time::Duration::from_secs(2)); +// } +// } +// } -#[cfg(test)] -mod crypto_tests { - use openssl::rsa::{Rsa, Padding}; - use openssl::ssl::{SslMethod, SslAcceptor, SslStream, SslFiletype, SslConnector, SslVerifyMode}; - use std::net::{TcpListener, TcpStream}; - use std::sync::Arc; - use std::thread; - use std::str; - - #[test] - // MARK: - working encryption example for rsa - fn gen_rsa() { - let rsa = Rsa::generate(1024).unwrap(); - - let ref1 = rsa.public_key_to_pem().unwrap(); - let ref2 = rsa.private_key_to_pem().unwrap(); - - let public = str::from_utf8(&ref1).unwrap().to_string(); - let private = str::from_utf8(&ref2).unwrap().to_string(); - - println!("public key size: {}", public.len()); - println!("{}", public); - - println!("private key size: {}", private.len()); - println!("{}", private); - - let data = b"this is a sentence"; - println!("before: {:?}", data); - - let mut buf = vec![0; rsa.size() as usize]; - let encrypted_len = rsa.private_encrypt(data, &mut buf, Padding::PKCS1).unwrap(); - println!("during: {:?}", &buf); - - let mut buf2 = vec![0; rsa.size() as usize]; - let _ = rsa.public_decrypt(&mut buf, &mut buf2, Padding::PKCS1).unwrap(); - println!("after: {:?}", &buf2); - } - - #[test] - fn tls_handshake() { - // spawn the server - thread::spawn(|| { - println!("creating acceptor"); - let mut acceptor = SslAcceptor::mozilla_modern(SslMethod::tls()).unwrap(); - acceptor.set_private_key_file("cert.pem", SslFiletype::PEM).unwrap(); - acceptor.set_certificate_chain_file("root.pem").unwrap(); - acceptor.check_private_key().unwrap(); - let acceptor = Arc::new(acceptor.build()); - - let listener = TcpListener::bind("0.0.0.0:6000").unwrap(); - - println!("entering loop"); - loop { - for stream in listener.incoming() { - println!("client accepted"); - match stream { - Ok(stream) => { - let acceptor = acceptor.clone(); - thread::spawn(move || { - let mut stream = acceptor.accept(stream).unwrap(); - - let mut buffer: [u8; 1024] = [0; 1024]; - - stream.ssl_read(&mut buffer).unwrap(); - let result = str::from_utf8(&buffer).unwrap(); - if buffer == "echo".as_bytes() { - let _ = stream.ssl_write("echo".as_bytes()).unwrap(); - } - }); - } - Err(e) => { /* connection failed */ } - } - } - } - }); - - let connector = SslConnector::builder(SslMethod::tls()).unwrap().build(); - - let stream = TcpStream::connect("localhost:6000").unwrap(); - let mut stream = connector.connect("127.0.0.1", stream).unwrap(); - - let _ = stream.ssl_write("echo".as_bytes()).unwrap(); - } -} \ No newline at end of file diff --git a/src/server/client/mod.rs b/src/server/client/mod.rs deleted file mode 100644 index c0ef8d2..0000000 --- a/src/server/client/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod client_profile; diff --git a/src/server/mod.rs b/src/server/mod.rs deleted file mode 100644 index de07bb5..0000000 --- a/src/server/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod client; -pub mod server_profile; diff --git a/src/server/server_profile.rs b/src/server/server_profile.rs deleted file mode 100644 index 1f2a07e..0000000 --- a/src/server/server_profile.rs +++ /dev/null @@ -1,607 +0,0 @@ -extern crate regex; -extern crate rayon; - -use crate::{ - server::{ - client::client_profile::Client, - - }, - commands::Commands -}; - -use std::{ - sync::{Arc, Mutex}, - net::{TcpStream, TcpListener}, - collections::HashMap, - io::prelude::*, - time::Duration, - io::Error, - thread, - io -}; - -use log::info; - -use crossbeam_channel::{Sender, Receiver, unbounded}; -use rust_chat_server::ThreadPool; -//use zeroize::Zeroize; -//use parking_lot::FairMutex; -//use dashmap::DashMap; -//use regex::Regex; - -#[derive(Debug)] -pub enum ServerMessages { - RequestUpdate(Arc>), - RequestInfo(String, Arc>), - Disconnect(String), - Shutdown, -} - -// MARK: - server struct -#[derive(Debug)] -pub struct Server { - name: Arc, - address: Arc, - author: Arc, - - connected_clients: Arc>>, - - thread_pool: ThreadPool, - - sender: Sender, - receiver: Receiver, -} - -// MARK: - server implemetation -impl Server { - pub fn new(name: &str, address: &str, author: &str) -> Self { - let (sender, receiver) = unbounded(); - - Self { - name: Arc::new(name.to_string()), - address: Arc::new(address.to_string()), - author: Arc::new(author.to_string()), - connected_clients: Arc::new(Mutex::new(HashMap::new())), - thread_pool: ThreadPool::new(16), - - sender, - receiver, - } - } - - #[allow(dead_code)] - pub fn get_name(&self) -> String { - self.name.to_string() - } - - #[allow(dead_code)] - pub fn get_address(&self) -> String { - self.address.to_string() - } - - #[allow(dead_code)] - pub fn get_author(&self) -> String { - self.author.to_string() - } - - pub fn start(&self) -> Result<(), io::Error>{ - println!("server: starting server..."); - - // MARK: - creating clones of the server property references - let name = self.name.clone(); - #[allow(dead_code)] - let address = self.address.clone(); - let author = self.author.clone(); - let connected_clients = self.connected_clients.clone(); - let sender = self.sender.clone(); - let receiver = self.receiver.clone(); - - // set up listener and buffer - let mut buffer = [0; 1024]; - let listener = TcpListener::bind(self.get_address())?; - listener.set_nonblocking(true)?; - - println!("server: spawning threads"); - let _ = thread::Builder::new().name("Server Thread".to_string()).spawn(move || { - - 'outer: loop { - std::thread::sleep(Duration::from_millis(100)); - - // get messages from the servers channel. - println!("server: getting messages"); - for i in receiver.try_iter() { - match i { - ServerMessages::Shutdown => { - // TODO: implement disconnecting all clients and shutting down the server. - println!("server: shutting down..."); - break 'outer; - }, - ServerMessages::RequestUpdate(stream_arc) => { - for (_k, v) in connected_clients.lock().unwrap().iter() { - let mut stream = stream_arc.lock().unwrap(); - let _ = Server::transmit_data(&mut stream, v.to_string().as_str()); - - if Server::read_data(&mut stream, &mut buffer).unwrap_or(Commands::Error(None)) == Commands::Success(None) { - println!("Success Confirmed"); - } else { - println!("no success read"); - let error = Commands::Error(None); - let _ = Server::transmit_data(&mut stream, error.to_string().as_str()); - } - } - }, - ServerMessages::RequestInfo(uuid, stream_arc) => { - let mut stream = stream_arc.lock().unwrap(); - - if let Some(client) = connected_clients.lock().unwrap().get(&uuid) { - let params: HashMap = [(String::from("uuid"), client.get_uuid()), (String::from("name"), client.get_username()), (String::from("host"), client.get_address())].iter().cloned().collect(); - let command = Commands::Success(Some(params)); - let _ = Server::transmit_data(&mut stream, command.to_string().as_str()); - } else { - let command = Commands::Success(None); - let _ = Server::transmit_data(&mut stream, command.to_string().as_str()); - } - }, - ServerMessages::Disconnect(uuid) => { - let mut clients = connected_clients.lock().unwrap(); - clients.remove(&uuid.to_string()); - let params: HashMap = [(String::from("uuid"), uuid)].iter().cloned().collect(); - let command = Commands::ClientRemove(Some(params)); - let _ = connected_clients.lock().unwrap().iter().map(move |(_k, v)| {v.get_sender().send(command.clone())}); - }, - } - } - - println!("server: checking for new connections"); - if let Ok((mut stream, _addr)) = listener.accept() { - stream.set_read_timeout(Some(Duration::from_millis(1000))).unwrap(); - let _ = stream.set_nonblocking(false); - - let request = Commands::Request(None); - let _ = Server::transmit_data(&mut stream, &request.to_string().as_str()); - - match Server::read_data(&mut stream, &mut buffer) { - Ok(command) => { - println!("Server: new connection sent - {:?}", command); - match command { - Commands::Connect(Some(data)) => { - let uuid = data.get("uuid").unwrap(); - let username = data.get("name").unwrap(); - let address = data.get("host").unwrap(); - - println!("{}", format!("Server: new Client connection: _addr = {}", address )); - - let client = Client::new(stream, sender.clone(), &uuid, &username, &address); - - connected_clients.lock().unwrap().insert(uuid.to_string(), client); - - let params: HashMap = [(String::from("name"), username.clone()), (String::from("host"), address.clone()), (String::from("uuid"), uuid.clone())].iter().cloned().collect(); - let new_client = Commands::Client(Some(params)); - - let _ = connected_clients.lock().unwrap().iter().map(|(_k, v)| v.sender.send(new_client.clone())); - }, - // TODO: - correct connection reset error when getting info. - Commands::Info(None) => { - println!("Server: info requested"); - let params: HashMap = [(String::from("name"), name.to_string().clone()), (String::from("owner"), author.to_string().clone())].iter().cloned().collect(); - let command = Commands::Info(Some(params)); - - let _ = Server::transmit_data(&mut stream, command.to_string().as_str()); - }, - _ => { - println!("Server: Invalid command sent"); - let _ = Server::transmit_data(&mut stream, Commands::Error(None).to_string().as_str()); - }, - } - }, - Err(_) => println!("ERROR: stream closed"), - } - } - // TODO: end - - - // handle each client for messages - println!("server: handing control to clients"); - for (_k, client) in connected_clients.lock().unwrap().iter_mut() { - client.handle_connection(); - } - } - println!("server: stopped"); - }); - println!("server: started"); - Ok(()) - } - - pub fn stop(&self) { - info!("server: sending stop message"); - let _ = self.sender.send(ServerMessages::Shutdown); - } - - fn transmit_data(stream: &mut TcpStream, data: &str) -> Result<(), Error>{ - println!("Transmitting..."); - println!("data: {}", data); - - /* - * This will throw an error and crash any thread, including the main thread, if - * the connection is lost before transmitting. Maybe change to handle any exceptions - * that may occur. - */ - let _ = stream.write(data.to_string().as_bytes())?; - stream.flush()?; - Ok(()) - } - - fn read_data(stream: &mut TcpStream, buffer: &mut [u8; 1024]) -> Result { - let _ = stream.read(buffer)?; - let command = Commands::from(buffer); - - Ok(command) - } -} - -impl ToString for Server { - fn to_string(&self) -> std::string::String { todo!() } -} - -impl Drop for Server { - fn drop(&mut self) { - println!("server dropped"); - let _ = self.sender.send(ServerMessages::Shutdown); - } -} - - -/* The new version of the server no long works with these unit - * tests. - * They will be fixed soon! - * TODO: fix unit tests - */ - - - -/*#[cfg(test)] -mod tests{ - use super::*; - use std::{thread, time}; - use std::sync::Once; - use std::time::Duration; - - lazy_static!{ - static ref SERVER_NAME: &'static str = "test"; - static ref SERVER_ADDRESS: &'static str = "0.0.0.0:6000"; - static ref SERVER_AUTHOR: &'static str = "test"; - static ref SERVER: Server<'static> = Server::new(&SERVER_NAME, &SERVER_ADDRESS, &SERVER_AUTHOR); - } - - static START: Once = Once::new(); - - /* - * These tests must be executed individually to ensure that no errors - * occur, this is due to the fact that the server is created everytime. - * Setup a system for the server to close after every test. - */ - fn setup_server(){ - unsafe{ - START.call_once(|| { - thread::spawn(|| { - SERVER.start(); - }); - }); - - let millis = time::Duration::from_millis(1000); - thread::sleep(millis); - } - } - - fn establish_client_connection(uuid: &str) -> TcpStream { - let mut buffer = [0; 1024]; - - let mut stream = TcpStream::connect("0.0.0.0:6000").unwrap(); - - let mut command = read_data(&stream, &mut buffer); - - assert_eq!(command, Commands::Request(None)); - - let msg: String = format!("!connect: uuid:{uuid} name:\"{name}\" host:\"{host}\"", uuid=uuid, name="alice", host="127.0.0.1"); - transmit_data(&stream, msg.as_str()); - - command = read_data(&stream, &mut buffer); - - assert_eq!(command, Commands::Success(None)); - - stream - } - - fn transmit_data(mut stream: &TcpStream, data: &str){ - stream.write(data.to_string().as_bytes()).unwrap(); - stream.flush().unwrap(); - } - - fn read_data(mut stream: &TcpStream, buffer: &mut [u8; 1024]) -> Commands { - match stream.read(buffer) { - Ok(_) => Commands::from(buffer), - Err(_) => Commands::Error(None), - } - } - - fn force_disconnect(mut stream: &TcpStream){ - let msg = "!disconnect:"; - transmit_data(&stream, msg); - } - - #[test] - fn test_server_connect(){ - let mut buffer = [0; 1024]; - - setup_server(); - - let mut stream = TcpStream::connect("0.0.0.0:6000").unwrap(); - - stream.read(&mut buffer).unwrap(); - let mut command = Commands::from(&mut buffer); - - assert_eq!(command, Commands::Request(None)); - - let msg = b"!connect: uuid:123456-1234-1234-123456 name:\"alice\" host:\"127.0.0.1\""; - stream.write(msg).unwrap(); - - stream.read(&mut buffer).unwrap(); - command = Commands::from(&mut buffer); - - assert_eq!(command, Commands::Success(None)); - - let msg = b"!disconnect:"; - stream.write(msg).unwrap(); - - let dur = time::Duration::from_millis(500); - thread::sleep(dur); - } - - #[test] - fn test_server_info(){ - let mut buffer = [0; 1024]; - - setup_server(); - - let mut stream = TcpStream::connect("0.0.0.0:6000").unwrap(); - - let command = read_data(&stream, &mut buffer); - - assert_eq!(command, Commands::Request(None)); - - let msg = "!info:"; - transmit_data(&stream, msg); - - let command = read_data(&stream, &mut buffer); - - let params: HashMap = [(String::from("name"), String::from("test")), (String::from("owner"), String::from("test"))].iter().cloned().collect(); - assert_eq!(command, Commands::Success(Some(params))); - } - - #[test] - fn test_client_info(){ - let mut buffer = [0; 1024]; - - setup_server(); - - let mut stream = establish_client_connection("1234-5542-2124-155"); - - let msg = "!info:"; - transmit_data(&stream, msg); - - let command = read_data(&stream, &mut buffer); - - let params: HashMap = [(String::from("name"), String::from("test")), (String::from("owner"), String::from("test"))].iter().cloned().collect(); - assert_eq!(command, Commands::Success(Some(params))); - - let msg = "!disconnect:"; - transmit_data(&stream, msg); - - let dur = time::Duration::from_millis(500); - thread::sleep(dur); - } - - #[test] - fn test_clientUpdate_solo(){ - let mut buffer = [0; 1024]; - - setup_server(); - - let mut stream = establish_client_connection("1222-555-6-7"); - - let msg = "!clientUpdate:"; - transmit_data(&stream, msg); - - let command = read_data(&stream, &mut buffer); - - assert_eq!(command, Commands::Success(None)); - - let msg = "!disconnect:"; - transmit_data(&stream, msg); - - let dur = time::Duration::from_millis(500); - thread::sleep(dur); - } - - - #[test] - fn test_clientUpdate_multi(){ - let mut buffer = [0; 1024]; - - setup_server(); - - let mut stream_one = establish_client_connection("0001-776-6-5"); - let mut stream_two = establish_client_connection("0010-776-6-5"); - let mut stream_three = establish_client_connection("0011-776-6-5"); - let mut stream_four = establish_client_connection("0100-776-6-5"); - - let client_uuids: [String; 3] = [String::from("0010-776-6-5"), String::from("0011-776-6-5"), String::from("0100-776-6-5")]; - let mut user_1 = true; - let mut user_2 = true; - let mut user_3 = true; - - for uuid in client_uuids.iter() { - let command = read_data(&stream_one, &mut buffer); - - if *uuid == String::from("0010-776-6-5") && user_1 { - let params: HashMap = [(String::from("uuid"), String::from("0010-776-6-5")), (String::from("name"), String::from("\"alice\"")), (String::from("host"), String::from("\"127.0.0.1\""))].iter().cloned().collect(); - assert_eq!(command, Commands::Client(Some(params))); - - user_1 = false; - } else if *uuid == String::from("0011-776-6-5") && user_2 { - let params: HashMap = [(String::from("uuid"), String::from("0011-776-6-5")), (String::from("name"), String::from("\"alice\"")), (String::from("host"), String::from("\"127.0.0.1\""))].iter().cloned().collect(); - assert_eq!(command, Commands::Client(Some(params))); - - user_2 = false; - } else if *uuid == String::from("0100-776-6-5") && user_3 { - let params: HashMap = [(String::from("uuid"), String::from("0100-776-6-5")), (String::from("name"), String::from("\"alice\"")), (String::from("host"), String::from("\"127.0.0.1\""))].iter().cloned().collect(); - assert_eq!(command, Commands::Client(Some(params))); - - user_3 = false; - } else { - assert!(false); - } - let msg = "!success:"; - transmit_data(&stream_one, msg); - } - - stream_one.set_read_timeout(Some(Duration::from_millis(3000))).unwrap(); - let mut unsuccessful = true; - while unsuccessful { - let msg = "!clientUpdate:"; - transmit_data(&stream_one, msg); - - let command = read_data(&stream_one, &mut buffer); - match command.clone() { - Commands::Error(None) => println!("resending..."), - _ => { - assert_eq!(command, Commands::Success(None)); - unsuccessful = false; - }, - } - } - stream_one.set_read_timeout(None).unwrap(); - - for x in 0..3 { - let command = read_data(&stream_one, &mut buffer); - - let command_clone = command.clone(); - match command{ - Commands::Client(Some(params)) => { - let uuid = params.get("uuid").unwrap(); - - if *uuid == String::from("0010-776-6-5") { - let params: HashMap = [(String::from("uuid"), String::from("0010-776-6-5")), (String::from("name"), String::from("\"alice\"")), (String::from("host"), String::from("\"127.0.0.1\""))].iter().cloned().collect(); - assert_eq!(command_clone, Commands::Client(Some(params))); - } else if *uuid == String::from("0011-776-6-5") { - let params: HashMap = [(String::from("uuid"), String::from("0011-776-6-5")), (String::from("name"), String::from("\"alice\"")), (String::from("host"), String::from("\"127.0.0.1\""))].iter().cloned().collect(); - assert_eq!(command_clone, Commands::Client(Some(params))); - } else if *uuid == String::from("0100-776-6-5") { - let params: HashMap = [(String::from("uuid"), String::from("0100-776-6-5")), (String::from("name"), String::from("\"alice\"")), (String::from("host"), String::from("\"127.0.0.1\""))].iter().cloned().collect(); - assert_eq!(command_clone, Commands::Client(Some(params))); - } else { - assert!(false); - } - }, - _ => assert!(false), - } - - let msg = "!success:"; - transmit_data(&stream_one, msg); - } - - let dur = time::Duration::from_millis(500); - thread::sleep(dur); - - let msg = "!disconnect:"; - transmit_data(&stream_one, msg); - transmit_data(&stream_two, msg); - transmit_data(&stream_three, msg); - transmit_data(&stream_four, msg); - - let dur = time::Duration::from_millis(500); - thread::sleep(dur); - } - - #[test] - fn test_clientInfo(){ - let mut buffer = [0; 1024]; - - setup_server(); - - let mut stream_one = establish_client_connection("0001-776-6-5"); - let mut stream_two = establish_client_connection("\"0010-776-6-5\""); - - let command = read_data(&stream_one, &mut buffer); - let params: HashMap = [(String::from("uuid"), String::from("\"0010-776-6-5\"")), (String::from("name"), String::from("\"alice\"")), (String::from("host"), String::from("\"127.0.0.1\""))].iter().cloned().collect(); - assert_eq!(command, Commands::Client(Some(params))); - - let msg = "!success:"; - transmit_data(&stream_one, msg); - - - stream_one.set_read_timeout(Some(Duration::from_millis(3000))).unwrap(); - let mut unsuccessful = true; - while unsuccessful { - let msg = "!clientInfo: uuid:\"0010-776-6-5\""; - transmit_data(&stream_one, msg); - - let command = read_data(&stream_one, &mut buffer); - match command.clone() { - Commands::Error(None) => println!("resending..."), - _ => { - let params: HashMap = [(String::from("uuid"), String::from("\"0010-776-6-5\"")), (String::from("name"), String::from("\"alice\"")), (String::from("host"), String::from("\"127.0.0.1\""))].iter().cloned().collect(); - assert_eq!(command, Commands::Success(Some(params))); - unsuccessful = false; - }, - } - } - stream_one.set_read_timeout(None).unwrap(); - - let msg = "!disconnect:"; - transmit_data(&stream_one, msg); - transmit_data(&stream_two, msg); - - let dur = time::Duration::from_millis(500); - thread::sleep(dur); - } - - #[test] - fn test_client_disconnect(){ - let mut buffer = [0; 1024]; - - setup_server(); - - let mut stream_one = establish_client_connection("0001-776-6-5"); - let mut stream_two = establish_client_connection("0010-776-6-5"); - - let command = read_data(&stream_one, &mut buffer); - let params: HashMap = [(String::from("uuid"), String::from("0010-776-6-5")), (String::from("name"), String::from("\"alice\"")), (String::from("host"), String::from("\"127.0.0.1\""))].iter().cloned().collect(); - assert_eq!(command, Commands::Client(Some(params))); - - let msg = "!success:"; - transmit_data(&stream_one, msg); - - let msg = "!disconnect:"; - transmit_data(&stream_two, msg); - - let command = read_data(&stream_one, &mut buffer); - let params: HashMap = [(String::from("uuid"), String::from("0010-776-6-5"))].iter().cloned().collect(); - assert_eq!(command, Commands::Client(Some(params))); - - let msg = "!success:"; - transmit_data(&stream_one, msg); - - stream_one.set_read_timeout(Some(Duration::from_millis(2000))).unwrap(); - match stream_one.peek(&mut buffer) { - Ok(_) => assert!(false), - Err(_) => assert!(true), - } - stream_one.set_read_timeout(None).unwrap(); - - let msg = "!disconnect:"; - transmit_data(&stream_one, msg); - - let dur = time::Duration::from_millis(500); - thread::sleep(dur); - } -}*/