diff --git a/Cargo.lock b/Cargo.lock index f7b5b74..3e86e06 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,10 +20,27 @@ dependencies = [ ] [[package]] -name = "bytes" +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "cfg-if" @@ -31,6 +48,57 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "clap" +version = "3.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2dbdf4bdacb33466e854ce889eee8dfd5729abf7ccd7664d0a2d60cd384440b" +dependencies = [ + "atty", + "bitflags", + "clap_derive", + "clap_lex", + "indexmap", + "lazy_static", + "strsim", + "termcolor", + "textwrap", +] + +[[package]] +name = "clap_derive" +version = "3.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25320346e922cffe59c0bbc5410c8d8784509efb321488971081313cb1e1a33c" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a37c35f1112dad5e6e0b1adaff798507497a18fceeb30cceb3bae7d1427b9213" +dependencies = [ + "os_str_bytes", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" + +[[package]] +name = "heck" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -40,6 +108,22 @@ dependencies = [ "libc", ] +[[package]] +name = "indexmap" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6012d540c5baa3589337a98ce73408de9b5a25ec9fc2c6fd6be8f0d39e0ca5a" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + [[package]] name = "libc" version = "0.2.126" @@ -65,24 +149,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "memchr" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" - -[[package]] -name = "mio" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713d550d9b44d89174e066b7a6217ae06234c10cb47819a88290d2b353c31799" -dependencies = [ - "libc", - "log", - "wasi", - "windows-sys", -] - [[package]] name = "num_cpus" version = "1.13.1" @@ -99,6 +165,12 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225" +[[package]] +name = "os_str_bytes" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21326818e99cfe6ce1e524c2a805c189a99b5ae555a35d19f9a284b427d86afa" + [[package]] name = "pin-project-lite" version = "0.2.9" @@ -113,6 +185,30 @@ dependencies = [ "servers", ] +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + [[package]] name = "proc-macro2" version = "1.0.39" @@ -137,20 +233,17 @@ version = "0.1.0" dependencies = [ "anyhow", "async-trait", + "clap", "libloading", "log", "tokio", ] [[package]] -name = "socket2" -version = "0.4.4" +name = "strsim" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" -dependencies = [ - "libc", - "winapi", -] +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" @@ -163,33 +256,30 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "textwrap" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" + [[package]] name = "tokio" version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f392c8f16bda3456c0b00c6de39cb100449b98de55ac41c6cdd2bfcf53a1245" dependencies = [ - "bytes", - "libc", - "memchr", - "mio", "num_cpus", "once_cell", "pin-project-lite", - "socket2", - "tokio-macros", - "winapi", -] - -[[package]] -name = "tokio-macros" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7" -dependencies = [ - "proc-macro2", - "quote", - "syn", ] [[package]] @@ -199,10 +289,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee" [[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +name = "version_check" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "winapi" @@ -220,51 +310,17 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-sys" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" -dependencies = [ - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" - -[[package]] -name = "windows_i686_gnu" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" - -[[package]] -name = "windows_i686_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" diff --git a/Cargo.toml b/Cargo.toml index 012748c..c949be1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ edition = "2021" [dependencies] anyhow = "1.0.57" async-trait = "0.1.56" +clap = { version = "3.1.18", features = ["derive"] } libloading = "0.7.3" log = "0.4.17" -tokio = { version = "1.19.0", features = ["rt-multi-thread", "macros", "net", "io-std", "io-util"] } +tokio = { version = "1.19.0", features = ["rt-multi-thread"] } diff --git a/plugin_test/src/lib.rs b/plugin_test/src/lib.rs index ebc0d32..af63328 100644 --- a/plugin_test/src/lib.rs +++ b/plugin_test/src/lib.rs @@ -1,5 +1,7 @@ use async_trait::async_trait; -use servers::{Client, Command, CommandManagerType, CommandRegistrar, Plugin, PluginRegistrar}; +use servers::{ + tcp::Client, Command, CommandManagerType, CommandRegistrar, Plugin, PluginRegistrar, +}; struct PluginTest; diff --git a/src/command_handler.rs b/src/command_handler.rs index 104b3cc..c2503d4 100644 --- a/src/command_handler.rs +++ b/src/command_handler.rs @@ -2,7 +2,7 @@ use std::{any::Any, sync::Arc}; use async_trait::async_trait; -use crate::Client; +use crate::tcp::Client; #[async_trait] pub trait Command: Any + Send + Sync { diff --git a/src/commands/help.rs b/src/commands/help.rs index 0a6591e..4dc45fe 100644 --- a/src/commands/help.rs +++ b/src/commands/help.rs @@ -1,6 +1,6 @@ use async_trait::async_trait; -use crate::{command_handler::Command, CommandManagerType}; +use crate::{command_handler::Command, tcp::Client, CommandManagerType}; pub struct CommandHelp; @@ -14,12 +14,7 @@ impl Command for CommandHelp { "show help" } - async fn execute( - &self, - client: &mut crate::Client, - _args: Vec<&str>, - commands: &CommandManagerType, - ) { + async fn execute(&self, client: &mut Client, _args: Vec<&str>, commands: &CommandManagerType) { for command in commands.iter() { client .send(&format!("{} - {}", command.name(), command.help())) diff --git a/src/lib.rs b/src/lib.rs index 711a3bc..5b47e75 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,8 +1,8 @@ -mod client; mod command_handler; mod commands; mod plugin_loader; -pub use client::*; pub use command_handler::*; pub use plugin_loader::*; + +pub mod tcp; diff --git a/src/main.rs b/src/main.rs index 8358fe4..c79f59e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,60 +1,35 @@ -use servers::{loader, Client, CommandManagerType}; -use tokio::{io::AsyncWriteExt, net::TcpListener}; +use clap::Parser; +use servers::tcp; -#[tokio::main] -async fn main() -> anyhow::Result<()> { - // listen Tcp server - let listener = TcpListener::bind("0.0.0.0:9999").await?; +#[derive(Parser)] +#[clap( + name = "servers", + about = "Simple Tcp server that supports expansion via plugins" +)] +struct Cli { + #[clap( + short = 'h', + long = "host", + default_value = "0.0.0.0", + help = "Tcp server host", + display_order = 1 + )] + host: String, + #[clap( + short = 'p', + long = "port", + default_value = "9999", + help = "Tcp server port [set 0 to random]", + display_order = 2 + )] + port: String, +} - println!("Tcp server started at: {}", listener.local_addr()?); +fn main() -> anyhow::Result<()> { + let cli = Cli::parse(); - // load plugins and commands - let (_plugin_manager, commands_manager) = loader()?; - - // Accepts a new incoming connection from this listener. - while let Ok((stream, _address)) = listener.accept().await { - let client = Client::new(stream); - - // handle client connection in new thread - tokio::spawn(handle_connection(client, commands_manager.clone())); - } - - Ok(()) -} - -async fn handle_connection(mut client: Client, commands: CommandManagerType) -> anyhow::Result<()> { - println!("New Client: {:?}", client.stream.peer_addr()?); - - loop { - // read client message/buffer - let buf = client.read().await?; - - // split message by whitespace - let args: &Vec<&str> = &buf.split_ascii_whitespace().collect(); - - // get command from args - let cmd = args[0]; - - // search if a command exists - for command in commands.iter() { - // if this is the entered command - if cmd == command.name() { - // execute command - command - .execute(&mut client, args[1..args.len()].to_vec(), &commands) - .await; - - // don't search for more commands - break; - } - } - - // if an I/O or EOF error, abort the connection - if client.stream.flush().await.is_err() { - // terminate connection - break; - } - } + // start tcp server + tcp::start_server(&cli.host, &cli.port)?; Ok(()) } diff --git a/src/client.rs b/src/tcp/client.rs similarity index 74% rename from src/client.rs rename to src/tcp/client.rs index 79141ad..be1404e 100644 --- a/src/client.rs +++ b/src/tcp/client.rs @@ -1,7 +1,7 @@ #![allow(clippy::unused_io_amount)] -use tokio::{ - io::{AsyncReadExt, AsyncWriteExt}, +use std::{ + io::{self, Read, Write}, net::TcpStream, }; @@ -21,7 +21,7 @@ impl Client { let mut buf = [0; 1024]; // read buffer from stream - self.stream.read(&mut buf).await?; + self.stream.read(&mut buf)?; // encode &[u8] to a String and replace null spaces (empty `\0` bytes) let decoded = String::from_utf8(buf.to_vec())?.replace('\0', ""); @@ -30,15 +30,11 @@ impl Client { } /// Send message to Client - pub async fn send(&mut self, content: &str) -> anyhow::Result<()> { + pub async fn send(&mut self, content: &str) -> io::Result<()> { // add a new line at the end of the content let content = format!("{content}\n\r"); // send message - self.stream - .write_all(content.as_bytes()) - .await?; - - Ok(()) + self.stream.write_all(content.as_bytes()) } } diff --git a/src/tcp/handle_connection.rs b/src/tcp/handle_connection.rs new file mode 100644 index 0000000..f77d03e --- /dev/null +++ b/src/tcp/handle_connection.rs @@ -0,0 +1,45 @@ +use std::io::Write; + +use crate::CommandManagerType; + +use super::Client; + +pub async fn handle_connection( + mut client: Client, + commands: CommandManagerType, +) -> anyhow::Result<()> { + println!("New Client: {:?}", client.stream.peer_addr()?); + + loop { + // read client message/buffer + let buf = client.read().await?; + + // split message by whitespace + let args: &Vec<&str> = &buf.split_ascii_whitespace().collect(); + + // get command from args + let cmd = args[0]; + + // search if a command exists + for command in commands.iter() { + // if this is the entered command + if cmd == command.name() { + // execute command + command + .execute(&mut client, args[1..args.len()].to_vec(), &commands) + .await; + + // don't search for more commands + break; + } + } + + // if an I/O or EOF error, abort the connection + if client.stream.flush().is_err() { + // terminate connection + break; + } + } + + Ok(()) +} diff --git a/src/tcp/mod.rs b/src/tcp/mod.rs new file mode 100644 index 0000000..0c8c698 --- /dev/null +++ b/src/tcp/mod.rs @@ -0,0 +1,7 @@ +mod client; +mod handle_connection; +mod start; + +pub use client::*; +pub use handle_connection::*; +pub use start::*; diff --git a/src/tcp/start.rs b/src/tcp/start.rs new file mode 100644 index 0000000..c571430 --- /dev/null +++ b/src/tcp/start.rs @@ -0,0 +1,28 @@ +use std::net::TcpListener; + +use crate::{ + loader, + tcp::{handle_connection, Client}, +}; + +pub fn start_server(host: &str, port: &str) -> anyhow::Result<()> { + // listen Tcp server + let listener = TcpListener::bind(format!("{host}:{port}"))?; + + println!("Tcp server started at: {}", listener.local_addr()?); + + // load plugins and commands + let (_plugin_manager, commands_manager) = loader()?; + + // Accepts a new incoming connection from this listener. + while let Ok((stream, _address)) = listener.accept() { + let client = Client::new(stream); + + let commands_manager = commands_manager.clone(); + + // handle client connection in new thread + tokio::spawn(handle_connection(client, commands_manager)); + } + + Ok(()) +}