diff --git a/src/bin/websurfx.rs b/src/bin/websurfx.rs index d8d7f5c..b4b989e 100644 --- a/src/bin/websurfx.rs +++ b/src/bin/websurfx.rs @@ -4,7 +4,6 @@ //! stdout and handles the command line arguments provided and launches the `websurfx` server. use std::net::TcpListener; - use websurfx::{config::parser::Config, run}; /// The function that launches the main server and registers all the routes of the website. @@ -16,15 +15,18 @@ use websurfx::{config::parser::Config, run}; #[actix_web::main] async fn main() -> std::io::Result<()> { // Initialize the parsed config file. - let config = Config::parse().unwrap(); + let config = Config::parse(true).unwrap(); - // Initializing logging middleware with level set to default or info. - if config.logging || config.debug { - use env_logger::Env; - env_logger::Builder::from_env(Env::default().default_filter_or("info")).init(); - } - - log::info!("started server on port {}", config.port); + log::info!( + "started server on port {} and IP {}", + config.port, + config.binding_ip + ); + log::info!( + "Open http://{}:{}/ in your browser", + config.port, + config.binding_ip + ); let listener = TcpListener::bind((config.binding_ip.clone(), config.port))?; diff --git a/src/config/parser.rs b/src/config/parser.rs index a665246..51a83ea 100644 --- a/src/config/parser.rs +++ b/src/config/parser.rs @@ -2,8 +2,9 @@ //! into rust readable form. use super::parser_models::Style; +use log::LevelFilter; use rlua::Lua; -use std::{collections::HashMap, format, fs, path::Path}; +use std::{collections::HashMap, format, fs, io::Write, path::Path, thread::available_parallelism}; // ------- Constants -------- static COMMON_DIRECTORY_NAME: &str = "websurfx"; @@ -23,6 +24,7 @@ static CONFIG_FILE_NAME: &str = "config.lua"; /// * `debug` - It stores the option to whether enable or disable debug mode. /// * `upstream_search_engines` - It stores all the engine names that were enabled by the user. /// * `request_timeout` - It stores the time (secs) which controls the server request timeout. +/// * `threads` - It stores the number of threads which controls the app will use to run. #[derive(Clone)] pub struct Config { pub port: u16, @@ -34,6 +36,7 @@ pub struct Config { pub debug: bool, pub upstream_search_engines: Vec, pub request_timeout: u8, + pub threads: u8, } /// Configuration options for the aggregator. @@ -51,12 +54,17 @@ impl Config { /// A function which parses the config.lua file and puts all the parsed options in the newly /// constructed Config struct and returns it. /// + /// # Arguments + /// + /// * `logging_initialized` - It takes a boolean which ensures that the logging doesn't get + /// initialized twice. + /// /// # Error /// /// Returns a lua parse error if parsing of the config.lua file fails or has a syntax error /// or io error if the config.lua file doesn't exists otherwise it returns a newly constructed /// Config struct with all the parsed config options from the parsed config file. - pub fn parse() -> Result> { + pub fn parse(logging_initialized: bool) -> Result> { Lua::new().context(|context| -> Result> { let globals = context.globals(); @@ -64,6 +72,38 @@ impl Config { .load(&fs::read_to_string(Config::config_path()?)?) .exec()?; + let parsed_threads: u8 = globals.get::<_, u8>("threads")?; + + let debug: bool = globals.get::<_, bool>("debug")?; + let logging:bool= globals.get::<_, bool>("logging")?; + + // Check whether logging has not been initialized before. + if logging_initialized { + // Initializing logging middleware with level set to default or info. + let mut log_level: LevelFilter = LevelFilter::Off; + if logging && debug == false { + log_level = LevelFilter::Info; + } else if debug { + log_level = LevelFilter::Trace; + }; + env_logger::Builder::new().filter(None, log_level).init(); + } + + let threads: u8 = if parsed_threads == 0 { + let total_num_of_threads:usize = available_parallelism()?.get() /2; + if debug || logging { + log::error!("Config Error: The value of `threads` option should be a non zero positive integer"); + log::info!("Falling back to using {} threads", total_num_of_threads) + } else { + std::io::stdout() + .lock() + .write_all(&format!("Config Error: The value of `threads` option should be a non zero positive integer\nFalling back to using {} threads\n", total_num_of_threads).into_bytes())?; + }; + total_num_of_threads as u8 + } else { + parsed_threads + }; + Ok(Config { port: globals.get::<_, u16>("port")?, binding_ip: globals.get::<_, String>("binding_ip")?, @@ -75,14 +115,15 @@ impl Config { aggregator: AggregatorConfig { random_delay: globals.get::<_, bool>("production_use")?, }, - logging: globals.get::<_, bool>("logging")?, - debug: globals.get::<_, bool>("debug")?, + logging, + debug, upstream_search_engines: globals .get::<_, HashMap>("upstream_search_engines")? .into_iter() .filter_map(|(key, value)| value.then_some(key)) .collect(), request_timeout: globals.get::<_, u8>("request_timeout")?, + threads, }) }) } diff --git a/src/lib.rs b/src/lib.rs index c14021e..fe8ee92 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -35,7 +35,7 @@ use handler::public_paths::public_path; /// use std::net::TcpListener; /// use websurfx::{config::parser::Config, run}; /// -/// let config = Config::parse().unwrap(); +/// let config = Config::parse(true).unwrap(); /// let listener = TcpListener::bind("127.0.0.1:8080").expect("Failed to bind address"); /// let server = run(listener,config).expect("Failed to start server"); /// ``` @@ -50,6 +50,8 @@ pub fn run(listener: TcpListener, config: Config) -> std::io::Result { let handlebars_ref: web::Data = web::Data::new(handlebars); + let cloned_config_threads_opt: u8 = config.threads; + let server = HttpServer::new(move || { let cors: Cors = Cors::default() .allow_any_origin() @@ -82,6 +84,7 @@ pub fn run(listener: TcpListener, config: Config) -> std::io::Result { .service(routes::settings) // settings page .default_service(web::route().to(routes::not_found)) // error page }) + .workers(cloned_config_threads_opt as usize) // Start server on 127.0.0.1 with the user provided port number. for example 127.0.0.1:8080. .listen(listener)? .run(); diff --git a/tests/index.rs b/tests/index.rs index 657a466..d886e13 100644 --- a/tests/index.rs +++ b/tests/index.rs @@ -8,7 +8,7 @@ fn spawn_app() -> String { // Binding to port 0 will trigger the OS to assign a port for us. let listener = TcpListener::bind("127.0.0.1:0").expect("Failed to bind random port"); let port = listener.local_addr().unwrap().port(); - let config = Config::parse().unwrap(); + let config = Config::parse(true).unwrap(); let server = run(listener, config).expect("Failed to bind address"); tokio::spawn(server); @@ -36,7 +36,7 @@ async fn test_index() { assert_eq!(res.status(), 200); let handlebars = handlebars(); - let config = Config::parse().unwrap(); + let config = Config::parse(false).unwrap(); let template = handlebars.render("index", &config.style).unwrap(); assert_eq!(res.text().await.unwrap(), template); } diff --git a/websurfx/config.lua b/websurfx/config.lua index 73bffe7..4f2633c 100644 --- a/websurfx/config.lua +++ b/websurfx/config.lua @@ -1,6 +1,7 @@ -- ### General ### logging = true -- an option to enable or disable logs. debug = false -- an option to enable or disable debug mode. +threads = 10 -- the amount of threads that the app will use to run (the value should be greater than 0). -- ### Server ### port = "8080" -- port on which server should be launched