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 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
//! Structures to represent configuration used by `aether_lib`
//!
//! - All time values are in milliseconds unless specified otherwise
//! - `_US` is used as suffix for time values in microseconds
//!
//! ## Configuration file
//! The default configuration file is to be stored in `$HOME/.config/aether/config.yaml` and must
//! be in [YAML](https://yaml.org/) format
//!
//! Note that any missing values will be replaced with default values. It is not recommended to
//! leave any missing values in the configuration file as the values need to follow certain
//! constaints. For example, `handshake_timeout` cannot be smaller than `peer_poll_time` because in
//! such a case, the handshake would timeout before even a single poll is complete.
use log::{info, warn};
use serde::{Deserialize, Serialize};
use std::{convert::TryFrom, default::Default, fs, path::Path};
use crate::error::AetherError;
/// Structure to represent configuration options for `aether_lib`
#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Default)]
#[serde(default)]
pub struct Config {
/// Configuration for [`peer`][crate::peer] module
pub aether: AetherConfig,
/// Configuration for [`handshake`][crate::peer::handshake] module
pub handshake: HandshakeConfig,
/// Configuration for [`link`][crate::link] module
pub link: LinkConfig,
}
/// Structure to represent configuration for [`peer`][crate::peer] module
#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq)]
#[serde(default)]
pub struct AetherConfig {
/// Duration to wait for Tracker server to respond (in ms)
pub server_retry_delay: u64,
/// How often to poll server for new connections
pub server_poll_time: u64,
/// How long to wait to retry handshake after a failed attempt
/// Also used as duration to wait to receive nonce from other peer during
/// authentication
pub handshake_retry_delay: u64,
/// Poll time to check if connection has been established
pub connection_check_delay: u64,
/// Magnitude by which to randomize retry delay
pub delta_time: u64,
/// General poll time to be used to check for updates to lists shared by threads
/// (in us)
pub poll_time_us: u64,
}
/// Structure to represent configuration for [`handshake`][crate::peer::handshake] module
#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq)]
#[serde(default)]
pub struct HandshakeConfig {
/// Poll time to send sequence or sequence+acknowledgement to the other peer
/// Also, the timeout for receiving sequence or sequence+acknowledgment from the other peer (in
/// ms)
pub peer_poll_time: u64,
/// Timeout after which handshake can be declared failed if not complete (in ms)
pub handshake_timeout: u64,
}
/// Structure to represent configuration for [`link`][crate::link] module
#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq)]
#[serde(default)]
pub struct LinkConfig {
/// Window size for the link. Determines how many packets are sent in a single burst
pub window_size: u16,
/// Time to wait for acknowledgement to be received
pub ack_wait_time: u64,
/// Poll time for shared memory structures
pub poll_time_us: u64,
/// Timeout or time of inactivity after which link is declared as broken
pub timeout: u64,
/// Time to wait for acknowledgment before sending packets again
pub retry_delay: u64,
/// Time to wait before sending another acknowledgment only packet when primary queue is empty
/// i.e. no more packets to be sent
pub ack_only_time: u64,
/// Number of times a packet can be retried before link is declared as broken
pub max_retries: i16,
}
impl Config {
/// Returns configuration read from `file_path`
/// Configuration file must be in [YAML](https://yaml.org/) format
/// This may return an [`AetherError`] if the file is not present or if the file
/// is not correctly formated as yaml
///
/// # Examples
///
/// ```
/// use aether_lib::config::Config;
/// use std::path::Path;
///
/// // For a file located inside /home/user/aether_config.yaml we can construct
/// // a path
/// let path = Path::new("/home/user/aether_config.yaml");
///
/// let config = Config::from_file(&path);
/// ```
pub fn from_file(file_path: &Path) -> Result<Config, AetherError> {
match fs::read_to_string(file_path) {
Ok(data) => match Config::try_from(data) {
Ok(config) => Ok(config),
Err(err) => Err(AetherError::YamlParse(err)),
},
Err(err) => Err(AetherError::FileRead(err)),
}
}
/// Returns configuration read from the default configuration file
/// If default configuration file is not found, the default internal configuration
/// is returned
///
/// # Examples
///
/// ```
/// use aether_lib::config::Config;
/// let config = Config::get_config();
/// ```
pub fn get_config() -> Result<Config, AetherError> {
match home::home_dir() {
Some(mut path_buf) => {
path_buf.push(".config");
path_buf.push("aether");
path_buf.push("config.yaml");
let path = path_buf.as_path();
info!(
"Reading configuration from {}",
path.to_str().unwrap_or("Cannot parse path")
);
match Config::from_file(path) {
Ok(config) => Ok(config),
Err(err) => match err {
AetherError::FileRead(file_err) => {
warn!("{:?}", file_err);
Ok(Config::default())
}
_ => Err(err),
},
}
}
None => Ok(Config::default()),
}
}
}
impl TryFrom<String> for Config {
type Error = serde_yaml::Error;
fn try_from(string: String) -> Result<Self, Self::Error> {
serde_yaml::from_str(&string)
}
}
impl TryFrom<Config> for String {
type Error = serde_yaml::Error;
fn try_from(value: Config) -> Result<Self, Self::Error> {
serde_yaml::to_string(&value)
}
}
/// Default values for [`AetherConfig`]
impl Default for AetherConfig {
fn default() -> Self {
Self {
server_retry_delay: 1_000,
server_poll_time: 1_000,
handshake_retry_delay: 1_500,
connection_check_delay: 1_000,
delta_time: 1000,
poll_time_us: 100,
}
}
}
/// Default values for [`HandshakeConfig`]
impl Default for HandshakeConfig {
fn default() -> Self {
Self {
peer_poll_time: 100,
handshake_timeout: 2_500,
}
}
}
/// Default values for ['LinkConfig`]
impl Default for LinkConfig {
fn default() -> Self {
Self {
window_size: 20,
ack_wait_time: 1_000,
poll_time_us: 100,
timeout: 10_000,
retry_delay: 100,
ack_only_time: 50,
max_retries: 10,
}
}
}
#[cfg(test)]
mod tests {
use crate::config::Config;
use std::{convert::TryFrom, fs, path::Path};
#[test]
fn read_test() {
let default = Config::default();
let path = "./tmp/config.yaml";
fs::create_dir_all("./tmp").unwrap();
fs::write(path, String::try_from(default).unwrap()).unwrap();
let config = Config::from_file(Path::new(path)).unwrap();
assert_eq!(config, default);
}
}