Skip to content

Commit

Permalink
Merge pull request #72 from w-henderson/default-config-env
Browse files Browse the repository at this point in the history
Look in the `HUMPHREY_CONF` environment variable for configuration
  • Loading branch information
w-henderson authored Feb 5, 2022
2 parents d5241ce + 343cedc commit 77e0582
Show file tree
Hide file tree
Showing 7 changed files with 87 additions and 27 deletions.
12 changes: 11 additions & 1 deletion docs/src/introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
- [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 |
9 changes: 9 additions & 0 deletions docs/src/server/configuration.md
Original file line number Diff line number Diff line change
@@ -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.

Expand Down
56 changes: 38 additions & 18 deletions humphrey-server/src/config/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand All @@ -145,28 +147,47 @@ 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,
/// Returns 400 Forbidden to every request, only available in static mode
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,
}

impl Config {
/// Attempts to load the configuration.
pub fn load() -> Result<Self, String> {
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())
}
}
Expand Down Expand Up @@ -323,6 +344,7 @@ impl Config {
};

Ok(Config {
source: ConfigSource::Default,
address,
port,
threads,
Expand Down Expand Up @@ -351,17 +373,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<str>) -> 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(())
}
Expand Down
4 changes: 3 additions & 1 deletion humphrey-server/src/config/default.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand Down
19 changes: 17 additions & 2 deletions humphrey-server/src/server/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -57,6 +57,7 @@ impl From<Config> 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();
Expand Down Expand Up @@ -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")]
Expand All @@ -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() {
Expand Down
8 changes: 5 additions & 3 deletions humphrey-server/src/tests/config.rs
Original file line number Diff line number Diff line change
@@ -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")]
Expand All @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
6 changes: 4 additions & 2 deletions humphrey-server/src/tests/include.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand All @@ -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,
Expand Down Expand Up @@ -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,
Expand Down

0 comments on commit 77e0582

Please sign in to comment.