From 8f591f24d5935950a7792e666acfd366e39d759e Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Thu, 27 Aug 2020 18:47:59 +0100 Subject: [PATCH 01/53] got running flag to display --- Cargo.toml | 3 + src/main.rs | 152 ++++++++++++++++------------------- src/server/server_profile.rs | 25 ++++-- 3 files changed, 94 insertions(+), 86 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d430020..6b6e30e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,9 @@ 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"] } +rustls = "0.18.1" +webpki = "0.21.3" +webpki-roots = "0.20.0" [profile.dev] diff --git a/src/main.rs b/src/main.rs index 7f651d7..0c66d4d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,7 +7,7 @@ use cursive::{ Cursive, menu::*, event::Key, - views::{ Dialog, TextView, LinearLayout, ListView, ResizedView, Panel }, + views::{ Dialog, TextView, LinearLayout, ListView, ResizedView, Panel, Menubar }, CursiveExt, align::Align, view::SizeConstraint, @@ -15,6 +15,8 @@ use cursive::{ //use std::sync::Arc; use std::time::Duration; use std::sync::Arc; +use std::sync::Weak; +use std::sync::Mutex; use crossterm::ErrorKind; use log::info; use clap::{App, Arg}; @@ -33,8 +35,8 @@ fn main() -> Result<(), ErrorKind> { .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 mut server = Server::new("Server-01", "0.0.0.0:6000", "noreply@email.com"); + let server_arc = Arc::new(Mutex::new(server)); let s1 = server_arc.clone(); let s2 = s1.clone(); @@ -49,8 +51,50 @@ fn main() -> Result<(), ErrorKind> { display.add_global_callback(Key::Esc, |s| s.select_menubar()); info!("Main: setting up menu bar"); - let _ = display.menubar() - .add_subtree("Server", + // setup menu bar + menu_bar(display.menubar(), &server_arc); + + println!("Main: entering loop"); + display.add_layer(control_panel(server_arc)); + display.add_layer(launch_screen()); + display.set_autohide_menu(false); + display.run(); + Ok(()) + } else { + let mut server = Server::new("Server-01", "0.0.0.0:6000", "noreply@email.com"); + + server.start()?; + loop { std::thread::sleep(Duration::from_secs(1)); } + } +} + +fn about() -> Dialog { + Dialog::new() + .content(TextView::new("Rust-Chat-Server\nmade by\n Mitchell Hardie\nMichael Bailey\nMit Licence") + .align(Align::center())) + .button("Close", |s| {let _ = s.pop_layer();} ) +} + +#[allow(dead_code)] +fn launch_screen() -> Dialog { + Dialog::new() + .content(TextView::new("\ + Welcome. + + --- Controls --- + * press for menu bar + * press for debug (FIXME) + * press to exit. + ").align(Align::top_left())) + .button("ok", |s| {s.pop_layer();}) +} + +fn menu_bar(bar: &mut Menubar, server_arc: &Arc>) { + + let s1 = Arc::downgrade(server_arc); + let s2 = Arc::downgrade(server_arc); + + bar.add_subtree("Server", MenuTree::new() .leaf("about", |s| s.add_layer(about())) @@ -58,52 +102,35 @@ fn main() -> Result<(), ErrorKind> { .leaf("quit", |s| s.quit())) .add_subtree("File", MenuTree::new() - .leaf("Start", move |_s| {let _ = s1.start();}) - .leaf("Stop", move |_s| {let _ = s2.stop();}) + .leaf("Start", move |s| { + let arc = s2.upgrade().unwrap(); + let _ = arc.lock().unwrap().start(); + let _ = s.pop_layer(); + s.add_layer(control_panel(arc)); + }) + .leaf("Stop", move |s| { + let arc = s1.upgrade().unwrap(); + let _ = arc.lock().unwrap().stop(); + let _ = s.pop_layer(); + s.add_layer(control_panel(arc)); + }) .delimiter() + // TODO: - create custom debug console .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));} - } } -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> { - +fn control_panel(server_arc: Arc>) -> 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")); + left.add_child(TextView::new(format!("running: {}", server_arc.lock().unwrap().running))); root.add_child(ResizedView::new(SizeConstraint::Full, SizeConstraint::Full, Panel::new(left))); root.add_child(ResizedView::new(SizeConstraint::Full, SizeConstraint::Full, Panel::new(right))); @@ -127,7 +154,7 @@ mod tests { let address = "0.0.0.0:6000"; let owner = "noreply@email.com"; - let server = Server::new(name, address, owner); + let mut server = Server::new(name, address, owner); let result = server.start(); assert_eq!(result.is_ok(), true); @@ -155,7 +182,7 @@ mod tests { let address = "0.0.0.0:6001"; let owner = "noreply@email.com"; - let server = Server::new(name, address, owner); + let mut server = Server::new(name, address, owner); let _ = server.start().unwrap(); let api_result = ClientApi::new(address); @@ -175,6 +202,10 @@ mod crypto_tests { use std::thread; use std::str; + use rustls; + use webpki; + use webpki_roots; + #[test] // MARK: - working encryption example for rsa fn gen_rsa() { @@ -206,47 +237,6 @@ mod crypto_tests { #[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/server_profile.rs b/src/server/server_profile.rs index 1f2a07e..dd60c68 100644 --- a/src/server/server_profile.rs +++ b/src/server/server_profile.rs @@ -38,7 +38,6 @@ pub enum ServerMessages { } // MARK: - server struct -#[derive(Debug)] pub struct Server { name: Arc, address: Arc, @@ -50,6 +49,10 @@ pub struct Server { sender: Sender, receiver: Receiver, + + pub running: bool, + + client_list_changed_handle: Box, } // MARK: - server implemetation @@ -66,6 +69,10 @@ impl Server { sender, receiver, + + running: false, + + client_list_changed_handle: Box::new(|s| println!("client list: {:#?}", s.get_client_list())) } } @@ -84,9 +91,16 @@ impl Server { self.author.to_string() } - pub fn start(&self) -> Result<(), io::Error>{ + pub fn get_client_list(&self) -> Vec { + let map = self.connected_clients.lock().unwrap(); + map.iter().map(|(_k, v)| format!("{:?}", v)).collect() + } + + 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)] @@ -205,15 +219,16 @@ impl Server { client.handle_connection(); } } - println!("server: stopped"); + info!("server: stopped"); }); - println!("server: started"); + info!("server: started"); Ok(()) } - pub fn stop(&self) { + 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>{ -- 2.40.1 From fb4eaa8ae45aa33574e2298b46682727dd3f662a Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Sun, 27 Sep 2020 08:32:34 +0100 Subject: [PATCH 02/53] Update LICENSE --- LICENSE | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) 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. -- 2.40.1 From d537502ad9556b886cafbcf2db8c5faa0e5c020f Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Sun, 27 Sep 2020 08:33:09 +0100 Subject: [PATCH 03/53] removed dashmap form commands --- src/commands/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 60d4605..d404de2 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -7,7 +7,7 @@ use regex::Regex; use std::ops::Index; use log::info; use zeroize::Zeroize; -//use dashmap::DashMap; + #[derive(Clone, Debug)] pub enum Commands { -- 2.40.1 From 776d4b733cdd45b902e142889f169dc92003bbef Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Sun, 27 Sep 2020 08:41:38 +0100 Subject: [PATCH 04/53] created server version 3 this works on the face that a ui framework will have an event loop. so changes include: + start sets up the listener and allows the server to ba called + stop sets the server to disconnect other users and close the listener + tick should be called by the event loop this will allow any new connections to be handled, any pending --- src/server/ServerV3.rs | 281 ++++++++++++++++++++++++++++++++++ src/server/client/clientV3.rs | 217 ++++++++++++++++++++++++++ 2 files changed, 498 insertions(+) create mode 100644 src/server/ServerV3.rs create mode 100644 src/server/client/clientV3.rs diff --git a/src/server/ServerV3.rs b/src/server/ServerV3.rs new file mode 100644 index 0000000..6f5eb98 --- /dev/null +++ b/src/server/ServerV3.rs @@ -0,0 +1,281 @@ +use std::{sync::{Mutex, Arc}, net::{TcpStream, TcpListener}, collections::HashMap, io, io::{Write, Read}, thread}; +use crate::{ + server::client::clientV3::Client, + commands::Commands +}; +use crossbeam_channel::{Sender, Receiver, unbounded}; +use log::info; +use std::time::Duration; + +#[derive(Debug)] +pub enum ServerMessages { + RequestUpdate(Arc>), + RequestInfo(String, Arc>), + Disconnect(String), + Shutdown, +} + +pub enum ServerState { + starting, + started, + stopping, + stopped, +} + +// MARK: - server struct +pub struct Server { + pub name: String, + pub address: String, + pub owner: String, + + pub state: ServerState, + + connected_clients: HashMap, + + sender: Sender, + receiver: Receiver, + listener: Option, + + buffer: [u8; 1024], + + client_list_changed_handle: Box, + + // 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::ready, + + // messages & connections + sender, + receiver, + listener: None, + + buffer: [0; 1024], + + // event handles + client_list_changed_handle: Box::new(|_s| info!("Server: client list changed.")), + + // metrics + o2s_rqst: 0, + c2s_msgs: 0, + s2s_msgs: 0, + s2c_msgs: 0, + } + ) + } + + #[allow(dead_code)] + pub fn get_name(&self) -> String { + self.name.clone() + } + + #[allow(dead_code)] + pub fn get_address(&self) -> String { + self.address.clone() + } + + #[allow(dead_code)] + pub fn get_owner(&self) -> String { + self.owner.clone() + } + + pub fn tick(&mut self) { + + // check to see if this server is ready to execute things. + if self.state != ServerState::ready { + () + } + + // check for any server messages in the channel + println!("server: getting messages"); + for i in self.receiver.try_iter() { + match i { + // server calls + ServerMessages::Shutdown => { + self.s2s_msgs += 1; + + println!("server: shutting down..."); + + for (k, v) in self.connected_clients.iter() { + v.sender.send(Commands::Disconnect(None)); + } + self.state = ServerState::stopping; + }, + + // client requests + ServerMessages::RequestUpdate(stream_arc) => { + self.c2s_msgs += 1; + + for (_k, v) in self.connected_clients.iter() { + 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()); + } + } + }, + + // client requests for 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()); + } + }, + + // client disconnect requests + 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())}); + + }, + } + } + + println!("server: checking for new connections"); + if let Ok((mut stream, _addr)) = self.listener.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(); + } + } + + pub fn start(&mut self) -> Result<(), io::Error> { + + let listener = TcpListener::bind(self.address)?; + listener.set_nonblocking(true)?; + + self.listener = Some(listener); + } + + pub fn stop(&mut self) { + info!("server: sending stop message"); + let _ = self.sender.send(ServerMessages::Shutdown); + self.state = ServerState::stopping; + } + + 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(()) + } + + fn recv_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); + } +} diff --git a/src/server/client/clientV3.rs b/src/server/client/clientV3.rs new file mode 100644 index 0000000..df257c3 --- /dev/null +++ b/src/server/client/clientV3.rs @@ -0,0 +1,217 @@ +extern crate regex; + +use std::{ + sync::Arc, + sync::Mutex, + net::{Shutdown, TcpStream}, + io::prelude::*, + io::Error, + //collections::HashMap, + time::{Instant, Duration}, + io, +}; + +use crossbeam_channel::{ + Sender, + Receiver, + TryRecvError, + unbounded +}; + +use log::info; + +use crate::{ + server::ServerV3::ServerMessages, + commands::Commands, +}; + + + +#[derive(Debug)] +pub struct Client { + uuid: String, + username: String, + address: String, + + last_heartbeat: Instant, + + stream: Arc>, + + pub sender: Sender, + receiver: Receiver, + + server_sender: Sender, +} + +impl Client { + 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: Arc::new(Mutex::new(stream)), + uuid: uuid.to_string(), + username: username.to_string(), + address: address.to_string(), + + sender, + receiver, + + server_sender, + + last_heartbeat: 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. + 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()); + }, + + Err(_) => { + // No data was read + }, + } + + 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"); + } + + 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())); + }, + _ => { }, + } + } + } + + 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); + } +} -- 2.40.1 From 0cc1d4a1ef566118dbb2d02c5fb528d5c7872db6 Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Sun, 27 Sep 2020 08:42:29 +0100 Subject: [PATCH 05/53] Update client_profile.rs set the channels t use crossbeam_channels --- src/server/client/client_profile.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/server/client/client_profile.rs b/src/server/client/client_profile.rs index e71db70..7cbd5c5 100644 --- a/src/server/client/client_profile.rs +++ b/src/server/client/client_profile.rs @@ -11,14 +11,13 @@ use std::{ io, }; -use crossbeam::{ +use crossbeam_channel::{ Sender, Receiver, TryRecvError, unbounded }; -use openssl::rsa::Rsa; use log::info; use crate::{ @@ -129,7 +128,7 @@ impl Client { } }, Err(_) => { - // no data was read + // No data was read }, } @@ -172,7 +171,7 @@ impl Client { } }, - /*no data available yet*/ + /*No data available yet*/ Err(TryRecvError::Empty) => {}, _ => {}, } -- 2.40.1 From 7bec2135ff81c6f061fbb6b66657565d90f4b32a Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Sun, 27 Sep 2020 08:43:10 +0100 Subject: [PATCH 06/53] Create About_Panel.rs added an about layer function that returns a view --- src/server/ui/About_Panel.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 src/server/ui/About_Panel.rs diff --git a/src/server/ui/About_Panel.rs b/src/server/ui/About_Panel.rs new file mode 100644 index 0000000..0a84e96 --- /dev/null +++ b/src/server/ui/About_Panel.rs @@ -0,0 +1,11 @@ +use cursive::views::{Dialog, TextView}; +use cursive::view::ViewWrapper; +use cursive::{Printer, View}; + +pub fn About() -> Box { + Box::new( + Dialog::new() + .content("rust chat server written by Mitchel Hardie & Michael Bailey (c) 2020") + .button("Close", |s| {s.pop_layer();}) + ) +} \ No newline at end of file -- 2.40.1 From c9cce63cecbdd48d1e50be4f95ecac4abc6c6051 Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Sun, 27 Sep 2020 08:44:25 +0100 Subject: [PATCH 07/53] Create control_panel.rs creating a control panel for the server --- src/server/ui/control_panel.rs | 64 ++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 src/server/ui/control_panel.rs diff --git a/src/server/ui/control_panel.rs b/src/server/ui/control_panel.rs new file mode 100644 index 0000000..1ea860c --- /dev/null +++ b/src/server/ui/control_panel.rs @@ -0,0 +1,64 @@ +use cursive::{View, Printer, Cursive}; +use cursive::event::{EventResult, Event}; +use cursive::views::{Panel, ResizedView, StackView, LinearLayout, TextView}; +use cursive::view::SizeConstraint; +use crate::server::ServerV3::Server; + +pub fn control_panel(s: &mut Cursive) -> Box { + Box::new( + ResizedView::new( + SizeConstraint::Fixed(s.screen_size().x-8), + SizeConstraint::Fixed(s.screen_size().y-8), + Panel::new( + LinearLayout::horizontal() + .child( + LinearLayout::vertical() + .child( + TextView::new(" ═════╡ Server ╞═════ ") + ) + .child( + TextView::new( + format!("Server name: {}", s.user_data::().unwrap().get_name()) + ) + ) + .child( + TextView::new( + format!("Server host: {}", s.user_data::().unwrap().get_address()) + ) + ) + .child( + TextView::new( + format!("Server owner: {}", s.user_data::().unwrap().get_owner()) + ) + ) + .child( + TextView::new( + format!(" ═════╡ metrics ╞═════ ") + ) + ) + .child( + TextView::new( + format!("Server o2s_rqst: {}", s.user_data::().unwrap().o2s_rqst) + ) + ) + .child( + TextView::new( + format!("Server c2s_msgs: {}", s.user_data::().unwrap().c2s_msgs) + ) + ) + .child( + TextView::new( + format!("Server s2s_msgs: {}", s.user_data::().unwrap().s2s_msgs) + ) + ) + .child( + TextView::new( + format!("Server s2c_msgs: {}", s.user_data::().unwrap().s2c_msgs) + ) + ) + ) + .child() + ) + ) + ) +} \ No newline at end of file -- 2.40.1 From e1b5ffd4ee5d3566df55cab37e18d2fc3d3c43b9 Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Sun, 27 Sep 2020 08:45:23 +0100 Subject: [PATCH 08/53] Update main.rs extracting the ui from the main function --- src/main.rs | 209 +++++++++++++++++++++++++++------------------------- 1 file changed, 108 insertions(+), 101 deletions(-) diff --git a/src/main.rs b/src/main.rs index 0c66d4d..3f26065 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,5 @@ +#![feature(in_band_lifetimes)] + mod client_api; mod commands; mod server; @@ -7,21 +9,28 @@ use cursive::{ Cursive, menu::*, event::Key, - views::{ Dialog, TextView, LinearLayout, ListView, ResizedView, Panel, Menubar }, CursiveExt, align::Align, view::SizeConstraint, + event::Event, + XY, +}; + +use std::{ + time::Duration, + sync::{ + Arc, + Mutex + } }; -//use std::sync::Arc; -use std::time::Duration; -use std::sync::Arc; -use std::sync::Weak; -use std::sync::Mutex; use crossterm::ErrorKind; use log::info; use clap::{App, Arg}; -use crate::server::server_profile::Server; + +use crate::server::ServerV3::Server; +use cursive::views::{Dialog, TextView, Menubar, LinearLayout, ResizedView, ListView, Panel}; +use crate::server::ui::server_view::ServerControlView; fn main() -> Result<(), ErrorKind> { let args = App::new("--rust chat server--") @@ -35,44 +44,70 @@ fn main() -> Result<(), ErrorKind> { .get_matches(); if args.is_present("graphical") { - let mut server = Server::new("Server-01", "0.0.0.0:6000", "noreply@email.com"); - let server_arc = Arc::new(Mutex::new(server)); - let s1 = server_arc.clone(); - let s2 = s1.clone(); - cursive::logger::init(); + let server = Server::new("server-001", "0.0.0.0:6000", "michael bailey"); - 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"); - // setup menu bar - menu_bar(display.menubar(), &server_arc); - - println!("Main: entering loop"); - display.add_layer(control_panel(server_arc)); - display.add_layer(launch_screen()); - display.set_autohide_menu(false); - display.run(); + ServerControlView::new(server.unwrap()); Ok(()) } else { - let mut server = Server::new("Server-01", "0.0.0.0:6000", "noreply@email.com"); + let mut server = crate::server::server_profile::Server::new("Server-01", "0.0.0.0:6000", "noreply@email.com"); server.start()?; loop { std::thread::sleep(Duration::from_secs(1)); } } } + +fn gen_ui() { + // MARK: - setup the server. + info!("Main: init Server"); + let server = Server::new("Server-01", "0.0.0.0:6000", "noreply@email.com"); + let server_arc = Arc::new(Mutex::new(server)); + + info!("Main: init display"); + let mut display = Cursive::default(); + + info!("Main: init cursive logger"); + cursive::logger::init(); + + info!("Main: setting user data"); + display.set_user_data(server_arc); + + // MARK: - setup callbacks + 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()); + display.set_autohide_menu(false); + display.add_global_callback(Event::WindowResize, |s| { + info!("Display: resized!"); + std::process::Command::new("open").args(&["-a","Terminal"]).output().expect("not on mac os"); + let _ = s.pop_layer(); + let p = control_panel(s.screen_size(), s.user_data::>>().unwrap().clone()); + s.add_layer(p); + s.refresh(); + }); + display.set_autorefresh(true); + + + info!("Main: getting sender and pushing events"); + let mut sender = display.cb_sink(); + sender.send(Box::new(|s| { + menu_bar(s.menubar()); + s.add_layer(launch_screen()); + })); + + info!("Main: entering loop"); + display.run(); +} + fn about() -> Dialog { Dialog::new() .content(TextView::new("Rust-Chat-Server\nmade by\n Mitchell Hardie\nMichael Bailey\nMit Licence") .align(Align::center())) - .button("Close", |s| {let _ = s.pop_layer();} ) + .button("Close", |s| { + let _ = s.pop_layer(); + }) } #[allow(dead_code)] @@ -86,14 +121,14 @@ fn launch_screen() -> Dialog { * press for debug (FIXME) * press to exit. ").align(Align::top_left())) - .button("ok", |s| {s.pop_layer();}) + .button("ok", |s| { + s.pop_layer(); + let p = control_panel(s.screen_size(), s.user_data::>>().unwrap().clone()); + s.add_layer(p); + }) } -fn menu_bar(bar: &mut Menubar, server_arc: &Arc>) { - - let s1 = Arc::downgrade(server_arc); - let s2 = Arc::downgrade(server_arc); - +fn menu_bar(bar: &mut Menubar) { bar.add_subtree("Server", MenuTree::new() .leaf("about", @@ -102,24 +137,41 @@ fn menu_bar(bar: &mut Menubar, server_arc: &Arc>) { .leaf("quit", |s| s.quit())) .add_subtree("File", MenuTree::new() - .leaf("Start", move |s| { - let arc = s2.upgrade().unwrap(); - let _ = arc.lock().unwrap().start(); - let _ = s.pop_layer(); - s.add_layer(control_panel(arc)); + .leaf("Start", |s| { + + let user_data_option = s.user_data::>>(); + + if let Some(user_data) = user_data_option { + let arc = user_data.clone(); + let lock_result = arc.lock(); + if let Ok(mut server) = lock_result { + let _ = server.start(); + let _ = s.pop_layer(); + let p = control_panel(s.screen_size(), s.user_data::>>().unwrap().clone()); + s.add_layer(p); + } + } }) - .leaf("Stop", move |s| { - let arc = s1.upgrade().unwrap(); - let _ = arc.lock().unwrap().stop(); - let _ = s.pop_layer(); - s.add_layer(control_panel(arc)); + .leaf("Stop", |s| { + let user_data_option = s.user_data::>>(); + + if let Some(user_data) = user_data_option { + let arc = user_data.clone(); + let lock_result = arc.lock(); + if let Ok(mut server) = lock_result { + let _ = server.stop(); + let _ = s.pop_layer(); + let p = control_panel(s.screen_size(), s.user_data::>>().unwrap().clone()); + s.add_layer(p); + } + } }) .delimiter() // TODO: - create custom debug console .leaf("Debug", |s| {s.toggle_debug_console();})); } -fn control_panel(server_arc: Arc>) -> ResizedView> { +fn control_panel(screen_size: XY, server_arc: Arc>) -> ResizedView> { let mut root = LinearLayout::horizontal(); let mut left = LinearLayout::vertical(); let mut right = ListView::new(); @@ -129,12 +181,16 @@ fn control_panel(server_arc: Arc>) -> ResizedView Date: Sun, 27 Sep 2020 08:46:03 +0100 Subject: [PATCH 09/53] Update mod.rs added new modules for the new ui and server. --- src/server/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/server/mod.rs b/src/server/mod.rs index de07bb5..52d8c4e 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -1,2 +1,4 @@ +pub mod ServerV3; pub mod client; pub mod server_profile; +pub mod ui; \ No newline at end of file -- 2.40.1 From 3276d71f985d61c62416b88d782413fe7720ae8e Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Sun, 27 Sep 2020 08:47:02 +0100 Subject: [PATCH 10/53] Update server_profile.rs removed the need for a arc for server properties --- src/server/server_profile.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/server/server_profile.rs b/src/server/server_profile.rs index dd60c68..4902533 100644 --- a/src/server/server_profile.rs +++ b/src/server/server_profile.rs @@ -61,9 +61,9 @@ impl Server { let (sender, receiver) = unbounded(); Self { - name: Arc::new(name.to_string()), - address: Arc::new(address.to_string()), - author: Arc::new(author.to_string()), + name: name.to_string(), + address: address.to_string(), + author: author.to_string(), connected_clients: Arc::new(Mutex::new(HashMap::new())), thread_pool: ThreadPool::new(16), -- 2.40.1 From 208c5c0b7f587970e173e02339dbf68ac4a2f0e6 Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Sun, 27 Sep 2020 08:47:47 +0100 Subject: [PATCH 11/53] Update server_profile.rs added spaces and other changes --- src/server/server_profile.rs | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/server/server_profile.rs b/src/server/server_profile.rs index 4902533..b42b81c 100644 --- a/src/server/server_profile.rs +++ b/src/server/server_profile.rs @@ -39,9 +39,9 @@ pub enum ServerMessages { // MARK: - server struct pub struct Server { - name: Arc, - address: Arc, - author: Arc, + pub name: String, + pub address: String, + pub author: String, connected_clients: Arc>>, @@ -115,6 +115,7 @@ impl Server { 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 || { @@ -138,7 +139,7 @@ impl Server { if Server::read_data(&mut stream, &mut buffer).unwrap_or(Commands::Error(None)) == Commands::Success(None) { println!("Success Confirmed"); } else { - println!("no success read"); + println!("No success read"); let error = Commands::Error(None); let _ = Server::transmit_data(&mut stream, error.to_string().as_str()); } @@ -146,7 +147,7 @@ impl Server { }, 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)); @@ -182,24 +183,24 @@ impl Server { 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()); }, _ => { @@ -265,7 +266,7 @@ impl Drop for Server { } -/* The new version of the server no long works with these unit +/* The new version of the server No long works with these unit * tests. * They will be fixed soon! * TODO: fix unit tests @@ -290,7 +291,7 @@ mod tests{ static START: Once = Once::new(); /* - * These tests must be executed individually to ensure that no errors + * 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. */ -- 2.40.1 From 4b5914b6252b084a893a32ad9a93fa07d312c598 Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Sun, 27 Sep 2020 08:48:03 +0100 Subject: [PATCH 12/53] Update server_profile.rs removed get client list --- src/server/server_profile.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/server/server_profile.rs b/src/server/server_profile.rs index b42b81c..e5f27ef 100644 --- a/src/server/server_profile.rs +++ b/src/server/server_profile.rs @@ -91,11 +91,6 @@ impl Server { self.author.to_string() } - pub fn get_client_list(&self) -> Vec { - let map = self.connected_clients.lock().unwrap(); - map.iter().map(|(_k, v)| format!("{:?}", v)).collect() - } - pub fn start(&mut self) -> Result<(), io::Error> { println!("server: starting server..."); -- 2.40.1 From 23f8c7adaccf0ce69ed4f2e912f61ccadf5c8567 Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Sun, 27 Sep 2020 08:48:43 +0100 Subject: [PATCH 13/53] Update server_profile.rs updated to account for the new threadpool location --- src/server/server_profile.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/server_profile.rs b/src/server/server_profile.rs index e5f27ef..c6cd161 100644 --- a/src/server/server_profile.rs +++ b/src/server/server_profile.rs @@ -23,11 +23,11 @@ use std::{ 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; +use crate::lib::ThreadPool; #[derive(Debug)] pub enum ServerMessages { -- 2.40.1 From 5775c276dd11576475d0a90c3c23b3618b4b0b6c Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Sun, 27 Sep 2020 08:48:55 +0100 Subject: [PATCH 14/53] Update server_profile.rs removed unused imports --- src/server/server_profile.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/server/server_profile.rs b/src/server/server_profile.rs index c6cd161..a09d18b 100644 --- a/src/server/server_profile.rs +++ b/src/server/server_profile.rs @@ -23,10 +23,6 @@ use std::{ use log::info; use crossbeam_channel::{Sender, Receiver, unbounded}; -//use zeroize::Zeroize; -//use parking_lot::FairMutex; -//use dashmap::DashMap; -//use regex::Regex; use crate::lib::ThreadPool; #[derive(Debug)] -- 2.40.1 From 829b222d5505b8bc0b4aec2c8dab33e242a60fb0 Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Sun, 27 Sep 2020 08:48:59 +0100 Subject: [PATCH 15/53] Update server_profile.rs --- src/server/server_profile.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/server/server_profile.rs b/src/server/server_profile.rs index a09d18b..60a249b 100644 --- a/src/server/server_profile.rs +++ b/src/server/server_profile.rs @@ -25,6 +25,7 @@ use log::info; use crossbeam_channel::{Sender, Receiver, unbounded}; use crate::lib::ThreadPool; + #[derive(Debug)] pub enum ServerMessages { RequestUpdate(Arc>), -- 2.40.1 From e9fb59793a293d963247bfd955171f2252084d0d Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Sun, 27 Sep 2020 08:49:08 +0100 Subject: [PATCH 16/53] Update server_profile.rs removed extern crate uses --- src/server/server_profile.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/server/server_profile.rs b/src/server/server_profile.rs index 60a249b..5c8e6ab 100644 --- a/src/server/server_profile.rs +++ b/src/server/server_profile.rs @@ -1,6 +1,3 @@ -extern crate regex; -extern crate rayon; - use crate::{ server::{ client::client_profile::Client, -- 2.40.1 From d371792cec487d7ae5f8618edfe747adbc1347ed Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Sun, 27 Sep 2020 08:49:44 +0100 Subject: [PATCH 17/53] Update server_profile.rs changed the function in the update list handler --- src/server/server_profile.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/server_profile.rs b/src/server/server_profile.rs index 5c8e6ab..7369fe9 100644 --- a/src/server/server_profile.rs +++ b/src/server/server_profile.rs @@ -66,7 +66,7 @@ impl Server { running: false, - client_list_changed_handle: Box::new(|s| println!("client list: {:#?}", s.get_client_list())) + client_list_changed_handle: Box::new(|_s| println!("help")) } } -- 2.40.1 From 734d79502052ee75e5589808d3fba3c72ec2333e Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Sun, 27 Sep 2020 08:58:07 +0100 Subject: [PATCH 18/53] Create server_view_controller.rs added a server view controller that holds the display and enables events it uses views from other modules to create the ui tree. the cursive user_data store is used to hiold the server --- src/server/ui/server_view_controller.rs | 36 +++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 src/server/ui/server_view_controller.rs diff --git a/src/server/ui/server_view_controller.rs b/src/server/ui/server_view_controller.rs new file mode 100644 index 0000000..e94be7e --- /dev/null +++ b/src/server/ui/server_view_controller.rs @@ -0,0 +1,36 @@ +pub struct ServerControlView { + display: Cursive, + + // MARK: - ViewModel stuff + + server_name: String, + server_host: String, + server_owner: String, + + client_list: Vec, + running: String, +} + +impl ServerViewController { + pub fn new(server: Server) { + + let mut v = Self { + display: Cursive::default(), + server_name: server.get_name().to_string(), + server_host: server.get_address().to_string(), + server_owner: server.get_owner().to_string(), + client_list: Vec::new(), + running: "None".to_string() + }; + + // set global shortcuts + v.display.add_global_callback(Event::CtrlChar('q'), |s| s.quit()); + v.display.add_global_callback(Event::CtrlChar('a'), |s| s.add_layer(About::new())); + + // TODO: - this will be tied to the server run function + // v.display.add_global_callback(Event::Refresh, |s| s.user_data::>().unwrap().); + + fn get_display_channel() -> CbSink { + Cursive::default().cb_sink().clone() + } +} -- 2.40.1 From 454355a1d0153be545d56a3f483d7348e8909c09 Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Sun, 27 Sep 2020 08:58:46 +0100 Subject: [PATCH 19/53] updated server view controller forgot the imports --- src/main.rs | 2 +- src/server/ui/server_view_controller.rs | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 3f26065..2f07c08 100644 --- a/src/main.rs +++ b/src/main.rs @@ -30,7 +30,7 @@ use clap::{App, Arg}; use crate::server::ServerV3::Server; use cursive::views::{Dialog, TextView, Menubar, LinearLayout, ResizedView, ListView, Panel}; -use crate::server::ui::server_view::ServerControlView; +use crate::server::ui::server_view_controller::ServerControlView; fn main() -> Result<(), ErrorKind> { let args = App::new("--rust chat server--") diff --git a/src/server/ui/server_view_controller.rs b/src/server/ui/server_view_controller.rs index e94be7e..afe842f 100644 --- a/src/server/ui/server_view_controller.rs +++ b/src/server/ui/server_view_controller.rs @@ -1,3 +1,10 @@ +use cursive::{Cursive, CursiveExt, CbSink, Printer, View}; +use cursive::event::{EventResult, Event}; +use cursive::views::{ResizedView, Dialog, TextView}; +use cursive::menu::MenuTree; +use cursive::view::ViewWrapper; +use std::sync::Arc; +use crate::server::ServerV3::Server; pub struct ServerControlView { display: Cursive, -- 2.40.1 From 32319c5fab9ca4ede3a7d2920ab0677478c62887 Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Sun, 27 Sep 2020 09:00:11 +0100 Subject: [PATCH 20/53] added module file for ui --- src/server/ui/mod.rs | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 src/server/ui/mod.rs diff --git a/src/server/ui/mod.rs b/src/server/ui/mod.rs new file mode 100644 index 0000000..ac59f49 --- /dev/null +++ b/src/server/ui/mod.rs @@ -0,0 +1,4 @@ +pub mod server_view_controller; +pub mod control_panel; +pub mod About_Panel; +pub mod main_menu; \ No newline at end of file -- 2.40.1 From d426f23e3b59ad3997de27ce435f905409e3058d Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Sun, 27 Sep 2020 10:47:51 +0100 Subject: [PATCH 21/53] moving menu bar to another file --- src/main.rs | 43 -------------------------------------- src/server/ui/main_menu.rs | 8 +++++++ 2 files changed, 8 insertions(+), 43 deletions(-) create mode 100644 src/server/ui/main_menu.rs diff --git a/src/main.rs b/src/main.rs index 2f07c08..a2961ed 100644 --- a/src/main.rs +++ b/src/main.rs @@ -128,49 +128,6 @@ fn launch_screen() -> Dialog { }) } -fn menu_bar(bar: &mut Menubar) { - bar.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", |s| { - - let user_data_option = s.user_data::>>(); - - if let Some(user_data) = user_data_option { - let arc = user_data.clone(); - let lock_result = arc.lock(); - if let Ok(mut server) = lock_result { - let _ = server.start(); - let _ = s.pop_layer(); - let p = control_panel(s.screen_size(), s.user_data::>>().unwrap().clone()); - s.add_layer(p); - } - } - }) - .leaf("Stop", |s| { - let user_data_option = s.user_data::>>(); - - if let Some(user_data) = user_data_option { - let arc = user_data.clone(); - let lock_result = arc.lock(); - if let Ok(mut server) = lock_result { - let _ = server.stop(); - let _ = s.pop_layer(); - let p = control_panel(s.screen_size(), s.user_data::>>().unwrap().clone()); - s.add_layer(p); - } - } - }) - .delimiter() - // TODO: - create custom debug console - .leaf("Debug", |s| {s.toggle_debug_console();})); -} - fn control_panel(screen_size: XY, server_arc: Arc>) -> ResizedView> { let mut root = LinearLayout::horizontal(); let mut left = LinearLayout::vertical(); diff --git a/src/server/ui/main_menu.rs b/src/server/ui/main_menu.rs new file mode 100644 index 0000000..ddaef08 --- /dev/null +++ b/src/server/ui/main_menu.rs @@ -0,0 +1,8 @@ +use cursive::menu::MenuTree; + +pub fn main_Menu() -> MenuTree { + MenuTree::new() + .leaf("About ^+A", |s| s.add_layer(About::new())) + .delimiter() + .leaf("Quit ^+Q", |s| s.quit()) +} \ No newline at end of file -- 2.40.1 From dbef4e6986d9c7fc85a63dfb2ee4b0289e398fa1 Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Sun, 27 Sep 2020 10:48:08 +0100 Subject: [PATCH 22/53] Update mod.rs added the new client to the module tree --- src/server/client/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/server/client/mod.rs b/src/server/client/mod.rs index c0ef8d2..0833e14 100644 --- a/src/server/client/mod.rs +++ b/src/server/client/mod.rs @@ -1 +1,2 @@ pub mod client_profile; +pub mod clientV3; -- 2.40.1 From 1023846b7c061d8ed50dbddd52d7ce57ce438023 Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Sun, 27 Sep 2020 10:48:21 +0100 Subject: [PATCH 23/53] Update ServerV3.rs removed use statement --- src/server/ServerV3.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/server/ServerV3.rs b/src/server/ServerV3.rs index 6f5eb98..c3ec3b5 100644 --- a/src/server/ServerV3.rs +++ b/src/server/ServerV3.rs @@ -1,6 +1,5 @@ use std::{sync::{Mutex, Arc}, net::{TcpStream, TcpListener}, collections::HashMap, io, io::{Write, Read}, thread}; use crate::{ - server::client::clientV3::Client, commands::Commands }; use crossbeam_channel::{Sender, Receiver, unbounded}; -- 2.40.1 From 82292433865362876f5311b24a06e24f36ea6c37 Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Sun, 27 Sep 2020 10:48:46 +0100 Subject: [PATCH 24/53] Update server_view_controller.rs changed the name of the ServercontrolView --- src/server/ui/server_view_controller.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/server/ui/server_view_controller.rs b/src/server/ui/server_view_controller.rs index afe842f..ad64fc6 100644 --- a/src/server/ui/server_view_controller.rs +++ b/src/server/ui/server_view_controller.rs @@ -9,6 +9,8 @@ pub struct ServerControlView { display: Cursive, // MARK: - ViewModel stuff +pub struct ServerViewController { + display: Cursive, server_name: String, server_host: String, -- 2.40.1 From 8aa05bd25a4f6aa54b94e904deb28af0e1531ab0 Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Sun, 27 Sep 2020 16:07:45 +0100 Subject: [PATCH 25/53] renamed the about panel. --- src/server/ui/{About_Panel.rs => about_panel.rs} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename src/server/ui/{About_Panel.rs => about_panel.rs} (57%) diff --git a/src/server/ui/About_Panel.rs b/src/server/ui/about_panel.rs similarity index 57% rename from src/server/ui/About_Panel.rs rename to src/server/ui/about_panel.rs index 0a84e96..5d1136f 100644 --- a/src/server/ui/About_Panel.rs +++ b/src/server/ui/about_panel.rs @@ -2,10 +2,10 @@ use cursive::views::{Dialog, TextView}; use cursive::view::ViewWrapper; use cursive::{Printer, View}; -pub fn About() -> Box { +pub fn about() -> Box { Box::new( Dialog::new() - .content("rust chat server written by Mitchel Hardie & Michael Bailey (c) 2020") + .content(TextView::new("rust chat server written by Mitchel Hardie & Michael Bailey (c) 2020")) .button("Close", |s| {s.pop_layer();}) ) } \ No newline at end of file -- 2.40.1 From e9d7dc3091eeb74593d89ade158f65d395be939d Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Sun, 27 Sep 2020 16:08:12 +0100 Subject: [PATCH 26/53] Update main.rs removed redundent and useless ui code --- src/main.rs | 91 ----------------------------------------------------- 1 file changed, 91 deletions(-) diff --git a/src/main.rs b/src/main.rs index a2961ed..841f6d1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -58,97 +58,6 @@ fn main() -> Result<(), ErrorKind> { } -fn gen_ui() { - // MARK: - setup the server. - info!("Main: init Server"); - let server = Server::new("Server-01", "0.0.0.0:6000", "noreply@email.com"); - let server_arc = Arc::new(Mutex::new(server)); - - info!("Main: init display"); - let mut display = Cursive::default(); - - info!("Main: init cursive logger"); - cursive::logger::init(); - - info!("Main: setting user data"); - display.set_user_data(server_arc); - - // MARK: - setup callbacks - 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()); - display.set_autohide_menu(false); - display.add_global_callback(Event::WindowResize, |s| { - info!("Display: resized!"); - std::process::Command::new("open").args(&["-a","Terminal"]).output().expect("not on mac os"); - let _ = s.pop_layer(); - let p = control_panel(s.screen_size(), s.user_data::>>().unwrap().clone()); - s.add_layer(p); - s.refresh(); - }); - display.set_autorefresh(true); - - - info!("Main: getting sender and pushing events"); - let mut sender = display.cb_sink(); - sender.send(Box::new(|s| { - menu_bar(s.menubar()); - s.add_layer(launch_screen()); - })); - - info!("Main: entering loop"); - display.run(); -} - -fn about() -> Dialog { - Dialog::new() - .content(TextView::new("Rust-Chat-Server\nmade by\n Mitchell Hardie\nMichael Bailey\nMit Licence") - .align(Align::center())) - .button("Close", |s| { - let _ = s.pop_layer(); - }) -} - -#[allow(dead_code)] -fn launch_screen() -> Dialog { - Dialog::new() - .content(TextView::new("\ - Welcome. - - --- Controls --- - * press for menu bar - * press for debug (FIXME) - * press to exit. - ").align(Align::top_left())) - .button("ok", |s| { - s.pop_layer(); - let p = control_panel(s.screen_size(), s.user_data::>>().unwrap().clone()); - s.add_layer(p); - }) -} - -fn control_panel(screen_size: XY, server_arc: Arc>) -> ResizedView> { - let mut root = LinearLayout::horizontal(); - let mut left = LinearLayout::vertical(); - let mut right = ListView::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("---| Server |---")); - left.add_child(TextView::new(format!("name: {}", server_arc.lock().unwrap().name))); - left.add_child(TextView::new(format!("owner: {}", server_arc.lock().unwrap().author))); - left.add_child(TextView::new(format!("host: {}", server_arc.lock().unwrap().address))); - left.add_child(TextView::new(format!("running: {}", server_arc.lock().unwrap().running))); - left.add_child(TextView::new(format!("screen size: {:?}", screen_size))); - - root.add_child(ResizedView::new(SizeConstraint::AtLeast(30), SizeConstraint::Full, Panel::new(left))); - root.add_child(ResizedView::new(SizeConstraint::Full, SizeConstraint::Full, Panel::new(right))); - ResizedView::new(SizeConstraint::Fixed(screen_size.x-4), SizeConstraint::Fixed(screen_size.y-4), Panel::new(root)) -} // MARK: - general testing zone #[cfg(test)] -- 2.40.1 From 2e6c6b9e628cd8fe5542461b4a23a592e64bae62 Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Sun, 27 Sep 2020 16:48:06 +0100 Subject: [PATCH 27/53] optimising imports --- src/client_api/mod.rs | 12 ++++--- src/commands/mod.rs | 10 +++--- src/lib.rs | 5 +-- src/main.rs | 27 +++------------ src/server/client/client_profile.rs | 21 ++++++------ .../client/{clientV3.rs => client_v3.rs} | 19 +++++------ src/server/server_profile.rs | 34 +++++++++---------- src/server/{ServerV3.rs => server_v3.rs} | 2 +- src/server/ui/about_panel.rs | 3 +- src/server/ui/control_panel.rs | 7 ++-- src/server/ui/server_view_controller.rs | 15 ++++---- 11 files changed, 65 insertions(+), 90 deletions(-) rename src/server/client/{clientV3.rs => client_v3.rs} (98%) rename src/server/{ServerV3.rs => server_v3.rs} (98%) diff --git a/src/client_api/mod.rs b/src/client_api/mod.rs index 1af8627..3181ac2 100644 --- a/src/client_api/mod.rs +++ b/src/client_api/mod.rs @@ -1,11 +1,13 @@ -use std::{net::TcpStream, io::{Write, Read}, io}; -use crate::{ - server::client::client_profile::Client, - commands::Commands, -}; +use std::{io::{Read, Write}, io, net::TcpStream}; use std::time::Duration; + use zeroize::Zeroize; +use crate::{ + commands::Commands, + server::client::client_profile::Client, +}; + pub struct ClientApi { socket: TcpStream, addr: String, diff --git a/src/commands/mod.rs b/src/commands/mod.rs index d404de2..c71fbc9 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -1,11 +1,11 @@ -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; diff --git a/src/lib.rs b/src/lib.rs index 4e15e7d..95cbc5a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,8 @@ -use std::thread; -use crossbeam::{unbounded , Sender, Receiver}; use std::sync::Arc; use std::sync::Mutex; +use std::thread; + +use crossbeam::{Receiver, Sender, unbounded}; enum Message { NewJob(Job), diff --git a/src/main.rs b/src/main.rs index 841f6d1..0269b99 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,32 +5,13 @@ mod commands; mod server; mod lib; -use cursive::{ - Cursive, - menu::*, - event::Key, - CursiveExt, - align::Align, - view::SizeConstraint, - event::Event, - XY, -}; +use std::time::Duration; -use std::{ - time::Duration, - sync::{ - Arc, - Mutex - } -}; -use crossterm::ErrorKind; -use log::info; use clap::{App, Arg}; +use crossterm::ErrorKind; - -use crate::server::ServerV3::Server; -use cursive::views::{Dialog, TextView, Menubar, LinearLayout, ResizedView, ListView, Panel}; -use crate::server::ui::server_view_controller::ServerControlView; +use crate::server::server_v3::Server; +use crate::server::ui::server_view_controller::ServerViewController; fn main() -> Result<(), ErrorKind> { let args = App::new("--rust chat server--") diff --git a/src/server/client/client_profile.rs b/src/server/client/client_profile.rs index 7cbd5c5..cce53c0 100644 --- a/src/server/client/client_profile.rs +++ b/src/server/client/client_profile.rs @@ -1,31 +1,30 @@ extern crate regex; use std::{ - sync::Arc, - sync::Mutex, - net::{Shutdown, TcpStream}, - io::prelude::*, - io::Error, - //collections::HashMap, - time::{Instant, Duration}, io, + io::Error, + io::prelude::*, + net::{Shutdown, TcpStream}, + sync::Arc, + //collections::HashMap, + sync::Mutex, + time::{Duration, Instant}, }; use crossbeam_channel::{ - Sender, Receiver, + Sender, TryRecvError, unbounded }; - use log::info; use crate::{ + commands::Commands, server::{ //server_profile::Server, server_profile::ServerMessages, - }, - commands::Commands + } }; diff --git a/src/server/client/clientV3.rs b/src/server/client/client_v3.rs similarity index 98% rename from src/server/client/clientV3.rs rename to src/server/client/client_v3.rs index df257c3..d0688dd 100644 --- a/src/server/client/clientV3.rs +++ b/src/server/client/client_v3.rs @@ -1,28 +1,27 @@ extern crate regex; use std::{ - sync::Arc, - sync::Mutex, - net::{Shutdown, TcpStream}, - io::prelude::*, - io::Error, - //collections::HashMap, - time::{Instant, Duration}, io, + io::Error, + io::prelude::*, + net::{Shutdown, TcpStream}, + sync::Arc, + //collections::HashMap, + sync::Mutex, + time::{Duration, Instant}, }; use crossbeam_channel::{ - Sender, Receiver, + Sender, TryRecvError, unbounded }; - use log::info; use crate::{ - server::ServerV3::ServerMessages, commands::Commands, + server::server_v3::ServerMessages, }; diff --git a/src/server/server_profile.rs b/src/server/server_profile.rs index 7369fe9..7a68729 100644 --- a/src/server/server_profile.rs +++ b/src/server/server_profile.rs @@ -1,28 +1,26 @@ +use std::{ + collections::HashMap, + io, + io::Error, + io::prelude::*, + net::{TcpListener, TcpStream}, + sync::{Arc, Mutex}, + thread, + time::Duration +}; + +use crossbeam_channel::{Receiver, Sender, unbounded}; +use log::info; + use crate::{ + commands::Commands, 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 crate::lib::ThreadPool; - #[derive(Debug)] pub enum ServerMessages { RequestUpdate(Arc>), diff --git a/src/server/ServerV3.rs b/src/server/server_v3.rs similarity index 98% rename from src/server/ServerV3.rs rename to src/server/server_v3.rs index c3ec3b5..bd47828 100644 --- a/src/server/ServerV3.rs +++ b/src/server/server_v3.rs @@ -1,4 +1,4 @@ -use std::{sync::{Mutex, Arc}, net::{TcpStream, TcpListener}, collections::HashMap, io, io::{Write, Read}, thread}; +use std::{collections::HashMap, io, io::{Read, Write}, net::{TcpListener, TcpStream}, sync::{Arc, Mutex}}; use crate::{ commands::Commands }; diff --git a/src/server/ui/about_panel.rs b/src/server/ui/about_panel.rs index 5d1136f..16e29ef 100644 --- a/src/server/ui/about_panel.rs +++ b/src/server/ui/about_panel.rs @@ -1,6 +1,5 @@ use cursive::views::{Dialog, TextView}; -use cursive::view::ViewWrapper; -use cursive::{Printer, View}; +use cursive::View; pub fn about() -> Box { Box::new( diff --git a/src/server/ui/control_panel.rs b/src/server/ui/control_panel.rs index 1ea860c..2e4a246 100644 --- a/src/server/ui/control_panel.rs +++ b/src/server/ui/control_panel.rs @@ -1,9 +1,8 @@ -use cursive::{View, Printer, Cursive}; -use cursive::event::{EventResult, Event}; -use cursive::views::{Panel, ResizedView, StackView, LinearLayout, TextView}; +use cursive::{Cursive, View}; use cursive::view::SizeConstraint; -use crate::server::ServerV3::Server; +use cursive::views::{LinearLayout, ListView, Panel, ResizedView, TextView}; +use crate::server::server_v3::Server; pub fn control_panel(s: &mut Cursive) -> Box { Box::new( ResizedView::new( diff --git a/src/server/ui/server_view_controller.rs b/src/server/ui/server_view_controller.rs index ad64fc6..31e88da 100644 --- a/src/server/ui/server_view_controller.rs +++ b/src/server/ui/server_view_controller.rs @@ -1,12 +1,9 @@ -use cursive::{Cursive, CursiveExt, CbSink, Printer, View}; -use cursive::event::{EventResult, Event}; -use cursive::views::{ResizedView, Dialog, TextView}; -use cursive::menu::MenuTree; -use cursive::view::ViewWrapper; -use std::sync::Arc; -use crate::server::ServerV3::Server; -pub struct ServerControlView { - display: Cursive, +use cursive::{CbSink, Cursive, CursiveExt}; + +use crate::server::server_v3::Server; +use crate::server::ui::about_panel::about; +use crate::server::ui::main_menu::main_menu; +use cursive::event::Event; // MARK: - ViewModel stuff pub struct ServerViewController { -- 2.40.1 From ef0d2c23d9fbdaa18ef790bee5fbb5d5acadc2d0 Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Sun, 27 Sep 2020 16:48:38 +0100 Subject: [PATCH 28/53] optimising imports optimising imports --- src/main.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main.rs b/src/main.rs index 0269b99..f29531a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -43,11 +43,12 @@ fn main() -> Result<(), ErrorKind> { // 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::collections::HashMap; + + use crate::client_api::ClientApi; + use crate::commands::Commands; + use crate::server::server_profile::Server; #[test] fn test_server_info() { -- 2.40.1 From cfd9388979aea1425661a92e2d2fe9b30f976a55 Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Sun, 27 Sep 2020 16:49:10 +0100 Subject: [PATCH 29/53] Update main.rs removing useless macros --- src/main.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index f29531a..b8f1d97 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,3 @@ -#![feature(in_band_lifetimes)] - mod client_api; mod commands; mod server; -- 2.40.1 From e98c5c58a5764aa6a9627f0acfa1908f513dd7d6 Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Sun, 27 Sep 2020 16:50:09 +0100 Subject: [PATCH 30/53] Allowing dead code --- src/client_api/mod.rs | 6 ++++++ src/commands/mod.rs | 1 + src/lib.rs | 1 + src/server/client/client_v3.rs | 4 ++++ src/server/server_profile.rs | 2 ++ src/server/server_v3.rs | 7 +++++++ src/server/ui/control_panel.rs | 1 + 7 files changed, 22 insertions(+) diff --git a/src/client_api/mod.rs b/src/client_api/mod.rs index 3181ac2..28c9075 100644 --- a/src/client_api/mod.rs +++ b/src/client_api/mod.rs @@ -8,6 +8,7 @@ use crate::{ server::client::client_profile::Client, }; +#[allow(dead_code)] pub struct ClientApi { socket: TcpStream, addr: String, @@ -17,6 +18,8 @@ pub struct ClientApi { } impl ClientApi { + + #[allow(dead_code)] pub fn new(addr: &str) -> Result { let socket = TcpStream::connect(addr)?; @@ -31,14 +34,17 @@ impl ClientApi { Ok(a) } + #[allow(dead_code)] pub fn set_on_client_add(&mut self, func: fn(Client) -> ()) { self.on_client_add_handle = func; } + #[allow(dead_code)] pub fn set_on_client_removed(&mut self, func: fn(String) -> ()) { self.on_client_remove_handle = func; } + #[allow(dead_code)] pub fn get_info(host: &str) -> Result { let mut buffer: [u8; 1024] = [0; 1024]; let addr = host.parse().unwrap(); diff --git a/src/commands/mod.rs b/src/commands/mod.rs index c71fbc9..9d968e0 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -52,6 +52,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 index 95cbc5a..93a4a81 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,6 +17,7 @@ pub struct ThreadPool{ type Job = Box; +#[allow(dead_code)] impl ThreadPool{ /// Create a new ThreadPool. /// diff --git a/src/server/client/client_v3.rs b/src/server/client/client_v3.rs index d0688dd..d7b5047 100644 --- a/src/server/client/client_v3.rs +++ b/src/server/client/client_v3.rs @@ -43,6 +43,7 @@ pub struct Client { } 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(); @@ -83,6 +84,7 @@ impl Client { } // TODO: - add heartbeat timer. + #[allow(dead_code)] pub fn handle_connection(&mut self) { let mut buffer = [0; 1024]; @@ -180,6 +182,7 @@ impl Client { 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); @@ -195,6 +198,7 @@ impl Client { } } + #[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); diff --git a/src/server/server_profile.rs b/src/server/server_profile.rs index 7a68729..4ad951f 100644 --- a/src/server/server_profile.rs +++ b/src/server/server_profile.rs @@ -30,6 +30,7 @@ pub enum ServerMessages { } // MARK: - server struct +#[allow(dead_code)] pub struct Server { pub name: String, pub address: String, @@ -213,6 +214,7 @@ impl Server { Ok(()) } + #[allow(dead_code)] pub fn stop(&mut self) { info!("server: sending stop message"); let _ = self.sender.send(ServerMessages::Shutdown); diff --git a/src/server/server_v3.rs b/src/server/server_v3.rs index bd47828..3f2b551 100644 --- a/src/server/server_v3.rs +++ b/src/server/server_v3.rs @@ -14,6 +14,7 @@ pub enum ServerMessages { Shutdown, } +#[allow(dead_code)] pub enum ServerState { starting, started, @@ -22,6 +23,7 @@ pub enum ServerState { } // MARK: - server struct +#[allow(dead_code)] pub struct Server { pub name: String, pub address: String, @@ -95,6 +97,7 @@ impl Server { self.owner.clone() } + #[allow(dead_code)] pub fn tick(&mut self) { // check to see if this server is ready to execute things. @@ -232,6 +235,7 @@ impl Server { } } + #[allow(dead_code)] pub fn start(&mut self) -> Result<(), io::Error> { let listener = TcpListener::bind(self.address)?; @@ -240,12 +244,14 @@ impl Server { self.listener = Some(listener); } + #[allow(dead_code)] pub fn stop(&mut self) { info!("server: sending stop message"); let _ = self.sender.send(ServerMessages::Shutdown); self.state = ServerState::stopping; } + #[allow(dead_code)] fn send_data(stream: &mut TcpStream, data: &str) -> Result<(), io::Error>{ println!("Transmitting..."); println!("data: {}", data); @@ -260,6 +266,7 @@ impl Server { Ok(()) } + #[allow(dead_code)] fn recv_data(stream: &mut TcpStream, buffer: &mut [u8; 1024]) -> Result { let _ = stream.read(buffer)?; let command = Commands::from(buffer); diff --git a/src/server/ui/control_panel.rs b/src/server/ui/control_panel.rs index 2e4a246..fa6d50d 100644 --- a/src/server/ui/control_panel.rs +++ b/src/server/ui/control_panel.rs @@ -3,6 +3,7 @@ use cursive::view::SizeConstraint; use cursive::views::{LinearLayout, ListView, Panel, ResizedView, TextView}; use crate::server::server_v3::Server; +#[allow(dead_code)] pub fn control_panel(s: &mut Cursive) -> Box { Box::new( ResizedView::new( -- 2.40.1 From 74452bc679d9aad2596f7fccce3e3ab9a2a0d767 Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Sun, 27 Sep 2020 16:50:24 +0100 Subject: [PATCH 31/53] removed unessesery function --- src/client_api/mod.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/client_api/mod.rs b/src/client_api/mod.rs index 28c9075..46254c8 100644 --- a/src/client_api/mod.rs +++ b/src/client_api/mod.rs @@ -72,8 +72,4 @@ impl ClientApi { } } } - - pub fn get_clients(&self) { - - } } -- 2.40.1 From 81e7d9b2110978927002b64f73ddbf2e6f68253a Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Sun, 27 Sep 2020 16:50:27 +0100 Subject: [PATCH 32/53] Update mod.rs --- src/commands/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 9d968e0..a4a50a2 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -8,7 +8,6 @@ use log::info; use regex::Regex; use zeroize::Zeroize; - #[derive(Clone, Debug)] pub enum Commands { /* TODO: this is the new commands system but still needs work. -- 2.40.1 From 7e17b2e0f6eb5a9d15744f8268c30d2db84a2f45 Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Sun, 27 Sep 2020 16:50:54 +0100 Subject: [PATCH 33/53] Update main.rs renamed Server view controller --- src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index b8f1d97..67ad69a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -26,7 +26,7 @@ fn main() -> Result<(), ErrorKind> { let server = Server::new("server-001", "0.0.0.0:6000", "michael bailey"); - ServerControlView::new(server.unwrap()); + ServerViewController::new(server.unwrap()); Ok(()) } else { let mut server = crate::server::server_profile::Server::new("Server-01", "0.0.0.0:6000", "noreply@email.com"); -- 2.40.1 From df39e03f9378c7e666016e9f8a83b5752659187f Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Sun, 27 Sep 2020 16:51:54 +0100 Subject: [PATCH 34/53] renamed functions --- src/server/client/mod.rs | 2 +- src/server/mod.rs | 2 +- src/server/ui/mod.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/server/client/mod.rs b/src/server/client/mod.rs index 0833e14..78bc78a 100644 --- a/src/server/client/mod.rs +++ b/src/server/client/mod.rs @@ -1,2 +1,2 @@ pub mod client_profile; -pub mod clientV3; +pub mod client_v3; diff --git a/src/server/mod.rs b/src/server/mod.rs index 52d8c4e..5803b99 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -1,4 +1,4 @@ -pub mod ServerV3; +pub mod server_v3; pub mod client; pub mod server_profile; pub mod ui; \ No newline at end of file diff --git a/src/server/ui/mod.rs b/src/server/ui/mod.rs index ac59f49..207759d 100644 --- a/src/server/ui/mod.rs +++ b/src/server/ui/mod.rs @@ -1,4 +1,4 @@ pub mod server_view_controller; pub mod control_panel; -pub mod About_Panel; +pub mod about_panel; pub mod main_menu; \ No newline at end of file -- 2.40.1 From 9ce38aeb49433d44cf53a979f192f3a3e543c087 Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Sun, 27 Sep 2020 16:52:16 +0100 Subject: [PATCH 35/53] fixed warning on unused result --- src/server/server_profile.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/server_profile.rs b/src/server/server_profile.rs index 4ad951f..da8dd51 100644 --- a/src/server/server_profile.rs +++ b/src/server/server_profile.rs @@ -92,7 +92,7 @@ impl Server { // MARK: - creating clones of the server property references let name = self.name.clone(); #[allow(dead_code)] - let address = self.address.clone(); + let _ = self.address.clone(); let author = self.author.clone(); let connected_clients = self.connected_clients.clone(); let sender = self.sender.clone(); -- 2.40.1 From e693d8fdda4962e3638e9d43f020e4e8dfa3205a Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Sun, 27 Sep 2020 16:52:28 +0100 Subject: [PATCH 36/53] optimised imports --- src/server/server_v3.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/server/server_v3.rs b/src/server/server_v3.rs index 3f2b551..9dd7e45 100644 --- a/src/server/server_v3.rs +++ b/src/server/server_v3.rs @@ -1,10 +1,13 @@ use std::{collections::HashMap, io, io::{Read, Write}, net::{TcpListener, TcpStream}, sync::{Arc, Mutex}}; +use std::time::Duration; + +use crossbeam_channel::{Receiver, Sender, unbounded}; +use log::info; + use crate::{ commands::Commands }; -use crossbeam_channel::{Sender, Receiver, unbounded}; -use log::info; -use std::time::Duration; +use crate::server::client::client_v3::Client; #[derive(Debug)] pub enum ServerMessages { -- 2.40.1 From f1838c1d2383be8a4f7a9f1369778d9269f1840a Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Sun, 27 Sep 2020 16:52:54 +0100 Subject: [PATCH 37/53] added eq and PartialEq support to serverState --- src/server/server_v3.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/server/server_v3.rs b/src/server/server_v3.rs index 9dd7e45..f190fe0 100644 --- a/src/server/server_v3.rs +++ b/src/server/server_v3.rs @@ -18,6 +18,7 @@ pub enum ServerMessages { } #[allow(dead_code)] +#[derive(Eq, PartialEq)] pub enum ServerState { starting, started, -- 2.40.1 From 610ec36836f7937a88fff68f8b6ac5ab27743436 Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Sun, 27 Sep 2020 16:53:30 +0100 Subject: [PATCH 38/53] Update server_v3.rs renamed serverstate states --- src/server/server_v3.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/server/server_v3.rs b/src/server/server_v3.rs index f190fe0..ac5d7c1 100644 --- a/src/server/server_v3.rs +++ b/src/server/server_v3.rs @@ -20,10 +20,10 @@ pub enum ServerMessages { #[allow(dead_code)] #[derive(Eq, PartialEq)] pub enum ServerState { - starting, - started, - stopping, - stopped, + Starting, + Started, + Stopping, + Stopped, } // MARK: - server struct @@ -65,7 +65,7 @@ impl Server { address: address.to_string(), owner: author.to_string(), connected_clients: HashMap::new(), - state: ServerState::ready, + state: ServerState::Stopped, // messages & connections sender, @@ -105,7 +105,7 @@ impl Server { pub fn tick(&mut self) { // check to see if this server is ready to execute things. - if self.state != ServerState::ready { + if self.state == ServerState::Stopped { () } @@ -122,7 +122,7 @@ impl Server { for (k, v) in self.connected_clients.iter() { v.sender.send(Commands::Disconnect(None)); } - self.state = ServerState::stopping; + self.state = ServerState::Stopping; }, // client requests @@ -252,7 +252,7 @@ impl Server { pub fn stop(&mut self) { info!("server: sending stop message"); let _ = self.sender.send(ServerMessages::Shutdown); - self.state = ServerState::stopping; + self.state = ServerState::Stopping; } #[allow(dead_code)] -- 2.40.1 From 2ae3ce853495269af81ab55f5a57e64d183ab5d4 Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Sun, 27 Sep 2020 16:54:18 +0100 Subject: [PATCH 39/53] Update server_v3.rs fixed the start function --- src/server/server_v3.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/server/server_v3.rs b/src/server/server_v3.rs index ac5d7c1..2d10c05 100644 --- a/src/server/server_v3.rs +++ b/src/server/server_v3.rs @@ -242,10 +242,11 @@ impl Server { #[allow(dead_code)] pub fn start(&mut self) -> Result<(), io::Error> { - let listener = TcpListener::bind(self.address)?; + let listener = TcpListener::bind(&self.address)?; listener.set_nonblocking(true)?; self.listener = Some(listener); + Ok(()) } #[allow(dead_code)] -- 2.40.1 From 257b2365dc8c4386f2dcd5b5b92283b6178f1746 Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Sun, 27 Sep 2020 16:54:33 +0100 Subject: [PATCH 40/53] Update server_v3.rs fixed reference issue with options --- src/server/server_v3.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/server_v3.rs b/src/server/server_v3.rs index 2d10c05..9cd2c23 100644 --- a/src/server/server_v3.rs +++ b/src/server/server_v3.rs @@ -183,7 +183,7 @@ impl Server { } println!("server: checking for new connections"); - if let Ok((mut stream, _addr)) = self.listener.accept() { + 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); -- 2.40.1 From 21f272aa1b7466af2e27f1383a272773e400e807 Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Sun, 27 Sep 2020 16:54:48 +0100 Subject: [PATCH 41/53] Update server_v3.rs fixed unused variable warning --- src/server/server_v3.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/server/server_v3.rs b/src/server/server_v3.rs index 9cd2c23..af74dbf 100644 --- a/src/server/server_v3.rs +++ b/src/server/server_v3.rs @@ -119,8 +119,8 @@ impl Server { println!("server: shutting down..."); - for (k, v) in self.connected_clients.iter() { - v.sender.send(Commands::Disconnect(None)); + for (_k, v) in self.connected_clients.iter() { + let _ = v.sender.send(Commands::Disconnect(None)); } self.state = ServerState::Stopping; }, -- 2.40.1 From f021804521bceef0ef0f11dcbf9b2dc02108f4ba Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Sun, 27 Sep 2020 16:55:26 +0100 Subject: [PATCH 42/53] Update client_v3.rs removed redundant error clause in handle connection --- src/server/client/client_v3.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/server/client/client_v3.rs b/src/server/client/client_v3.rs index d7b5047..deb675a 100644 --- a/src/server/client/client_v3.rs +++ b/src/server/client/client_v3.rs @@ -125,9 +125,7 @@ impl Client { self.send_data(Commands::Error(None).to_string().as_str()); }, - Err(_) => { - // No data was read - }, + } println!("buffer"); -- 2.40.1 From 1e75844ee97efeb9a984ec716e63fd70949baa90 Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Sun, 27 Sep 2020 16:56:09 +0100 Subject: [PATCH 43/53] Update control_panel.rs fixed issue where i didnt put anything into a child function call --- src/server/ui/control_panel.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/server/ui/control_panel.rs b/src/server/ui/control_panel.rs index fa6d50d..f0065d8 100644 --- a/src/server/ui/control_panel.rs +++ b/src/server/ui/control_panel.rs @@ -57,7 +57,9 @@ pub fn control_panel(s: &mut Cursive) -> Box { ) ) ) - .child() + .child( + ListView::new() + ) ) ) ) -- 2.40.1 From 779fd6858c04124d820849cfd8e7cf72aa9417e4 Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Sun, 27 Sep 2020 16:56:12 +0100 Subject: [PATCH 44/53] Update control_panel.rs --- src/server/ui/control_panel.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/server/ui/control_panel.rs b/src/server/ui/control_panel.rs index f0065d8..1e578fb 100644 --- a/src/server/ui/control_panel.rs +++ b/src/server/ui/control_panel.rs @@ -3,6 +3,7 @@ use cursive::view::SizeConstraint; use cursive::views::{LinearLayout, ListView, Panel, ResizedView, TextView}; use crate::server::server_v3::Server; + #[allow(dead_code)] pub fn control_panel(s: &mut Cursive) -> Box { Box::new( -- 2.40.1 From 11e7192dcf4a70d6c58443526442e88468d67e74 Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Sun, 27 Sep 2020 16:56:29 +0100 Subject: [PATCH 45/53] Update main_menu.rs renamed the main menu function --- src/server/ui/main_menu.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/server/ui/main_menu.rs b/src/server/ui/main_menu.rs index ddaef08..1e1fd58 100644 --- a/src/server/ui/main_menu.rs +++ b/src/server/ui/main_menu.rs @@ -1,6 +1,8 @@ use cursive::menu::MenuTree; -pub fn main_Menu() -> MenuTree { +use crate::server::ui::about_panel::about; + +pub fn main_menu() -> MenuTree { MenuTree::new() .leaf("About ^+A", |s| s.add_layer(About::new())) .delimiter() -- 2.40.1 From 6277278e4a1ba0e9c6d8ce1f15a33d9107d14acf Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Sun, 27 Sep 2020 16:56:53 +0100 Subject: [PATCH 46/53] Update server_v3.rs added unit tests --- src/server/server_v3.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/server/server_v3.rs b/src/server/server_v3.rs index af74dbf..8a271b0 100644 --- a/src/server/server_v3.rs +++ b/src/server/server_v3.rs @@ -290,3 +290,22 @@ impl Drop for Server { let _ = self.sender.send(ServerMessages::Shutdown); } } + +#[cfg(test)] +mod tests { + use crate::server::server_v3::Server; + + #[test] + fn test_creation() { + let server = Server::new( + "test server", + "0.0.0.0:6000", + "michael" + ); + + + assert_eq!(server.name, "test server"); + assert_eq!(server.address, "0.0.0.0:6000") + assert_eq!(server) + } +} \ No newline at end of file -- 2.40.1 From e1e5bbd29e7e3a504b967606fd6e01a15e08b906 Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Sun, 27 Sep 2020 16:57:07 +0100 Subject: [PATCH 47/53] Update server_view_controller.rs --- src/server/ui/server_view_controller.rs | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/server/ui/server_view_controller.rs b/src/server/ui/server_view_controller.rs index 31e88da..6cd0c2d 100644 --- a/src/server/ui/server_view_controller.rs +++ b/src/server/ui/server_view_controller.rs @@ -6,6 +6,15 @@ use crate::server::ui::main_menu::main_menu; use cursive::event::Event; // MARK: - ViewModel stuff +#[allow(dead_code)] +pub enum UpdateTypes { + AddClient() +} + +/// # ServerViewConroller +/// +/// This Struct contains all the controller logic to allow the server to interact with the view +#[allow(dead_code)] pub struct ServerViewController { display: Cursive, @@ -17,6 +26,7 @@ pub struct ServerViewController { running: String, } +#[allow(dead_code)] impl ServerViewController { pub fn new(server: Server) { @@ -31,11 +41,20 @@ impl ServerViewController { // set global shortcuts v.display.add_global_callback(Event::CtrlChar('q'), |s| s.quit()); - v.display.add_global_callback(Event::CtrlChar('a'), |s| s.add_layer(About::new())); + v.display.add_global_callback(Event::CtrlChar('a'), |s| s.add_layer(about())); + + // seting up menubar + v.display.menubar().add_subtree("Server", main_menu()); + v.display.set_autohide_menu(false) + + // setup the display menubar. // TODO: - this will be tied to the server run function // v.display.add_global_callback(Event::Refresh, |s| s.user_data::>().unwrap().); + } + + fn get_display_channel() -> CbSink { Cursive::default().cb_sink().clone() } -- 2.40.1 From e212865be10f5367fbb84e5599cde73aff74d2dc Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Sun, 27 Sep 2020 16:57:23 +0100 Subject: [PATCH 48/53] adding documentation --- src/server/client/client_v3.rs | 5 ++--- src/server/ui/server_view_controller.rs | 1 - 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/server/client/client_v3.rs b/src/server/client/client_v3.rs index deb675a..09e4555 100644 --- a/src/server/client/client_v3.rs +++ b/src/server/client/client_v3.rs @@ -24,8 +24,6 @@ use crate::{ server::server_v3::ServerMessages, }; - - #[derive(Debug)] pub struct Client { uuid: String, @@ -41,8 +39,9 @@ pub struct Client { 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(); diff --git a/src/server/ui/server_view_controller.rs b/src/server/ui/server_view_controller.rs index 6cd0c2d..bebecc4 100644 --- a/src/server/ui/server_view_controller.rs +++ b/src/server/ui/server_view_controller.rs @@ -5,7 +5,6 @@ use crate::server::ui::about_panel::about; use crate::server::ui::main_menu::main_menu; use cursive::event::Event; - // MARK: - ViewModel stuff #[allow(dead_code)] pub enum UpdateTypes { AddClient() -- 2.40.1 From 3fbfc7f3d88cb17894fb079712724f67d438aab9 Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Sun, 27 Sep 2020 16:58:24 +0100 Subject: [PATCH 49/53] Update main_menu.rs fixed accidental renaming --- src/server/ui/main_menu.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/ui/main_menu.rs b/src/server/ui/main_menu.rs index 1e1fd58..614c126 100644 --- a/src/server/ui/main_menu.rs +++ b/src/server/ui/main_menu.rs @@ -4,7 +4,7 @@ use crate::server::ui::about_panel::about; pub fn main_menu() -> MenuTree { MenuTree::new() - .leaf("About ^+A", |s| s.add_layer(About::new())) + .leaf("About ^+A", |s| s.add_layer(about())) .delimiter() .leaf("Quit ^+Q", |s| s.quit()) } \ No newline at end of file -- 2.40.1 From f13ee420637021e166e7b8430556dd1a48f3a5c5 Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Sun, 27 Sep 2020 16:58:39 +0100 Subject: [PATCH 50/53] Update server v3 tests --- src/server/server_v3.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/server/server_v3.rs b/src/server/server_v3.rs index 8a271b0..d3eb857 100644 --- a/src/server/server_v3.rs +++ b/src/server/server_v3.rs @@ -305,7 +305,7 @@ mod tests { assert_eq!(server.name, "test server"); - assert_eq!(server.address, "0.0.0.0:6000") - assert_eq!(server) + assert_eq!(server.address, "0.0.0.0:6000"); + assert_eq!(server.owner, "michael"); } } \ No newline at end of file -- 2.40.1 From f06f4e3ca272811aa9cefdb9d8884ab6511e15c6 Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Sun, 7 Feb 2021 18:16:08 +0000 Subject: [PATCH 51/53] Moving files around and implementing more of the client manager. --- Cargo.toml | 8 +- src/app/Traits.rs | 12 + src/app/bundle.rs | 11 + src/app/mod.rs | 2 + src/client_api/mod.rs | 75 --- src/lib.rs | 106 --- src/lib/mod.rs | 108 +++ src/lib/prelude.rs | 0 .../client/client_profile.rs | 1 - .../client_management}/client/client_v3.rs | 20 +- .../server/client_management/client/mod.rs | 30 + .../server/client_management/client/traits.rs | 13 + src/lib/server/client_management/mod.rs | 52 ++ src/lib/server/client_management/traits.rs | 12 + src/lib/server/config.rs | 9 + src/lib/server/mod.rs | 5 + src/lib/server/server.rs | 633 ++++++++++++++++++ src/lib/server/server_v3.rs | 372 ++++++++++ src/main.rs | 138 ++-- src/server/client/mod.rs | 2 - src/server/mod.rs | 4 - src/server/server_profile.rs | 612 ----------------- src/server/server_v3.rs | 311 --------- src/server/ui/about_panel.rs | 10 - src/server/ui/control_panel.rs | 67 -- src/server/ui/main_menu.rs | 10 - src/server/ui/mod.rs | 4 - src/server/ui/server_view_controller.rs | 60 -- 28 files changed, 1336 insertions(+), 1351 deletions(-) create mode 100644 src/app/Traits.rs create mode 100644 src/app/bundle.rs create mode 100644 src/app/mod.rs delete mode 100644 src/client_api/mod.rs delete mode 100644 src/lib.rs create mode 100644 src/lib/mod.rs create mode 100644 src/lib/prelude.rs rename src/{server => lib/server/client_management}/client/client_profile.rs (99%) rename src/{server => lib/server/client_management}/client/client_v3.rs (95%) create mode 100644 src/lib/server/client_management/client/mod.rs create mode 100644 src/lib/server/client_management/client/traits.rs create mode 100644 src/lib/server/client_management/mod.rs create mode 100644 src/lib/server/client_management/traits.rs create mode 100644 src/lib/server/config.rs create mode 100644 src/lib/server/mod.rs create mode 100644 src/lib/server/server.rs create mode 100644 src/lib/server/server_v3.rs delete mode 100644 src/server/client/mod.rs delete mode 100644 src/server/mod.rs delete mode 100644 src/server/server_profile.rs delete mode 100644 src/server/server_v3.rs delete mode 100644 src/server/ui/about_panel.rs delete mode 100644 src/server/ui/control_panel.rs delete mode 100644 src/server/ui/main_menu.rs delete mode 100644 src/server/ui/mod.rs delete mode 100644 src/server/ui/server_view_controller.rs diff --git a/Cargo.toml b/Cargo.toml index 6b6e30e..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,11 +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"] } -rustls = "0.18.1" -webpki = "0.21.3" -webpki-roots = "0.20.0" +serde = { version = "1.0", features = ["derive"] } +url = "2.2.0" [profile.dev] 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 46254c8..0000000 --- a/src/client_api/mod.rs +++ /dev/null @@ -1,75 +0,0 @@ -use std::{io::{Read, Write}, io, net::TcpStream}; -use std::time::Duration; - -use zeroize::Zeroize; - -use crate::{ - commands::Commands, - server::client::client_profile::Client, -}; - -#[allow(dead_code)] -pub struct ClientApi { - socket: TcpStream, - addr: String, - - pub on_client_add_handle: fn(Client) -> (), - pub on_client_remove_handle: fn(String) -> (), -} - -impl ClientApi { - - #[allow(dead_code)] - 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) - } - - #[allow(dead_code)] - pub fn set_on_client_add(&mut self, func: fn(Client) -> ()) { - self.on_client_add_handle = func; - } - - #[allow(dead_code)] - pub fn set_on_client_removed(&mut self, func: fn(String) -> ()) { - self.on_client_remove_handle = func; - } - - #[allow(dead_code)] - 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")) - } - } - } -} diff --git a/src/lib.rs b/src/lib.rs deleted file mode 100644 index 93a4a81..0000000 --- a/src/lib.rs +++ /dev/null @@ -1,106 +0,0 @@ -use std::sync::Arc; -use std::sync::Mutex; -use std::thread; - -use crossbeam::{Receiver, Sender, unbounded}; - -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/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 99% rename from src/server/client/client_profile.rs rename to src/lib/server/client_management/client/client_profile.rs index cce53c0..1d5ce47 100644 --- a/src/server/client/client_profile.rs +++ b/src/lib/server/client_management/client/client_profile.rs @@ -6,7 +6,6 @@ use std::{ io::prelude::*, net::{Shutdown, TcpStream}, sync::Arc, - //collections::HashMap, sync::Mutex, time::{Duration, Instant}, }; diff --git a/src/server/client/client_v3.rs b/src/lib/server/client_management/client/client_v3.rs similarity index 95% rename from src/server/client/client_v3.rs rename to src/lib/server/client_management/client/client_v3.rs index 09e4555..4a7ac26 100644 --- a/src/server/client/client_v3.rs +++ b/src/lib/server/client_management/client/client_v3.rs @@ -5,8 +5,6 @@ use std::{ io::Error, io::prelude::*, net::{Shutdown, TcpStream}, - sync::Arc, - //collections::HashMap, sync::Mutex, time::{Duration, Instant}, }; @@ -26,15 +24,18 @@ use crate::{ #[derive(Debug)] pub struct Client { + + parent: Option<&ClientManager> + uuid: String, username: String, address: String, - last_heartbeat: Instant, + last_heartbeat: Option, - stream: Arc>, + stream: Option>, - pub sender: Sender, + sender: Sender, receiver: Receiver, server_sender: Sender, @@ -48,7 +49,7 @@ impl Client { stream.set_read_timeout(Some(Duration::from_secs(1))).unwrap(); Client { - stream: Arc::new(Mutex::new(stream)), + stream: Some(Mutex::new(stream)), uuid: uuid.to_string(), username: username.to_string(), address: address.to_string(), @@ -58,7 +59,7 @@ impl Client { server_sender, - last_heartbeat: Instant::now(), + last_heartbeat: Some(Instant::now()), } } @@ -215,3 +216,8 @@ impl Drop for Client { 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..e4f98fa --- /dev/null +++ b/src/lib/server/client_management/client/mod.rs @@ -0,0 +1,30 @@ +// pub mod client_profile; +// pub mod client_v3; +pub mod traits; + +use serde::{Serialize, Deserialize}; +use std::net::TcpStream; +use std::sync::Weak; +use uuid::Uuid; + +use super::traits::TClientManager; +use super::ClientManager; + +pub enum ClientMessage { + a, + b, +} + +#[derive(Serialize, Deserialize)] +pub struct Client { + uuid: String, + username: String, + address: String, + + #[serde(skip)] + stream: Option, + + #[serde(skip)] + owner: Option> +} + 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..31953d6 --- /dev/null +++ b/src/lib/server/client_management/client/traits.rs @@ -0,0 +1,13 @@ +use uuid::Uuid; + +pub trait TClient { + fn new(uuid: Uuid, name: String, addr: String); + + fn send(&self, bytes: Vec) -> Result<(), &str>; + fn recv(&self) -> Option>; + + fn sendMsg(&self, msg: TClientMessage) -> Result<(), &str>; + fn recvMsg(&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..916d593 --- /dev/null +++ b/src/lib/server/client_management/mod.rs @@ -0,0 +1,52 @@ +mod traits; +pub mod client; + +use std::sync::Weak; +use std::sync::Arc; + +use crossbeam_channel::{Sender, Receiver}; + +use uuid::Uuid; + +use self::client::Client; +use self::client::ClientMessage; +// use client::client_v3::Client; +use self::traits::TClientManager; + + + +enum ClientManagerMessages { + +} + +pub struct ClientManager { + clients: Vec>, + + weak_self: Option>, + + sender: Sender, + receiver: Receiver, +} + +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..70f9a5e --- /dev/null +++ b/src/lib/server/client_management/traits.rs @@ -0,0 +1,12 @@ +use std::sync::Arc; + +use uuid::Uuid; + +use super::client::traits; + +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..0de742b --- /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 67ad69a..2c81892 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,95 +1,79 @@ -mod client_api; -mod commands; -mod server; mod lib; -use std::time::Duration; - use clap::{App, Arg}; -use crossterm::ErrorKind; -use crate::server::server_v3::Server; -use crate::server::ui::server_view_controller::ServerViewController; +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-001", "0.0.0.0:6000", "michael bailey"); - - ServerViewController::new(server.unwrap()); - Ok(()) - } else { - let mut server = crate::server::server_profile::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 } - // MARK: - general testing zone -#[cfg(test)] -mod tests { - use std::{thread, time}; - use std::collections::HashMap; +// #[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 crate::client_api::ClientApi; - use crate::commands::Commands; - use crate::server::server_profile::Server; +// #[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 mut 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 mut 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 api_result.is_ok() { +// 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)); - } - } -} \ 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 78bc78a..0000000 --- a/src/server/client/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod client_profile; -pub mod client_v3; diff --git a/src/server/mod.rs b/src/server/mod.rs deleted file mode 100644 index 5803b99..0000000 --- a/src/server/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod server_v3; -pub mod client; -pub mod server_profile; -pub mod ui; \ No newline at end of file diff --git a/src/server/server_profile.rs b/src/server/server_profile.rs deleted file mode 100644 index da8dd51..0000000 --- a/src/server/server_profile.rs +++ /dev/null @@ -1,612 +0,0 @@ -use std::{ - collections::HashMap, - io, - io::Error, - io::prelude::*, - net::{TcpListener, TcpStream}, - sync::{Arc, Mutex}, - thread, - time::Duration -}; - -use crossbeam_channel::{Receiver, Sender, unbounded}; -use log::info; - -use crate::{ - commands::Commands, - server::{ - client::client_profile::Client, - - } -}; -use crate::lib::ThreadPool; - -#[derive(Debug)] -pub enum ServerMessages { - RequestUpdate(Arc>), - RequestInfo(String, Arc>), - Disconnect(String), - Shutdown, -} - -// MARK: - server struct -#[allow(dead_code)] -pub struct Server { - pub name: String, - pub address: String, - pub author: String, - - connected_clients: Arc>>, - - thread_pool: ThreadPool, - - sender: Sender, - receiver: Receiver, - - pub running: bool, - - client_list_changed_handle: Box, -} - -// MARK: - server implemetation -impl Server { - pub fn new(name: &str, address: &str, author: &str) -> Self { - let (sender, receiver) = unbounded(); - - Self { - name: name.to_string(), - address: address.to_string(), - author: author.to_string(), - connected_clients: Arc::new(Mutex::new(HashMap::new())), - thread_pool: ThreadPool::new(16), - - 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() - } - - #[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(&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 _ = 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(()) - } - - #[allow(dead_code)] - 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)] -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/server/server_v3.rs b/src/server/server_v3.rs deleted file mode 100644 index d3eb857..0000000 --- a/src/server/server_v3.rs +++ /dev/null @@ -1,311 +0,0 @@ -use std::{collections::HashMap, io, io::{Read, Write}, net::{TcpListener, TcpStream}, sync::{Arc, Mutex}}; -use std::time::Duration; - -use crossbeam_channel::{Receiver, Sender, unbounded}; -use log::info; - -use crate::{ - commands::Commands -}; -use crate::server::client::client_v3::Client; - -#[derive(Debug)] -pub enum ServerMessages { - RequestUpdate(Arc>), - RequestInfo(String, Arc>), - Disconnect(String), - Shutdown, -} - -#[allow(dead_code)] -#[derive(Eq, PartialEq)] -pub enum ServerState { - Starting, - Started, - Stopping, - Stopped, -} - -// MARK: - server struct -#[allow(dead_code)] -pub struct Server { - pub name: String, - pub address: String, - pub owner: String, - - pub state: ServerState, - - connected_clients: HashMap, - - sender: Sender, - receiver: Receiver, - listener: Option, - - buffer: [u8; 1024], - - client_list_changed_handle: Box, - - // 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], - - // event handles - client_list_changed_handle: Box::new(|_s| info!("Server: client list changed.")), - - // metrics - o2s_rqst: 0, - c2s_msgs: 0, - s2s_msgs: 0, - s2c_msgs: 0, - } - ) - } - - #[allow(dead_code)] - pub fn get_name(&self) -> String { - self.name.clone() - } - - #[allow(dead_code)] - pub fn get_address(&self) -> String { - self.address.clone() - } - - #[allow(dead_code)] - pub fn get_owner(&self) -> String { - self.owner.clone() - } - - #[allow(dead_code)] - pub fn tick(&mut self) { - - // check to see if this server is ready to execute things. - if self.state == ServerState::Stopped { - () - } - - // check for any server messages in the channel - println!("server: getting messages"); - for i in self.receiver.try_iter() { - match i { - // server calls - ServerMessages::Shutdown => { - self.s2s_msgs += 1; - - println!("server: shutting down..."); - - for (_k, v) in self.connected_clients.iter() { - let _ = v.sender.send(Commands::Disconnect(None)); - } - self.state = ServerState::Stopping; - }, - - // client requests - ServerMessages::RequestUpdate(stream_arc) => { - self.c2s_msgs += 1; - - for (_k, v) in self.connected_clients.iter() { - 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()); - } - } - }, - - // client requests for 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()); - } - }, - - // client disconnect requests - 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())}); - - }, - } - } - - 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(); - } - } - - #[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); - Ok(()) - } - - #[allow(dead_code)] - pub fn stop(&mut self) { - info!("server: sending stop message"); - let _ = self.sender.send(ServerMessages::Shutdown); - self.state = ServerState::Stopping; - } - - #[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 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); - } -} - -#[cfg(test)] -mod tests { - use crate::server::server_v3::Server; - - #[test] - fn test_creation() { - let server = Server::new( - "test server", - "0.0.0.0:6000", - "michael" - ); - - - assert_eq!(server.name, "test server"); - assert_eq!(server.address, "0.0.0.0:6000"); - assert_eq!(server.owner, "michael"); - } -} \ No newline at end of file diff --git a/src/server/ui/about_panel.rs b/src/server/ui/about_panel.rs deleted file mode 100644 index 16e29ef..0000000 --- a/src/server/ui/about_panel.rs +++ /dev/null @@ -1,10 +0,0 @@ -use cursive::views::{Dialog, TextView}; -use cursive::View; - -pub fn about() -> Box { - Box::new( - Dialog::new() - .content(TextView::new("rust chat server written by Mitchel Hardie & Michael Bailey (c) 2020")) - .button("Close", |s| {s.pop_layer();}) - ) -} \ No newline at end of file diff --git a/src/server/ui/control_panel.rs b/src/server/ui/control_panel.rs deleted file mode 100644 index 1e578fb..0000000 --- a/src/server/ui/control_panel.rs +++ /dev/null @@ -1,67 +0,0 @@ -use cursive::{Cursive, View}; -use cursive::view::SizeConstraint; -use cursive::views::{LinearLayout, ListView, Panel, ResizedView, TextView}; - -use crate::server::server_v3::Server; - -#[allow(dead_code)] -pub fn control_panel(s: &mut Cursive) -> Box { - Box::new( - ResizedView::new( - SizeConstraint::Fixed(s.screen_size().x-8), - SizeConstraint::Fixed(s.screen_size().y-8), - Panel::new( - LinearLayout::horizontal() - .child( - LinearLayout::vertical() - .child( - TextView::new(" ═════╡ Server ╞═════ ") - ) - .child( - TextView::new( - format!("Server name: {}", s.user_data::().unwrap().get_name()) - ) - ) - .child( - TextView::new( - format!("Server host: {}", s.user_data::().unwrap().get_address()) - ) - ) - .child( - TextView::new( - format!("Server owner: {}", s.user_data::().unwrap().get_owner()) - ) - ) - .child( - TextView::new( - format!(" ═════╡ metrics ╞═════ ") - ) - ) - .child( - TextView::new( - format!("Server o2s_rqst: {}", s.user_data::().unwrap().o2s_rqst) - ) - ) - .child( - TextView::new( - format!("Server c2s_msgs: {}", s.user_data::().unwrap().c2s_msgs) - ) - ) - .child( - TextView::new( - format!("Server s2s_msgs: {}", s.user_data::().unwrap().s2s_msgs) - ) - ) - .child( - TextView::new( - format!("Server s2c_msgs: {}", s.user_data::().unwrap().s2c_msgs) - ) - ) - ) - .child( - ListView::new() - ) - ) - ) - ) -} \ No newline at end of file diff --git a/src/server/ui/main_menu.rs b/src/server/ui/main_menu.rs deleted file mode 100644 index 614c126..0000000 --- a/src/server/ui/main_menu.rs +++ /dev/null @@ -1,10 +0,0 @@ -use cursive::menu::MenuTree; - -use crate::server::ui::about_panel::about; - -pub fn main_menu() -> MenuTree { - MenuTree::new() - .leaf("About ^+A", |s| s.add_layer(about())) - .delimiter() - .leaf("Quit ^+Q", |s| s.quit()) -} \ No newline at end of file diff --git a/src/server/ui/mod.rs b/src/server/ui/mod.rs deleted file mode 100644 index 207759d..0000000 --- a/src/server/ui/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod server_view_controller; -pub mod control_panel; -pub mod about_panel; -pub mod main_menu; \ No newline at end of file diff --git a/src/server/ui/server_view_controller.rs b/src/server/ui/server_view_controller.rs deleted file mode 100644 index bebecc4..0000000 --- a/src/server/ui/server_view_controller.rs +++ /dev/null @@ -1,60 +0,0 @@ -use cursive::{CbSink, Cursive, CursiveExt}; - -use crate::server::server_v3::Server; -use crate::server::ui::about_panel::about; -use crate::server::ui::main_menu::main_menu; -use cursive::event::Event; - -#[allow(dead_code)] -pub enum UpdateTypes { - AddClient() -} - -/// # ServerViewConroller -/// -/// This Struct contains all the controller logic to allow the server to interact with the view -#[allow(dead_code)] -pub struct ServerViewController { - display: Cursive, - - server_name: String, - server_host: String, - server_owner: String, - - client_list: Vec, - running: String, -} - -#[allow(dead_code)] -impl ServerViewController { - pub fn new(server: Server) { - - let mut v = Self { - display: Cursive::default(), - server_name: server.get_name().to_string(), - server_host: server.get_address().to_string(), - server_owner: server.get_owner().to_string(), - client_list: Vec::new(), - running: "None".to_string() - }; - - // set global shortcuts - v.display.add_global_callback(Event::CtrlChar('q'), |s| s.quit()); - v.display.add_global_callback(Event::CtrlChar('a'), |s| s.add_layer(about())); - - // seting up menubar - v.display.menubar().add_subtree("Server", main_menu()); - v.display.set_autohide_menu(false) - - // setup the display menubar. - - // TODO: - this will be tied to the server run function - // v.display.add_global_callback(Event::Refresh, |s| s.user_data::>().unwrap().); - - } - - - fn get_display_channel() -> CbSink { - Cursive::default().cb_sink().clone() - } -} -- 2.40.1 From 7165c60fa77537c72035b331c627cb5373b27447 Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Sun, 7 Feb 2021 21:17:59 +0000 Subject: [PATCH 52/53] tidying up client struct --- .../server/client_management/client/mod.rs | 24 +++++++++++++++++++ .../server/client_management/client/traits.rs | 18 +++++++++++--- src/lib/server/client_management/traits.rs | 3 +++ src/lib/server/mod.rs | 4 ++-- 4 files changed, 44 insertions(+), 5 deletions(-) diff --git a/src/lib/server/client_management/client/mod.rs b/src/lib/server/client_management/client/mod.rs index e4f98fa..68c21ef 100644 --- a/src/lib/server/client_management/client/mod.rs +++ b/src/lib/server/client_management/client/mod.rs @@ -5,16 +5,29 @@ 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, @@ -28,3 +41,14 @@ pub struct Client { 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 index 31953d6..9129eb6 100644 --- a/src/lib/server/client_management/client/traits.rs +++ b/src/lib/server/client_management/client/traits.rs @@ -1,13 +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); + fn new(uuid: Uuid, name: String, addr: String) -> Arc; fn send(&self, bytes: Vec) -> Result<(), &str>; fn recv(&self) -> Option>; - fn sendMsg(&self, msg: TClientMessage) -> Result<(), &str>; - fn recvMsg(&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/traits.rs b/src/lib/server/client_management/traits.rs index 70f9a5e..e1a733e 100644 --- a/src/lib/server/client_management/traits.rs +++ b/src/lib/server/client_management/traits.rs @@ -4,6 +4,9 @@ use uuid::Uuid; use super::client::traits; +/** + * @michael-bailey + */ pub trait TClientManager { fn addClient(&self, client: Arc); fn removeClient(&self, id: Uuid); diff --git a/src/lib/server/mod.rs b/src/lib/server/mod.rs index 0de742b..2d1cca4 100644 --- a/src/lib/server/mod.rs +++ b/src/lib/server/mod.rs @@ -1,5 +1,5 @@ pub mod client_management; -pub mod server; -pub mod server_v3; +// pub mod server; +// pub mod server_v3; pub struct Server {} -- 2.40.1 From a7590d9f2293106b9fc48df0df6bddd9d7d0f0c1 Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Mon, 8 Feb 2021 17:46:22 +0000 Subject: [PATCH 53/53] Update mod.rs + added self referenceing to teh client manager with a weak reference + added channels to the client manager --- src/lib/server/client_management/mod.rs | 39 ++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/src/lib/server/client_management/mod.rs b/src/lib/server/client_management/mod.rs index 916d593..5a81880 100644 --- a/src/lib/server/client_management/mod.rs +++ b/src/lib/server/client_management/mod.rs @@ -3,8 +3,9 @@ pub mod client; use std::sync::Weak; use std::sync::Arc; +use std::sync::Mutex; -use crossbeam_channel::{Sender, Receiver}; +use crossbeam_channel::{Sender, Receiver, unbounded}; use uuid::Uuid; @@ -13,21 +14,51 @@ 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: Option>, + 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!() } -- 2.40.1