File size: 8,312 Bytes
137c62e
 
 
5e2669b
09227d8
ca4447f
d8bd2fe
e4476aa
c796ae8
09227d8
a7a28ed
137c62e
 
049b1c1
137c62e
049b1c1
4402168
049b1c1
137c62e
8c239e2
049b1c1
 
8c239e2
5a8d61f
 
 
049b1c1
e2907ed
049b1c1
8e7dc68
049b1c1
d4df901
41ab8a2
 
049b1c1
c39d9ff
049b1c1
6d3396b
049b1c1
d8bd2fe
5d06cce
 
fb231de
70a5a24
c60fdb8
fb231de
b9d651c
c584a7d
5d06cce
b1df4f1
 
c796ae8
 
2a4dd07
 
2e64fd5
 
137c62e
 
 
 
e2907ed
137c62e
4b4dc28
 
 
2a4dd07
4b4dc28
137c62e
 
 
e2907ed
137c62e
4b4dc28
e4476aa
 
137c62e
e4476aa
 
137c62e
14d8bf9
d8bd2fe
14d8bf9
 
 
21403d0
e4476aa
 
 
d8bd2fe
e4476aa
 
 
 
 
 
 
 
 
 
d8bd2fe
14d8bf9
4eb75a8
14d8bf9
b9d651c
 
 
 
 
 
8e56ac8
b9d651c
d8bd2fe
5a8d61f
14d8bf9
851ea31
5a8d61f
 
 
 
 
 
 
 
 
 
 
14d8bf9
c796ae8
 
 
 
 
 
 
b9d651c
2e64fd5
14d8bf9
 
 
b9d651c
14d8bf9
 
 
b9d651c
8c239e2
14d8bf9
b9d651c
14d8bf9
b9d651c
 
 
41ab8a2
14d8bf9
 
 
 
 
b9d651c
14d8bf9
b9d651c
 
 
 
 
5a8d61f
 
c796ae8
137c62e
 
 
15dfda6
 
049b1c1
 
 
 
 
15dfda6
 
 
 
 
 
 
 
 
 
 
 
87e230d
15dfda6
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
//! This module provides the functionality to parse the lua config and convert the config options
//! into rust readable form.

use crate::handler::{file_path, FileType};

use crate::models::parser_models::{AggregatorConfig, RateLimiter, Style};
use log::LevelFilter;
use mlua::Lua;
use reqwest::Proxy;
use std::{collections::HashMap, fs, thread::available_parallelism};

/// A named struct which stores the parsed config file options.
pub struct Config {
    /// It stores the parsed port number option on which the server should launch.
    pub port: u16,
    /// It stores the parsed ip address option on which the server should launch
    pub binding_ip: String,
    /// It stores the theming options for the website.
    pub style: Style,
    #[cfg(feature = "redis-cache")]
    /// It stores the redis connection url address on which the redis
    /// client should connect.
    pub redis_url: String,
    #[cfg(any(feature = "redis-cache", feature = "memory-cache"))]
    /// It stores the max TTL for search results in cache.
    pub cache_expiry_time: u16,
    /// It stores the option to whether enable or disable production use.
    pub aggregator: AggregatorConfig,
    /// It stores the option to whether enable or disable logs.
    pub logging: bool,
    /// It stores the option to whether enable or disable debug mode.
    pub debug: bool,
    /// It toggles whether to use adaptive HTTP windows
    pub adaptive_window: bool,
    /// It stores all the engine names that were enabled by the user.
    pub upstream_search_engines: HashMap<String, bool>,
    /// It stores the time (secs) which controls the server request timeout.
    pub request_timeout: u8,
    /// It stores the number of threads which controls the app will use to run.
    pub threads: u8,
    /// Set the keep-alive time for client connections to the HTTP server
    pub client_connection_keep_alive: u8,
    /// It stores configuration options for the ratelimiting middleware.
    pub rate_limiter: RateLimiter,
    /// It stores the level of safe search to be used for restricting content in the
    /// search results.
    pub safe_search: u8,
    /// It stores the TCP connection keepalive duration in seconds.
    pub tcp_connection_keep_alive: u8,
    /// It stores the pool idle connection timeout in seconds.
    pub pool_idle_connection_timeout: u8,
    /// Url of the proxy to use for outgoing requests.
    pub proxy: Option<Proxy>,
    /// It stores the number of https connections to keep in the pool.
    pub number_of_https_connections: u8,
    /// It stores the operating system's TLS certificates for https requests.
    pub operating_system_tls_certificates: bool,
}

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. Pass false if the logger has not yet been initialized.
    ///
    /// # 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(logging_initialized: bool) -> Result<Self, Box<dyn std::error::Error>> {
        let lua = Lua::new();
        let globals = lua.globals();

        lua.load(&fs::read_to_string(file_path(FileType::Config)?)?)
            .exec()?;

        let parsed_threads: u8 = globals.get("threads")?;

        let debug: bool = globals.get("debug")?;
        let logging: bool = globals.get("logging")?;
        let adaptive_window: bool = globals.get("adaptive_window")?;

        if !logging_initialized {
            set_logging_level(debug, logging);
        }

        let threads: u8 = if parsed_threads == 0 {
            let total_num_of_threads: usize = available_parallelism()?.get() / 2;
            log::error!(
                "Config Error: The value of `threads` option should be a non zero positive integer"
            );
            log::error!("Falling back to using {} threads", total_num_of_threads);
            total_num_of_threads as u8
        } else {
            parsed_threads
        };

        let rate_limiter: HashMap<String, u8> = globals.get("rate_limiter")?;

        let parsed_safe_search: u8 = globals.get::<_>("safe_search")?;
        let safe_search: u8 = match parsed_safe_search {
            0..=4 => parsed_safe_search,
            _ => {
                log::error!("Config Error: The value of `safe_search` option should be a non zero positive integer from 0 to 4.");
                log::error!("Falling back to using the value `1` for the option");
                1
            }
        };

        #[cfg(any(feature = "redis-cache", feature = "memory-cache"))]
        let parsed_cet = globals.get::<_>("cache_expiry_time")?;
        #[cfg(any(feature = "redis-cache", feature = "memory-cache"))]
        let cache_expiry_time = match parsed_cet {
            0..=59 => {
                log::error!(
                    "Config Error: The value of `cache_expiry_time` must be greater than 60"
                );
                log::error!("Falling back to using the value `60` for the option");
                60
            }
            _ => parsed_cet,
        };

        let proxy_opt: Option<String> = globals.get::<_>("proxy")?;
        let proxy = proxy_opt.and_then(|proxy_str| {
            Proxy::all(proxy_str).ok().and_then(|_| {
                log::error!("Invalid proxy url, defaulting to no proxy.");
                None
            })
        });

        Ok(Config {
            operating_system_tls_certificates: globals
                .get::<_>("operating_system_tls_certificates")?,
            port: globals.get::<_>("port")?,
            binding_ip: globals.get::<_>("binding_ip")?,
            style: Style::new(
                globals.get::<_>("theme")?,
                globals.get::<_>("colorscheme")?,
                globals.get::<_>("animation")?,
            ),
            #[cfg(feature = "redis-cache")]
            redis_url: globals.get::<_>("redis_url")?,
            aggregator: AggregatorConfig {
                random_delay: globals.get::<_>("production_use")?,
            },
            logging,
            debug,
            adaptive_window,
            upstream_search_engines: globals.get::<_>("upstream_search_engines")?,
            request_timeout: globals.get::<_>("request_timeout")?,
            tcp_connection_keep_alive: globals.get::<_>("tcp_connection_keep_alive")?,
            pool_idle_connection_timeout: globals.get::<_>("pool_idle_connection_timeout")?,
            number_of_https_connections: globals.get::<_>("number_of_https_connections")?,
            threads,
            client_connection_keep_alive: globals.get::<_>("client_connection_keep_alive")?,
            rate_limiter: RateLimiter {
                number_of_requests: rate_limiter["number_of_requests"],
                time_limit: rate_limiter["time_limit"],
            },
            safe_search,
            #[cfg(any(feature = "redis-cache", feature = "memory-cache"))]
            cache_expiry_time,
            proxy,
        })
    }
}

/// a helper function that sets the proper logging level
///
/// # Arguments
///
/// * `debug` - It takes the option to whether enable or disable debug mode.
/// * `logging` - It takes the option to whether enable or disable logs.
fn set_logging_level(debug: bool, logging: bool) {
    if let Ok(pkg_env_var) = std::env::var("PKG_ENV") {
        if pkg_env_var.to_lowercase() == "dev" {
            env_logger::Builder::new()
                .filter(None, LevelFilter::Trace)
                .init();
            return;
        }
    }

    // Initializing logging middleware with level set to default or info.
    let log_level = match (debug, logging) {
        (true, true) => LevelFilter::Debug,
        (true, false) => LevelFilter::Debug,
        (false, true) => LevelFilter::Info,
        (false, false) => LevelFilter::Error,
    };

    env_logger::Builder::new().filter(None, log_level).init();
}