From e2efa6ca8d35f6c037074872334e1a47fa73a1af Mon Sep 17 00:00:00 2001 From: William Henderson Date: Sat, 5 Feb 2022 16:37:47 +0000 Subject: [PATCH 1/5] Implemented configuration environment variable (closes #70) --- humphrey-server/src/config/config.rs | 51 +++++++++++++++++---------- humphrey-server/src/config/default.rs | 4 ++- humphrey-server/src/server/server.rs | 19 ++++++++-- 3 files changed, 53 insertions(+), 21 deletions(-) diff --git a/humphrey-server/src/config/config.rs b/humphrey-server/src/config/config.rs index 2fc57a4..66b0584 100644 --- a/humphrey-server/src/config/config.rs +++ b/humphrey-server/src/config/config.rs @@ -5,18 +5,20 @@ use crate::config::tree::{parse_conf, ConfigNode}; use crate::logger::LogLevel; use crate::proxy::{EqMutex, LoadBalancer}; use crate::rand::Lcg; -use crate::server::logger::Logger; use std::collections::HashMap; -use std::env::args; +use std::env::{args, var}; use std::fs::File; use std::io::Read; use std::net::IpAddr; +use std::path::Path; use std::time::Duration; /// Represents the parsed and validated configuration. #[derive(Debug, PartialEq)] pub struct Config { + /// Where the configuration was located. + pub source: ConfigSource, /// The address to host the server on pub address: String, /// The port to host the server on @@ -55,7 +57,7 @@ pub struct HostConfig { } /// Represents the type of a route. -#[derive(Debug, PartialEq)] +#[derive(Copy, Clone, Debug, PartialEq)] pub enum RouteType { /// Serve a single file. File, @@ -94,7 +96,7 @@ pub struct LoggingConfig { } /// Represents configuration for the cache. -#[derive(Clone, Debug, PartialEq, Eq, Default)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)] pub struct CacheConfig { /// The maximum size of the cache, in bytes pub size_limit: usize, @@ -136,7 +138,7 @@ pub struct PluginConfig { } /// Represents an algorithm for load balancing. -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum LoadBalancerMode { /// Evenly distributes load in a repeating pattern RoundRobin, @@ -145,7 +147,7 @@ pub enum LoadBalancerMode { } /// Represents a method of applying the blacklist. -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum BlacklistMode { /// Does not allow any access from blacklisted addresses Block, @@ -153,20 +155,34 @@ pub enum BlacklistMode { Forbidden, } +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum ConfigSource { + Argument, + EnvironmentVariable, + CurrentDirectory, + Default, +} + impl Config { /// Attempts to load the configuration. pub fn load() -> Result { - if let Ok((filename, config_string)) = load_config_file() { + let (path, source) = if let Some(arg_path) = args().nth(1) { + (arg_path, ConfigSource::Argument) + } else if Path::new("humphrey.conf").exists() { + ("humphrey.conf".into(), ConfigSource::CurrentDirectory) + } else if let Ok(env_path) = var("HUMPHREY_CONF") { + (env_path, ConfigSource::EnvironmentVariable) + } else { + ("".into(), ConfigSource::Default) + }; + + if let Ok((filename, config_string)) = load_config_file(path) { let tree = parse_conf(&config_string, &filename).map_err(|e| e.to_string())?; - let config = Self::from_tree(tree)?; + let mut config = Self::from_tree(tree)?; + config.source = source; Ok(config) } else { - let logger = Logger::default(); - logger.warn( - "Configuration file not specified or inaccessible, falling back to default configuration", - ); - Ok(Config::default()) } } @@ -323,6 +339,7 @@ impl Config { }; Ok(Config { + source: ConfigSource::Default, address, port, threads, @@ -351,17 +368,15 @@ impl Config { } /// Loads the configuration file. -fn load_config_file() -> Result<(String, String), ()> { - let path = args().nth(1).unwrap_or_else(|| "humphrey.conf".into()); - - if let Ok(mut file) = File::open(&path) { +fn load_config_file(path: impl AsRef) -> Result<(String, String), ()> { + if let Ok(mut file) = File::open(path.as_ref()) { // The file can be opened let mut string = String::new(); if file.read_to_string(&mut string).is_ok() { // The file can be read - Ok((path, string)) + Ok((path.as_ref().to_string(), string)) } else { Err(()) } diff --git a/humphrey-server/src/config/default.rs b/humphrey-server/src/config/default.rs index d0cd144..6996872 100644 --- a/humphrey-server/src/config/default.rs +++ b/humphrey-server/src/config/default.rs @@ -1,13 +1,15 @@ //! Provides default values for the configuration. use crate::config::{ - BlacklistConfig, BlacklistMode, Config, HostConfig, LoggingConfig, RouteConfig, RouteType, + BlacklistConfig, BlacklistMode, Config, ConfigSource, HostConfig, LoggingConfig, RouteConfig, + RouteType, }; use crate::server::logger::LogLevel; impl Default for Config { fn default() -> Self { Self { + source: ConfigSource::Default, address: "0.0.0.0".into(), port: 80, threads: 32, diff --git a/humphrey-server/src/server/server.rs b/humphrey-server/src/server/server.rs index f3ac8f8..3b7822f 100644 --- a/humphrey-server/src/server/server.rs +++ b/humphrey-server/src/server/server.rs @@ -15,7 +15,7 @@ use std::process::exit; use std::thread::spawn; use crate::cache::Cache; -use crate::config::{BlacklistMode, Config, HostConfig, RouteType}; +use crate::config::{BlacklistMode, Config, ConfigSource, HostConfig, RouteType}; use crate::logger::{monitor_thread, Logger}; use crate::proxy::proxy_handler; use crate::r#static::{directory_handler, file_handler, redirect_handler}; @@ -57,6 +57,7 @@ impl From for AppState { /// Main function for the static server. pub fn main(config: Config) { let connection_timeout = config.connection_timeout; + let source = config.source; let (monitor_tx, monitor_rx) = channel(); let mask = config.logging.level.to_event_mask(); @@ -99,6 +100,21 @@ pub fn main(config: Config) { let addr = format!("{}:{}", state.config.address, state.config.port); let logger = &state.logger; + match source { + ConfigSource::Argument => logger.info("Configuration loaded from argument path"), + ConfigSource::EnvironmentVariable => { + logger.info("Configuration loaded from HUMPHREY_CONF environment variable path") + } + ConfigSource::CurrentDirectory => { + logger.info("Configuration loaded from humphrey.conf in the current directory") + } + ConfigSource::Default => { + logger.warn("Configuration file not found or invalid, using defaults") + } + } + + logger.debug(&format!("Configuration: {:?}", state.config)); + logger.info("Starting server"); #[cfg(feature = "plugins")] @@ -109,7 +125,6 @@ pub fn main(config: Config) { }; logger.info(&format!("Running at {}", addr)); - logger.debug(&format!("Configuration: {:?}", state.config)); #[cfg(feature = "tls")] if state.config.tls_config.is_some() { From d67f9b34144e84e3ef03592660927ff156df0686 Mon Sep 17 00:00:00 2001 From: William Henderson Date: Sat, 5 Feb 2022 16:39:56 +0000 Subject: [PATCH 2/5] Updated Server tests for config update --- humphrey-server/src/tests/config.rs | 8 +++++--- humphrey-server/src/tests/include.rs | 6 ++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/humphrey-server/src/tests/config.rs b/humphrey-server/src/tests/config.rs index 978f0e7..c06237e 100644 --- a/humphrey-server/src/tests/config.rs +++ b/humphrey-server/src/tests/config.rs @@ -1,11 +1,10 @@ #![allow(unused_imports)] use super::tree::CONF; use humphrey_server::config::config::{ - BlacklistConfig, BlacklistMode, CacheConfig, Config, LoadBalancerMode, LoggingConfig, - RouteConfig, + BlacklistConfig, BlacklistMode, CacheConfig, Config, ConfigSource, HostConfig, + LoadBalancerMode, LoggingConfig, RouteConfig, RouteType, }; use humphrey_server::config::tree::{parse_conf, ConfigNode}; -use humphrey_server::config::{HostConfig, RouteType}; use humphrey_server::logger::LogLevel; #[cfg(feature = "plugins")] @@ -31,6 +30,7 @@ fn test_parse_config() { }; let expected_conf = Config { + source: ConfigSource::Default, address: "0.0.0.0".into(), port: 80, threads: 32, @@ -93,6 +93,7 @@ fn test_host_config() { let conf = Config::from_tree(tree).unwrap(); let expected_conf = Config { + source: ConfigSource::Default, address: "0.0.0.0".into(), port: 80, threads: 32, @@ -158,6 +159,7 @@ fn comma_separated_routes() { let conf = Config::from_tree(tree).unwrap(); let expected_conf = Config { + source: ConfigSource::Default, address: "0.0.0.0".into(), port: 80, threads: 32, diff --git a/humphrey-server/src/tests/include.rs b/humphrey-server/src/tests/include.rs index 66a6478..cc509bf 100644 --- a/humphrey-server/src/tests/include.rs +++ b/humphrey-server/src/tests/include.rs @@ -1,7 +1,7 @@ use humphrey_server::config::tree::parse_conf; use humphrey_server::config::{ - BlacklistConfig, BlacklistMode, CacheConfig, Config, HostConfig, LoadBalancerMode, - LoggingConfig, RouteConfig, RouteType, + BlacklistConfig, BlacklistMode, CacheConfig, Config, ConfigSource, HostConfig, + LoadBalancerMode, LoggingConfig, RouteConfig, RouteType, }; use humphrey_server::logger::LogLevel; use humphrey_server::proxy::{EqMutex, LoadBalancer}; @@ -20,6 +20,7 @@ fn include_route() { let config = Config::from_tree(parse_conf(string, "include_route.conf").unwrap()); let expected_conf = Ok(Config { + source: ConfigSource::Default, address: "0.0.0.0".into(), port: 80, threads: 32, @@ -68,6 +69,7 @@ fn nested_include() { let config = Config::from_tree(parse_conf(string, "nested_include_root.conf").unwrap()); let expected_conf = Ok(Config { + source: ConfigSource::Default, address: "0.0.0.0".into(), port: 80, threads: 32, From 4f2ddcc7ef979d1a961abe10f403f9b3519095f1 Mon Sep 17 00:00:00 2001 From: William Henderson Date: Sat, 5 Feb 2022 16:41:12 +0000 Subject: [PATCH 3/5] Added `ConfigSource` documentation --- humphrey-server/src/config/config.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/humphrey-server/src/config/config.rs b/humphrey-server/src/config/config.rs index 66b0584..f5b01ae 100644 --- a/humphrey-server/src/config/config.rs +++ b/humphrey-server/src/config/config.rs @@ -155,11 +155,16 @@ pub enum BlacklistMode { Forbidden, } +/// Represents the source of the configuration. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum ConfigSource { + /// The configuration was found at a path specified in a command-line argument. Argument, + /// The configuration was found at a path specified in the environment variable `HUMPHREY_CONF`. EnvironmentVariable, + /// The configuration was found in the current directory at `humphrey.conf`. CurrentDirectory, + /// The configuration was not found, so the default configuration was used. Default, } From 7f768111a6bd0dc3ff4813c3f26ec407507ff07b Mon Sep 17 00:00:00 2001 From: William Henderson Date: Sat, 5 Feb 2022 16:46:23 +0000 Subject: [PATCH 4/5] Added "Locating the Configuration" section to the docs --- docs/src/server/configuration.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/src/server/configuration.md b/docs/src/server/configuration.md index 214f64d..87236a0 100644 --- a/docs/src/server/configuration.md +++ b/docs/src/server/configuration.md @@ -1,6 +1,15 @@ # Configuration Humphrey's configuration format is similar to that of Nginx. Comments begin with a `#` and are ignored by the parser. Separate configuration files can be included with the `include` directive, like in Nginx. +## Locating the Configuration +Humphrey looks in three places for the configuration, before falling back to the default. If no configuration file can be found, the server will log a warning and start using the default configuration. + +1. The path specified as a command-line argument, for example when running `humphrey /path/to/config.conf`. +2. The `humphrey.conf` file in the current directory. +3. The path specified in the `HUMPHREY_CONF` environment variable. + +It is important to note that if a file is found at any of these locations but is invalid, the server will log an error and exit instead of continuing to the next location. + ## Example An example configuration file with all the supported directives specified is shown below. From 343cedcbc6766da94908c2f427c134eaf423e4ae Mon Sep 17 00:00:00 2001 From: William Henderson Date: Sat, 5 Feb 2022 16:50:23 +0000 Subject: [PATCH 5/5] Indicated what versions the book is for --- docs/src/introduction.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/docs/src/introduction.md b/docs/src/introduction.md index 4464efc..1e9a758 100644 --- a/docs/src/introduction.md +++ b/docs/src/introduction.md @@ -15,4 +15,14 @@ The simple authentication system is also provided by a separate crate, often ref - [A basic web application using Humphrey Core](core/getting-started.md) - [Using WebSocket with Humphrey Core](websocket/getting-started.md) - [Using PHP with Humphrey Server](server/using-php.md) -- [Creating a Humphrey Server plugin](server/creating-a-plugin.md) \ No newline at end of file +- [Creating a Humphrey Server plugin](server/creating-a-plugin.md) + +## Latest Versions +This book is up-to-date with the following crate versions. + +| Crate | Version | +| ----- | ------- | +| Humphrey Core | 0.5.0 | +| Humphrey Server | 0.5.0 | +| Humphrey WebSocket | 0.2.1 | +| Humphrey Auth | 0.1.3 | \ No newline at end of file