Skip to main content

rust_web_server/entry_point/
mod.rs

1#[cfg(test)]
2mod tests;
3pub mod command_line_args;
4pub mod config_file;
5pub mod environment_variables;
6
7
8use std::{env};
9use crate::virtual_host::VirtualHostConfig;
10
11use crate::entry_point::command_line_args::{override_environment_variables_from_command_line_args};
12use crate::entry_point::config_file::override_environment_variables_from_config;
13use crate::entry_point::environment_variables::read_system_environment_variables;
14
15#[derive(PartialEq, Eq, Clone, Debug)]
16pub struct Config {}
17
18impl Config {
19    pub const RWS_CONFIG_IP: &'static str = "RWS_CONFIG_IP";
20    /// Default is `0.0.0.0` so the server is reachable inside containers and K8s pods.
21    /// For local development you can override to `127.0.0.1` via env var or config file.
22    pub const RWS_CONFIG_IP_DEFAULT_VALUE: &'static str = "0.0.0.0";
23
24    /// Log format: `"combined"` (default, Combined Log Format) or `"json"` (structured JSON).
25    pub const RWS_CONFIG_LOG_FORMAT: &'static str = "RWS_CONFIG_LOG_FORMAT";
26    pub const RWS_CONFIG_LOG_FORMAT_DEFAULT_VALUE: &'static str = "json";
27
28    pub const RWS_CONFIG_PORT: &'static str = "RWS_CONFIG_PORT";
29    pub const RWS_CONFIG_PORT_DEFAULT_VALUE: &'static str = "7878";
30
31    pub const RWS_CONFIG_THREAD_COUNT: &'static str = "RWS_CONFIG_THREAD_COUNT";
32    pub const RWS_CONFIG_THREAD_COUNT_DEFAULT_VALUE: &'static str = "200";
33
34    pub const RWS_CONFIG_CORS_ALLOW_ALL: &'static str = "RWS_CONFIG_CORS_ALLOW_ALL";
35    pub const RWS_CONFIG_CORS_ALLOW_ALL_DEFAULT_VALUE: &'static str = "true";
36
37    pub const RWS_CONFIG_CORS_ALLOW_ORIGINS: &'static str = "RWS_CONFIG_CORS_ALLOW_ORIGINS";
38    pub const RWS_CONFIG_CORS_ALLOW_ORIGINS_DEFAULT_VALUE: &'static str = "";
39
40    pub const RWS_CONFIG_CORS_ALLOW_CREDENTIALS: &'static str = "RWS_CONFIG_CORS_ALLOW_CREDENTIALS";
41    pub const RWS_CONFIG_CORS_ALLOW_CREDENTIALS_DEFAULT_VALUE: &'static str = "";
42
43    pub const RWS_CONFIG_CORS_ALLOW_HEADERS: &'static str = "RWS_CONFIG_CORS_ALLOW_HEADERS";
44    pub const RWS_CONFIG_CORS_ALLOW_HEADERS_DEFAULT_VALUE: &'static str = "";
45
46    pub const RWS_CONFIG_CORS_ALLOW_METHODS: &'static str = "RWS_CONFIG_CORS_ALLOW_METHODS";
47    pub const RWS_CONFIG_CORS_ALLOW_METHODS_DEFAULT_VALUE: &'static str = "";
48
49    pub const RWS_CONFIG_CORS_EXPOSE_HEADERS: &'static str = "RWS_CONFIG_CORS_EXPOSE_HEADERS";
50    pub const RWS_CONFIG_CORS_EXPOSE_HEADERS_DEFAULT_VALUE: &'static str = "";
51
52    pub const RWS_CONFIG_CORS_MAX_AGE: &'static str = "RWS_CONFIG_CORS_MAX_AGE";
53    pub const RWS_CONFIG_CORS_MAX_AGE_DEFAULT_VALUE: &'static str = "86400";
54
55    pub const RWS_CONFIG_REQUEST_ALLOCATION_SIZE_IN_BYTES: &'static str = "RWS_CONFIG_REQUEST_ALLOCATION_SIZE_IN_BYTES";
56    pub const RWS_CONFIG_REQUEST_ALLOCATION_SIZE_IN_BYTES_DEFAULT_VALUE: &'static str = "10000";
57
58    pub const RWS_CONFIG_TLS_CERT_FILE: &'static str = "RWS_CONFIG_TLS_CERT_FILE";
59    pub const RWS_CONFIG_TLS_CERT_FILE_DEFAULT_VALUE: &'static str = "";
60
61    pub const RWS_CONFIG_TLS_KEY_FILE: &'static str = "RWS_CONFIG_TLS_KEY_FILE";
62    pub const RWS_CONFIG_TLS_KEY_FILE_DEFAULT_VALUE: &'static str = "";
63
64    /// Path to a PEM-encoded CA certificate used to verify client certificates (mTLS).
65    /// When set, the TLS handshake requires a valid client certificate signed by this CA.
66    /// Connections without a valid cert are rejected at the TLS layer (before any HTTP processing).
67    pub const RWS_CONFIG_TLS_CLIENT_CA_FILE: &'static str = "RWS_CONFIG_TLS_CLIENT_CA_FILE";
68    pub const RWS_CONFIG_TLS_CLIENT_CA_FILE_DEFAULT_VALUE: &'static str = "";
69
70    /// When non-empty, a plain-HTTP listener on this port redirects all requests to HTTPS.
71    /// Set to e.g. `"80"` when running on standard ports. Requires TLS to be configured.
72    pub const RWS_CONFIG_HTTP_REDIRECT_PORT: &'static str = "RWS_CONFIG_HTTP_REDIRECT_PORT";
73    pub const RWS_CONFIG_HTTP_REDIRECT_PORT_DEFAULT_VALUE: &'static str = "";
74
75    // ── ACME (Automatic Certificate Management Environment) ───────────────────
76
77    /// Comma-separated list of domain names to obtain a certificate for.
78    /// Setting this activates ACME at startup. Example: `"example.com,www.example.com"`
79    pub const RWS_CONFIG_ACME_DOMAINS: &'static str = "RWS_CONFIG_ACME_DOMAINS";
80    pub const RWS_CONFIG_ACME_DOMAINS_DEFAULT_VALUE: &'static str = "";
81
82    /// Contact email sent to the CA. Recommended but not required.
83    pub const RWS_CONFIG_ACME_EMAIL: &'static str = "RWS_CONFIG_ACME_EMAIL";
84    pub const RWS_CONFIG_ACME_EMAIL_DEFAULT_VALUE: &'static str = "";
85
86    /// Set to `"true"` to use the Let's Encrypt staging environment (for testing).
87    pub const RWS_CONFIG_ACME_STAGING: &'static str = "RWS_CONFIG_ACME_STAGING";
88    pub const RWS_CONFIG_ACME_STAGING_DEFAULT_VALUE: &'static str = "false";
89
90    /// Custom ACME directory URL. Defaults to Let's Encrypt production.
91    pub const RWS_CONFIG_ACME_DIRECTORY: &'static str = "RWS_CONFIG_ACME_DIRECTORY";
92    pub const RWS_CONFIG_ACME_DIRECTORY_DEFAULT_VALUE: &'static str = "";
93
94    /// Where to write the provisioned certificate chain (PEM). Defaults to `RWS_CONFIG_TLS_CERT_FILE`.
95    pub const RWS_CONFIG_ACME_CERT_PATH: &'static str = "RWS_CONFIG_ACME_CERT_PATH";
96    pub const RWS_CONFIG_ACME_CERT_PATH_DEFAULT_VALUE: &'static str = "";
97
98    /// Where to write the certificate's private key (PEM). Defaults to `RWS_CONFIG_TLS_KEY_FILE`.
99    pub const RWS_CONFIG_ACME_KEY_PATH: &'static str = "RWS_CONFIG_ACME_KEY_PATH";
100    pub const RWS_CONFIG_ACME_KEY_PATH_DEFAULT_VALUE: &'static str = "";
101
102    /// Port for the temporary HTTP-01 challenge server (default 80).
103    /// Must be reachable from the internet on port 80. Not used with DNS-01.
104    pub const RWS_CONFIG_ACME_CHALLENGE_PORT: &'static str = "RWS_CONFIG_ACME_CHALLENGE_PORT";
105    pub const RWS_CONFIG_ACME_CHALLENGE_PORT_DEFAULT_VALUE: &'static str = "80";
106
107    /// Renew when fewer than this many days remain on the certificate (default 30).
108    pub const RWS_CONFIG_ACME_RENEW_BEFORE_DAYS: &'static str = "RWS_CONFIG_ACME_RENEW_BEFORE_DAYS";
109    pub const RWS_CONFIG_ACME_RENEW_BEFORE_DAYS_DEFAULT_VALUE: &'static str = "30";
110
111    /// Path to persist the ACME account key between restarts (default `acme_account.key`).
112    pub const RWS_CONFIG_ACME_ACCOUNT_KEY_PATH: &'static str = "RWS_CONFIG_ACME_ACCOUNT_KEY_PATH";
113    pub const RWS_CONFIG_ACME_ACCOUNT_KEY_PATH_DEFAULT_VALUE: &'static str = "acme_account.key";
114
115
116    pub const RWS_DEFAULT_IP: &'static str = "127.0.0.1";
117    pub const RWS_DEFAULT_PORT: &'static i32 = &7878;
118    pub const RWS_DEFAULT_THREAD_COUNT: &'static i32 = &200;
119    pub const RWS_DEFAULT_REQUEST_ALLOCATION_SIZE_IN_BYTES: &'static i64 = &10000;
120
121
122}
123
124pub fn bootstrap() {
125    read_system_environment_variables();
126    override_environment_variables_from_config(None);
127    override_environment_variables_from_command_line_args();
128}
129
130pub fn set_default_values() {
131    println!("  Initializing default values");
132
133    let is_var_set = env::var(Config::RWS_CONFIG_IP).is_ok();
134    if !is_var_set {
135        env::set_var(Config::RWS_CONFIG_IP, Config::RWS_CONFIG_IP_DEFAULT_VALUE);
136        println!("    Default value  for '{}' is '{}'", Config::RWS_CONFIG_IP, Config::RWS_CONFIG_IP_DEFAULT_VALUE);
137    } else {
138        println!("    There is an environment variable  for '{}', default value won't be set", Config::RWS_CONFIG_IP);
139    }
140
141
142    let is_var_set = env::var(Config::RWS_CONFIG_PORT).is_ok();
143    if !is_var_set {
144        env::set_var(Config::RWS_CONFIG_PORT, Config::RWS_CONFIG_PORT_DEFAULT_VALUE);
145        println!("    Default value  for '{}' is '{}'", Config::RWS_CONFIG_PORT, Config::RWS_CONFIG_PORT_DEFAULT_VALUE);
146    } else {
147        println!("    There is an environment variable  for '{}', default value won't be set", Config::RWS_CONFIG_PORT);
148    }
149
150    let is_var_set = env::var(Config::RWS_CONFIG_REQUEST_ALLOCATION_SIZE_IN_BYTES).is_ok();
151    if !is_var_set {
152        env::set_var(Config::RWS_CONFIG_REQUEST_ALLOCATION_SIZE_IN_BYTES, Config::RWS_CONFIG_REQUEST_ALLOCATION_SIZE_IN_BYTES_DEFAULT_VALUE);
153        println!("    Default value  for '{}' is '{}'", Config::RWS_CONFIG_REQUEST_ALLOCATION_SIZE_IN_BYTES, Config::RWS_CONFIG_REQUEST_ALLOCATION_SIZE_IN_BYTES_DEFAULT_VALUE);
154    } else {
155        println!("    There is an environment variable  for '{}', default value won't be set", Config::RWS_CONFIG_REQUEST_ALLOCATION_SIZE_IN_BYTES);
156    }
157
158
159    let is_var_set = env::var(Config::RWS_CONFIG_THREAD_COUNT).is_ok();
160    if !is_var_set {
161        env::set_var(Config::RWS_CONFIG_THREAD_COUNT, Config::RWS_CONFIG_THREAD_COUNT_DEFAULT_VALUE);
162        println!("    Default value  for '{}' is '{}'", Config::RWS_CONFIG_THREAD_COUNT, Config::RWS_CONFIG_THREAD_COUNT_DEFAULT_VALUE);
163    } else {
164        println!("    There is an environment variable  for '{}', default value won't be set", Config::RWS_CONFIG_THREAD_COUNT);
165    }
166
167    let is_var_set = env::var(Config::RWS_CONFIG_CORS_ALLOW_ALL).is_ok();
168    if !is_var_set {
169        env::set_var(Config::RWS_CONFIG_CORS_ALLOW_ALL, Config::RWS_CONFIG_CORS_ALLOW_ALL_DEFAULT_VALUE);
170        println!("    Default value  for '{}' is '{}'", Config::RWS_CONFIG_CORS_ALLOW_ALL, Config::RWS_CONFIG_CORS_ALLOW_ALL_DEFAULT_VALUE);
171    } else {
172        println!("    There is an environment variable  for '{}', default value won't be set", Config::RWS_CONFIG_CORS_ALLOW_ALL);
173    }
174
175
176    let is_var_set = env::var(Config::RWS_CONFIG_CORS_ALLOW_ORIGINS).is_ok();
177    if !is_var_set {
178        env::set_var(Config::RWS_CONFIG_CORS_ALLOW_ORIGINS, Config::RWS_CONFIG_CORS_ALLOW_ORIGINS_DEFAULT_VALUE);
179        println!("    Default value  for '{}' is '{}'", Config::RWS_CONFIG_CORS_ALLOW_ORIGINS, Config::RWS_CONFIG_CORS_ALLOW_ORIGINS_DEFAULT_VALUE);
180    } else {
181        println!("    There is an environment variable  for '{}', default value won't be set", Config::RWS_CONFIG_CORS_ALLOW_ORIGINS);
182    }
183
184    let is_var_set = env::var(Config::RWS_CONFIG_CORS_ALLOW_CREDENTIALS).is_ok();
185    if !is_var_set {
186        env::set_var(Config::RWS_CONFIG_CORS_ALLOW_CREDENTIALS, Config::RWS_CONFIG_CORS_ALLOW_CREDENTIALS_DEFAULT_VALUE);
187        println!("    Default value  for '{}' is '{}'", Config::RWS_CONFIG_CORS_ALLOW_CREDENTIALS, Config::RWS_CONFIG_CORS_ALLOW_CREDENTIALS_DEFAULT_VALUE);
188    } else {
189        println!("    There is an environment variable  for '{}', default value won't be set", Config::RWS_CONFIG_CORS_ALLOW_CREDENTIALS);
190    }
191
192    let is_var_set = env::var(Config::RWS_CONFIG_CORS_ALLOW_HEADERS).is_ok();
193    if !is_var_set {
194        env::set_var(Config::RWS_CONFIG_CORS_ALLOW_HEADERS, Config::RWS_CONFIG_CORS_ALLOW_HEADERS_DEFAULT_VALUE);
195        println!("    Default value  for '{}' is '{}'", Config::RWS_CONFIG_CORS_ALLOW_HEADERS, Config::RWS_CONFIG_CORS_ALLOW_HEADERS_DEFAULT_VALUE);
196    } else {
197        println!("    There is an environment variable  for '{}', default value won't be set", Config::RWS_CONFIG_CORS_ALLOW_HEADERS);
198    }
199
200
201    let is_var_set = env::var(Config::RWS_CONFIG_CORS_ALLOW_METHODS).is_ok();
202    if !is_var_set {
203        env::set_var(Config::RWS_CONFIG_CORS_ALLOW_METHODS, Config::RWS_CONFIG_CORS_ALLOW_METHODS_DEFAULT_VALUE);
204        println!("    Default value  for '{}' is '{}'", Config::RWS_CONFIG_CORS_ALLOW_METHODS, Config::RWS_CONFIG_CORS_ALLOW_METHODS_DEFAULT_VALUE);
205    } else {
206        println!("    There is an environment variable  for '{}', default value won't be set", Config::RWS_CONFIG_CORS_ALLOW_METHODS);
207    }
208
209    let is_var_set = env::var(Config::RWS_CONFIG_CORS_EXPOSE_HEADERS).is_ok();
210    if !is_var_set {
211        env::set_var(Config::RWS_CONFIG_CORS_EXPOSE_HEADERS, Config::RWS_CONFIG_CORS_EXPOSE_HEADERS_DEFAULT_VALUE);
212        println!("    Default value  for '{}' is '{}'", Config::RWS_CONFIG_CORS_EXPOSE_HEADERS, Config::RWS_CONFIG_CORS_EXPOSE_HEADERS_DEFAULT_VALUE);
213    } else {
214        println!("    There is an environment variable  for '{}', default value won't be set", Config::RWS_CONFIG_CORS_EXPOSE_HEADERS);
215    }
216
217    let is_var_set = env::var(Config::RWS_CONFIG_CORS_MAX_AGE).is_ok();
218    if !is_var_set {
219        env::set_var(Config::RWS_CONFIG_CORS_MAX_AGE, Config::RWS_CONFIG_CORS_MAX_AGE_DEFAULT_VALUE);
220        println!("    Default value  for '{}' is '{}'", Config::RWS_CONFIG_CORS_MAX_AGE, Config::RWS_CONFIG_CORS_MAX_AGE_DEFAULT_VALUE);
221    } else {
222        println!("    There is an environment variable  for '{}', default value won't be set", Config::RWS_CONFIG_CORS_MAX_AGE);
223    }
224
225
226    let is_var_set = env::var(Config::RWS_CONFIG_TLS_CERT_FILE).is_ok();
227    if !is_var_set {
228        env::set_var(Config::RWS_CONFIG_TLS_CERT_FILE, Config::RWS_CONFIG_TLS_CERT_FILE_DEFAULT_VALUE);
229        println!("    Default value  for '{}' is '{}'", Config::RWS_CONFIG_TLS_CERT_FILE, Config::RWS_CONFIG_TLS_CERT_FILE_DEFAULT_VALUE);
230    } else {
231        println!("    There is an environment variable  for '{}', default value won't be set", Config::RWS_CONFIG_TLS_CERT_FILE);
232    }
233
234    let is_var_set = env::var(Config::RWS_CONFIG_TLS_KEY_FILE).is_ok();
235    if !is_var_set {
236        env::set_var(Config::RWS_CONFIG_TLS_KEY_FILE, Config::RWS_CONFIG_TLS_KEY_FILE_DEFAULT_VALUE);
237        println!("    Default value  for '{}' is '{}'", Config::RWS_CONFIG_TLS_KEY_FILE, Config::RWS_CONFIG_TLS_KEY_FILE_DEFAULT_VALUE);
238    } else {
239        println!("    There is an environment variable  for '{}', default value won't be set", Config::RWS_CONFIG_TLS_KEY_FILE);
240    }
241
242    let is_var_set = env::var(Config::RWS_CONFIG_HTTP_REDIRECT_PORT).is_ok();
243    if !is_var_set {
244        env::set_var(Config::RWS_CONFIG_HTTP_REDIRECT_PORT, Config::RWS_CONFIG_HTTP_REDIRECT_PORT_DEFAULT_VALUE);
245        println!("    Default value  for '{}' is '{}'", Config::RWS_CONFIG_HTTP_REDIRECT_PORT, Config::RWS_CONFIG_HTTP_REDIRECT_PORT_DEFAULT_VALUE);
246    } else {
247        println!("    There is an environment variable  for '{}', default value won't be set", Config::RWS_CONFIG_HTTP_REDIRECT_PORT);
248    }
249
250    let is_var_set = env::var(Config::RWS_CONFIG_LOG_FORMAT).is_ok();
251    if !is_var_set {
252        env::set_var(Config::RWS_CONFIG_LOG_FORMAT, Config::RWS_CONFIG_LOG_FORMAT_DEFAULT_VALUE);
253        println!("    Default value  for '{}' is '{}'", Config::RWS_CONFIG_LOG_FORMAT, Config::RWS_CONFIG_LOG_FORMAT_DEFAULT_VALUE);
254    } else {
255        println!("    There is an environment variable  for '{}', default value won't be set", Config::RWS_CONFIG_LOG_FORMAT);
256    }
257
258    println!("  End of initializing default values\n");
259}
260
261
262pub fn get_ip_port_thread_count() -> (String, i32, i32) {
263    let mut ip : String = Config::RWS_CONFIG_IP_DEFAULT_VALUE.to_string();
264    let mut port: i32 = *Config::RWS_DEFAULT_PORT;
265    let mut thread_count: i32 = *Config::RWS_DEFAULT_THREAD_COUNT;
266
267    let boxed_ip = env::var(Config::RWS_CONFIG_IP);
268    if boxed_ip.is_ok() {
269        ip = boxed_ip.unwrap()
270    }
271
272    let boxed_port = env::var(Config::RWS_CONFIG_PORT);
273    if boxed_port.is_ok() {
274        let _port = boxed_port.unwrap();
275        let boxed_parse = _port.parse::<i32>();
276        if boxed_parse.is_ok() {
277            port = boxed_parse.unwrap();
278        } else {
279            eprintln!("unable to parse port value, expected number, got {}, variable: {}",
280                      _port, Config::RWS_CONFIG_PORT);
281        }
282    } else {
283        eprintln!("unable to parse port value, variable: {}", Config::RWS_CONFIG_PORT);
284    }
285
286    let boxed_thread_count = env::var(Config::RWS_CONFIG_THREAD_COUNT);
287    if boxed_thread_count.is_ok() {
288        let _thread_count = boxed_thread_count.unwrap();
289        let boxed_parse = _thread_count.parse();
290        if boxed_parse.is_ok() {
291            thread_count = boxed_parse.unwrap()
292        } else {
293            eprintln!("unable to parse thread count value, expected number, got {}, variable: {}",
294                      thread_count, Config::RWS_CONFIG_THREAD_COUNT);
295        }
296
297    } else {
298        eprintln!("unable to parse thread count value, variable: {}", Config::RWS_CONFIG_THREAD_COUNT);
299    }
300
301    (ip, port, thread_count)
302}
303
304/// Read all `[[virtual_host]]` entries from config / env vars.
305///
306/// Each entry must have `RWS_CONFIG_VIRTUAL_HOST_{N}_DOMAIN` set; reading
307/// stops at the first missing index.  `cert_file` and `key_file` default to
308/// empty string if omitted.
309pub fn get_virtual_hosts() -> Vec<VirtualHostConfig> {
310    let mut hosts = Vec::new();
311    let mut i = 0usize;
312    loop {
313        match env::var(format!("RWS_CONFIG_VIRTUAL_HOST_{}_DOMAIN", i)) {
314            Err(_) => break,
315            Ok(domain) => {
316                let cert_file = env::var(format!("RWS_CONFIG_VIRTUAL_HOST_{}_CERT_FILE", i))
317                    .unwrap_or_default();
318                let key_file = env::var(format!("RWS_CONFIG_VIRTUAL_HOST_{}_KEY_FILE", i))
319                    .unwrap_or_default();
320                hosts.push(VirtualHostConfig { domain, cert_file, key_file });
321                i += 1;
322            }
323        }
324    }
325    hosts
326}
327
328pub fn get_request_allocation_size() -> i64 {
329    let mut request_allocation_size: i64 = *Config::RWS_DEFAULT_REQUEST_ALLOCATION_SIZE_IN_BYTES;
330
331    let boxed_port = env::var(Config::RWS_CONFIG_REQUEST_ALLOCATION_SIZE_IN_BYTES);
332    if boxed_port.is_ok() {
333        let _request_allocation_size = boxed_port.unwrap();
334        let boxed_parse = _request_allocation_size.parse::<i64>();
335        if boxed_parse.is_ok() {
336            request_allocation_size = boxed_parse.unwrap();
337        } else {
338            eprintln!("unable to parse port value, expected number, got {}, variable: {}",
339                      _request_allocation_size, Config::RWS_CONFIG_REQUEST_ALLOCATION_SIZE_IN_BYTES);
340        }
341    } else {
342        eprintln!("unable to parse request allocation size value, variable: {}", Config::RWS_CONFIG_REQUEST_ALLOCATION_SIZE_IN_BYTES);
343    }
344
345
346    request_allocation_size
347}
348