From 62453a1be4e85faf8d3e82a68d1dbd7249bea372 Mon Sep 17 00:00:00 2001 From: Esteban Borai Date: Mon, 18 Nov 2024 19:10:30 -0300 Subject: [PATCH] feat(server): use cli structure and `start` subcommand --- crates/http-server/src/cli/command/mod.rs | 11 ++++ crates/http-server/src/cli/command/start.rs | 51 +++++++++++++++++++ crates/http-server/src/{cli.rs => cli/mod.rs} | 11 ++-- crates/http-server/src/config.rs | 2 + crates/http-server/src/main.rs | 31 ++++------- crates/http-server/src/server/mod.rs | 27 ++++++---- 6 files changed, 95 insertions(+), 38 deletions(-) create mode 100644 crates/http-server/src/cli/command/mod.rs create mode 100644 crates/http-server/src/cli/command/start.rs rename crates/http-server/src/{cli.rs => cli/mod.rs} (59%) diff --git a/crates/http-server/src/cli/command/mod.rs b/crates/http-server/src/cli/command/mod.rs new file mode 100644 index 00000000..82aed77e --- /dev/null +++ b/crates/http-server/src/cli/command/mod.rs @@ -0,0 +1,11 @@ +mod start; + +use clap::Parser; + +use self::start::StartOpt; + +#[derive(Debug, Parser)] +pub enum Command { + /// Start the HTTP server + Start(StartOpt), +} diff --git a/crates/http-server/src/cli/command/start.rs b/crates/http-server/src/cli/command/start.rs new file mode 100644 index 00000000..12b2f7b4 --- /dev/null +++ b/crates/http-server/src/cli/command/start.rs @@ -0,0 +1,51 @@ +use std::net::IpAddr; +use std::process::exit; +use std::sync::Arc; + +use anyhow::Result; +use clap::Parser; +use tokio::runtime::Builder; +use tracing::{error, info}; + +use crate::config::Config; +use crate::server::Server; + +const THREAD_NAME: &str = "http-server"; + +#[derive(Debug, Parser)] +pub struct StartOpt { + /// Host (IP) to bind the server + #[clap(long, default_value = "0.0.0.0")] + pub host: IpAddr, + /// Port to bind the server + #[clap(short = 'p', long, default_value = "7878")] + pub port: u16, +} + +impl StartOpt { + pub fn exec(&self) -> Result<()> { + let rt = Builder::new_multi_thread() + .enable_all() + .thread_name(THREAD_NAME) + .build()?; + let rt = Arc::new(rt); + let config = Config { + host: self.host, + port: self.port, + }; + let server = Server::new(config); + + rt.block_on(async { + match server.run(Arc::clone(&rt)).await { + Ok(_) => { + info!("Server exited successfuly"); + Ok(()) + } + Err(error) => { + error!(%error, "Server exited with error"); + exit(1); + } + } + }) + } +} diff --git a/crates/http-server/src/cli.rs b/crates/http-server/src/cli/mod.rs similarity index 59% rename from crates/http-server/src/cli.rs rename to crates/http-server/src/cli/mod.rs index fd05e0dd..30af0843 100644 --- a/crates/http-server/src/cli.rs +++ b/crates/http-server/src/cli/mod.rs @@ -1,15 +1,16 @@ -use std::net::IpAddr; +pub mod command; use clap::Parser; +use self::command::Command; + #[derive(Debug, Parser)] #[command( name = "http-server", author = "Esteban Borai ", - about = "Simple and configurable command-line HTTP server\nSource: https://github.com/EstebanBorai/http-server", - next_line_help = true + about = "Simple and configurable command-line HTTP server\nSource: https://github.com/http-server-rs/http-server" )] pub struct Cli { - pub host: IpAddr, - pub port: u16, + #[command(subcommand)] + pub command: Command, } diff --git a/crates/http-server/src/config.rs b/crates/http-server/src/config.rs index c06d6cd6..8e2e0d73 100644 --- a/crates/http-server/src/config.rs +++ b/crates/http-server/src/config.rs @@ -1,6 +1,8 @@ use std::net::IpAddr; pub struct Config { + /// The IP address to bind to. pub host: IpAddr, + /// The port to bind to. pub port: u16, } diff --git a/crates/http-server/src/main.rs b/crates/http-server/src/main.rs index 9b340e1d..ced279e9 100644 --- a/crates/http-server/src/main.rs +++ b/crates/http-server/src/main.rs @@ -3,34 +3,21 @@ pub mod config; pub mod plugin; pub mod server; -use std::{process::exit, sync::Arc}; - use anyhow::Result; -use tokio::runtime::Builder; +use clap::Parser; -use self::server::Server; +use self::cli::command::Command; +use self::cli::Cli; fn main() -> Result<()> { - let rt = Builder::new_multi_thread() - .enable_all() - .thread_name("http-server") - .build()?; - let rt = Arc::new(rt); - tracing_subscriber::fmt() .with_max_level(tracing::Level::DEBUG) .init(); + let args = Cli::parse(); + + match args.command { + Command::Start(opt) => opt.exec()?, + } - rt.block_on(async { - match Server::run(Arc::clone(&rt)).await { - Ok(_) => { - println!("Server exited successfuly"); - Ok(()) - } - Err(error) => { - eprint!("{:?}", error); - exit(1); - } - } - }) + Ok(()) } diff --git a/crates/http-server/src/server/mod.rs b/crates/http-server/src/server/mod.rs index 67866d2a..eee5c4e4 100644 --- a/crates/http-server/src/server/mod.rs +++ b/crates/http-server/src/server/mod.rs @@ -1,4 +1,8 @@ -use std::{convert::Infallible, net::SocketAddr, path::PathBuf, str::FromStr, sync::Arc}; +use std::convert::Infallible; +use std::net::SocketAddr; +use std::path::PathBuf; +use std::str::FromStr; +use std::sync::Arc; use anyhow::Result; use http_body_util::{BodyExt, Full}; @@ -7,28 +11,32 @@ use hyper::{ server::conn::http1, Method, Request, Response, }; -use hyper_util::{rt::TokioIo, service::TowerToHyperService}; +use hyper_util::rt::TokioIo; +use hyper_util::service::TowerToHyperService; use tokio::net::TcpListener; use tokio::runtime::Runtime; use tower::ServiceBuilder; use tower_http::cors::{Any, CorsLayer}; -use tracing::info; +use crate::config::Config; use crate::plugin::ExternalFunctions; -pub struct Server {} +pub struct Server { + config: Config, +} impl Server { - pub async fn run(rt: Arc) -> Result<()> { - info!("Initializing server"); + pub fn new(config: Config) -> Self { + Server { config } + } - let addr = SocketAddr::from(([0, 0, 0, 0], 3000)); + pub async fn run(self, rt: Arc) -> Result<()> { + let addr = SocketAddr::from((self.config.host, self.config.port)); let listener = TcpListener::bind(addr).await?; let functions = Arc::new(ExternalFunctions::new()); let plugin_library = PathBuf::from_str("./target/debug/libfile_explorer.dylib").unwrap(); let config = PathBuf::from_str("./config.toml").unwrap(); let handle = Arc::new(rt.handle().to_owned()); - let local_ip = local_ip_address::local_ip(); unsafe { functions @@ -37,9 +45,6 @@ impl Server { .expect("Function loading failed"); } - info!(%addr, "Server Listening"); - info!(?local_ip, "Local Network"); - loop { let (stream, _) = listener.accept().await?; let io = TokioIo::new(stream);