index : static-web-server.git

ascending towards madness

author Jose Quintana <1700322+joseluisq@users.noreply.github.com> 2023-11-04 3:11:07.0 +00:00:00
committer GitHub <noreply@github.com> 2023-11-04 3:11:07.0 +00:00:00
commit
e89ce29085322d3186eebb59de250ed3f40ab859 [patch]
tree
b34594706df9fd4a85ed48febd38a480fc2387f2
parent
d06ad0f6fb3251c20a35c5e61e00d11e714a6a9c
download
e89ce29085322d3186eebb59de250ed3f40ab859.tar.gz

feat: auto-config file detection at startup (#281)

The TOML config file is now detected at startup time using a new
default `./config.toml` value. It makes sure to check this file path
and load it if found. Otherwise, the behavior continues as usual.

So it's not mandatory to provide a flag or env variable explicitly if
the default file path is wanted.

Also, additional debug information and file validations are provided.

Diff

 src/bin/server.rs   |  2 +-
 src/server.rs       | 10 ++++++++--
 src/settings/cli.rs | 13 ++++++++++---
 src/settings/mod.rs | 46 ++++++++++++++++++++++++++++------------------
 src/winservice.rs   | 12 +++++-------
 5 files changed, 52 insertions(+), 31 deletions(-)

diff --git a/src/bin/server.rs b/src/bin/server.rs
index 367ad0f..b1cc092 100644
--- a/src/bin/server.rs
+++ b/src/bin/server.rs
@@ -25,7 +25,7 @@ fn main() -> Result {
        if let Some(commands) = opts.general.commands {
            match commands {
                Commands::Install {} => {
                    return winservice::install_service(opts.general.config_file);
                    return winservice::install_service(&opts.general.config_file);
                }
                Commands::Uninstall {} => {
                    return winservice::uninstall_service();
diff --git a/src/server.rs b/src/server.rs
index 8d95876..a98e97c 100644
--- a/src/server.rs
+++ b/src/server.rs
@@ -109,8 +109,14 @@ impl Server {
        server_info!("log level: {}", general.log_level);

        // Config file option
        if let Some(config_file) = general.config_file {
            server_info!("config file: {}", config_file.display());
        let config_file = general.config_file;
        if config_file.is_file() {
            server_info!("config file used: {}", config_file.display());
        } else {
            tracing::debug!(
                "config file path not found or not a regular file: {}",
                config_file.display()
            );
        }

        // Determine TCP listener either file descriptor or TCP socket
diff --git a/src/settings/cli.rs b/src/settings/cli.rs
index a7d8d8a..7bc1df9 100644
--- a/src/settings/cli.rs
+++ b/src/settings/cli.rs
@@ -11,6 +11,7 @@ use std::path::PathBuf;

#[cfg(feature = "directory-listing")]
use crate::directory_listing::DirListFmt;
use crate::Result;

/// General server configuration available in CLI and config file options.
#[derive(Parser, Debug)]
@@ -344,9 +345,15 @@ pub struct General {
    /// Defines a grace period in seconds after a `SIGTERM` signal is caught which will delay the server before to shut it down gracefully. The maximum value is 255 seconds.
    pub grace_period: u8,

    #[arg(long, short = 'w', env = "SERVER_CONFIG_FILE")]
    #[arg(
        long,
        short = 'w',
        default_value = "./config.toml",
        value_parser = value_parser_pathbuf,
        env = "SERVER_CONFIG_FILE"
    )]
    /// Server TOML configuration file path.
    pub config_file: Option<PathBuf>,
    pub config_file: PathBuf,

    #[arg(
        long,
@@ -466,7 +473,7 @@ pub enum Commands {
    Uninstall {},
}

fn value_parser_pathbuf(s: &str) -> crate::Result<PathBuf, String> {
fn value_parser_pathbuf(s: &str) -> Result<PathBuf, String> {
    Ok(PathBuf::from(s))
}

diff --git a/src/settings/mod.rs b/src/settings/mod.rs
index 2e43f08..e7f00f5 100644
--- a/src/settings/mod.rs
+++ b/src/settings/mod.rs
@@ -11,7 +11,7 @@ use globset::{Glob, GlobMatcher};
use headers::HeaderMap;
use hyper::StatusCode;
use regex::Regex;
use std::path::PathBuf;
use std::path::{Path, PathBuf};

use crate::{helpers, logger, Context, Result};

@@ -161,11 +161,14 @@ impl Settings {

        // Handle "config file options" and set them when available
        // NOTE: All config file based options shouldn't be mandatory, therefore `Some()` wrapped
        if let Some((settings, path_resolved)) = get_file_settings(opts.config_file)? {
            config_file = Some(path_resolved);
        if let Some((settings, config_file_resolved)) = read_file_settings(&opts.config_file)? {
            config_file = config_file_resolved;

            // File-based "general" options
            if let Some(general) = settings.general {
            let has_general_settings = settings.general.is_some();
            if has_general_settings {
                let general = settings.general.unwrap();

                if let Some(v) = general.host {
                    host = v
                }
@@ -310,11 +313,20 @@ impl Settings {
                }
            }

            // Logging system initialization
            // Logging system initialization in config file context
            if log_init {
                logger::init(log_level.as_str())?;
            }
            tracing::debug!("toml configuration file read successfully");

            tracing::debug!("config file read successfully");
            tracing::debug!("config file path provided: {}", opts.config_file.display());
            tracing::debug!("config file path resolved: {}", config_file.display());

            if !has_general_settings {
                server_warn!(
                    "config file empty or no `general` settings found, using default values"
                );
            }

            // File-based "advanced" options
            if let Some(advanced) = settings.advanced {
@@ -473,7 +485,7 @@ impl Settings {
                });
            }
        } else if log_init {
            // Logging system initialization
            // Logging system initialization on demand
            logger::init(log_level.as_str())?;
        }

@@ -543,19 +555,17 @@ impl Settings {
    }
}

fn get_file_settings(file_path_opt: Option<PathBuf>) -> Result<Option<(FileSettings, PathBuf)>> {
    if let Some(ref file_path) = file_path_opt {
        if file_path.is_file() {
            let file_path_resolved = file_path
                .canonicalize()
                .with_context(|| "error resolving toml config file path")?;
fn read_file_settings(config_file: &Path) -> Result<Option<(FileSettings, PathBuf)>> {
    if config_file.is_file() {
        let file_path_resolved = config_file
            .canonicalize()
            .with_context(|| "unable to resolve toml config file path")?;

            let settings = FileSettings::read(&file_path_resolved).with_context(|| {
                "can not read toml config file because has invalid or unsupported format/options"
            })?;
        let settings = FileSettings::read(&file_path_resolved).with_context(|| {
            "unable to read toml config file because has invalid format or unsupported options"
        })?;

            return Ok(Some((settings, file_path_resolved)));
        }
        return Ok(Some((settings, file_path_resolved)));
    }
    Ok(None)
}
diff --git a/src/winservice.rs b/src/winservice.rs
index b6bc5e7..64dab9d 100644
--- a/src/winservice.rs
+++ b/src/winservice.rs
@@ -9,7 +9,7 @@
use std::ffi::OsString;
use std::thread;
use std::time::Duration;
use std::{env, path::PathBuf};
use std::{env, path::Path};

use windows_service::{
    define_windows_service,
@@ -174,7 +174,7 @@ pub fn run_server_as_service() -> Result {
}

/// Install a Windows Service for SWS.
pub fn install_service(config_file: Option<PathBuf>) -> Result {
pub fn install_service(config_file: &Path) -> Result {
    let manager_access = ServiceManagerAccess::CONNECT | ServiceManagerAccess::CREATE_SERVICE;
    let service_manager = ServiceManager::local_computer(None::<&str>, manager_access)?;

@@ -185,11 +185,9 @@ pub fn install_service(config_file: Option<PathBuf>) -> Result {
    let mut service_binary_arguments = vec![OsString::from("--windows-service=true")];

    // Append a `--config-file` path to the binary arguments if present
    if let Some(f) = config_file {
        let f = helpers::adjust_canonicalization(&f);
        if !f.is_empty() {
            service_binary_arguments.push(OsString::from(["--config-file=", &f].concat()));
        }
    let f = helpers::adjust_canonicalization(config_file);
    if !f.is_empty() {
        service_binary_arguments.push(OsString::from(["--config-file=", &f].concat()));
    }

    // Run the current service as `System` type