XFFXFF commited on
Commit
c4a2d87
2 Parent(s): 5b4e7c7 e02fafd

Merge pull request #171 from neon-mmd/feat-add-threads-opt

Browse files

✨ Config option to allow users to customize the number of threads used by the app

Files changed (7) hide show
  1. Cargo.lock +14 -13
  2. Cargo.toml +1 -1
  3. src/bin/websurfx.rs +11 -9
  4. src/config/parser.rs +45 -4
  5. src/lib.rs +4 -1
  6. tests/index.rs +2 -2
  7. websurfx/config.lua +1 -0
Cargo.lock CHANGED
@@ -460,11 +460,12 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
460
 
461
  [[package]]
462
  name = "cc"
463
- version = "1.0.79"
464
  source = "registry+https://github.com/rust-lang/crates.io-index"
465
- checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
466
  dependencies = [
467
  "jobserver",
 
468
  ]
469
 
470
  [[package]]
@@ -1912,9 +1913,9 @@ checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
1912
 
1913
  [[package]]
1914
  name = "pest"
1915
- version = "2.7.1"
1916
  source = "registry+https://github.com/rust-lang/crates.io-index"
1917
- checksum = "0d2d1d55045829d65aad9d389139882ad623b33b904e7c9f1b10c5b8927298e5"
1918
  dependencies = [
1919
  "thiserror",
1920
  "ucd-trie",
@@ -1922,9 +1923,9 @@ dependencies = [
1922
 
1923
  [[package]]
1924
  name = "pest_derive"
1925
- version = "2.7.1"
1926
  source = "registry+https://github.com/rust-lang/crates.io-index"
1927
- checksum = "5f94bca7e7a599d89dea5dfa309e217e7906c3c007fb9c3299c40b10d6a315d3"
1928
  dependencies = [
1929
  "pest",
1930
  "pest_generator",
@@ -1932,9 +1933,9 @@ dependencies = [
1932
 
1933
  [[package]]
1934
  name = "pest_generator"
1935
- version = "2.7.1"
1936
  source = "registry+https://github.com/rust-lang/crates.io-index"
1937
- checksum = "99d490fe7e8556575ff6911e45567ab95e71617f43781e5c05490dc8d75c965c"
1938
  dependencies = [
1939
  "pest",
1940
  "pest_meta",
@@ -1945,9 +1946,9 @@ dependencies = [
1945
 
1946
  [[package]]
1947
  name = "pest_meta"
1948
- version = "2.7.1"
1949
  source = "registry+https://github.com/rust-lang/crates.io-index"
1950
- checksum = "2674c66ebb4b4d9036012091b537aae5878970d6999f81a265034d85b136b341"
1951
  dependencies = [
1952
  "once_cell",
1953
  "pest",
@@ -2523,9 +2524,9 @@ dependencies = [
2523
 
2524
  [[package]]
2525
  name = "rustix"
2526
- version = "0.38.4"
2527
  source = "registry+https://github.com/rust-lang/crates.io-index"
2528
- checksum = "0a962918ea88d644592894bc6dc55acc6c0956488adcebbfb6e273506b7fd6e5"
2529
  dependencies = [
2530
  "bitflags 2.3.3",
2531
  "errno",
@@ -3518,7 +3519,7 @@ dependencies = [
3518
 
3519
  [[package]]
3520
  name = "websurfx"
3521
- version = "0.15.3"
3522
  dependencies = [
3523
  "actix-files",
3524
  "actix-web",
 
460
 
461
  [[package]]
462
  name = "cc"
463
+ version = "1.0.81"
464
  source = "registry+https://github.com/rust-lang/crates.io-index"
465
+ checksum = "6c6b2562119bf28c3439f7f02db99faf0aa1a8cdfe5772a2ee155d32227239f0"
466
  dependencies = [
467
  "jobserver",
468
+ "libc",
469
  ]
470
 
471
  [[package]]
 
1913
 
1914
  [[package]]
1915
  name = "pest"
1916
+ version = "2.7.2"
1917
  source = "registry+https://github.com/rust-lang/crates.io-index"
1918
+ checksum = "1acb4a4365a13f749a93f1a094a7805e5cfa0955373a9de860d962eaa3a5fe5a"
1919
  dependencies = [
1920
  "thiserror",
1921
  "ucd-trie",
 
1923
 
1924
  [[package]]
1925
  name = "pest_derive"
1926
+ version = "2.7.2"
1927
  source = "registry+https://github.com/rust-lang/crates.io-index"
1928
+ checksum = "666d00490d4ac815001da55838c500eafb0320019bbaa44444137c48b443a853"
1929
  dependencies = [
1930
  "pest",
1931
  "pest_generator",
 
1933
 
1934
  [[package]]
1935
  name = "pest_generator"
1936
+ version = "2.7.2"
1937
  source = "registry+https://github.com/rust-lang/crates.io-index"
1938
+ checksum = "68ca01446f50dbda87c1786af8770d535423fa8a53aec03b8f4e3d7eb10e0929"
1939
  dependencies = [
1940
  "pest",
1941
  "pest_meta",
 
1946
 
1947
  [[package]]
1948
  name = "pest_meta"
1949
+ version = "2.7.2"
1950
  source = "registry+https://github.com/rust-lang/crates.io-index"
1951
+ checksum = "56af0a30af74d0445c0bf6d9d051c979b516a1a5af790d251daee76005420a48"
1952
  dependencies = [
1953
  "once_cell",
1954
  "pest",
 
2524
 
2525
  [[package]]
2526
  name = "rustix"
2527
+ version = "0.38.6"
2528
  source = "registry+https://github.com/rust-lang/crates.io-index"
2529
+ checksum = "1ee020b1716f0a80e2ace9b03441a749e402e86712f15f16fe8a8f75afac732f"
2530
  dependencies = [
2531
  "bitflags 2.3.3",
2532
  "errno",
 
3519
 
3520
  [[package]]
3521
  name = "websurfx"
3522
+ version = "0.16.0"
3523
  dependencies = [
3524
  "actix-files",
3525
  "actix-web",
Cargo.toml CHANGED
@@ -1,6 +1,6 @@
1
  [package]
2
  name = "websurfx"
3
- version = "0.15.3"
4
  edition = "2021"
5
  description = "An open-source alternative to Searx that provides clean, ad-free, and organic results with incredible speed while keeping privacy and security in mind."
6
  repository = "https://github.com/neon-mmd/websurfx"
 
1
  [package]
2
  name = "websurfx"
3
+ version = "0.16.0"
4
  edition = "2021"
5
  description = "An open-source alternative to Searx that provides clean, ad-free, and organic results with incredible speed while keeping privacy and security in mind."
6
  repository = "https://github.com/neon-mmd/websurfx"
src/bin/websurfx.rs CHANGED
@@ -4,7 +4,6 @@
4
  //! stdout and handles the command line arguments provided and launches the `websurfx` server.
5
 
6
  use std::net::TcpListener;
7
-
8
  use websurfx::{config::parser::Config, run};
9
 
10
  /// 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};
16
  #[actix_web::main]
17
  async fn main() -> std::io::Result<()> {
18
  // Initialize the parsed config file.
19
- let config = Config::parse().unwrap();
20
-
21
- // Initializing logging middleware with level set to default or info.
22
- if config.logging || config.debug {
23
- use env_logger::Env;
24
- env_logger::Builder::from_env(Env::default().default_filter_or("info")).init();
25
- }
26
 
27
- log::info!("started server on port {}", config.port);
 
 
 
 
 
 
 
 
 
28
 
29
  let listener = TcpListener::bind((config.binding_ip.clone(), config.port))?;
30
 
 
4
  //! stdout and handles the command line arguments provided and launches the `websurfx` server.
5
 
6
  use std::net::TcpListener;
 
7
  use websurfx::{config::parser::Config, run};
8
 
9
  /// The function that launches the main server and registers all the routes of the website.
 
15
  #[actix_web::main]
16
  async fn main() -> std::io::Result<()> {
17
  // Initialize the parsed config file.
18
+ let config = Config::parse(true).unwrap();
 
 
 
 
 
 
19
 
20
+ log::info!(
21
+ "started server on port {} and IP {}",
22
+ config.port,
23
+ config.binding_ip
24
+ );
25
+ log::info!(
26
+ "Open http://{}:{}/ in your browser",
27
+ config.port,
28
+ config.binding_ip
29
+ );
30
 
31
  let listener = TcpListener::bind((config.binding_ip.clone(), config.port))?;
32
 
src/config/parser.rs CHANGED
@@ -2,8 +2,9 @@
2
  //! into rust readable form.
3
 
4
  use super::parser_models::Style;
 
5
  use rlua::Lua;
6
- use std::{collections::HashMap, format, fs, path::Path};
7
 
8
  // ------- Constants --------
9
  static COMMON_DIRECTORY_NAME: &str = "websurfx";
@@ -23,6 +24,7 @@ static CONFIG_FILE_NAME: &str = "config.lua";
23
  /// * `debug` - It stores the option to whether enable or disable debug mode.
24
  /// * `upstream_search_engines` - It stores all the engine names that were enabled by the user.
25
  /// * `request_timeout` - It stores the time (secs) which controls the server request timeout.
 
26
  #[derive(Clone)]
27
  pub struct Config {
28
  pub port: u16,
@@ -34,6 +36,7 @@ pub struct Config {
34
  pub debug: bool,
35
  pub upstream_search_engines: Vec<String>,
36
  pub request_timeout: u8,
 
37
  }
38
 
39
  /// Configuration options for the aggregator.
@@ -51,12 +54,17 @@ impl Config {
51
  /// A function which parses the config.lua file and puts all the parsed options in the newly
52
  /// constructed Config struct and returns it.
53
  ///
 
 
 
 
 
54
  /// # Error
55
  ///
56
  /// Returns a lua parse error if parsing of the config.lua file fails or has a syntax error
57
  /// or io error if the config.lua file doesn't exists otherwise it returns a newly constructed
58
  /// Config struct with all the parsed config options from the parsed config file.
59
- pub fn parse() -> Result<Self, Box<dyn std::error::Error>> {
60
  Lua::new().context(|context| -> Result<Self, Box<dyn std::error::Error>> {
61
  let globals = context.globals();
62
 
@@ -64,6 +72,38 @@ impl Config {
64
  .load(&fs::read_to_string(Config::config_path()?)?)
65
  .exec()?;
66
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
  Ok(Config {
68
  port: globals.get::<_, u16>("port")?,
69
  binding_ip: globals.get::<_, String>("binding_ip")?,
@@ -75,14 +115,15 @@ impl Config {
75
  aggregator: AggregatorConfig {
76
  random_delay: globals.get::<_, bool>("production_use")?,
77
  },
78
- logging: globals.get::<_, bool>("logging")?,
79
- debug: globals.get::<_, bool>("debug")?,
80
  upstream_search_engines: globals
81
  .get::<_, HashMap<String, bool>>("upstream_search_engines")?
82
  .into_iter()
83
  .filter_map(|(key, value)| value.then_some(key))
84
  .collect(),
85
  request_timeout: globals.get::<_, u8>("request_timeout")?,
 
86
  })
87
  })
88
  }
 
2
  //! into rust readable form.
3
 
4
  use super::parser_models::Style;
5
+ use log::LevelFilter;
6
  use rlua::Lua;
7
+ use std::{collections::HashMap, format, fs, io::Write, path::Path, thread::available_parallelism};
8
 
9
  // ------- Constants --------
10
  static COMMON_DIRECTORY_NAME: &str = "websurfx";
 
24
  /// * `debug` - It stores the option to whether enable or disable debug mode.
25
  /// * `upstream_search_engines` - It stores all the engine names that were enabled by the user.
26
  /// * `request_timeout` - It stores the time (secs) which controls the server request timeout.
27
+ /// * `threads` - It stores the number of threads which controls the app will use to run.
28
  #[derive(Clone)]
29
  pub struct Config {
30
  pub port: u16,
 
36
  pub debug: bool,
37
  pub upstream_search_engines: Vec<String>,
38
  pub request_timeout: u8,
39
+ pub threads: u8,
40
  }
41
 
42
  /// Configuration options for the aggregator.
 
54
  /// A function which parses the config.lua file and puts all the parsed options in the newly
55
  /// constructed Config struct and returns it.
56
  ///
57
+ /// # Arguments
58
+ ///
59
+ /// * `logging_initialized` - It takes a boolean which ensures that the logging doesn't get
60
+ /// initialized twice.
61
+ ///
62
  /// # Error
63
  ///
64
  /// Returns a lua parse error if parsing of the config.lua file fails or has a syntax error
65
  /// or io error if the config.lua file doesn't exists otherwise it returns a newly constructed
66
  /// Config struct with all the parsed config options from the parsed config file.
67
+ pub fn parse(logging_initialized: bool) -> Result<Self, Box<dyn std::error::Error>> {
68
  Lua::new().context(|context| -> Result<Self, Box<dyn std::error::Error>> {
69
  let globals = context.globals();
70
 
 
72
  .load(&fs::read_to_string(Config::config_path()?)?)
73
  .exec()?;
74
 
75
+ let parsed_threads: u8 = globals.get::<_, u8>("threads")?;
76
+
77
+ let debug: bool = globals.get::<_, bool>("debug")?;
78
+ let logging:bool= globals.get::<_, bool>("logging")?;
79
+
80
+ // Check whether logging has not been initialized before.
81
+ if logging_initialized {
82
+ // Initializing logging middleware with level set to default or info.
83
+ let mut log_level: LevelFilter = LevelFilter::Off;
84
+ if logging && debug == false {
85
+ log_level = LevelFilter::Info;
86
+ } else if debug {
87
+ log_level = LevelFilter::Trace;
88
+ };
89
+ env_logger::Builder::new().filter(None, log_level).init();
90
+ }
91
+
92
+ let threads: u8 = if parsed_threads == 0 {
93
+ let total_num_of_threads:usize = available_parallelism()?.get() /2;
94
+ if debug || logging {
95
+ log::error!("Config Error: The value of `threads` option should be a non zero positive integer");
96
+ log::info!("Falling back to using {} threads", total_num_of_threads)
97
+ } else {
98
+ std::io::stdout()
99
+ .lock()
100
+ .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())?;
101
+ };
102
+ total_num_of_threads as u8
103
+ } else {
104
+ parsed_threads
105
+ };
106
+
107
  Ok(Config {
108
  port: globals.get::<_, u16>("port")?,
109
  binding_ip: globals.get::<_, String>("binding_ip")?,
 
115
  aggregator: AggregatorConfig {
116
  random_delay: globals.get::<_, bool>("production_use")?,
117
  },
118
+ logging,
119
+ debug,
120
  upstream_search_engines: globals
121
  .get::<_, HashMap<String, bool>>("upstream_search_engines")?
122
  .into_iter()
123
  .filter_map(|(key, value)| value.then_some(key))
124
  .collect(),
125
  request_timeout: globals.get::<_, u8>("request_timeout")?,
126
+ threads,
127
  })
128
  })
129
  }
src/lib.rs CHANGED
@@ -34,7 +34,7 @@ use handler::public_paths::public_path;
34
  /// use std::net::TcpListener;
35
  /// use websurfx::{config::parser::Config, run};
36
  ///
37
- /// let config = Config::parse().unwrap();
38
  /// let listener = TcpListener::bind("127.0.0.1:8080").expect("Failed to bind address");
39
  /// let server = run(listener,config).expect("Failed to start server");
40
  /// ```
@@ -49,6 +49,8 @@ pub fn run(listener: TcpListener, config: Config) -> std::io::Result<Server> {
49
 
50
  let handlebars_ref: web::Data<Handlebars> = web::Data::new(handlebars);
51
 
 
 
52
  let server = HttpServer::new(move || {
53
  App::new()
54
  .app_data(handlebars_ref.clone())
@@ -70,6 +72,7 @@ pub fn run(listener: TcpListener, config: Config) -> std::io::Result<Server> {
70
  .service(routes::settings) // settings page
71
  .default_service(web::route().to(routes::not_found)) // error page
72
  })
 
73
  // Start server on 127.0.0.1 with the user provided port number. for example 127.0.0.1:8080.
74
  .listen(listener)?
75
  .run();
 
34
  /// use std::net::TcpListener;
35
  /// use websurfx::{config::parser::Config, run};
36
  ///
37
+ /// let config = Config::parse(true).unwrap();
38
  /// let listener = TcpListener::bind("127.0.0.1:8080").expect("Failed to bind address");
39
  /// let server = run(listener,config).expect("Failed to start server");
40
  /// ```
 
49
 
50
  let handlebars_ref: web::Data<Handlebars> = web::Data::new(handlebars);
51
 
52
+ let cloned_config_threads_opt: u8 = config.threads;
53
+
54
  let server = HttpServer::new(move || {
55
  App::new()
56
  .app_data(handlebars_ref.clone())
 
72
  .service(routes::settings) // settings page
73
  .default_service(web::route().to(routes::not_found)) // error page
74
  })
75
+ .workers(cloned_config_threads_opt as usize)
76
  // Start server on 127.0.0.1 with the user provided port number. for example 127.0.0.1:8080.
77
  .listen(listener)?
78
  .run();
tests/index.rs CHANGED
@@ -8,7 +8,7 @@ fn spawn_app() -> String {
8
  // Binding to port 0 will trigger the OS to assign a port for us.
9
  let listener = TcpListener::bind("127.0.0.1:0").expect("Failed to bind random port");
10
  let port = listener.local_addr().unwrap().port();
11
- let config = Config::parse().unwrap();
12
  let server = run(listener, config).expect("Failed to bind address");
13
 
14
  tokio::spawn(server);
@@ -36,7 +36,7 @@ async fn test_index() {
36
  assert_eq!(res.status(), 200);
37
 
38
  let handlebars = handlebars();
39
- let config = Config::parse().unwrap();
40
  let template = handlebars.render("index", &config.style).unwrap();
41
  assert_eq!(res.text().await.unwrap(), template);
42
  }
 
8
  // Binding to port 0 will trigger the OS to assign a port for us.
9
  let listener = TcpListener::bind("127.0.0.1:0").expect("Failed to bind random port");
10
  let port = listener.local_addr().unwrap().port();
11
+ let config = Config::parse(true).unwrap();
12
  let server = run(listener, config).expect("Failed to bind address");
13
 
14
  tokio::spawn(server);
 
36
  assert_eq!(res.status(), 200);
37
 
38
  let handlebars = handlebars();
39
+ let config = Config::parse(false).unwrap();
40
  let template = handlebars.render("index", &config.style).unwrap();
41
  assert_eq!(res.text().await.unwrap(), template);
42
  }
websurfx/config.lua CHANGED
@@ -1,6 +1,7 @@
1
  -- ### General ###
2
  logging = true -- an option to enable or disable logs.
3
  debug = false -- an option to enable or disable debug mode.
 
4
 
5
  -- ### Server ###
6
  port = "8080" -- port on which server should be launched
 
1
  -- ### General ###
2
  logging = true -- an option to enable or disable logs.
3
  debug = false -- an option to enable or disable debug mode.
4
+ threads = 10 -- the amount of threads that the app will use to run (the value should be greater than 0).
5
 
6
  -- ### Server ###
7
  port = "8080" -- port on which server should be launched