From 8981f5c551d0bfb35c9367a21b6976062d81ad0b Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Fri, 17 Jul 2020 20:52:10 +0100 Subject: [PATCH 01/34] Update mod.rs --- src/server/commands/mod.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/server/commands/mod.rs b/src/server/commands/mod.rs index 361d053..5bb36c4 100644 --- a/src/server/commands/mod.rs +++ b/src/server/commands/mod.rs @@ -206,6 +206,13 @@ impl From for Commands { } } +impl From<&[u8]> for Commands { + fn from(data: &[u8]) -> Self { + let incoming_message = String::from(String::from_utf8_lossy(data)).as_str(); + Commands::from(incoming_message) + } +} + #[cfg(test)] mod test_commands_v2 { use super::Commands; -- 2.40.1 From 8a07d278bf2a49ab774baadfe80516e38053b2a5 Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Fri, 17 Jul 2020 20:52:28 +0100 Subject: [PATCH 02/34] added derive clone --- src/server/client/client_profile.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/server/client/client_profile.rs b/src/server/client/client_profile.rs index f9646c4..b0a5f88 100644 --- a/src/server/client/client_profile.rs +++ b/src/server/client/client_profile.rs @@ -14,8 +14,8 @@ use regex::Regex; use crossbeam::{channel, Sender, Receiver, TryRecvError}; use crossbeam_channel::unbounded; - -pub struct Client<'client_lifetime>{ +#[derive(Clone)] +pub struct Client<'client_lifetime> { connected: bool, stream: Arc, uuid: String, @@ -70,8 +70,6 @@ impl<'a> Client<'a> { match self.rx_channel.try_recv() { /*command is on the channel*/ - - Ok(command) => { let a = command.clone(); match command { -- 2.40.1 From 4cd27856ba1629070172adb665245c79ea2b08b7 Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Tue, 21 Jul 2020 16:55:06 +0100 Subject: [PATCH 03/34] Update main.rs --- src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 1f86a00..ef809e8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,7 +6,7 @@ mod server; use crate::server::server_profile::Server; fn main(){ - lazy_static!{ + lazy_static! { static ref server_name: &'static str = "Server-01"; static ref server_address: &'static str = "0.0.0.0:6000"; static ref server_author: &'static str = "noreply@email.com"; -- 2.40.1 From 743433ab570f6b2665886c67024153eed2469649 Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Mon, 3 Aug 2020 12:36:55 +0100 Subject: [PATCH 04/34] Update Cargo.toml + added cursive, crossterm, zeroize, and rayon. --- Cargo.toml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index dbac325..6e45bba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,13 +9,21 @@ edition = "2018" [dependencies] regex = "1" crossbeam = "0.7" -parking_lot = "0.10" crossbeam-channel = "0.4" crossbeam-utils = "0.7" crossbeam-queue = "0.2" +parking_lot = "0.10" dashmap = "3.11.4" async-std = "1.6.2" lazy_static = "1.4.0" +rayon = "1.3.1" +diesel = { version = "1.4.5", features = ["sqlite"] } +zeroize = "1.1.0" +cursive = { version = "0.15.0", default-features = false, features = ["crossterm-backend"]} +crossterm = "0.17.7" +log = "0.4" + + [profile.dev] opt-level = 0 -- 2.40.1 From f05323361c50ce0f8f8b78259531c086d960f2e5 Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Mon, 3 Aug 2020 12:37:30 +0100 Subject: [PATCH 05/34] Create mod.rs ~ coverted Commands to commands-v2 --- src/commands/mod.rs | 160 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 src/commands/mod.rs diff --git a/src/commands/mod.rs b/src/commands/mod.rs new file mode 100644 index 0000000..642d6c8 --- /dev/null +++ b/src/commands/mod.rs @@ -0,0 +1,160 @@ +use std::string::ToString; +use std::collections::HashMap; + +use std::borrow::Borrow; +use regex::Regex; +use std::ops::{Index}; + +// MARK: - command struct +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum Commands { + Request(Option>), + Info(Option>), + + HeartBeat(Option>), + + Connect(Option>), + Disconnect(Option>), + + ClientUpdate(Option>), + ClientInfo(Option>), + ClientRemove(Option>), + Client(Option>), + + Success(Option>), + Error(Option>), +} + +impl ToString for Commands { + + fn to_string(&self) -> std::string::String { + let mut out_string = String::new(); + + let (command, parameters) = match self { + Commands::Request(arguments) => { ("!request:", arguments) }, + Commands::Info(arguments) => { ("!info:", arguments) }, + Commands::HeartBeat(arguments) => {("!heartbeat:", arguments)}, + Commands::Connect(arguments) => { ("!connect:", arguments) }, + Commands::Disconnect(arguments) => { ("!disconnect:", arguments) }, + Commands::ClientUpdate(arguments) => { ("!clientUpdate:", arguments) }, + Commands::ClientInfo(arguments) => { ("!clientInfo:", arguments) }, + Commands::ClientRemove(arguments) => { ("!clientRemove", arguments) } + Commands::Client(arguments) => { ("!client:", arguments) }, + Commands::Error(arguments) => { ("!error:", arguments) }, + _ => { ("!error:", &None) } + }; + + out_string.push_str(command); + + if parameters.is_some() { + let hash_map = parameters.borrow().as_ref().unwrap(); + for (k, v) in hash_map.iter() { + out_string.push_str(" "); + out_string.push_str(k.as_str()); + out_string.push_str(":"); + + if v.contains(":") { + out_string.push_str(format!("\"{}\"",v.as_str()).as_str()) + } else { + out_string.push_str(v.as_str()); + } + + + } + } + out_string + } +} + +impl From<&str> for Commands { + fn from(data: &str) -> Self { + let regex = Regex::new(r###"(\?|!)([a-zA-z0-9]*):|([a-zA-z]*):([a-zA-Z0-9@\-\+\[\]{}_=/.]+|("(.*?)")+)"###).unwrap(); + let mut iter = regex.find_iter(data); + let command_opt = iter.next(); + + if command_opt.is_none() { + return Commands::Error(None); + } + let command = command_opt.unwrap().as_str(); + + + println!("command: {:?}", command); + + let mut map: HashMap = HashMap::new(); + + for i in iter { + let parameter = i.as_str().to_string(); + let parts:Vec<&str> = parameter.split(":").collect(); + + map.insert(parts.index(0).to_string(), parts.index(1).to_string()); + } + + let params = if map.capacity() > 1 {Some(map)} else { None }; + + match command { + "!request:" => Commands::Request(params), + "!info:" => Commands::Info(params), + + "!heartbeat:" => Commands::HeartBeat(params), + + "!connect:" => Commands::Connect(params), + "!disconnect:" => Commands::Disconnect(params), + + "!clientUpdate:" => Commands::ClientUpdate(params), + "!clientInfo:" => Commands::ClientInfo(params), + "!client:" => Commands::Client(params), + "!clientRemove:" => Commands::ClientRemove(params), + + "!success:" => Commands::Success(params), + "!error:" => Commands::Error(params), + + _ => Commands::Error(params), + } + } +} + +impl From for Commands { + fn from(data: String) -> Self { + Commands::from(data.as_str()) + } +} + +impl From<&[u8; 1024]> for Commands { + fn from(data: &[u8; 1024]) -> Self { + let incoming_message = String::from(String::from_utf8_lossy(data)); + Commands::from(incoming_message) + } +} + +#[cfg(test)] +mod test_commands_v2 { + #![feature(test)] + extern crate test; + use super::Commands; + use std::collections::HashMap; + use test::Bencher; + + #[test] + fn test_creation_from_string() { + let command_result = Commands::from("!connect: name:bop host:127.0.0.1 uuid:123456-1234-1234-123456"); + () + } + + #[test] + fn test_to_string() { + + let mut a: HashMap = HashMap::new(); + a.insert("name".to_string(), "michael".to_string()); + a.insert("host".to_string(), "127.0.0.1".to_string()); + a.insert("uuid".to_string(), "123456-1234-1234-123456".to_string()); + + let command = Commands::Connect(Some(a)); + + println!("{:?}", command.to_string()) + } + + #[bench] + fn benchmark(b: &mut Bencher) { + b.iter(|| Commands::from("!connect: host:192.168.0.1 name:\"michael-bailey\" uuid:123456-1234-1234-123456")) + } +} -- 2.40.1 From d56a7209c235ef7517217724b6912020b8db1692 Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Mon, 3 Aug 2020 12:39:50 +0100 Subject: [PATCH 06/34] Update server_profile.rs + added thread spawning. +added channels for server communication outside of the thread. + added main loop process. ~ changes the tcp listener to be non blocking. + added loop labels to break out of. + impl drop for the server that signals the thread to break. --- src/server/server_profile.rs | 283 ++++++++++++++++++++--------------- 1 file changed, 164 insertions(+), 119 deletions(-) diff --git a/src/server/server_profile.rs b/src/server/server_profile.rs index 0446524..ba264a6 100644 --- a/src/server/server_profile.rs +++ b/src/server/server_profile.rs @@ -1,35 +1,71 @@ extern crate regex; +extern crate rayon; -use crate::server::client::client_profile::Client; -use crate::server::commands::{Commands}; +use crate::{ + server::{ + client::client_profile::Client, + commands::{Commands} + } +}; +use std::{ + sync::{Arc, Mutex}, + net::{TcpStream, TcpListener}, + collections::HashMap, + io::prelude::*, + thread, + io +}; +use log::info; + +use crossbeam_channel::{Sender, Receiver, unbounded}; use rust_chat_server::ThreadPool; -use std::net::{TcpStream, TcpListener}; -use std::sync::{Arc, Mutex}; -use crossbeam_channel::Sender; -use parking_lot::FairMutex; -use std::collections::HashMap; -use dashmap::DashMap; -use std::io::prelude::*; -use regex::Regex; +use zeroize::Zeroize; +use std::time::Duration; + +#[derive(Debug)] +pub enum ServerMessages { + #[allow(dead_code)] + RequestUpdate(String), + #[allow(dead_code)] + RequestInfo(String, String), + #[allow(dead_code)] + RequestDisconnect(String), + #[allow(dead_code)] + Shutdown, +} + +// MARK: - server struct + +pub struct Server { + pub name: String, + pub address: String, + pub author: String, + + connected_clients: Arc>>, -pub struct Server<'z> { - name: &'z str, - address: &'z str, - author: &'z str, - connected_clients: Arc>>>, thread_pool: ThreadPool, + + sender: Sender, + receiver: Receiver, + } // MARK: - server implemetation -impl<'z> Server<'z> { - pub fn new(name: &'z str, address: &'z str, author: &'z str) -> Self { +impl Server { + pub fn new(name: &str, address: &str, author: &str) -> Self { + let (sender, receiver) = unbounded(); + Self { - name: name, - address: address, - author: author, + 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, } } @@ -37,62 +73,108 @@ impl<'z> Server<'z> { self.address.to_string() } - pub fn start(&'static self) { - let listener = TcpListener::bind(self.get_address()).unwrap(); + pub fn start(&self) -> Result<(), io::Error>{ + info!("server: starting server..."); + // clone elements for thread + let client_map = self.connected_clients.clone(); + let sender = self.sender.clone(); + let receiver = self.receiver.clone(); + + let server_details = (self.name.clone(), self.author.clone(), self.address.clone()); + + // set up listener and buffer + let listener = TcpListener::bind(self.get_address())?; + listener.set_nonblocking(true); + let mut buffer = [0; 1024]; - loop { - if let Ok((mut stream, addr)) = listener.accept() { - println!("Server: new connection, {}", addr); + info!("server: spawning threads"); + thread::Builder::new().name("Server Thread".to_string()).spawn(move || { + 'outer: loop { + // get messages from the servers channel. + info!("server: getting messages"); + for i in receiver.try_iter() { + match i { + ServerMessages::Shutdown => { + // TODO: implement disconnecting all clients and shutting down the server + info!("server: shutting down..."); - let request = Commands::Request(None); - //request.to_string(); - self.transmit_data(&stream, &request.to_string().as_str()); - - stream.read(&mut buffer).unwrap(); - - let incoming_message = String::from(String::from_utf8_lossy(&buffer)); - let command = Commands::from(incoming_message); - match command { - Commands::Connect(Some(data)) => { - let uuid = data.get("uuid").unwrap(); - let username = data.get("name").unwrap(); - let address = data.get("host").unwrap(); - - let stream = Arc::new(stream); - let mut client = Client::new(self, stream, &uuid, &username, &address); - - let mut clients_hashmap = self.connected_clients.lock().unwrap(); - clients_hashmap.insert(uuid.to_string(), client.get_transmitter().clone()); - std::mem::drop(clients_hashmap); - - self.thread_pool.execute(move || { - client.handle_connection(); - }); - - 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)); - - self.update_all_clients(new_client); - }, - Commands::Info(None) => { - let mut params: HashMap = HashMap::new(); - params.insert(String::from("name"), self.name.to_string().clone()); - params.insert(String::from("owner"), self.author.to_string().clone()); - - let command = Commands::Info(Some(params)); - - self.transmit_data(&stream, command.to_string().as_str()); - }, - _ => { - println!("Invalid command!"); - self.transmit_data(&stream, Commands::Error(None).to_string().as_str()); - }, + break 'outer; + }, + _ => {} + } } - } - } + + info!("server: checking for new connections"); + if let Ok((mut stream, addr)) = listener.accept() { + stream.set_read_timeout(Some(Duration::from_millis(100))).unwrap(); + + let request = Commands::Request(None); + //request.to_string(); + stream.write_all(&request.to_string().as_bytes()); + stream.flush(); + stream.read(&mut buffer).unwrap(); + + let incoming_message = String::from(String::from_utf8_lossy(&buffer)); + let command = Commands::from(incoming_message); + // clears the buffer. + buffer.zeroize(); + + match command { + Commands::Connect(Some(data)) => { + let uuid = data.get("uuid").unwrap(); + let username = data.get("name").unwrap(); + let address = data.get("host").unwrap(); + + info!("{}", format!("Server: new Client connection: addr = {}", address )); + + let client = Client::new(stream, sender.clone(), uuid.clone(), username.clone(), address.clone()); + + client_map.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)); + + client_map.lock().unwrap().iter().map(|(k, v)| v.sender.send(new_client.clone())); + }, + Commands::Info(None) => { + info!("Server: info requested"); + let mut params: HashMap = HashMap::new(); + params.insert(String::from("name"), server_details.0.clone()); + params.insert(String::from("owner"), server_details.1.clone()); + + let command = Commands::Info(Some(params)); + + stream.write_all(&command.to_string().as_bytes()); + stream.flush(); + }, + _ => { + info!("Server: Invalid command sent"); + stream.write_all(Commands::Error(None).to_string().as_bytes()); + stream.flush(); + }, + } + } + // TODO: end - + + // handle each client for messages + info!("server: handing control to clients"); + for (_k, v) in client_map.lock().unwrap().iter() { + v.handle_connection(); + } + } + info!("server: stopped") + }); + info!("server: started"); + Ok(()) } + pub fn stop(&self) { + info!("server: sending stop message"); + self.sender.send(ServerMessages::Shutdown); + } + + #[allow(dead_code)] pub fn get_info(&self, tx: Sender) { let mut params: HashMap = HashMap::new(); params.insert(String::from("name"), self.name.to_string().clone()); @@ -102,10 +184,11 @@ impl<'z> Server<'z> { tx.send(command).unwrap(); } + #[allow(dead_code)] pub fn update_all_clients(&self, command: Commands){ let clients = self.connected_clients.lock().unwrap(); - for tx in clients.values(){ - tx.send(command.clone()).unwrap(); + for client in clients.values(){ + client.sender.send(command.clone()).unwrap(); } } @@ -121,53 +204,15 @@ impl<'z> Server<'z> { stream.write(data.to_string().as_bytes()).unwrap(); stream.flush().unwrap(); } +} - //deprecated - /* - pub fn tokenize(&self, incoming_message: &str) -> Result{ - let command_regex = Regex::new(r###"(\?|!)([a-zA-z0-9]*):|([a-zA-z]*):([a-zA-Z0-9\-\+\[\]{}_=/]+|("(.*?)")+)"###).unwrap(); - - if command_regex.is_match(incoming_message){ - let command = self.match_command(&incoming_message.to_string()); - let command = match command{ - ClientCommands::Connect(mut addons) => { - self.regex_data(&command_regex, &incoming_message.replace("!connect: ", ""), &mut addons); - ClientCommands::Connect(addons) - }, - ClientCommands::ClientInfo(mut addons) => { - self.regex_data(&command_regex, &incoming_message.replace("!clientInfo: ", ""), &mut addons); - ClientCommands::ClientInfo(addons) - }, - _ => { - println!("no addons"); - command - }, - }; - Ok(command) - } else { - Err("data did not match regex!") - } - } - - - fn match_command(&self, command: &String) -> ClientCommands{ - match command{ - _ if command.starts_with("!info:") => ClientCommands::Info, - _ if command.starts_with("!connect:") => ClientCommands::Connect(HashMap::new()), - _ if command.starts_with("!disconnect:") => ClientCommands::Disconnect, - _ if command.starts_with("!clientUpdate:") => ClientCommands::ClientUpdate, - _ if command.starts_with("!clientInfo:") => ClientCommands::ClientInfo(HashMap::new()), - _ => ClientCommands::Unknown, - } - } - */ - - fn regex_data(&self, command_regex: &Regex, data: &str, command_addons: &mut HashMap){ - for figure in command_regex.find_iter(data){ - let segment = figure.as_str().to_string(); - let contents: Vec<&str> = segment.split(":").collect(); - println!("key: {}, value: {}", contents[0].to_string(), contents[1].to_string()); - command_addons.insert(contents[0].to_string(), contents[1].to_string()); - } +impl Drop for Server { + fn drop(&mut self) { + println!("server dropped"); + let _ = self.sender.send(ServerMessages::Shutdown); } } + +struct ServerDelegate { + +} \ No newline at end of file -- 2.40.1 From 46a6b30681b3b56a61b66e128732dcae2ba6cbcb Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Mon, 3 Aug 2020 12:42:42 +0100 Subject: [PATCH 07/34] Update client_profile.rs + derived debug + added senders for it's thread. + added channel sender for the server. + working on heartbeat. ~ changed the handle to be a single function call invoked by the server --- src/server/client/client_profile.rs | 251 +++++++++++----------------- 1 file changed, 93 insertions(+), 158 deletions(-) diff --git a/src/server/client/client_profile.rs b/src/server/client/client_profile.rs index 1b31101..9c0f8e7 100644 --- a/src/server/client/client_profile.rs +++ b/src/server/client/client_profile.rs @@ -1,193 +1,128 @@ extern crate regex; -use crate::server::server_profile::Server; -use crate::server::commands::{Commands}; +use std::{ + sync::Arc, + net::{Shutdown, TcpStream}, + io::prelude::*, +}; +use crossbeam::{Sender, Receiver, TryRecvError, unbounded}; -use std::net::{Shutdown, TcpStream}; -use std::sync::Arc; -use parking_lot::FairMutex; -use dashmap::DashMap; -use std::io::prelude::*; +use crate::{ + server::{ + server_profile::ServerMessages, + commands::Commands + } +}; +use std::sync::Mutex; use std::time::Duration; -use regex::Regex; -use crossbeam::{Sender, Receiver, TryRecvError}; -use crossbeam_channel::unbounded; +#[derive(Debug)] +pub struct Client { -pub struct Client<'a> { - connected: bool, - stream: Arc, - uuid: String, - username: String, - address: String, - server: &'a Server<'a>, - tx_channel: Sender, - rx_channel: Receiver, + pub uuid: String, + pub username: String, + pub address: String, + + stream_arc: Arc>, + + heartbeat_ticker: Arc>, + + pub sender: Sender, + receiver: Receiver, + + server_sender: Sender, } -impl<'a> Client<'a> { - pub fn new(server: &'a Server<'static>, stream: Arc, uuid: &String, username: &String, address: &String) -> Self{ - let (tx_channel, rx_channel): (Sender, Receiver) = unbounded(); +impl Client { + pub fn new(stream: TcpStream, server_sender: Sender, uuid: String, username: String, address: String) -> Self { + let (sender, receiver): (Sender, Receiver) = unbounded(); + stream.set_read_timeout(Some(Duration::from_secs(1))).unwrap(); Client { - connected: true, - stream, + stream_arc: Arc::new(Mutex::new(stream)), uuid: uuid.to_string(), username: username.to_string(), address: address.to_string(), - server, - tx_channel, - rx_channel, + + heartbeat_ticker: Arc::new(Mutex::new(5)), + + sender, + receiver, + + server_sender, + } } - fn get_stream(&self) -> &TcpStream{ - &self.stream - } + #[allow(unused_variables)] + pub fn handle_connection(&self) { + println!("buffer"); + let mut buffer = [0; 1024]; - pub fn get_transmitter(&self) -> &Sender{ - &self.tx_channel - } - - pub fn get_uuid(&self) -> &String{ - &self.uuid - } + // test to see if there is anything for the client to receive from its channel + println!("{}: channel checks", self.uuid); + match self.receiver.try_recv() { + /*command is on the channel*/ - pub fn get_username(&self) -> &String{ - &self.username - } + Ok(Commands::Info(Some(params))) => { + self.transmit_data(Commands::Info(Some(params)).to_string().as_str()); + }, - pub fn get_address(&self) -> &String{ - &self.address - } + Ok(Commands::Disconnect(None)) => { - pub fn handle_connection(&self){ - self.stream.set_read_timeout(Some(Duration::from_millis(3000))).unwrap(); - let mut buffer = [0; 1024]; - - while self.connected { - match self.rx_channel.try_recv() { - /*command is on the channel*/ - - Ok(command) => { - let a = command.clone(); - match command { - - Commands::Info(Some(params)) => { - self.transmit_data(a.to_string().as_str()); - }, - Commands::Disconnect(None) => { - - }, - Commands::ClientRemove(Some(params)) => {}, - Commands::Client(Some(params)) => { - self.transmit_data(a.to_string().as_str()); - - /*let command = Commands::from(&buffer); - match command{ - Commands::Success(None) => { - println!("sucess confirmed"); - }, - _ => { - let error = Commands::Error(None); - self.transmit_data(error.to_string().as_str()); - }, - } - */ - }, - Commands::Success(data) => {}, - _ => {}, - } - }, - /*sender disconnected*/ - Err(TryRecvError::Disconnected) => {}, - /*no data available yet*/ - Err(TryRecvError::Empty) => {}, } - - match self.stream.peek(&mut buffer){ - Ok(_) => { - self.get_stream().read(&mut buffer).unwrap(); - - let incoming_message = String::from(String::from_utf8_lossy(&buffer)); - let command = Commands::from(incoming_message.clone()); - println!("Request: {}", &incoming_message); + Ok(Commands::ClientRemove(Some(params))) => { }, + Ok(Commands::Success(params)) => { self.transmit_data(Commands::Success(params).to_string().as_str()); }, + Ok(Commands::Client(Some(params))) => { self.transmit_data(Commands::Client(Some(params)).to_string().as_str()); }, - /*command behaviour*/ - match command { - Commands::Connect(Some(params)) => todo!(), - _ => todo!(), - } - }, - Err(_) => { - println!("no data peeked"); + /*sender disconnected*/ + Err(TryRecvError::Disconnected) => { + self.server_sender.send(ServerMessages::RequestDisconnect(self.uuid.clone())); + }, + /*no data available yet*/ + Err(TryRecvError::Empty) => {}, + _ => {} + } + + println!("socket"); + let a = self.stream_arc.lock().unwrap().peek(&mut buffer).is_ok(); + println!("does have content: {}", a); + if self.stream_arc.lock().unwrap().peek(&mut buffer).is_ok() { + let mut stream = self.stream_arc.lock().unwrap(); + + stream.read(&mut buffer).unwrap(); + + let command = Commands::from(&buffer); + + // match incomming commands + println!("command"); + match command { + Commands::Disconnect(None) => { + self.server_sender.send(ServerMessages::RequestDisconnect(self.uuid.clone())).expect("sending message to server failed"); }, + Commands::HeartBeat(None) => { + self.transmit_data(Commands::HeartBeat(None).to_string().as_str()) + } + _ => { + + self.transmit_data(Commands::Error(None).to_string().as_str()) + } } } - println!("---thread exit---"); + println!("end"); } - // deprecated - /* - pub fn connect(&self, server: &Server, connected_clients: &Arc>>, data: &HashMap){ - let mut clients_hashmap = connected_clients.lock().unwrap(); - let uuid = self.get_uuid().to_string(); - clients_hashmap.insert(uuid, self.clone()); - std::mem::drop(clients_hashmap); - - let new_client = Commands::Client(data.clone()); - server.update_all_clients(&new_client); - - self.transmit_success(&String::from("")); - } - */ - + // move into a drop perhaps + #[allow(dead_code)] pub fn disconnect(&mut self){ - self.stream.shutdown(Shutdown::Both).expect("shutdown call failed"); - self.connected = false; + self.stream_arc.lock().unwrap().shutdown(Shutdown::Both).expect("shutdown call failed"); } pub fn transmit_data(&self, data: &str){ - println!("Transmitting..."); - println!("data: {}", data); + println!("Transmitting data: {}", data); - self.get_stream().write(data.to_string().as_bytes()).unwrap(); - self.get_stream().flush().unwrap(); - } - - // deprecated - pub fn confirm_success(&self, buffer: &mut [u8; 1024], data: &String){ - let success_regex = Regex::new(r###"!success:"###).unwrap(); - - let _ = match self.get_stream().read(&mut *buffer) { - Err(error) => self.transmit_error(&String::from("")), - Ok(success) => { - let incoming_message = String::from_utf8_lossy(&buffer[..]); - if success_regex.is_match(&incoming_message){ - println!("success"); - }else{ - self.transmit_error(&String::from("")); - } - }, - }; - } - - pub fn transmit_success(&self, data: &String){ - let mut success_message = "!success:".to_string(); - if !data.is_empty(){ - success_message.push_str(&" ".to_string()); - success_message.push_str(&data.to_string()); - } - self.transmit_data(&success_message); - } - - fn transmit_error(&self, data: &String){ - let mut error_message = "!error:".to_string(); - if !data.is_empty(){ - error_message.push_str(&" ".to_string()); - error_message.push_str(&data.to_string()); - } - self.transmit_data(&error_message); + self.stream_arc.lock().unwrap().write_all(data.to_string().as_bytes()).unwrap(); + self.stream_arc.lock().unwrap().flush().unwrap(); } } -- 2.40.1 From 22ed3db6bb827c172b63a80716a3787d0d98f483 Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Mon, 3 Aug 2020 12:43:03 +0100 Subject: [PATCH 08/34] Create mod.rs + started wok on a client api struct. --- src/client_api/mod.rs | 70 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 src/client_api/mod.rs diff --git a/src/client_api/mod.rs b/src/client_api/mod.rs new file mode 100644 index 0000000..da356eb --- /dev/null +++ b/src/client_api/mod.rs @@ -0,0 +1,70 @@ +use std::{ + net::TcpStream, + io::{Write, Read} +}; +use crate::{ + server::client::client_profile::Client, + commands::Commands, +}; +use zeroize::Zeroize; +use std::time::Duration; +use async_std::net::SocketAddrV4; +use std::str::FromStr; +use std::net::SocketAddr; + + +pub struct ClientApi { + socket: TcpStream, + addr: String, + + pub on_client_add_handle: fn(Client) -> (), + pub on_client_remove_handle: fn(String) -> (), +} + +impl ClientApi { + pub fn new(addr: &str) -> Self { + let socket = TcpStream::connect(addr).expect("connection failed"); + + let on_add = |_client: Client| {println!("Client_api: Client added {:?}", _client)}; + let on_remove = |_uuid: String| {println!("Client_api: Client removed {}", _uuid)}; + + + Self { + socket, + addr: addr.to_string(), + on_client_add_handle: on_add, + on_client_remove_handle: on_remove, + } + } + + pub fn set_on_client_add(&mut self, Fn: fn(Client) -> ()) { + self.on_client_add_handle = Fn; + } + + pub fn set_on_client_removed(&mut self, Fn: fn(String) -> ()) { + self.on_client_remove_handle = Fn; + } + + pub fn get_info(host: &str) -> Option { + let mut buffer: [u8; 1024] = [0; 1024]; + let addr = SocketAddr::from_str(host).ok()?; + let mut stream = TcpStream::connect_timeout(&addr, Duration::from_millis(500)).ok()?; + + stream.read(&mut buffer).ok()?; + + match Commands::from(&buffer) { + Commands::Request(None) => { + stream.write_all(Commands::Info(None).to_string().as_bytes()).unwrap(); + stream.read(&mut buffer).ok()?; + Some(Commands::from(String::from(String::from_utf8_lossy(&buffer)))) + }, + _ => { + None + } + } + } + + pub fn get_clients(&self) { + + } +} \ No newline at end of file -- 2.40.1 From 20afff29310ed31ca37fe810f18bf7117164c286 Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Mon, 3 Aug 2020 12:43:53 +0100 Subject: [PATCH 09/34] Update main.rs + added a ui using cursive. + moved server star and stop into the ui. + added specific functions for constructing the ui --- src/main.rs | 140 ++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 124 insertions(+), 16 deletions(-) diff --git a/src/main.rs b/src/main.rs index ef809e8..2dd15ac 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,23 +1,131 @@ -#[macro_use] -extern crate lazy_static; +#![feature(test)] +mod client_api; +mod commands; mod server; + use crate::server::server_profile::Server; +use client_api::ClientApi; +use crossterm::ErrorKind; +use cursive::{ + Cursive, + menu::*, + event::Key, + views::{ Dialog, TextView, LinearLayout, ListView, ResizedView, Panel }, + Rect, + CursiveExt, + align::{Align, HAlign}, + view::SizeConstraint, +}; +use std::sync::Arc; +use log::info; -fn main(){ - lazy_static! { - static ref server_name: &'static str = "Server-01"; - static ref server_address: &'static str = "0.0.0.0:6000"; - static ref server_author: &'static str = "noreply@email.com"; - static ref SERVER: Server<'static> = Server::new(&server_name, &server_address, &server_author); - } - /* - let server_name = String::from("Server-01"); - let server_address = String::from("0.0.0.0:6000"); - let server_author = String::from("noreply@email.com"); - */ +fn main() -> Result<(), ErrorKind> { + let server = Server::new("Server-01", "0.0.0.0:6000", "noreply@email.com"); + let server_arc = Arc::new(server); + let s1 = server_arc.clone(); + let s2 = s1.clone(); - //let server = Server::new(server_name, server_address, server_author); - SERVER.start(); + cursive::logger::init(); + + info!("Main: init Display"); + let mut Display = Cursive::default(); + + info!("Main: setting up callbacks"); + Display.add_global_callback(Key::Backspace, |s| s.quit()); + Display.add_global_callback(Key::Tab, |s| s.toggle_debug_console()); + Display.add_global_callback(Key::Esc, |s| s.select_menubar()); + + info!("Main: setting up menu bar"); + let _ = Display.menubar() + .add_subtree("Server", + MenuTree::new() + .leaf("About", + |s| s.add_layer(About())) + .delimiter() + .leaf("quit", |s| s.quit())) + .add_subtree("File", + MenuTree::new() + .leaf("Start", move |s| {s1.start();}) + .leaf("Stop", move |s| {s2.stop();}) + .delimiter() + .leaf("Debug", |s| {s.toggle_debug_console();})); + info!("Main: entering loop"); + Display.add_layer(Control_Panel()); + Display.run(); + Ok(()) } + +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())} ) +} + +fn Launch_screen() -> Dialog { + Dialog::new() + .content(TextView::new("\ + Server. + * press for menu bar + * press for debug (FIXME) + * press to exit. + ").align(Align::center())) + .button("ok", |s| {s.pop_layer();}) +} + +fn Control_Panel() -> ResizedView> { + + let mut root = LinearLayout::horizontal(); + let mut left = LinearLayout::vertical(); + let mut right = ListView::new(); + right.add_child("test", TextView::new("")); + right.add_child("test", TextView::new("")); + right.add_delimiter(); + right.add_child("test", TextView::new("")); + right.add_child("test", TextView::new("")); + + left.add_child(TextView::new("Hello world")); + + root.add_child(left); + root.add_child(right); + ResizedView::new(SizeConstraint::AtLeast(32), SizeConstraint::AtLeast(32), Panel::new(root)) +} + +// MARK: - general testing zone +#[cfg(test)] +mod tests { + #![feature(test)] + use super::Server; + use crate::client_api::ClientApi; + use std::thread::spawn; + use std::collections::HashMap; + use crate::commands::Commands; + + #[test] + fn test_server_info() { + // setup the server + let name = "Server-01"; + let address = "0.0.0.0:6000"; + let owner = "noreply@email.com"; + + let server = Server::new(name, address, owner); + let _ = server.start().unwrap(); + + let api = ClientApi::get_info("127.0.0.1:6000"); + if api.is_some() { + + 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)); + + + let api = api.unwrap(); + assert_eq!(api, expected); + } else { + return + } + } +} \ No newline at end of file -- 2.40.1 From 164542a56b6ef82d54b9002554cc32aedde313ae Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Wed, 5 Aug 2020 16:30:13 +0100 Subject: [PATCH 10/34] changed ui --- src/main.rs | 6 +++--- src/server/client/client_profile.rs | 5 +++-- src/server/commands/mod.rs | 4 ++-- src/server/server_profile.rs | 5 +++-- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/main.rs b/src/main.rs index 2dd15ac..32da7a9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -87,9 +87,9 @@ fn Control_Panel() -> ResizedView> { left.add_child(TextView::new("Hello world")); - root.add_child(left); - root.add_child(right); - ResizedView::new(SizeConstraint::AtLeast(32), SizeConstraint::AtLeast(32), Panel::new(root)) + root.add_child(ResizedView::new(SizeConstraint::Full, SizeConstraint::Full, Panel::new(left))); + root.add_child(ResizedView::new(SizeConstraint::Full, SizeConstraint::Full, Panel::new(right))); + ResizedView::new(SizeConstraint::Fixed(64), SizeConstraint::Fixed(20), Panel::new(root)) } // MARK: - general testing zone diff --git a/src/server/client/client_profile.rs b/src/server/client/client_profile.rs index 9c0f8e7..014f1b7 100644 --- a/src/server/client/client_profile.rs +++ b/src/server/client/client_profile.rs @@ -10,8 +10,9 @@ use crossbeam::{Sender, Receiver, TryRecvError, unbounded}; use crate::{ server::{ server_profile::ServerMessages, - commands::Commands - } + }, + commands::Commands + }; use std::sync::Mutex; use std::time::Duration; diff --git a/src/server/commands/mod.rs b/src/server/commands/mod.rs index 2a8ae19..d15c145 100644 --- a/src/server/commands/mod.rs +++ b/src/server/commands/mod.rs @@ -203,8 +203,8 @@ impl From for Commands { impl From<&[u8]> for Commands { fn from(data: &[u8]) -> Self { - let incoming_message = String::from(String::from_utf8_lossy(data)).as_str(); - Commands::from(incoming_message) + let incoming_message = String::from(String::from_utf8_lossy(data)); + Commands::from(incoming_message.as_str()) } } diff --git a/src/server/server_profile.rs b/src/server/server_profile.rs index ba264a6..0b1ba5b 100644 --- a/src/server/server_profile.rs +++ b/src/server/server_profile.rs @@ -4,8 +4,9 @@ extern crate rayon; use crate::{ server::{ client::client_profile::Client, - commands::{Commands} - } + + }, + commands::Commands }; use std::{ sync::{Arc, Mutex}, -- 2.40.1 From a1474d01ded436bf431370f23c1461453d7fb7a3 Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Fri, 7 Aug 2020 23:48:05 +0100 Subject: [PATCH 11/34] increment version + clap crate ~ attempting fix on getting info + added clap arg parsing + added rudementary client, remove and update and error --- Cargo.toml | 3 +- src/client_api/mod.rs | 32 ++++---- src/main.rs | 120 ++++++++++++++++++---------- src/server/client/client_profile.rs | 115 ++++++++++++++++---------- src/server/commands/mod.rs | 3 +- src/server/server_profile.rs | 43 +++++----- 6 files changed, 193 insertions(+), 123 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6e45bba..a7f433e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rust-chat-server" -version = "0.1.0" +version = "0.1.5" authors = ["Mitchell "] edition = "2018" @@ -22,6 +22,7 @@ zeroize = "1.1.0" cursive = { version = "0.15.0", default-features = false, features = ["crossterm-backend"]} crossterm = "0.17.7" log = "0.4" +clap = "3.0.0-beta.1" diff --git a/src/client_api/mod.rs b/src/client_api/mod.rs index da356eb..2db5472 100644 --- a/src/client_api/mod.rs +++ b/src/client_api/mod.rs @@ -1,16 +1,12 @@ -use std::{ - net::TcpStream, - io::{Write, Read} -}; +use std::{net::TcpStream, io::{Write, Read}, io}; use crate::{ server::client::client_profile::Client, commands::Commands, }; -use zeroize::Zeroize; use std::time::Duration; -use async_std::net::SocketAddrV4; use std::str::FromStr; use std::net::SocketAddr; +use zeroize::Zeroize; pub struct ClientApi { @@ -37,29 +33,31 @@ impl ClientApi { } } - pub fn set_on_client_add(&mut self, Fn: fn(Client) -> ()) { - self.on_client_add_handle = Fn; + pub fn set_on_client_add(&mut self, func: fn(Client) -> ()) { + self.on_client_add_handle = func; } - pub fn set_on_client_removed(&mut self, Fn: fn(String) -> ()) { - self.on_client_remove_handle = Fn; + pub fn set_on_client_removed(&mut self, func: fn(String) -> ()) { + self.on_client_remove_handle = func; } - pub fn get_info(host: &str) -> Option { + pub fn get_info(host: &str) -> Result { let mut buffer: [u8; 1024] = [0; 1024]; - let addr = SocketAddr::from_str(host).ok()?; - let mut stream = TcpStream::connect_timeout(&addr, Duration::from_millis(500)).ok()?; + let addr = host.parse().unwrap(); + let mut stream = TcpStream::connect_timeout(&addr, Duration::from_millis(10000))?; - stream.read(&mut buffer).ok()?; + stream.read(&mut buffer)?; match Commands::from(&buffer) { Commands::Request(None) => { + buffer.zeroize(); stream.write_all(Commands::Info(None).to_string().as_bytes()).unwrap(); - stream.read(&mut buffer).ok()?; - Some(Commands::from(String::from(String::from_utf8_lossy(&buffer)))) + let a = stream.read(&mut buffer); + a?; + Ok(Commands::from(String::from(String::from_utf8_lossy(&buffer)))) }, _ => { - None + Err(io::Error::new(io::ErrorKind::InvalidData, "the data was not expected")) } } } diff --git a/src/main.rs b/src/main.rs index 32da7a9..7bbdcb1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,69 +1,86 @@ #![feature(test)] +#![allow(dead_code)] mod client_api; mod commands; mod server; - -use crate::server::server_profile::Server; -use client_api::ClientApi; -use crossterm::ErrorKind; use cursive::{ Cursive, menu::*, event::Key, views::{ Dialog, TextView, LinearLayout, ListView, ResizedView, Panel }, - Rect, CursiveExt, - align::{Align, HAlign}, + align::Align, view::SizeConstraint, }; use std::sync::Arc; +use crossterm::ErrorKind; use log::info; +use clap::{App, Arg}; + +use crate::server::server_profile::Server; fn main() -> Result<(), ErrorKind> { - let server = Server::new("Server-01", "0.0.0.0:6000", "noreply@email.com"); - let server_arc = Arc::new(server); - let s1 = server_arc.clone(); - let s2 = s1.clone(); - cursive::logger::init(); + 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(); - info!("Main: init Display"); - let mut Display = Cursive::default(); + if args.is_present("graphical") { + let server = Server::new("Server-01", "0.0.0.0:6000", "noreply@email.com"); + let server_arc = Arc::new(server); + let s1 = server_arc.clone(); + let s2 = s1.clone(); - 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()); + cursive::logger::init(); - info!("Main: setting up menu bar"); - let _ = Display.menubar() - .add_subtree("Server", - MenuTree::new() - .leaf("About", - |s| s.add_layer(About())) - .delimiter() - .leaf("quit", |s| s.quit())) - .add_subtree("File", - MenuTree::new() - .leaf("Start", move |s| {s1.start();}) - .leaf("Stop", move |s| {s2.stop();}) - .delimiter() - .leaf("Debug", |s| {s.toggle_debug_console();})); - info!("Main: entering loop"); - Display.add_layer(Control_Panel()); - Display.run(); - Ok(()) + info!("Main: init display"); + let mut display = Cursive::default(); + + info!("Main: setting up callbacks"); + display.add_global_callback(Key::Backspace, |s| s.quit()); + display.add_global_callback(Key::Tab, |s| s.toggle_debug_console()); + display.add_global_callback(Key::Esc, |s| s.select_menubar()); + + info!("Main: setting up menu bar"); + let _ = display.menubar() + .add_subtree("Server", + MenuTree::new() + .leaf("about", + |s| s.add_layer(about())) + .delimiter() + .leaf("quit", |s| s.quit())) + .add_subtree("File", + MenuTree::new() + .leaf("Start", move |_s| {let _ = s1.start();}) + .leaf("Stop", move |_s| {let _ = s2.stop();}) + .delimiter() + .leaf("Debug", |s| {s.toggle_debug_console();})); + info!("Main: entering loop"); + display.add_layer(control_panel()); + display.run(); + Ok(()) + } else { + let server = Server::new("Server-01", "0.0.0.0:6000", "noreply@email.com"); + server.start()?; + loop {} + } } -fn About() -> Dialog { +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())} ) + ).button("Close", |s| {let _ = s.pop_layer(); s.add_layer(control_panel())} ) } -fn Launch_screen() -> Dialog { +fn launch_screen() -> Dialog { Dialog::new() .content(TextView::new("\ Server. @@ -74,7 +91,7 @@ fn Launch_screen() -> Dialog { .button("ok", |s| {s.pop_layer();}) } -fn Control_Panel() -> ResizedView> { +fn control_panel() -> ResizedView> { let mut root = LinearLayout::horizontal(); let mut left = LinearLayout::vertical(); @@ -101,6 +118,7 @@ mod tests { use std::thread::spawn; use std::collections::HashMap; use crate::commands::Commands; + use log::info; #[test] fn test_server_info() { @@ -113,7 +131,8 @@ mod tests { let _ = server.start().unwrap(); let api = ClientApi::get_info("127.0.0.1:6000"); - if api.is_some() { + assert_eq!(api.is_ok(), true); + if api.is_ok() { let mut map = HashMap::new(); map.insert("name".to_string(), name.to_string()); @@ -121,11 +140,30 @@ mod tests { let expected = Commands::Info(Some(map)); - let api = api.unwrap(); assert_eq!(api, expected); } else { return } } + + #[test] + fn test_server_connect() { + let name = "Server-01"; + let address = "0.0.0.0:6000"; + let owner = "noreply@email.com"; + + let server = Server::new(name, address, owner); + let _ = server.start().unwrap(); + + let api = ClientApi::get_info("127.0.0.1:6000"); + assert_eq!(api.is_ok(), true); + if let Commands::Success(Some(params)) = api.unwrap() { + let mut api = ClientApi::new(address); + + api.on_client_add_handle = |s| info!("new clinet: {:?}", s); + api.on_client_remove_handle = |s| info!("removed clinet: {:?}", s); + + } + } } \ No newline at end of file diff --git a/src/server/client/client_profile.rs b/src/server/client/client_profile.rs index 014f1b7..2fc2e15 100644 --- a/src/server/client/client_profile.rs +++ b/src/server/client/client_profile.rs @@ -1,21 +1,21 @@ -extern crate regex; - use std::{ - sync::Arc, - net::{Shutdown, TcpStream}, io::prelude::*, + sync::Arc, + io, + sync::Mutex, + time::{Instant, Duration}, + net::{TcpStream, Shutdown} }; -use crossbeam::{Sender, Receiver, TryRecvError, unbounded}; - -use crate::{ - server::{ - server_profile::ServerMessages, - }, - commands::Commands - +use crossbeam::{ + Sender, + Receiver, + TryRecvError, + unbounded }; -use std::sync::Mutex; -use std::time::Duration; +use log::info; + +use crate::server::server_profile::ServerMessages; +use crate::commands::Commands; #[derive(Debug)] pub struct Client { @@ -24,9 +24,9 @@ pub struct Client { pub username: String, pub address: String, - stream_arc: Arc>, + last_heartbeat: Arc>, - heartbeat_ticker: Arc>, + stream_arc: Arc>, pub sender: Sender, receiver: Receiver, @@ -45,41 +45,57 @@ impl Client { username: username.to_string(), address: address.to_string(), - heartbeat_ticker: Arc::new(Mutex::new(5)), - sender, receiver, server_sender, + last_heartbeat: Arc::new(Mutex::new(Instant::now())), } } - #[allow(unused_variables)] + // TODO: - add heartbeat timer. pub fn handle_connection(&self) { + info!("{}: handling connection", self.uuid); + println!("buffer"); let mut buffer = [0; 1024]; // test to see if there is anything for the client to receive from its channel - println!("{}: channel checks", self.uuid); + match self.receiver.try_recv() { /*command is on the channel*/ - Ok(Commands::Info(Some(params))) => { - self.transmit_data(Commands::Info(Some(params)).to_string().as_str()); + Ok(Commands::ClientRemove(Some(params))) => { + let retry: u8 = 3; + 'retry_loop1: loop { + if retry < 1 { + self.transmit_data(Commands::Error(None).to_string().as_str()); + break 'retry_loop1 + } + self.transmit_data(Commands::ClientRemove(Some(params.clone())).to_string().as_str()); + let _ = self.stream_arc.lock().unwrap().read(&mut buffer); + let command = Commands::from(&buffer); + if command == Commands::Success(None) { + break 'retry_loop1; + } + } }, + Ok(Commands::Client(Some(params))) => { + let retry: u8 = 3; + 'retry_loop2: loop { + if retry < 1 { + self.transmit_data(Commands::Error(None).to_string().as_str()); + break 'retry_loop2; + } + self.transmit_data(Commands::Client(Some(params.clone())).to_string().as_str()); + let _ = self.stream_arc.lock().unwrap().read(&mut buffer); + let command = Commands::from(&buffer); + if command == Commands::Success(None) { + break 'retry_loop2; + } + } - Ok(Commands::Disconnect(None)) => { - - } - - Ok(Commands::ClientRemove(Some(params))) => { }, - Ok(Commands::Success(params)) => { self.transmit_data(Commands::Success(params).to_string().as_str()); }, - Ok(Commands::Client(Some(params))) => { self.transmit_data(Commands::Client(Some(params)).to_string().as_str()); }, - - /*sender disconnected*/ - Err(TryRecvError::Disconnected) => { - self.server_sender.send(ServerMessages::RequestDisconnect(self.uuid.clone())); }, /*no data available yet*/ Err(TryRecvError::Empty) => {}, @@ -100,14 +116,18 @@ impl Client { println!("command"); match command { Commands::Disconnect(None) => { - self.server_sender.send(ServerMessages::RequestDisconnect(self.uuid.clone())).expect("sending message to server failed"); + self.server_sender.send(ServerMessages::Disconnect(self.uuid.clone())).expect("sending message to server failed"); }, Commands::HeartBeat(None) => { - self.transmit_data(Commands::HeartBeat(None).to_string().as_str()) + *self.last_heartbeat.lock().unwrap() = Instant::now(); + let _ = stream.write_all(Commands::Success(None).to_string().as_bytes()); + }, + Commands::ClientUpdate(None) => { + let _ = self.server_sender.send(ServerMessages::RequestUpdate(self.uuid.clone())); + let _ = stream.write_all(Commands::Success(None).to_string().as_bytes()); } _ => { - - self.transmit_data(Commands::Error(None).to_string().as_str()) + let _ = stream.write_all(Commands::Error(None).to_string().as_bytes()); } } } @@ -120,10 +140,25 @@ impl Client { self.stream_arc.lock().unwrap().shutdown(Shutdown::Both).expect("shutdown call failed"); } - pub fn transmit_data(&self, data: &str){ + pub fn transmit_data(&self, data: &str) { println!("Transmitting data: {}", data); - self.stream_arc.lock().unwrap().write_all(data.to_string().as_bytes()).unwrap(); - self.stream_arc.lock().unwrap().flush().unwrap(); + let error_result = self.stream_arc.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())); + }, + _ => { } + } + } + } +} + +impl Drop for Client { + fn drop(&mut self) { + let _ = self.stream_arc.lock().unwrap().write_all(Commands::Disconnect(None).to_string().as_bytes()); + let _ = self.stream_arc.lock().unwrap().shutdown(Shutdown::Both); } } diff --git a/src/server/commands/mod.rs b/src/server/commands/mod.rs index d15c145..27a4a61 100644 --- a/src/server/commands/mod.rs +++ b/src/server/commands/mod.rs @@ -12,7 +12,6 @@ mod message; use std::string::ToString; use std::collections::HashMap; -use dashmap::DashMap; use std::borrow::Borrow; use regex::Regex; use std::ops::Index; @@ -168,7 +167,7 @@ impl From<&str> for Commands { for i in iter { let parameter = i.as_str().to_string(); - let mut parts:Vec<&str> = parameter.split(":").collect(); + let parts:Vec<&str> = parameter.split(":").collect(); map.insert(parts.index(0).to_string(), parts.index(1).to_string()); } diff --git a/src/server/server_profile.rs b/src/server/server_profile.rs index 0b1ba5b..97dd810 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, @@ -17,6 +14,9 @@ use std::{ io }; +use regex::Regex; +use rayon::prelude::*; + use log::info; use crossbeam_channel::{Sender, Receiver, unbounded}; @@ -31,7 +31,7 @@ pub enum ServerMessages { #[allow(dead_code)] RequestInfo(String, String), #[allow(dead_code)] - RequestDisconnect(String), + Disconnect(String), #[allow(dead_code)] Shutdown, } @@ -85,12 +85,12 @@ impl Server { // set up listener and buffer let listener = TcpListener::bind(self.get_address())?; - listener.set_nonblocking(true); + listener.set_nonblocking(true)?; let mut buffer = [0; 1024]; info!("server: spawning threads"); - thread::Builder::new().name("Server Thread".to_string()).spawn(move || { + let _ = thread::Builder::new().name("Server Thread".to_string()).spawn(move || { 'outer: loop { // get messages from the servers channel. info!("server: getting messages"); @@ -100,6 +100,7 @@ impl Server { // TODO: implement disconnecting all clients and shutting down the server info!("server: shutting down..."); + break 'outer; }, _ => {} @@ -107,14 +108,14 @@ impl Server { } info!("server: checking for new connections"); - if let Ok((mut stream, addr)) = listener.accept() { - stream.set_read_timeout(Some(Duration::from_millis(100))).unwrap(); + if let Ok((mut stream, _addr)) = listener.accept() { + stream.set_read_timeout(Some(Duration::from_millis(10000))).unwrap(); let request = Commands::Request(None); //request.to_string(); - stream.write_all(&request.to_string().as_bytes()); - stream.flush(); - stream.read(&mut buffer).unwrap(); + let _ = stream.write_all(&request.to_string().as_bytes()); + let _ = stream.flush(); + let _ = stream.read(&mut buffer).unwrap(); let incoming_message = String::from(String::from_utf8_lossy(&buffer)); let command = Commands::from(incoming_message); @@ -127,7 +128,7 @@ impl Server { let username = data.get("name").unwrap(); let address = data.get("host").unwrap(); - info!("{}", format!("Server: new Client connection: addr = {}", address )); + info!("{}", format!("Server: new Client connection: _addr = {}", address )); let client = Client::new(stream, sender.clone(), uuid.clone(), username.clone(), address.clone()); @@ -136,8 +137,10 @@ impl Server { 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)); - client_map.lock().unwrap().iter().map(|(k, v)| v.sender.send(new_client.clone())); + let _ = client_map.lock().unwrap().iter().map(|(_k, v)| v.sender.send(new_client.clone())); }, + + // TODO: - correct connection reset error when getting info. Commands::Info(None) => { info!("Server: info requested"); let mut params: HashMap = HashMap::new(); @@ -146,13 +149,13 @@ impl Server { let command = Commands::Info(Some(params)); - stream.write_all(&command.to_string().as_bytes()); - stream.flush(); + let _ = stream.write_all(&command.to_string().as_bytes()); + let _ = stream.flush(); }, _ => { info!("Server: Invalid command sent"); - stream.write_all(Commands::Error(None).to_string().as_bytes()); - stream.flush(); + let _ = stream.write_all(Commands::Error(None).to_string().as_bytes()); + let _ = stream.flush(); }, } } @@ -172,7 +175,7 @@ impl Server { pub fn stop(&self) { info!("server: sending stop message"); - self.sender.send(ServerMessages::Shutdown); + let _ = self.sender.send(ServerMessages::Shutdown); } #[allow(dead_code)] @@ -212,8 +215,4 @@ impl Drop for Server { println!("server dropped"); let _ = self.sender.send(ServerMessages::Shutdown); } -} - -struct ServerDelegate { - } \ No newline at end of file -- 2.40.1 From ef86b89042d69847945a13b27cc7fa528a07cadf Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Sun, 9 Aug 2020 11:36:14 +0100 Subject: [PATCH 12/34] added proper definition of traits for classes --- .gitignore | 3 ++- Cargo.toml | 2 +- src/client_api/mod.rs | 4 +--- src/commands/mod.rs | 16 ++++++++++++++-- src/server/client/client_profile.rs | 8 ++++++-- src/server/server_profile.rs | 28 ++++++++++++++++++---------- 6 files changed, 42 insertions(+), 19 deletions(-) diff --git a/.gitignore b/.gitignore index c11cf5e..df45ac5 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,5 @@ Cargo.lock .DS_Store .idea -*.properties \ No newline at end of file +*.properties +.vscode/launch.json diff --git a/Cargo.toml b/Cargo.toml index a7f433e..ec04711 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,12 +17,12 @@ dashmap = "3.11.4" async-std = "1.6.2" lazy_static = "1.4.0" rayon = "1.3.1" -diesel = { version = "1.4.5", features = ["sqlite"] } zeroize = "1.1.0" cursive = { version = "0.15.0", default-features = false, features = ["crossterm-backend"]} crossterm = "0.17.7" log = "0.4" clap = "3.0.0-beta.1" +rust-bert = "0.7.11" diff --git a/src/client_api/mod.rs b/src/client_api/mod.rs index 2db5472..5af504e 100644 --- a/src/client_api/mod.rs +++ b/src/client_api/mod.rs @@ -4,8 +4,6 @@ use crate::{ commands::Commands, }; use std::time::Duration; -use std::str::FromStr; -use std::net::SocketAddr; use zeroize::Zeroize; @@ -46,7 +44,7 @@ impl ClientApi { let addr = host.parse().unwrap(); let mut stream = TcpStream::connect_timeout(&addr, Duration::from_millis(10000))?; - stream.read(&mut buffer)?; + let _ = stream.read(&mut buffer)?; match Commands::from(&buffer) { Commands::Request(None) => { diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 642d6c8..61e0f6c 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -1,5 +1,6 @@ use std::string::ToString; use std::collections::HashMap; +use std::str::FromStr; use std::borrow::Borrow; use regex::Regex; @@ -25,6 +26,11 @@ pub enum Commands { Error(Option>), } +pub enum CommandParseError { + UnknownCommand, + NoString, +} + impl ToString for Commands { fn to_string(&self) -> std::string::String { @@ -58,14 +64,20 @@ impl ToString for Commands { } else { out_string.push_str(v.as_str()); } - - } } out_string } } +impl FromStr for Commands { + type Err = CommandParseError; + + fn from_str(_: &str) -> std::result::Result { + todo!(); + } +} + impl From<&str> for Commands { fn from(data: &str) -> Self { let regex = Regex::new(r###"(\?|!)([a-zA-z0-9]*):|([a-zA-z]*):([a-zA-Z0-9@\-\+\[\]{}_=/.]+|("(.*?)")+)"###).unwrap(); diff --git a/src/server/client/client_profile.rs b/src/server/client/client_profile.rs index 2fc2e15..4de3925 100644 --- a/src/server/client/client_profile.rs +++ b/src/server/client/client_profile.rs @@ -108,7 +108,7 @@ impl Client { if self.stream_arc.lock().unwrap().peek(&mut buffer).is_ok() { let mut stream = self.stream_arc.lock().unwrap(); - stream.read(&mut buffer).unwrap(); + let _ = stream.read(&mut buffer).unwrap(); let command = Commands::from(&buffer); @@ -123,7 +123,7 @@ impl Client { let _ = stream.write_all(Commands::Success(None).to_string().as_bytes()); }, Commands::ClientUpdate(None) => { - let _ = self.server_sender.send(ServerMessages::RequestUpdate(self.uuid.clone())); + let _ = self.server_sender.send(ServerMessages::RequestUpdate(self.stream_arc.clone())); let _ = stream.write_all(Commands::Success(None).to_string().as_bytes()); } _ => { @@ -156,6 +156,10 @@ impl Client { } } +impl ToString for Client { + fn to_string(&self) -> std::string::String { todo!() } +} + impl Drop for Client { fn drop(&mut self) { let _ = self.stream_arc.lock().unwrap().write_all(Commands::Disconnect(None).to_string().as_bytes()); diff --git a/src/server/server_profile.rs b/src/server/server_profile.rs index 97dd810..2912119 100644 --- a/src/server/server_profile.rs +++ b/src/server/server_profile.rs @@ -14,8 +14,6 @@ use std::{ io }; -use regex::Regex; -use rayon::prelude::*; use log::info; @@ -27,9 +25,9 @@ use std::time::Duration; #[derive(Debug)] pub enum ServerMessages { #[allow(dead_code)] - RequestUpdate(String), + RequestUpdate(Arc>), #[allow(dead_code)] - RequestInfo(String, String), + RequestInfo(String, Arc>), #[allow(dead_code)] Disconnect(String), #[allow(dead_code)] @@ -74,7 +72,7 @@ impl Server { self.address.to_string() } - pub fn start(&self) -> Result<(), io::Error>{ + pub fn start<'a>(&self) -> Result<(), io::Error>{ info!("server: starting server..."); // clone elements for thread let client_map = self.connected_clients.clone(); @@ -100,9 +98,15 @@ impl Server { // TODO: implement disconnecting all clients and shutting down the server info!("server: shutting down..."); - break 'outer; }, + ServerMessages::RequestUpdate(stream_arc) => { + let mut stream = stream_arc.lock().unwrap(); + for (_k, v) in client_map.lock().unwrap().iter() { + let _ = &stream.write_all(v.to_string().as_bytes()); + let _ = &stream.flush(); + } + } _ => {} } } @@ -149,8 +153,8 @@ impl Server { let command = Commands::Info(Some(params)); - let _ = stream.write_all(&command.to_string().as_bytes()); - let _ = stream.flush(); + stream.write_all(command.to_string().as_bytes()).unwrap(); + stream.flush().unwrap(); }, _ => { info!("Server: Invalid command sent"); @@ -167,7 +171,7 @@ impl Server { v.handle_connection(); } } - info!("server: stopped") + info!("server: stopped"); }); info!("server: started"); Ok(()) @@ -205,11 +209,15 @@ impl Server { * the connection is lost before transmitting. Maybe change to handle any exceptions * that may occur. */ - stream.write(data.to_string().as_bytes()).unwrap(); + let _ = stream.write(data.to_string().as_bytes()).unwrap(); stream.flush().unwrap(); } } +impl ToString for Server { + fn to_string(&self) -> std::string::String { todo!() } +} + impl Drop for Server { fn drop(&mut self) { println!("server dropped"); -- 2.40.1 From 03ab7290c55df203f389c8cf063d3f9bad6d2671 Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Sun, 9 Aug 2020 21:17:17 +0100 Subject: [PATCH 13/34] added functions --- Cargo.toml | 3 +-- src/commands/mod.rs | 20 +++++++++----------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ec04711..5351899 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,10 +20,9 @@ rayon = "1.3.1" zeroize = "1.1.0" cursive = { version = "0.15.0", default-features = false, features = ["crossterm-backend"]} crossterm = "0.17.7" -log = "0.4" clap = "3.0.0-beta.1" rust-bert = "0.7.11" - +log = "0.4" [profile.dev] diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 61e0f6c..3bbd6e8 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -73,19 +73,13 @@ impl ToString for Commands { impl FromStr for Commands { type Err = CommandParseError; - fn from_str(_: &str) -> std::result::Result { - todo!(); - } -} - -impl From<&str> for Commands { - fn from(data: &str) -> Self { + fn from_str(data: &str) -> std::result::Result { let regex = Regex::new(r###"(\?|!)([a-zA-z0-9]*):|([a-zA-z]*):([a-zA-Z0-9@\-\+\[\]{}_=/.]+|("(.*?)")+)"###).unwrap(); let mut iter = regex.find_iter(data); let command_opt = iter.next(); if command_opt.is_none() { - return Commands::Error(None); + return Err(CommandParseError::NoString); } let command = command_opt.unwrap().as_str(); @@ -103,7 +97,7 @@ impl From<&str> for Commands { let params = if map.capacity() > 1 {Some(map)} else { None }; - match command { + Ok(match command { "!request:" => Commands::Request(params), "!info:" => Commands::Info(params), @@ -121,13 +115,17 @@ impl From<&str> for Commands { "!error:" => Commands::Error(params), _ => Commands::Error(params), - } + }) } } impl From for Commands { fn from(data: String) -> Self { - Commands::from(data.as_str()) + if let Ok(data) = data.as_str().parse() { + data + } else { + Commands::Error(None) + } } } -- 2.40.1 From 5057cec2830ebdc4e9d8ba7f7957950759eaa98a Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Tue, 11 Aug 2020 23:35:00 +0100 Subject: [PATCH 14/34] modified the command to use a trait to parse &str --- src/commands/mod.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 3bbd6e8..2747afc 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -5,6 +5,7 @@ use std::str::FromStr; use std::borrow::Borrow; use regex::Regex; use std::ops::{Index}; +use log::info; // MARK: - command struct #[derive(Clone, Debug, Eq, PartialEq)] @@ -26,6 +27,7 @@ pub enum Commands { Error(Option>), } +#[derive(Debug)] pub enum CommandParseError { UnknownCommand, NoString, @@ -84,7 +86,7 @@ impl FromStr for Commands { let command = command_opt.unwrap().as_str(); - println!("command: {:?}", command); + println!("command parsed to: {:?}", command); let mut map: HashMap = HashMap::new(); @@ -114,7 +116,7 @@ impl FromStr for Commands { "!success:" => Commands::Success(params), "!error:" => Commands::Error(params), - _ => Commands::Error(params), + _ => Commands::Error(None), }) } } @@ -124,6 +126,7 @@ impl From for Commands { if let Ok(data) = data.as_str().parse() { data } else { + info!("Command: failed to parse with"); Commands::Error(None) } } @@ -143,10 +146,12 @@ mod test_commands_v2 { use super::Commands; use std::collections::HashMap; use test::Bencher; + use std::str::FromStr; + use super::CommandParseError; #[test] fn test_creation_from_string() { - let command_result = Commands::from("!connect: name:bop host:127.0.0.1 uuid:123456-1234-1234-123456"); + let command_result = Commands::from_str("!connect: name:bop host:127.0.0.1 uuid:123456-1234-1234-123456").expect("parse error"); () } @@ -165,6 +170,6 @@ mod test_commands_v2 { #[bench] fn benchmark(b: &mut Bencher) { - b.iter(|| Commands::from("!connect: host:192.168.0.1 name:\"michael-bailey\" uuid:123456-1234-1234-123456")) + b.iter(|| {let a = Commands::from_str("!connect: host:192.168.0.1 name:\"michael-bailey\" uuid:123456-1234-1234-123456").unwrap();}) } } -- 2.40.1 From dfcc3fbedc173666be190938bd86cf13b220dbee Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Tue, 11 Aug 2020 23:35:47 +0100 Subject: [PATCH 15/34] Update main.rs ~ hindered the main thread when in non gui mode to lowwer resources --- src/main.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 7bbdcb1..30a9159 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,6 +4,7 @@ mod client_api; mod commands; mod server; +mod lib; use cursive::{ Cursive, @@ -15,6 +16,7 @@ use cursive::{ view::SizeConstraint, }; use std::sync::Arc; +use std::time::Duration; use crossterm::ErrorKind; use log::info; use clap::{App, Arg}; @@ -70,7 +72,7 @@ fn main() -> Result<(), ErrorKind> { } else { let server = Server::new("Server-01", "0.0.0.0:6000", "noreply@email.com"); server.start()?; - loop {} + loop {std::thread::sleep(Duration::from_secs(1));} } } -- 2.40.1 From 593fbc96ed0a0a1171633cacf1b11155d40914e2 Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Tue, 11 Aug 2020 23:36:05 +0100 Subject: [PATCH 16/34] Update client_profile.rs removing the transmit method --- src/server/client/client_profile.rs | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/server/client/client_profile.rs b/src/server/client/client_profile.rs index 4de3925..23b522b 100644 --- a/src/server/client/client_profile.rs +++ b/src/server/client/client_profile.rs @@ -62,37 +62,44 @@ impl Client { let mut buffer = [0; 1024]; // 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 retry: u8 = 3; + let mut stream = self.stream_arc.lock().unwrap(); + let mut retry: u8 = 3; 'retry_loop1: loop { if retry < 1 { self.transmit_data(Commands::Error(None).to_string().as_str()); break 'retry_loop1 } + self.transmit_data(Commands::ClientRemove(Some(params.clone())).to_string().as_str()); - let _ = self.stream_arc.lock().unwrap().read(&mut buffer); + let _ = stream.read(&mut buffer); let command = Commands::from(&buffer); if command == Commands::Success(None) { break 'retry_loop1; + } else { + retry -= 1; } } }, Ok(Commands::Client(Some(params))) => { - let retry: u8 = 3; + let mut stream = self.stream_arc.lock().unwrap(); + let mut retry: u8 = 3; 'retry_loop2: loop { if retry < 1 { - self.transmit_data(Commands::Error(None).to_string().as_str()); + stream.write_all(Commands::Error(None).to_string().as_bytes()); break 'retry_loop2; } - self.transmit_data(Commands::Client(Some(params.clone())).to_string().as_str()); - let _ = self.stream_arc.lock().unwrap().read(&mut buffer); + stream.write_all(Commands::Client(Some(params.clone())).to_string().as_bytes()); + let _ = stream.read(&mut buffer); let command = Commands::from(&buffer); if command == Commands::Success(None) { break 'retry_loop2; + } else { + retry -= 1; } } @@ -103,8 +110,6 @@ impl Client { } println!("socket"); - let a = self.stream_arc.lock().unwrap().peek(&mut buffer).is_ok(); - println!("does have content: {}", a); if self.stream_arc.lock().unwrap().peek(&mut buffer).is_ok() { let mut stream = self.stream_arc.lock().unwrap(); -- 2.40.1 From b1ad04ed50cdbc55597d7fa30a1a3d4c1347a983 Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Tue, 11 Aug 2020 23:36:27 +0100 Subject: [PATCH 17/34] Update server_profile.rs added Dissconnect method --- src/server/server_profile.rs | 92 ++++++++++++++++++++---------------- 1 file changed, 50 insertions(+), 42 deletions(-) diff --git a/src/server/server_profile.rs b/src/server/server_profile.rs index 2912119..ecdee28 100644 --- a/src/server/server_profile.rs +++ b/src/server/server_profile.rs @@ -90,6 +90,8 @@ impl Server { info!("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. info!("server: getting messages"); for i in receiver.try_iter() { @@ -107,6 +109,9 @@ impl Server { let _ = &stream.flush(); } } + ServerMessages::Disconnect(uuid) => { + client_map.lock().unwrap().remove(&uuid); + }, _ => {} } } @@ -114,53 +119,56 @@ impl Server { info!("server: checking for new connections"); if let Ok((mut stream, _addr)) = listener.accept() { stream.set_read_timeout(Some(Duration::from_millis(10000))).unwrap(); + let _ = stream.set_nonblocking(false); let request = Commands::Request(None); //request.to_string(); let _ = stream.write_all(&request.to_string().as_bytes()); let _ = stream.flush(); - let _ = stream.read(&mut buffer).unwrap(); - - let incoming_message = String::from(String::from_utf8_lossy(&buffer)); - let command = Commands::from(incoming_message); - // clears the buffer. - buffer.zeroize(); - - match command { - Commands::Connect(Some(data)) => { - let uuid = data.get("uuid").unwrap(); - let username = data.get("name").unwrap(); - let address = data.get("host").unwrap(); - - info!("{}", format!("Server: new Client connection: _addr = {}", address )); - - let client = Client::new(stream, sender.clone(), uuid.clone(), username.clone(), address.clone()); - - client_map.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 _ = client_map.lock().unwrap().iter().map(|(_k, v)| v.sender.send(new_client.clone())); - }, - - // TODO: - correct connection reset error when getting info. - Commands::Info(None) => { - info!("Server: info requested"); - let mut params: HashMap = HashMap::new(); - params.insert(String::from("name"), server_details.0.clone()); - params.insert(String::from("owner"), server_details.1.clone()); - - let command = Commands::Info(Some(params)); - - stream.write_all(command.to_string().as_bytes()).unwrap(); - stream.flush().unwrap(); - }, - _ => { - info!("Server: Invalid command sent"); - let _ = stream.write_all(Commands::Error(None).to_string().as_bytes()); - let _ = stream.flush(); - }, + let _ = stream.read(&mut buffer); + if let Ok(size) = stream.read(&mut buffer) { + let incoming_message = String::from(String::from_utf8_lossy(&buffer)); + let command = Commands::from(incoming_message); + info!("Server: new connection sent - {:?}", command); + // clears the buffer. + buffer.zeroize(); + + match command { + Commands::Connect(Some(data)) => { + let uuid = data.get("uuid").unwrap(); + let username = data.get("name").unwrap(); + let address = data.get("host").unwrap(); + + info!("{}", format!("Server: new Client connection: _addr = {}", address )); + + let client = Client::new(stream, sender.clone(), uuid.clone(), username.clone(), address.clone()); + + client_map.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 _ = client_map.lock().unwrap().iter().map(|(_k, v)| v.sender.send(new_client.clone())); + }, + + // TODO: - correct connection reset error when getting info. + Commands::Info(None) => { + info!("Server: info requested"); + let mut params: HashMap = HashMap::new(); + params.insert(String::from("name"), server_details.0.clone()); + params.insert(String::from("owner"), server_details.1.clone()); + + let command = Commands::Info(Some(params)); + + stream.write_all(command.to_string().as_bytes()).expect("Server -Info: writing failed"); + stream.flush().expect("Server -Info: flushing errored"); + }, + _ => { + info!("Server: Invalid command sent"); + let _ = stream.write_all(Commands::Error(None).to_string().as_bytes()); + let _ = stream.flush(); + }, + } } } // TODO: end - -- 2.40.1 From be167055e8ad4c431d04f6cbec77d0a0e02c219f Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Wed, 12 Aug 2020 16:44:24 +0100 Subject: [PATCH 18/34] connecting and disconnecting works --- src/client_api/mod.rs | 28 +++++--- src/main.rs | 31 +++----- src/server/client/client_profile.rs | 105 ++++++++++++++-------------- src/server/server_profile.rs | 22 +++--- 4 files changed, 96 insertions(+), 90 deletions(-) diff --git a/src/client_api/mod.rs b/src/client_api/mod.rs index 5af504e..5afe4e1 100644 --- a/src/client_api/mod.rs +++ b/src/client_api/mod.rs @@ -16,19 +16,18 @@ pub struct ClientApi { } impl ClientApi { - pub fn new(addr: &str) -> Self { - let socket = TcpStream::connect(addr).expect("connection failed"); + 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)}; - - - Self { + let a = Self { socket, addr: addr.to_string(), on_client_add_handle: on_add, on_client_remove_handle: on_remove, - } + }; + Ok(a) } pub fn set_on_client_add(&mut self, func: fn(Client) -> ()) { @@ -42,16 +41,23 @@ impl ClientApi { 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(10000))?; + 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(&buffer) { Commands::Request(None) => { + println!("zeroing"); buffer.zeroize(); - stream.write_all(Commands::Info(None).to_string().as_bytes()).unwrap(); - let a = stream.read(&mut buffer); - a?; + 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)))) }, _ => { diff --git a/src/main.rs b/src/main.rs index 30a9159..8b1e4a1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -114,13 +114,10 @@ fn control_panel() -> ResizedView> { // MARK: - general testing zone #[cfg(test)] mod tests { - #![feature(test)] - use super::Server; + use crate::server::server_profile::Server; use crate::client_api::ClientApi; - use std::thread::spawn; use std::collections::HashMap; use crate::commands::Commands; - use log::info; #[test] fn test_server_info() { @@ -130,42 +127,36 @@ mod tests { let owner = "noreply@email.com"; let server = Server::new(name, address, owner); - let _ = server.start().unwrap(); + let result = server.start(); + assert_eq!(result.is_ok(), true); let api = ClientApi::get_info("127.0.0.1:6000"); assert_eq!(api.is_ok(), true); - if api.is_ok() { - + 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)); - - let api = api.unwrap(); + println!("expected: {:?}", expected); assert_eq!(api, expected); - } else { - return } } #[test] fn test_server_connect() { let name = "Server-01"; - let address = "0.0.0.0:6000"; + let address = "0.0.0.0:6001"; let owner = "noreply@email.com"; let server = Server::new(name, address, owner); let _ = server.start().unwrap(); - let api = ClientApi::get_info("127.0.0.1:6000"); - assert_eq!(api.is_ok(), true); - if let Commands::Success(Some(params)) = api.unwrap() { - let mut api = ClientApi::new(address); - - api.on_client_add_handle = |s| info!("new clinet: {:?}", s); - api.on_client_remove_handle = |s| info!("removed clinet: {:?}", s); - + let api_result = ClientApi::new(address); + assert_eq!(api_result.is_ok(), true); + if let Ok(api) = api_result { + std::thread::sleep(std::time::Duration::from_secs(2)); } } } \ No newline at end of file diff --git a/src/server/client/client_profile.rs b/src/server/client/client_profile.rs index 23b522b..538b279 100644 --- a/src/server/client/client_profile.rs +++ b/src/server/client/client_profile.rs @@ -56,60 +56,14 @@ impl Client { // TODO: - add heartbeat timer. pub fn handle_connection(&self) { - info!("{}: handling connection", self.uuid); - - println!("buffer"); let mut buffer = [0; 1024]; - // 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 stream = self.stream_arc.lock().unwrap(); - let mut retry: u8 = 3; - 'retry_loop1: loop { - if retry < 1 { - self.transmit_data(Commands::Error(None).to_string().as_str()); - break 'retry_loop1 - } - - self.transmit_data(Commands::ClientRemove(Some(params.clone())).to_string().as_str()); - let _ = stream.read(&mut buffer); - let command = Commands::from(&buffer); - if command == Commands::Success(None) { - break 'retry_loop1; - } else { - retry -= 1; - } - } - }, - Ok(Commands::Client(Some(params))) => { - let mut stream = self.stream_arc.lock().unwrap(); - let mut retry: u8 = 3; - 'retry_loop2: loop { - if retry < 1 { - stream.write_all(Commands::Error(None).to_string().as_bytes()); - break 'retry_loop2; - } - stream.write_all(Commands::Client(Some(params.clone())).to_string().as_bytes()); - let _ = stream.read(&mut buffer); - let command = Commands::from(&buffer); - if command == Commands::Success(None) { - break 'retry_loop2; - } else { - retry -= 1; - } - } - - }, - /*no data available yet*/ - Err(TryRecvError::Empty) => {}, - _ => {} + // TODO: - Check heartbeat + { + info!("heartbeat") } - - println!("socket"); + + info!("{}: handling connection", self.uuid); if self.stream_arc.lock().unwrap().peek(&mut buffer).is_ok() { let mut stream = self.stream_arc.lock().unwrap(); @@ -136,6 +90,55 @@ impl Client { } } } + + 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 stream = self.stream_arc.lock().unwrap(); + let mut retry: u8 = 3; + 'retry_loop1: loop { + if retry < 1 { + self.transmit_data(Commands::Error(None).to_string().as_str()); + break 'retry_loop1 + } else { + self.transmit_data(Commands::ClientRemove(Some(params.clone())).to_string().as_str()); + let _ = stream.read(&mut buffer); + let command = Commands::from(&buffer); + if command == Commands::Success(None) { + break 'retry_loop1; + } else { + retry -= 1; + } + } + } + }, + Ok(Commands::Client(Some(params))) => { + let mut stream = self.stream_arc.lock().unwrap(); + let mut retry: u8 = 3; + 'retry_loop2: loop { + if retry < 1 { + let _ = stream.write_all(Commands::Error(None).to_string().as_bytes()); + break 'retry_loop2; + } else { + let _ = stream.write_all(Commands::Client(Some(params.clone())).to_string().as_bytes()); + let _ = stream.read(&mut buffer); + let command = Commands::from(&buffer); + if command == Commands::Success(None) { + break 'retry_loop2; + } else { + retry -= 1; + } + } + } + + }, + /*no data available yet*/ + Err(TryRecvError::Empty) => {}, + _ => {} + } println!("end"); } diff --git a/src/server/server_profile.rs b/src/server/server_profile.rs index ecdee28..c03f3fe 100644 --- a/src/server/server_profile.rs +++ b/src/server/server_profile.rs @@ -60,8 +60,7 @@ impl Server { address: address.to_string(), author: author.to_string(), connected_clients: Arc::new(Mutex::new(HashMap::new())), - thread_pool: ThreadPool::new(16), - + thread_pool: ThreadPool::new(16), sender, receiver, @@ -72,6 +71,10 @@ impl Server { self.address.to_string() } + pub fn set_port(&mut self) { + + } + pub fn start<'a>(&self) -> Result<(), io::Error>{ info!("server: starting server..."); // clone elements for thread @@ -118,7 +121,7 @@ impl Server { info!("server: checking for new connections"); if let Ok((mut stream, _addr)) = listener.accept() { - stream.set_read_timeout(Some(Duration::from_millis(10000))).unwrap(); + stream.set_read_timeout(Some(Duration::from_millis(1000))).unwrap(); let _ = stream.set_nonblocking(false); let request = Commands::Request(None); @@ -129,7 +132,7 @@ impl Server { if let Ok(size) = stream.read(&mut buffer) { let incoming_message = String::from(String::from_utf8_lossy(&buffer)); let command = Commands::from(incoming_message); - info!("Server: new connection sent - {:?}", command); + println!("Server: new connection sent - {:?}", command); // clears the buffer. buffer.zeroize(); @@ -153,18 +156,21 @@ impl Server { // TODO: - correct connection reset error when getting info. Commands::Info(None) => { - info!("Server: info requested"); + println!("Server: info requested"); let mut params: HashMap = HashMap::new(); params.insert(String::from("name"), server_details.0.clone()); params.insert(String::from("owner"), server_details.1.clone()); let command = Commands::Info(Some(params)); - stream.write_all(command.to_string().as_bytes()).expect("Server -Info: writing failed"); - stream.flush().expect("Server -Info: flushing errored"); + let result = stream.write_all(command.to_string().as_bytes()); + if let Err(error) = result { + println!("Server: error {:?}", error); + } + let _ = stream.flush(); }, _ => { - info!("Server: Invalid command sent"); + println!("Server: Invalid command sent"); let _ = stream.write_all(Commands::Error(None).to_string().as_bytes()); let _ = stream.flush(); }, -- 2.40.1 From dbf49a65b2d71732636273c0dca60b20bf37e4bd Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Wed, 12 Aug 2020 20:56:05 +0100 Subject: [PATCH 19/34] Revert "Merge branch 'master' into ref-method" This reverts commit 1a6b344b5858ba04936c711988c40d4b64e2377f, reversing changes made to be167055e8ad4c431d04f6cbec77d0a0e02c219f. --- Cargo.toml | 1 - src/client_api/mod.rs | 50 ++-- src/commands/mod.rs | 5 +- src/lib.rs | 2 - src/main.rs | 17 +- src/server/commands/mod.rs | 233 ++++++++++++++++++ src/server/mod.rs | 1 + src/server/server_profile.rs | 443 +---------------------------------- 8 files changed, 269 insertions(+), 483 deletions(-) create mode 100644 src/server/commands/mod.rs diff --git a/Cargo.toml b/Cargo.toml index f335bc7..5351899 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,6 @@ rust-bert = "0.7.11" log = "0.4" - [profile.dev] opt-level = 0 diff --git a/src/client_api/mod.rs b/src/client_api/mod.rs index 80b6241..5afe4e1 100644 --- a/src/client_api/mod.rs +++ b/src/client_api/mod.rs @@ -1,16 +1,10 @@ -use std::{ - net::TcpStream, - io::{Write, Read} -}; +use std::{net::TcpStream, io::{Write, Read}, io}; use crate::{ server::client::client_profile::Client, commands::Commands, }; -use zeroize::Zeroize; use std::time::Duration; -use async_std::net::SocketAddrV4; -use std::str::FromStr; -use std::net::SocketAddr; +use zeroize::Zeroize; pub struct ClientApi { @@ -32,32 +26,42 @@ impl ClientApi { addr: addr.to_string(), on_client_add_handle: on_add, on_client_remove_handle: on_remove, - } + }; + Ok(a) } - pub fn set_on_client_add(&mut self, Fn: fn(Client) -> ()) { - self.on_client_add_handle = Fn; + pub fn set_on_client_add(&mut self, func: fn(Client) -> ()) { + self.on_client_add_handle = func; } - pub fn set_on_client_removed(&mut self, Fn: fn(String) -> ()) { - self.on_client_remove_handle = Fn; + pub fn set_on_client_removed(&mut self, func: fn(String) -> ()) { + self.on_client_remove_handle = func; } - pub fn get_info(host: &str) -> Option { + pub fn get_info(host: &str) -> Result { let mut buffer: [u8; 1024] = [0; 1024]; - let addr = SocketAddr::from_str(host).ok()?; - let mut stream = TcpStream::connect_timeout(&addr, Duration::from_millis(500)).ok()?; + let addr = host.parse().unwrap(); + let mut stream = TcpStream::connect_timeout(&addr, Duration::from_millis(1000))?; - stream.read(&mut buffer).ok()?; - - match Commands::from(&mut buffer) { + let _ = stream.read(&mut buffer)?; + println!("data recieved: {:?}", &buffer[0..20]); + match Commands::from(&buffer) { Commands::Request(None) => { - stream.write_all(Commands::Info(None).to_string().as_bytes()).unwrap(); - stream.read(&mut buffer).ok()?; - Some(Commands::from(String::from(String::from_utf8_lossy(&buffer)))) + 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)))) }, _ => { - None + Err(io::Error::new(io::ErrorKind::InvalidData, "the data was not expected")) } } } diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 85b712e..2747afc 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -32,7 +32,6 @@ pub enum CommandParseError { UnknownCommand, NoString, } -} impl ToString for Commands { @@ -49,7 +48,6 @@ impl ToString for Commands { Commands::ClientInfo(arguments) => { ("!clientInfo:", arguments) }, Commands::ClientRemove(arguments) => { ("!clientRemove", arguments) } Commands::Client(arguments) => { ("!client:", arguments) }, - Commands::Success(arguments) => { ("!success:", arguments) }, Commands::Error(arguments) => { ("!error:", arguments) }, _ => { ("!error:", &None) } }; @@ -153,7 +151,8 @@ mod test_commands_v2 { #[test] fn test_creation_from_string() { - let command_result = Commands::from("!connect: name:bop host:127.0.0.1 uuid:123456-1234-1234-123456"); + let command_result = Commands::from_str("!connect: name:bop host:127.0.0.1 uuid:123456-1234-1234-123456").expect("parse error"); + () } #[test] diff --git a/src/lib.rs b/src/lib.rs index 4e15e7d..b6e16e3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,7 +8,6 @@ enum Message { Terminate, } -#[derive(Debug)] pub struct ThreadPool{ workers: Vec, sender: Sender, @@ -51,7 +50,6 @@ impl ThreadPool{ } } -#[derive(Debug)] struct Worker { id: usize, thread: Option>, diff --git a/src/main.rs b/src/main.rs index 0ec981e..8b1e4a1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -21,22 +21,7 @@ use crossterm::ErrorKind; use log::info; use clap::{App, Arg}; - use crate::server::server_profile::Server; -use client_api::ClientApi; -use crossterm::ErrorKind; -use cursive::{ - Cursive, - menu::*, - event::Key, - views::{ Dialog, TextView, LinearLayout, ListView, ResizedView, Panel }, - Rect, - CursiveExt, - align::{Align, HAlign}, - view::SizeConstraint, -}; -use std::sync::Arc; -use log::info; fn main() -> Result<(), ErrorKind> { @@ -174,4 +159,4 @@ mod tests { std::thread::sleep(std::time::Duration::from_secs(2)); } } -} +} \ No newline at end of file diff --git a/src/server/commands/mod.rs b/src/server/commands/mod.rs new file mode 100644 index 0000000..27a4a61 --- /dev/null +++ b/src/server/commands/mod.rs @@ -0,0 +1,233 @@ +mod request; +mod info; +mod success; +mod error; +mod connect; +mod disconnect; +mod client_update; +mod client_info; +mod client; +mod test; +mod message; + +use std::string::ToString; +use std::collections::HashMap; +use std::borrow::Borrow; +use regex::Regex; +use std::ops::Index; + + + +/* +impl ClientCommands{ + pub fn execute(&self, client: &mut Client, server: &Server, buffer: &mut [u8; 1024], connected_clients: &Arc>>){ + let stream = client.get_stream(); + match &*self{ + ClientCommands::Info => { + let server_details = server.get_info(); + + client.transmit_success(&server_details); + }, + ClientCommands::Connect(data) => { + connect::add_client(connected_clients, client); + + let new_client = ServerCommands::Client(data.clone()); + server.update_all_clients(&new_client); + + client.transmit_success(&String::from("")); + }, + ClientCommands::Disconnect => { + disconnect::remove_client(connected_clients, client); + + let mut data: HashMap = HashMap::new(); + data.insert("uuid".to_string(), client.get_uuid().to_string()); + + let old_client = ServerCommands::ClientRemove(data); + server.update_all_clients(&old_client); + + client.transmit_success(&String::from("")); + client.disconnect(); + println!("disconnected!"); + }, + ClientCommands::ClientUpdate => { + let clients_hashmap = connected_clients.lock().unwrap(); + for (key, value) in clients_hashmap.iter(){ + let formatted_data = client_update::format_client_data(&key, &value); + client.transmit_data(&formatted_data); + + client.confirm_success(buffer, &formatted_data); + } + client.transmit_success(&String::from("")); + client.confirm_success(buffer, &String::from("!success:")); + }, + ClientCommands::ClientInfo(data) => { + let requested_data = client_info::get_client_data(connected_clients, &data); + client.transmit_data(&requested_data); + }, + ClientCommands::Unknown => { + println!("Unknown Command"); + }, + } + } +} + +impl ServerCommands{ + pub fn execute(&self, client: &mut Client, buffer: &mut [u8; 1024]){ + match &*self{ + ServerCommands::Client(data) => { + let mut message = String::from(""); + message.push_str(&"!client: name:"); + message.push_str(&data.get("name").unwrap()); + message.push_str(&" host:"); + message.push_str(&data.get("host").unwrap()); + message.push_str(&" uuid:"); + message.push_str(&data.get("uuid").unwrap()); + + client.transmit_data(&message); + + client.confirm_success(buffer, &message); + }, + ServerCommands::ClientRemove(data) => { + let mut message = String::from(""); + message.push_str(&"!client: uuid:"); + message.push_str(&data.get("uuid").unwrap()); + + client.transmit_data(&message); + + client.confirm_success(buffer, &message); + }, + ServerCommands::Unknown => { + println!("Unknown Command!"); + }, + } + } +} +*/ + +// MARK: - commands_v2 electric boogaloo +#[derive(Clone)] +pub enum Commands { + Request(Option>), + Info(Option>), + + Connect(Option>), + Disconnect(Option>), + + ClientUpdate(Option>), + ClientInfo(Option>), + ClientRemove(Option>), + Client(Option>), + + Success(Option>), + Error(Option>), +} + +impl ToString for Commands { + + fn to_string(&self) -> std::string::String { + let mut out_string = String::new(); + + let (command, parameters) = match self { + Commands::Request(arguments) => { ("!request:", arguments) }, + Commands::Info(arguments) => { ("!info:", arguments) }, + Commands::Connect(arguments) => { ("!connect:", arguments) }, + Commands::Disconnect(arguments) => { ("!disconnect:", arguments) }, + Commands::ClientUpdate(arguments) => { ("!clientUpdate:", arguments) }, + Commands::ClientInfo(arguments) => { ("!clientInfo:", arguments) }, + Commands::Client(arguments) => { ("!client:", arguments) }, + Commands::Error(arguments) => { ("!error:", arguments) }, + _ => { ("!error:", &None) } + }; + + out_string.push_str(command); + + if parameters.is_some() { + let hash_map = parameters.borrow().as_ref().unwrap(); + for (k, v) in hash_map.iter() { + out_string.push_str(" "); + out_string.push_str(k.as_str()); + out_string.push_str(":"); + out_string.push_str(v.as_str()) + } + } + + out_string + } +} + +impl From<&str> for Commands { + fn from(data: &str) -> Self { + let regex = Regex::new(r###"(\?|!)([a-zA-z0-9]*):|([a-zA-z]*):([a-zA-Z0-9\-\+\[\]{}_=/]+|("(.*?)")+)"###).unwrap(); + let mut iter = regex.find_iter(data); + let command = iter.next().unwrap().as_str(); + + println!("command: {:?}", command); + + let mut map: HashMap = HashMap::new(); + + for i in iter { + let parameter = i.as_str().to_string(); + let parts:Vec<&str> = parameter.split(":").collect(); + + map.insert(parts.index(0).to_string(), parts.index(1).to_string()); + } + + let params = if map.capacity() > 1 {Some(map)} else { None }; + + match command { + "!request:" => Commands::Request(params), + "!info:" => Commands::Info(params), + + "!connect:" => Commands::Connect(params), + "!disconnect:" => Commands::Disconnect(params), + + "!clientUpdate:" => Commands::ClientUpdate(params), + "!clientInfo:" => Commands::ClientInfo(params), + "!client:" => Commands::Client(params), + "!clientRemove:" => Commands::ClientRemove(params), + + "!success:" => Commands::Success(params), + "!error:" => Commands::Error(params), + + _ => Commands::Error(params), + } + } +} + +impl From for Commands { + fn from(data: String) -> Self { + Commands::from(data.as_str()) + } +} + +impl From<&[u8]> for Commands { + fn from(data: &[u8]) -> Self { + let incoming_message = String::from(String::from_utf8_lossy(data)); + Commands::from(incoming_message.as_str()) + } +} + +#[cfg(test)] +mod test_commands_v2 { + use super::Commands; + use std::collections::HashMap; + + #[test] + fn test_creation_from_string() { + let command_result = Commands::from("!connect: name:bop host:127.0.0.1 uuid:123456-1234-1234-123456"); + () + } + + #[test] + fn test_to_string() { + + let mut a: HashMap = HashMap::new(); + a.insert("name".to_string(), "michael".to_string()); + a.insert("host".to_string(), "127.0.0.1".to_string()); + a.insert("uuid".to_string(), "123456-1234-1234-123456".to_string()); + + let command = Commands::Connect(Some(a)); + + println!("{:?}", command.to_string()) + } +} diff --git a/src/server/mod.rs b/src/server/mod.rs index de07bb5..170e11e 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -1,2 +1,3 @@ pub mod client; +pub mod commands; pub mod server_profile; diff --git a/src/server/server_profile.rs b/src/server/server_profile.rs index fb60ab9..c03f3fe 100644 --- a/src/server/server_profile.rs +++ b/src/server/server_profile.rs @@ -66,11 +66,7 @@ impl Server { receiver, } } - - pub fn get_name(&self) -> String{ - self.name.to_string() - } - + pub fn get_address(&self) -> String{ self.address.to_string() } @@ -79,7 +75,7 @@ impl Server { } - pub fn start(&self) -> Result<(), io::Error>{ + pub fn start<'a>(&self) -> Result<(), io::Error>{ info!("server: starting server..."); // clone elements for thread let client_map = self.connected_clients.clone(); @@ -199,12 +195,6 @@ impl Server { info!("server: sending stop message"); let _ = self.sender.send(ServerMessages::Shutdown); } - - pub fn start(&'static self) -> Result<(), io::Error> { - info!("server: starting server..."); - // clone elements for thread - let client_map = self.connected_clients.clone(); - let receiver = self.receiver.clone(); #[allow(dead_code)] pub fn get_info(&self, tx: Sender) { @@ -212,83 +202,8 @@ impl Server { params.insert(String::from("name"), self.name.to_string().clone()); params.insert(String::from("owner"), self.author.to_string().clone()); - info!("server: spawning threads"); - thread::Builder::new().name("Server Thread".to_string()).spawn(move || { - let mut buffer = [0; 1024]; - - 'outer: loop { - // get messages from the servers channel. - info!("server: getting messages"); - for i in receiver.try_iter() { - match i { - ServerMessages::Shutdown => { - // TODO: implement disconnecting all clients and shutting down the server - info!("server: shutting down..."); - - break 'outer; - }, - _ => {}, - } - } - - info!("server: checking for new connections"); - if let Ok((mut stream, addr)) = listener.accept() { - stream.set_read_timeout(Some(Duration::from_millis(10000))).unwrap(); - - let request = Commands::Request(None); - self.transmit_data(&stream, &request.to_string().as_str()); - - match self.read_data(&stream, &mut buffer) { - Ok(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(); - - info!("{}", format!("Server: new Client connection: addr = {}", address )); - - let mut client = Client::new(self, stream, &uuid, &username, &address); - - let tx = client.get_transmitter(); - - let mut clients_hashmap = self.connected_clients.lock().unwrap(); - clients_hashmap.insert(uuid.to_string(), tx.clone()); - std::mem::drop(clients_hashmap); - - let success = Commands::Success(None); - tx.send(success).unwrap(); - - self.thread_pool.execute(move || { - client.handle_connection(); - }); - - 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)); - self.update_all_clients(uuid.as_str(), new_client); - }, - Commands::Info(None) => { - info!("Server: info requested"); - - let params: HashMap = [(String::from("name"), self.name.to_string().clone()), (String::from("owner"), self.author.to_string().clone())].iter().cloned().collect(); - let command = Commands::Success(Some(params)); - - self.transmit_data(&stream, command.to_string().as_str()); - }, - _ => { - info!("Server: Invalid command sent"); - self.transmit_data(&stream, Commands::Error(None).to_string().as_str()); - }, - } - }, - Err(_) => println!("ERROR: stream closed"), - } - } - } - info!("server: stopped") - }); - info!("server: started"); - Ok(()) + let command = Commands::Info(Some(params)); + tx.send(command).unwrap(); } #[allow(dead_code)] @@ -322,352 +237,4 @@ impl Drop for Server { println!("server dropped"); let _ = self.sender.send(ServerMessages::Shutdown); } -} - -#[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); - } -} +} \ No newline at end of file -- 2.40.1 From 5e1ba2e110d0377979453ef71b4fd2635d1ad08b Mon Sep 17 00:00:00 2001 From: Mitchell Date: Sat, 15 Aug 2020 22:08:34 +0100 Subject: [PATCH 20/34] removed extra brace --- src/commands/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 85b712e..b3c1197 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -32,7 +32,6 @@ pub enum CommandParseError { UnknownCommand, NoString, } -} impl ToString for Commands { -- 2.40.1 From b4a49918fc41d42f13c1cb8963246638585aff82 Mon Sep 17 00:00:00 2001 From: Mitchell Date: Sun, 16 Aug 2020 12:38:54 +0100 Subject: [PATCH 21/34] duplicated folder --- src/server/commands/client.rs | 0 src/server/commands/client_info.rs | 29 ---------------------------- src/server/commands/client_update.rs | 5 ----- src/server/commands/connect.rs | 13 ------------- src/server/commands/disconnect.rs | 10 ---------- src/server/commands/error.rs | 0 src/server/commands/info.rs | 0 src/server/commands/message.rs | 0 src/server/commands/request.rs | 0 src/server/commands/success.rs | 0 src/server/commands/test.rs | 0 11 files changed, 57 deletions(-) delete mode 100644 src/server/commands/client.rs delete mode 100644 src/server/commands/client_info.rs delete mode 100644 src/server/commands/client_update.rs delete mode 100644 src/server/commands/connect.rs delete mode 100644 src/server/commands/disconnect.rs delete mode 100644 src/server/commands/error.rs delete mode 100644 src/server/commands/info.rs delete mode 100644 src/server/commands/message.rs delete mode 100644 src/server/commands/request.rs delete mode 100644 src/server/commands/success.rs delete mode 100644 src/server/commands/test.rs diff --git a/src/server/commands/client.rs b/src/server/commands/client.rs deleted file mode 100644 index e69de29..0000000 diff --git a/src/server/commands/client_info.rs b/src/server/commands/client_info.rs deleted file mode 100644 index 50b1a95..0000000 --- a/src/server/commands/client_info.rs +++ /dev/null @@ -1,29 +0,0 @@ -/*use crate::server::client::client_profile::Client; - -use std::sync::Mutex; -use std::sync::Arc; -use std::collections::HashMap; - -pub fn get_client_data(clients_ref: &Arc>>, data: &HashMap) -> String{ - let clients_hashmap = clients_ref.lock().unwrap(); - let uuid = data.get("uuid").unwrap(); - println!("uuid: {}", uuid); - - for (key, value) in clients_hashmap.iter(){ - println!("{}",key); - } - let client = clients_hashmap.get(uuid); - match client{ - Some(data) => { - let mut message = String::from("!success:"); - message.push_str(&" uuid:".to_string()); - message.push_str(&data.get_uuid().to_string()); - message.push_str(&" host:".to_string()); - message.push_str(&data.get_address().to_string()); - message.push_str(&" username:".to_string()); - message.push_str(&data.get_username().to_string()); - message - }, - None => String::from("client not online"), - } -}*/ diff --git a/src/server/commands/client_update.rs b/src/server/commands/client_update.rs deleted file mode 100644 index 199054d..0000000 --- a/src/server/commands/client_update.rs +++ /dev/null @@ -1,5 +0,0 @@ -/*use crate::server::client::client_profile::Client; - -pub fn format_client_data(uuid: &String, client: &Client) -> String{ - ["!client: username:",client.get_username(), " uuid:", uuid, " host:\"", client.get_address(), "\""].concat() -}*/ diff --git a/src/server/commands/connect.rs b/src/server/commands/connect.rs deleted file mode 100644 index ec4f1b7..0000000 --- a/src/server/commands/connect.rs +++ /dev/null @@ -1,13 +0,0 @@ -/*use crate::server::client::client_profile::Client; - -use std::sync::Mutex; -use std::sync::Arc; -use std::collections::HashMap; -use dashmap::DashMap; - -pub fn add_client(clients_ref: &Arc>>, client: &Client){ - let mut clients_hashmap = clients_ref.lock().unwrap(); - let uuid = client.get_uuid().to_string(); - //clients_hashmap.insert(uuid, client.clone()); -} -*/ diff --git a/src/server/commands/disconnect.rs b/src/server/commands/disconnect.rs deleted file mode 100644 index d4ba8e4..0000000 --- a/src/server/commands/disconnect.rs +++ /dev/null @@ -1,10 +0,0 @@ -/*use crate::server::client::client_profile::Client; - -use std::sync::Mutex; -use std::sync::Arc; -use std::collections::HashMap; - -pub fn remove_client(clients_ref: &Arc>>, client: &Client){ - let mut clients_hashmap = clients_ref.lock().unwrap(); - clients_hashmap.remove(client.get_uuid()).unwrap(); -}*/ diff --git a/src/server/commands/error.rs b/src/server/commands/error.rs deleted file mode 100644 index e69de29..0000000 diff --git a/src/server/commands/info.rs b/src/server/commands/info.rs deleted file mode 100644 index e69de29..0000000 diff --git a/src/server/commands/message.rs b/src/server/commands/message.rs deleted file mode 100644 index e69de29..0000000 diff --git a/src/server/commands/request.rs b/src/server/commands/request.rs deleted file mode 100644 index e69de29..0000000 diff --git a/src/server/commands/success.rs b/src/server/commands/success.rs deleted file mode 100644 index e69de29..0000000 diff --git a/src/server/commands/test.rs b/src/server/commands/test.rs deleted file mode 100644 index e69de29..0000000 -- 2.40.1 From 2837ab52e1550e7952a672bb322fff696d0bcb4d Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Sun, 16 Aug 2020 17:12:50 +0100 Subject: [PATCH 22/34] Update Cargo.toml - removed machine learning lib --- Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 5351899..3accca5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,6 @@ zeroize = "1.1.0" cursive = { version = "0.15.0", default-features = false, features = ["crossterm-backend"]} crossterm = "0.17.7" clap = "3.0.0-beta.1" -rust-bert = "0.7.11" log = "0.4" -- 2.40.1 From ea98cc7688528e8c3ed5d17d17c803e0c43e11b7 Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Sun, 16 Aug 2020 17:13:28 +0100 Subject: [PATCH 23/34] removed old commands files. --- src/server/commands/client.rs | 0 src/server/commands/client_info.rs | 29 ---- src/server/commands/client_update.rs | 5 - src/server/commands/connect.rs | 13 -- src/server/commands/disconnect.rs | 10 -- src/server/commands/error.rs | 0 src/server/commands/info.rs | 0 src/server/commands/message.rs | 0 src/server/commands/mod.rs | 233 --------------------------- src/server/commands/request.rs | 0 src/server/commands/success.rs | 0 src/server/commands/test.rs | 0 12 files changed, 290 deletions(-) delete mode 100644 src/server/commands/client.rs delete mode 100644 src/server/commands/client_info.rs delete mode 100644 src/server/commands/client_update.rs delete mode 100644 src/server/commands/connect.rs delete mode 100644 src/server/commands/disconnect.rs delete mode 100644 src/server/commands/error.rs delete mode 100644 src/server/commands/info.rs delete mode 100644 src/server/commands/message.rs delete mode 100644 src/server/commands/mod.rs delete mode 100644 src/server/commands/request.rs delete mode 100644 src/server/commands/success.rs delete mode 100644 src/server/commands/test.rs diff --git a/src/server/commands/client.rs b/src/server/commands/client.rs deleted file mode 100644 index e69de29..0000000 diff --git a/src/server/commands/client_info.rs b/src/server/commands/client_info.rs deleted file mode 100644 index 50b1a95..0000000 --- a/src/server/commands/client_info.rs +++ /dev/null @@ -1,29 +0,0 @@ -/*use crate::server::client::client_profile::Client; - -use std::sync::Mutex; -use std::sync::Arc; -use std::collections::HashMap; - -pub fn get_client_data(clients_ref: &Arc>>, data: &HashMap) -> String{ - let clients_hashmap = clients_ref.lock().unwrap(); - let uuid = data.get("uuid").unwrap(); - println!("uuid: {}", uuid); - - for (key, value) in clients_hashmap.iter(){ - println!("{}",key); - } - let client = clients_hashmap.get(uuid); - match client{ - Some(data) => { - let mut message = String::from("!success:"); - message.push_str(&" uuid:".to_string()); - message.push_str(&data.get_uuid().to_string()); - message.push_str(&" host:".to_string()); - message.push_str(&data.get_address().to_string()); - message.push_str(&" username:".to_string()); - message.push_str(&data.get_username().to_string()); - message - }, - None => String::from("client not online"), - } -}*/ diff --git a/src/server/commands/client_update.rs b/src/server/commands/client_update.rs deleted file mode 100644 index 199054d..0000000 --- a/src/server/commands/client_update.rs +++ /dev/null @@ -1,5 +0,0 @@ -/*use crate::server::client::client_profile::Client; - -pub fn format_client_data(uuid: &String, client: &Client) -> String{ - ["!client: username:",client.get_username(), " uuid:", uuid, " host:\"", client.get_address(), "\""].concat() -}*/ diff --git a/src/server/commands/connect.rs b/src/server/commands/connect.rs deleted file mode 100644 index ec4f1b7..0000000 --- a/src/server/commands/connect.rs +++ /dev/null @@ -1,13 +0,0 @@ -/*use crate::server::client::client_profile::Client; - -use std::sync::Mutex; -use std::sync::Arc; -use std::collections::HashMap; -use dashmap::DashMap; - -pub fn add_client(clients_ref: &Arc>>, client: &Client){ - let mut clients_hashmap = clients_ref.lock().unwrap(); - let uuid = client.get_uuid().to_string(); - //clients_hashmap.insert(uuid, client.clone()); -} -*/ diff --git a/src/server/commands/disconnect.rs b/src/server/commands/disconnect.rs deleted file mode 100644 index d4ba8e4..0000000 --- a/src/server/commands/disconnect.rs +++ /dev/null @@ -1,10 +0,0 @@ -/*use crate::server::client::client_profile::Client; - -use std::sync::Mutex; -use std::sync::Arc; -use std::collections::HashMap; - -pub fn remove_client(clients_ref: &Arc>>, client: &Client){ - let mut clients_hashmap = clients_ref.lock().unwrap(); - clients_hashmap.remove(client.get_uuid()).unwrap(); -}*/ diff --git a/src/server/commands/error.rs b/src/server/commands/error.rs deleted file mode 100644 index e69de29..0000000 diff --git a/src/server/commands/info.rs b/src/server/commands/info.rs deleted file mode 100644 index e69de29..0000000 diff --git a/src/server/commands/message.rs b/src/server/commands/message.rs deleted file mode 100644 index e69de29..0000000 diff --git a/src/server/commands/mod.rs b/src/server/commands/mod.rs deleted file mode 100644 index 27a4a61..0000000 --- a/src/server/commands/mod.rs +++ /dev/null @@ -1,233 +0,0 @@ -mod request; -mod info; -mod success; -mod error; -mod connect; -mod disconnect; -mod client_update; -mod client_info; -mod client; -mod test; -mod message; - -use std::string::ToString; -use std::collections::HashMap; -use std::borrow::Borrow; -use regex::Regex; -use std::ops::Index; - - - -/* -impl ClientCommands{ - pub fn execute(&self, client: &mut Client, server: &Server, buffer: &mut [u8; 1024], connected_clients: &Arc>>){ - let stream = client.get_stream(); - match &*self{ - ClientCommands::Info => { - let server_details = server.get_info(); - - client.transmit_success(&server_details); - }, - ClientCommands::Connect(data) => { - connect::add_client(connected_clients, client); - - let new_client = ServerCommands::Client(data.clone()); - server.update_all_clients(&new_client); - - client.transmit_success(&String::from("")); - }, - ClientCommands::Disconnect => { - disconnect::remove_client(connected_clients, client); - - let mut data: HashMap = HashMap::new(); - data.insert("uuid".to_string(), client.get_uuid().to_string()); - - let old_client = ServerCommands::ClientRemove(data); - server.update_all_clients(&old_client); - - client.transmit_success(&String::from("")); - client.disconnect(); - println!("disconnected!"); - }, - ClientCommands::ClientUpdate => { - let clients_hashmap = connected_clients.lock().unwrap(); - for (key, value) in clients_hashmap.iter(){ - let formatted_data = client_update::format_client_data(&key, &value); - client.transmit_data(&formatted_data); - - client.confirm_success(buffer, &formatted_data); - } - client.transmit_success(&String::from("")); - client.confirm_success(buffer, &String::from("!success:")); - }, - ClientCommands::ClientInfo(data) => { - let requested_data = client_info::get_client_data(connected_clients, &data); - client.transmit_data(&requested_data); - }, - ClientCommands::Unknown => { - println!("Unknown Command"); - }, - } - } -} - -impl ServerCommands{ - pub fn execute(&self, client: &mut Client, buffer: &mut [u8; 1024]){ - match &*self{ - ServerCommands::Client(data) => { - let mut message = String::from(""); - message.push_str(&"!client: name:"); - message.push_str(&data.get("name").unwrap()); - message.push_str(&" host:"); - message.push_str(&data.get("host").unwrap()); - message.push_str(&" uuid:"); - message.push_str(&data.get("uuid").unwrap()); - - client.transmit_data(&message); - - client.confirm_success(buffer, &message); - }, - ServerCommands::ClientRemove(data) => { - let mut message = String::from(""); - message.push_str(&"!client: uuid:"); - message.push_str(&data.get("uuid").unwrap()); - - client.transmit_data(&message); - - client.confirm_success(buffer, &message); - }, - ServerCommands::Unknown => { - println!("Unknown Command!"); - }, - } - } -} -*/ - -// MARK: - commands_v2 electric boogaloo -#[derive(Clone)] -pub enum Commands { - Request(Option>), - Info(Option>), - - Connect(Option>), - Disconnect(Option>), - - ClientUpdate(Option>), - ClientInfo(Option>), - ClientRemove(Option>), - Client(Option>), - - Success(Option>), - Error(Option>), -} - -impl ToString for Commands { - - fn to_string(&self) -> std::string::String { - let mut out_string = String::new(); - - let (command, parameters) = match self { - Commands::Request(arguments) => { ("!request:", arguments) }, - Commands::Info(arguments) => { ("!info:", arguments) }, - Commands::Connect(arguments) => { ("!connect:", arguments) }, - Commands::Disconnect(arguments) => { ("!disconnect:", arguments) }, - Commands::ClientUpdate(arguments) => { ("!clientUpdate:", arguments) }, - Commands::ClientInfo(arguments) => { ("!clientInfo:", arguments) }, - Commands::Client(arguments) => { ("!client:", arguments) }, - Commands::Error(arguments) => { ("!error:", arguments) }, - _ => { ("!error:", &None) } - }; - - out_string.push_str(command); - - if parameters.is_some() { - let hash_map = parameters.borrow().as_ref().unwrap(); - for (k, v) in hash_map.iter() { - out_string.push_str(" "); - out_string.push_str(k.as_str()); - out_string.push_str(":"); - out_string.push_str(v.as_str()) - } - } - - out_string - } -} - -impl From<&str> for Commands { - fn from(data: &str) -> Self { - let regex = Regex::new(r###"(\?|!)([a-zA-z0-9]*):|([a-zA-z]*):([a-zA-Z0-9\-\+\[\]{}_=/]+|("(.*?)")+)"###).unwrap(); - let mut iter = regex.find_iter(data); - let command = iter.next().unwrap().as_str(); - - println!("command: {:?}", command); - - let mut map: HashMap = HashMap::new(); - - for i in iter { - let parameter = i.as_str().to_string(); - let parts:Vec<&str> = parameter.split(":").collect(); - - map.insert(parts.index(0).to_string(), parts.index(1).to_string()); - } - - let params = if map.capacity() > 1 {Some(map)} else { None }; - - match command { - "!request:" => Commands::Request(params), - "!info:" => Commands::Info(params), - - "!connect:" => Commands::Connect(params), - "!disconnect:" => Commands::Disconnect(params), - - "!clientUpdate:" => Commands::ClientUpdate(params), - "!clientInfo:" => Commands::ClientInfo(params), - "!client:" => Commands::Client(params), - "!clientRemove:" => Commands::ClientRemove(params), - - "!success:" => Commands::Success(params), - "!error:" => Commands::Error(params), - - _ => Commands::Error(params), - } - } -} - -impl From for Commands { - fn from(data: String) -> Self { - Commands::from(data.as_str()) - } -} - -impl From<&[u8]> for Commands { - fn from(data: &[u8]) -> Self { - let incoming_message = String::from(String::from_utf8_lossy(data)); - Commands::from(incoming_message.as_str()) - } -} - -#[cfg(test)] -mod test_commands_v2 { - use super::Commands; - use std::collections::HashMap; - - #[test] - fn test_creation_from_string() { - let command_result = Commands::from("!connect: name:bop host:127.0.0.1 uuid:123456-1234-1234-123456"); - () - } - - #[test] - fn test_to_string() { - - let mut a: HashMap = HashMap::new(); - a.insert("name".to_string(), "michael".to_string()); - a.insert("host".to_string(), "127.0.0.1".to_string()); - a.insert("uuid".to_string(), "123456-1234-1234-123456".to_string()); - - let command = Commands::Connect(Some(a)); - - println!("{:?}", command.to_string()) - } -} diff --git a/src/server/commands/request.rs b/src/server/commands/request.rs deleted file mode 100644 index e69de29..0000000 diff --git a/src/server/commands/success.rs b/src/server/commands/success.rs deleted file mode 100644 index e69de29..0000000 diff --git a/src/server/commands/test.rs b/src/server/commands/test.rs deleted file mode 100644 index e69de29..0000000 -- 2.40.1 From c1236bdcd3a8502088e54cc0b9c08e2cfc889ab1 Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Sun, 16 Aug 2020 17:13:41 +0100 Subject: [PATCH 24/34] fixed bug with the 1 --- src/commands/mod.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 2747afc..ef9b05e 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -33,6 +33,7 @@ pub enum CommandParseError { NoString, } + impl ToString for Commands { fn to_string(&self) -> std::string::String { @@ -48,6 +49,7 @@ impl ToString for Commands { Commands::ClientInfo(arguments) => { ("!clientInfo:", arguments) }, Commands::ClientRemove(arguments) => { ("!clientRemove", arguments) } Commands::Client(arguments) => { ("!client:", arguments) }, + Commands::Success(arguments) => { ("!success:", arguments) }, Commands::Error(arguments) => { ("!error:", arguments) }, _ => { ("!error:", &None) } }; @@ -97,7 +99,7 @@ impl FromStr for Commands { map.insert(parts.index(0).to_string(), parts.index(1).to_string()); } - let params = if map.capacity() > 1 {Some(map)} else { None }; + let params = if map.capacity() > 0 {Some(map)} else { None }; Ok(match command { "!request:" => Commands::Request(params), @@ -151,8 +153,7 @@ mod test_commands_v2 { #[test] fn test_creation_from_string() { - let command_result = Commands::from_str("!connect: name:bop host:127.0.0.1 uuid:123456-1234-1234-123456").expect("parse error"); - () + let command_result = Commands::from_str("!connect: name:bop host:127.0.0.1 uuid:123456-1234-1234-123456"); } #[test] -- 2.40.1 From ca3e559353eace923c4f3136f0de30794f9162c6 Mon Sep 17 00:00:00 2001 From: michael-bailey Date: Sun, 16 Aug 2020 17:13:53 +0100 Subject: [PATCH 25/34] Update mod.rs removed old commands --- src/server/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/server/mod.rs b/src/server/mod.rs index 170e11e..de07bb5 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -1,3 +1,2 @@ pub mod client; -pub mod commands; pub mod server_profile; -- 2.40.1 From f24a4f72dff65b8b0a258497f3019bbae269e373 Mon Sep 17 00:00:00 2001 From: Mitchell Date: Sun, 16 Aug 2020 17:15:23 +0100 Subject: [PATCH 26/34] functionality for individual commands --- src/commands/behaviors.rs | 73 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 src/commands/behaviors.rs diff --git a/src/commands/behaviors.rs b/src/commands/behaviors.rs new file mode 100644 index 0000000..8938041 --- /dev/null +++ b/src/commands/behaviors.rs @@ -0,0 +1,73 @@ +struct Request {} + +struct Info {} + +struct Connect {} + +struct Disconnect {} + +struct ClientUpdate {} + +struct ClientInfo {} + +struct ClientRemove {} + +struct Client {} + +struct Success {} + +struct Error {} + +trait Runnables { + fn run(&self); +} + +impl Runnables for Request { + fn run() { + } +} + +impl Runnables for Request { + fn run() { + } +} + +impl Runnables for Request { + fn run() { + } +} + +impl Runnables for Request { + fn run() { + } +} + +impl Runnables for Request { + fn run() { + } +} + +impl Runnables for Request { + fn run() { + } +} + +impl Runnables for Request { + fn run() { + } +} + +impl Runnables for Request { + fn run() { + } +} + +impl Runnables for Request { + fn run() { + } +} + +impl Runnables for Request { + fn run() { + } +} -- 2.40.1 From 399b9b8c59ad9aa7f57c40775d7b63b80d26ad1e Mon Sep 17 00:00:00 2001 From: Mitchell Date: Sun, 16 Aug 2020 17:15:34 +0100 Subject: [PATCH 27/34] changed enum layout --- src/commands/mod.rs | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 90b68de..c288e9e 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -7,8 +7,27 @@ use std::ops::Index; use zeroize::Zeroize; #[derive(Clone, Debug)] -pub enum Commands { - Request(Option>), +pub enum Commands { + // Common fields: + executable: T, + params: Option>, + + // Variants: + Request {}, + Info {}, + + Connect {}, + Disconnect {}, + + ClientUpdate {}, + ClientInfo {}, + ClientRemove {}, + Client {}, + + Success {}, + Error {}, + + /*Request(Option>), Info(Option>), Connect(Option>), @@ -20,7 +39,11 @@ pub enum Commands { Client(Option>), Success(Option>), - Error(Option>), + Error(Option>),*/ +} + +trait Operations { + fn execute(&self); } impl Commands { @@ -51,6 +74,12 @@ impl Commands { } } +impl Operations for Commands { + fn execute(&self) { + self.executable.run(); + } +} + impl PartialEq for Commands { fn eq(&self, other: &Self) -> bool { match (self, other) { -- 2.40.1 From b393223bfa33ea499275f83decd4377b9ef0ce6f Mon Sep 17 00:00:00 2001 From: Mitchell Date: Sun, 16 Aug 2020 18:29:35 +0100 Subject: [PATCH 28/34] server info unit test fix --- src/client_api/mod.rs | 6 +++--- src/commands/mod.rs | 7 ------- src/main.rs | 10 ++++++++-- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/client_api/mod.rs b/src/client_api/mod.rs index 5afe4e1..1f2084b 100644 --- a/src/client_api/mod.rs +++ b/src/client_api/mod.rs @@ -42,8 +42,8 @@ impl ClientApi { 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)?; + + let _ = stream.read(&mut buffer)?; println!("data recieved: {:?}", &buffer[0..20]); match Commands::from(&buffer) { Commands::Request(None) => { @@ -69,4 +69,4 @@ impl ClientApi { pub fn get_clients(&self) { } -} \ No newline at end of file +} diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 820e34d..fa11066 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -143,10 +143,8 @@ impl From<&[u8; 1024]> for Commands { #[cfg(test)] mod test_commands_v2 { #![feature(test)] - extern crate test; use super::Commands; use std::collections::HashMap; - use test::Bencher; use std::str::FromStr; use super::CommandParseError; @@ -167,9 +165,4 @@ mod test_commands_v2 { println!("{:?}", command.to_string()) } - - #[bench] - fn benchmark(b: &mut Bencher) { - b.iter(|| {let a = Commands::from_str("!connect: host:192.168.0.1 name:\"michael-bailey\" uuid:123456-1234-1234-123456").unwrap();}) - } } diff --git a/src/main.rs b/src/main.rs index 8b1e4a1..7e1946b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -#![feature(test)] +//#![feature(test)] #![allow(dead_code)] mod client_api; @@ -118,6 +118,8 @@ mod tests { use crate::client_api::ClientApi; use std::collections::HashMap; use crate::commands::Commands; + use std::{thread, time}; + use std::time::Duration; #[test] fn test_server_info() { @@ -128,8 +130,12 @@ mod tests { let server = Server::new(name, address, owner); let result = server.start(); + assert_eq!(result.is_ok(), true); + 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 { @@ -159,4 +165,4 @@ mod tests { std::thread::sleep(std::time::Duration::from_secs(2)); } } -} \ No newline at end of file +} -- 2.40.1 From 68b3ebf74b5e24d3b5f3936c5e011b7e034ef441 Mon Sep 17 00:00:00 2001 From: Mitchell Date: Sun, 16 Aug 2020 18:30:00 +0100 Subject: [PATCH 29/34] extra read was causing data to be lost --- src/server/server_profile.rs | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/server/server_profile.rs b/src/server/server_profile.rs index c03f3fe..5f0e304 100644 --- a/src/server/server_profile.rs +++ b/src/server/server_profile.rs @@ -76,7 +76,7 @@ impl Server { } pub fn start<'a>(&self) -> Result<(), io::Error>{ - info!("server: starting server..."); + println!("server: starting server..."); // clone elements for thread let client_map = self.connected_clients.clone(); let sender = self.sender.clone(); @@ -90,18 +90,18 @@ impl Server { let mut buffer = [0; 1024]; - info!("server: spawning threads"); + 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. - info!("server: getting messages"); + println!("server: getting messages"); for i in receiver.try_iter() { match i { ServerMessages::Shutdown => { // TODO: implement disconnecting all clients and shutting down the server - info!("server: shutting down..."); + println!("server: shutting down..."); break 'outer; }, @@ -119,7 +119,7 @@ impl Server { } } - info!("server: checking for new connections"); + 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); @@ -128,7 +128,6 @@ impl Server { //request.to_string(); let _ = stream.write_all(&request.to_string().as_bytes()); let _ = stream.flush(); - let _ = stream.read(&mut buffer); if let Ok(size) = stream.read(&mut buffer) { let incoming_message = String::from(String::from_utf8_lossy(&buffer)); let command = Commands::from(incoming_message); @@ -142,7 +141,7 @@ impl Server { let username = data.get("name").unwrap(); let address = data.get("host").unwrap(); - info!("{}", format!("Server: new Client connection: _addr = {}", address )); + println!("{}", format!("Server: new Client connection: _addr = {}", address )); let client = Client::new(stream, sender.clone(), uuid.clone(), username.clone(), address.clone()); @@ -180,14 +179,14 @@ impl Server { // TODO: end - // handle each client for messages - info!("server: handing control to clients"); + println!("server: handing control to clients"); for (_k, v) in client_map.lock().unwrap().iter() { v.handle_connection(); } } - info!("server: stopped"); + println!("server: stopped"); }); - info!("server: started"); + println!("server: started"); Ok(()) } @@ -237,4 +236,4 @@ impl Drop for Server { println!("server dropped"); let _ = self.sender.send(ServerMessages::Shutdown); } -} \ No newline at end of file +} -- 2.40.1 From c7edea3984e5bf8e4f293bde1a6d7b681bac44bc Mon Sep 17 00:00:00 2001 From: Mitchell Date: Tue, 18 Aug 2020 13:00:37 +0100 Subject: [PATCH 30/34] new commands system with branching removed --- src/commands/behaviors.rs | 50 ++++++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/src/commands/behaviors.rs b/src/commands/behaviors.rs index 8938041..a5facd9 100644 --- a/src/commands/behaviors.rs +++ b/src/commands/behaviors.rs @@ -18,8 +18,8 @@ struct Success {} struct Error {} -trait Runnables { - fn run(&self); +trait ClientRunnables { + fn client_execution(client: &Client); } impl Runnables for Request { @@ -27,47 +27,59 @@ impl Runnables for Request { } } -impl Runnables for Request { +impl ClientRunnables for Info { + fn client_execution(client: &Client) { + let params = client.get_server_info(); + let command = Commands::Success(Some(params)); + + client.transmit_data(command.to_string().as_str()); + } +} + +impl Runnables for Connect { fn run() { } } -impl Runnables for Request { +impl Runnables for Disconnect { fn run() { } } -impl Runnables for Request { +impl ClientRunnables for ClientUpdate { + fn client_execution(client: &Client) { + let mut command = Commands::Success(None); + client.transmit_data(command.to_string().as_str()); + + let data: HashMap = [(String::from("uuid"), client.get_uuid())].iter().cloned().collect(); + let command = Commands::ClientUpdate(Some(data)); + + self.server.update_all_clients(self.uuid.as_str(), command); + + } +} + +impl Runnables for ClientInfo { fn run() { } } -impl Runnables for Request { +impl Runnables for ClientRemove { fn run() { } } -impl Runnables for Request { +impl Runnables for Client { fn run() { } } -impl Runnables for Request { +impl Runnables for Success { fn run() { } } -impl Runnables for Request { - fn run() { - } -} - -impl Runnables for Request { - fn run() { - } -} - -impl Runnables for Request { +impl Runnables for Error { fn run() { } } -- 2.40.1 From c49bfb281e54e815aab88e6701cddcf27f462024 Mon Sep 17 00:00:00 2001 From: Mitchell Date: Tue, 18 Aug 2020 13:27:29 +0100 Subject: [PATCH 31/34] removed dead code tags to enum types --- 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 c66d43f..3bcc9bb 100644 --- a/src/server/server_profile.rs +++ b/src/server/server_profile.rs @@ -32,11 +32,8 @@ use regex::Regex; #[derive(Debug)] pub enum ServerMessages { RequestUpdate(String), - #[allow(dead_code)] RequestInfo(String, String), - #[allow(dead_code)] RequestDisconnect(String), - #[allow(dead_code)] Shutdown, } -- 2.40.1 From 573a625a04d84764a98c494d881402175d61d045 Mon Sep 17 00:00:00 2001 From: Mitchell Date: Wed, 19 Aug 2020 22:24:10 +0100 Subject: [PATCH 32/34] new client api from ref-method branch --- src/client_api/mod.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/client_api/mod.rs b/src/client_api/mod.rs index 1f2084b..1af8627 100644 --- a/src/client_api/mod.rs +++ b/src/client_api/mod.rs @@ -6,7 +6,6 @@ use crate::{ use std::time::Duration; use zeroize::Zeroize; - pub struct ClientApi { socket: TcpStream, addr: String, @@ -45,7 +44,7 @@ impl ClientApi { let _ = stream.read(&mut buffer)?; println!("data recieved: {:?}", &buffer[0..20]); - match Commands::from(&buffer) { + match Commands::from(&mut buffer) { Commands::Request(None) => { println!("zeroing"); buffer.zeroize(); -- 2.40.1 From 81c5949de34f0bacd4450877c335ee523f6639ce Mon Sep 17 00:00:00 2001 From: Mitchell Date: Wed, 19 Aug 2020 22:24:29 +0100 Subject: [PATCH 33/34] new changes from ref-method branch --- src/lib.rs | 2 ++ src/main.rs | 28 +++++++++++++++++----------- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index b6e16e3..4e15e7d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,7 @@ enum Message { Terminate, } +#[derive(Debug)] pub struct ThreadPool{ workers: Vec, sender: Sender, @@ -50,6 +51,7 @@ impl ThreadPool{ } } +#[derive(Debug)] struct Worker { id: usize, thread: Option>, diff --git a/src/main.rs b/src/main.rs index 7e1946b..abcf019 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,5 @@ -//#![feature(test)] -#![allow(dead_code)] +#[macro_use] +extern crate lazy_static; mod client_api; mod commands; @@ -15,7 +15,7 @@ use cursive::{ align::Align, view::SizeConstraint, }; -use std::sync::Arc; +//use std::sync::Arc; use std::time::Duration; use crossterm::ErrorKind; use log::info; @@ -24,6 +24,12 @@ use clap::{App, Arg}; use crate::server::server_profile::Server; fn main() -> Result<(), ErrorKind> { + lazy_static!{ + static ref SERVER_NAME: &'static str = "Server-01"; + static ref SERVER_ADDRESS: &'static str = "0.0.0.0:6000"; + static ref SERVER_AUTHOR: &'static str = "noreply@email.com"; + static ref SERVER: Server<'static> = Server::new(&SERVER_NAME, &SERVER_ADDRESS, &SERVER_AUTHOR); + } let args = App::new("--rust chat server--") .version("0.1.5") @@ -36,10 +42,10 @@ 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 s1 = server_arc.clone(); - let s2 = s1.clone(); + //let server = Server::new("Server-01", "0.0.0.0:6000", "noreply@email.com"); + //let server_arc = Arc::new(server); + //let s1 = server_arc.clone(); + //let s2 = s1.clone(); cursive::logger::init(); @@ -61,8 +67,8 @@ 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 _ = SERVER.start();}) + .leaf("Stop", move |_s| {let _ = SERVER.stop();}) .delimiter() .leaf("Debug", |s| {s.toggle_debug_console();})); info!("Main: entering loop"); @@ -70,8 +76,8 @@ fn main() -> Result<(), ErrorKind> { display.run(); Ok(()) } else { - let server = Server::new("Server-01", "0.0.0.0:6000", "noreply@email.com"); - server.start()?; + //let server = Server::new("Server-01", "0.0.0.0:6000", "noreply@email.com"); + SERVER.start()?; loop {std::thread::sleep(Duration::from_secs(1));} } } -- 2.40.1 From b8440e8290658f994a0cf204a841d817723b6ff5 Mon Sep 17 00:00:00 2001 From: Mitchell Date: Wed, 19 Aug 2020 22:25:27 +0100 Subject: [PATCH 34/34] all basic command functionality added allows for all commands from server side and client side to be tested correctly --- src/server/client/client_profile.rs | 145 ++++++++++++++++++---------- 1 file changed, 95 insertions(+), 50 deletions(-) diff --git a/src/server/client/client_profile.rs b/src/server/client/client_profile.rs index 538b279..9038645 100644 --- a/src/server/client/client_profile.rs +++ b/src/server/client/client_profile.rs @@ -1,28 +1,43 @@ +extern crate regex; + use std::{ - io::prelude::*, sync::Arc, - io, sync::Mutex, + net::{Shutdown, TcpStream}, + io::prelude::*, + io::Error, + //collections::HashMap, time::{Instant, Duration}, - net::{TcpStream, Shutdown} + io, }; + use crossbeam::{ Sender, Receiver, TryRecvError, unbounded }; + +//use zeroize::Zeroize; use log::info; -use crate::server::server_profile::ServerMessages; -use crate::commands::Commands; +use crate::{ + server::{ + //server_profile::Server, + server_profile::ServerMessages, + }, + commands::Commands + +}; + +//use parking_lot::FairMutex; +//use dashmap::DashMap; #[derive(Debug)] pub struct Client { - - pub uuid: String, - pub username: String, - pub address: String, + uuid: String, + username: String, + address: String, last_heartbeat: Arc>, @@ -35,7 +50,7 @@ pub struct Client { } impl Client { - pub fn new(stream: TcpStream, server_sender: Sender, uuid: String, username: String, address: String) -> Self { + 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(); @@ -54,8 +69,28 @@ impl Client { } } + #[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(&self) { + pub fn handle_connection(&mut self) { let mut buffer = [0; 1024]; // TODO: - Check heartbeat @@ -64,40 +99,45 @@ impl Client { } info!("{}: handling connection", self.uuid); - if self.stream_arc.lock().unwrap().peek(&mut buffer).is_ok() { - let mut stream = self.stream_arc.lock().unwrap(); - - let _ = stream.read(&mut buffer).unwrap(); - - let command = Commands::from(&buffer); - - // match incomming commands - println!("command"); - match command { - Commands::Disconnect(None) => { - self.server_sender.send(ServerMessages::Disconnect(self.uuid.clone())).expect("sending message to server failed"); - }, - Commands::HeartBeat(None) => { - *self.last_heartbeat.lock().unwrap() = Instant::now(); - let _ = stream.write_all(Commands::Success(None).to_string().as_bytes()); - }, - Commands::ClientUpdate(None) => { - let _ = self.server_sender.send(ServerMessages::RequestUpdate(self.stream_arc.clone())); - let _ = stream.write_all(Commands::Success(None).to_string().as_bytes()); + match self.read_data(&mut buffer) { + Ok(command) => { + // match incomming commands + println!("command"); + match command { + Commands::Disconnect(None) => { + self.server_sender.send(ServerMessages::Disconnect(self.uuid.clone())).expect("sending message to server failed"); + self.stream_arc.lock().unwrap().shutdown(Shutdown::Both).expect("shutdown call failed"); + }, + Commands::HeartBeat(None) => { + *self.last_heartbeat.lock().unwrap() = Instant::now(); + self.transmit_data(Commands::Success(None).to_string().as_str()); + }, + Commands::ClientUpdate(None) => { + self.transmit_data(Commands::Success(None).to_string().as_str()); + let _ = self.server_sender.send(ServerMessages::RequestUpdate(self.stream_arc.clone())); + }, + Commands::ClientInfo(Some(params)) => { + let uuid = params.get("uuid").unwrap(); + let _ = self.server_sender.send(ServerMessages::RequestInfo(uuid.clone(), self.stream_arc.clone())); + }, + // TODO: may or may not be needed? + Commands::Error(None) => { + }, + _ => { + self.transmit_data(Commands::Error(None).to_string().as_str()); + }, } - _ => { - let _ = stream.write_all(Commands::Error(None).to_string().as_bytes()); - } - } + }, + 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*/ - + /*command is on the channel*/ Ok(Commands::ClientRemove(Some(params))) => { - let mut stream = self.stream_arc.lock().unwrap(); let mut retry: u8 = 3; 'retry_loop1: loop { if retry < 1 { @@ -105,9 +145,8 @@ impl Client { break 'retry_loop1 } else { self.transmit_data(Commands::ClientRemove(Some(params.clone())).to_string().as_str()); - let _ = stream.read(&mut buffer); - let command = Commands::from(&buffer); - if command == Commands::Success(None) { + + if self.read_data(&mut buffer).unwrap_or(Commands::Error(None)) == Commands::Success(None) { break 'retry_loop1; } else { retry -= 1; @@ -116,17 +155,15 @@ impl Client { } }, Ok(Commands::Client(Some(params))) => { - let mut stream = self.stream_arc.lock().unwrap(); let mut retry: u8 = 3; 'retry_loop2: loop { if retry < 1 { - let _ = stream.write_all(Commands::Error(None).to_string().as_bytes()); + self.transmit_data(Commands::Error(None).to_string().as_str()); break 'retry_loop2; } else { - let _ = stream.write_all(Commands::Client(Some(params.clone())).to_string().as_bytes()); - let _ = stream.read(&mut buffer); - let command = Commands::from(&buffer); - if command == Commands::Success(None) { + self.transmit_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; @@ -137,9 +174,9 @@ impl Client { }, /*no data available yet*/ Err(TryRecvError::Empty) => {}, - _ => {} + _ => {}, } - println!("end"); + println!("---Client Thread Exit---"); } // move into a drop perhaps @@ -158,10 +195,18 @@ impl Client { io::ErrorKind::NotConnected => { let _ = self.server_sender.send(ServerMessages::Disconnect(self.uuid.clone())); }, - _ => { } + _ => { }, } } } + + fn read_data(&mut self, buffer: &mut [u8; 1024]) -> Result { + self.stream_arc.lock().unwrap().read(buffer)?; + let command = Commands::from(buffer); + + Ok(command) + } + } impl ToString for Client { -- 2.40.1