sozu_command_lib/
config.rs

1//! # Sōzu's configuration
2//!
3//! This module is responsible for parsing the `config.toml` provided by the flag `--config`
4//! when starting Sōzu.
5//!
6//! Here is the workflow for generating a working config:
7//!
8//! ```text
9//!     config.toml   ->   FileConfig    ->  ConfigBuilder   ->  Config
10//! ```
11//!
12//! `config.toml` is parsed to `FileConfig`, a structure that itself contains a lot of substructures
13//! whose names start with `File-` and end with `-Config`, like `FileHttpFrontendConfig` for instance.
14//!
15//! The instance of `FileConfig` is then passed to a `ConfigBuilder` that populates a final `Config`
16//! with listeners and clusters.
17//!
18//! To illustrate:
19//!
20//! ```ignore
21//! use sozu_command_lib::config::{FileConfig, ConfigBuilder};
22//!
23//! let file_config = FileConfig::load_from_path("../config.toml")
24//!     .expect("Could not load config.toml");
25//!
26//! let config = ConfigBuilder::new(file_config, "../assets/config.toml")
27//!     .into_config()
28//!     .expect("Could not build config");
29//! ```
30//!
31//! Note that the path to `config.toml` is used twice: the first time, to parse the file,
32//! the second time, to keep the path in the config for later use.
33//!
34//! However, there is a simpler way that combines all this:
35//!
36//! ```ignore
37//! use sozu_command_lib::config::Config;
38//!
39//! let config = Config::load_from_path("../assets/config.toml")
40//!     .expect("Could not build config from the path");
41//! ```
42//!
43//! ## How values are chosen
44//!
45//! Values are chosen in this order of priority:
46//!
47//! 1. values defined in a section of the TOML file, for instance, timeouts for a specific listener
48//! 2. values defined globally in the TOML file, like timeouts or buffer size
49//! 3. if a variable has not been set in the TOML file, it will be set to a default defined here
50use std::{
51    collections::{BTreeMap, HashMap, HashSet},
52    env, fmt,
53    fs::{create_dir_all, metadata, File},
54    io::{ErrorKind, Read},
55    net::SocketAddr,
56    ops::Range,
57    path::PathBuf,
58};
59
60use crate::{
61    certificate::split_certificate_chain,
62    logging::AccessLogFormat,
63    proto::command::{
64        request::RequestType, ActivateListener, AddBackend, AddCertificate, CertificateAndKey,
65        Cluster, CustomHttpAnswers, HttpListenerConfig, HttpsListenerConfig, ListenerType,
66        LoadBalancingAlgorithms, LoadBalancingParams, LoadMetric, MetricsConfiguration, PathRule,
67        ProtobufAccessLogFormat, ProxyProtocolConfig, Request, RequestHttpFrontend,
68        RequestTcpFrontend, RulePosition, ServerConfig, ServerMetricsConfig, SocketAddress,
69        TcpListenerConfig, TlsVersion, WorkerRequest,
70    },
71    ObjectKind,
72};
73
74/// provides all supported cipher suites exported by Rustls TLS
75/// provider as it support only strongly secure ones.
76///
77/// See the [documentation](https://docs.rs/rustls/latest/rustls/static.ALL_CIPHER_SUITES.html)
78pub const DEFAULT_RUSTLS_CIPHER_LIST: [&str; 9] = [
79    // TLS 1.3 cipher suites
80    "TLS13_AES_256_GCM_SHA384",
81    "TLS13_AES_128_GCM_SHA256",
82    "TLS13_CHACHA20_POLY1305_SHA256",
83    // TLS 1.2 cipher suites
84    "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
85    "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
86    "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
87    "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
88    "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
89    "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
90];
91
92pub const DEFAULT_CIPHER_SUITES: [&str; 4] = [
93    "TLS_AES_256_GCM_SHA384",
94    "TLS_AES_128_GCM_SHA256",
95    "TLS_AES_128_CCM_SHA256",
96    "TLS_CHACHA20_POLY1305_SHA256",
97];
98
99pub const DEFAULT_SIGNATURE_ALGORITHMS: [&str; 9] = [
100    "ECDSA+SHA256",
101    "ECDSA+SHA384",
102    "ECDSA+SHA512",
103    "RSA+SHA256",
104    "RSA+SHA384",
105    "RSA+SHA512",
106    "RSA-PSS+SHA256",
107    "RSA-PSS+SHA384",
108    "RSA-PSS+SHA512",
109];
110
111pub const DEFAULT_GROUPS_LIST: [&str; 4] = ["P-521", "P-384", "P-256", "x25519"];
112
113/// maximum time of inactivity for a frontend socket (60 seconds)
114pub const DEFAULT_FRONT_TIMEOUT: u32 = 60;
115
116/// maximum time of inactivity for a backend socket (30 seconds)
117pub const DEFAULT_BACK_TIMEOUT: u32 = 30;
118
119/// maximum time to connect to a backend server (3 seconds)
120pub const DEFAULT_CONNECT_TIMEOUT: u32 = 3;
121
122/// maximum time to receive a request since the connection started (10 seconds)
123pub const DEFAULT_REQUEST_TIMEOUT: u32 = 10;
124
125/// maximum time to wait for a worker to respond, until it is deemed NotAnswering (10 seconds)
126pub const DEFAULT_WORKER_TIMEOUT: u32 = 10;
127
128/// a name applied to sticky sessions ("SOZUBALANCEID")
129pub const DEFAULT_STICKY_NAME: &str = "SOZUBALANCEID";
130
131/// Interval between checking for zombie sessions, (30 minutes)
132pub const DEFAULT_ZOMBIE_CHECK_INTERVAL: u32 = 1_800;
133
134/// timeout to accept connection events in the accept queue (60 seconds)
135pub const DEFAULT_ACCEPT_QUEUE_TIMEOUT: u32 = 60;
136
137/// number of workers, i.e. Sōzu processes that scale horizontally (2)
138pub const DEFAULT_WORKER_COUNT: u16 = 2;
139
140/// wether a worker is automatically restarted when it crashes (true)
141pub const DEFAULT_WORKER_AUTOMATIC_RESTART: bool = true;
142
143/// wether to save the state automatically (false)
144pub const DEFAULT_AUTOMATIC_STATE_SAVE: bool = false;
145
146/// minimum number of buffers (1)
147pub const DEFAULT_MIN_BUFFERS: u64 = 1;
148
149/// maximum number of buffers (1 000)
150pub const DEFAULT_MAX_BUFFERS: u64 = 1_000;
151
152/// size of the buffers, in bytes (16 KB)
153pub const DEFAULT_BUFFER_SIZE: u64 = 16_393;
154
155/// maximum number of simultaneous connections (10 000)
156pub const DEFAULT_MAX_CONNECTIONS: usize = 10_000;
157
158/// size of the buffer for the channels, in bytes. Must be bigger than the size of the data received. (1 MB)
159pub const DEFAULT_COMMAND_BUFFER_SIZE: u64 = 1_000_000;
160
161/// maximum size of the buffer for the channels, in bytes. (2 MB)
162pub const DEFAULT_MAX_COMMAND_BUFFER_SIZE: u64 = 2_000_000;
163
164/// wether to avoid register cluster metrics in the local drain
165pub const DEFAULT_DISABLE_CLUSTER_METRICS: bool = false;
166
167pub const MAX_LOOP_ITERATIONS: usize = 100000;
168
169/// Number of TLS 1.3 tickets to send to a client when establishing a connection.
170/// The tickets allow the client to resume a session. This protects the client
171/// agains session tracking. Increases the number of getrandom syscalls,
172/// with little influence on performance. Defaults to 4.
173pub const DEFAULT_SEND_TLS_13_TICKETS: u64 = 4;
174
175/// for both logs and access logs
176pub const DEFAULT_LOG_TARGET: &str = "stdout";
177
178#[derive(Debug)]
179pub enum IncompatibilityKind {
180    PublicAddress,
181    ProxyProtocol,
182}
183
184#[derive(Debug)]
185pub enum MissingKind {
186    Field(String),
187    Protocol,
188    SavedState,
189}
190
191#[derive(thiserror::Error, Debug)]
192pub enum ConfigError {
193    #[error("env path not found: {0}")]
194    Env(String),
195    #[error("Could not open file {path_to_open}: {io_error}")]
196    FileOpen {
197        path_to_open: String,
198        io_error: std::io::Error,
199    },
200    #[error("Could not read file {path_to_read}: {io_error}")]
201    FileRead {
202        path_to_read: String,
203        io_error: std::io::Error,
204    },
205    #[error("the field {kind:?} of {object:?} with id or address {id} is incompatible with the rest of the options")]
206    Incompatible {
207        kind: IncompatibilityKind,
208        object: ObjectKind,
209        id: String,
210    },
211    #[error("Invalid '{0}' field for a TCP frontend")]
212    InvalidFrontendConfig(String),
213    #[error("invalid path {0:?}")]
214    InvalidPath(PathBuf),
215    #[error("listening address {0:?} is already used in the configuration")]
216    ListenerAddressAlreadyInUse(SocketAddr),
217    #[error("missing {0:?}")]
218    Missing(MissingKind),
219    #[error("could not get parent directory for file {0}")]
220    NoFileParent(String),
221    #[error("Could not get the path of the saved state")]
222    SaveStatePath(String),
223    #[error("Can not determine path to sozu socket: {0}")]
224    SocketPathError(String),
225    #[error("toml decoding error: {0}")]
226    DeserializeToml(String),
227    #[error("Can not set this frontend on a {0:?} listener")]
228    WrongFrontendProtocol(ListenerProtocol),
229    #[error("Can not build a {expected:?} listener from a {found:?} config")]
230    WrongListenerProtocol {
231        expected: ListenerProtocol,
232        found: Option<ListenerProtocol>,
233    },
234}
235
236/// An HTTP, HTTPS or TCP listener as parsed from the `Listeners` section in the toml
237#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
238#[serde(deny_unknown_fields)]
239pub struct ListenerBuilder {
240    pub address: SocketAddr,
241    pub protocol: Option<ListenerProtocol>,
242    pub public_address: Option<SocketAddr>,
243    pub answer_301: Option<String>,
244    pub answer_400: Option<String>,
245    pub answer_401: Option<String>,
246    pub answer_404: Option<String>,
247    pub answer_408: Option<String>,
248    pub answer_413: Option<String>,
249    pub answer_502: Option<String>,
250    pub answer_503: Option<String>,
251    pub answer_504: Option<String>,
252    pub answer_507: Option<String>,
253    pub tls_versions: Option<Vec<TlsVersion>>,
254    pub cipher_list: Option<Vec<String>>,
255    pub cipher_suites: Option<Vec<String>>,
256    pub expect_proxy: Option<bool>,
257    #[serde(default = "default_sticky_name")]
258    pub sticky_name: String,
259    pub certificate: Option<String>,
260    pub certificate_chain: Option<String>,
261    pub key: Option<String>,
262    /// maximum time of inactivity for a frontend socket
263    pub front_timeout: Option<u32>,
264    /// maximum time of inactivity for a backend socket
265    pub back_timeout: Option<u32>,
266    /// maximum time to connect to a backend server
267    pub connect_timeout: Option<u32>,
268    /// maximum time to receive a request since the connection started
269    pub request_timeout: Option<u32>,
270    /// A [Config] to pull defaults from
271    pub config: Option<Config>,
272    /// Number of TLS 1.3 tickets to send to a client when establishing a connection.
273    /// The ticket allow the client to resume a session. This protects the client
274    /// agains session tracking. Defaults to 4.
275    pub send_tls13_tickets: Option<u64>,
276}
277
278pub fn default_sticky_name() -> String {
279    DEFAULT_STICKY_NAME.to_string()
280}
281
282impl ListenerBuilder {
283    /// starts building an HTTP Listener with config values for timeouts,
284    /// or defaults if no config is provided
285    pub fn new_http(address: SocketAddress) -> ListenerBuilder {
286        Self::new(address, ListenerProtocol::Http)
287    }
288
289    /// starts building an HTTPS Listener with config values for timeouts,
290    /// or defaults if no config is provided
291    pub fn new_tcp(address: SocketAddress) -> ListenerBuilder {
292        Self::new(address, ListenerProtocol::Tcp)
293    }
294
295    /// starts building a TCP Listener with config values for timeouts,
296    /// or defaults if no config is provided
297    pub fn new_https(address: SocketAddress) -> ListenerBuilder {
298        Self::new(address, ListenerProtocol::Https)
299    }
300
301    /// starts building a Listener
302    fn new(address: SocketAddress, protocol: ListenerProtocol) -> ListenerBuilder {
303        ListenerBuilder {
304            address: address.into(),
305            answer_301: None,
306            answer_401: None,
307            answer_400: None,
308            answer_404: None,
309            answer_408: None,
310            answer_413: None,
311            answer_502: None,
312            answer_503: None,
313            answer_504: None,
314            answer_507: None,
315            back_timeout: None,
316            certificate_chain: None,
317            certificate: None,
318            cipher_list: None,
319            cipher_suites: None,
320            config: None,
321            connect_timeout: None,
322            expect_proxy: None,
323            front_timeout: None,
324            key: None,
325            protocol: Some(protocol),
326            public_address: None,
327            request_timeout: None,
328            send_tls13_tickets: None,
329            sticky_name: DEFAULT_STICKY_NAME.to_string(),
330            tls_versions: None,
331        }
332    }
333
334    pub fn with_public_address(&mut self, public_address: Option<SocketAddr>) -> &mut Self {
335        if let Some(address) = public_address {
336            self.public_address = Some(address);
337        }
338        self
339    }
340
341    pub fn with_answer_404_path<S>(&mut self, answer_404_path: Option<S>) -> &mut Self
342    where
343        S: ToString,
344    {
345        if let Some(path) = answer_404_path {
346            self.answer_404 = Some(path.to_string());
347        }
348        self
349    }
350
351    pub fn with_answer_503_path<S>(&mut self, answer_503_path: Option<S>) -> &mut Self
352    where
353        S: ToString,
354    {
355        if let Some(path) = answer_503_path {
356            self.answer_503 = Some(path.to_string());
357        }
358        self
359    }
360
361    pub fn with_tls_versions(&mut self, tls_versions: Vec<TlsVersion>) -> &mut Self {
362        self.tls_versions = Some(tls_versions);
363        self
364    }
365
366    pub fn with_cipher_list(&mut self, cipher_list: Option<Vec<String>>) -> &mut Self {
367        self.cipher_list = cipher_list;
368        self
369    }
370
371    pub fn with_cipher_suites(&mut self, cipher_suites: Option<Vec<String>>) -> &mut Self {
372        self.cipher_suites = cipher_suites;
373        self
374    }
375
376    pub fn with_expect_proxy(&mut self, expect_proxy: bool) -> &mut Self {
377        self.expect_proxy = Some(expect_proxy);
378        self
379    }
380
381    pub fn with_sticky_name<S>(&mut self, sticky_name: Option<S>) -> &mut Self
382    where
383        S: ToString,
384    {
385        if let Some(name) = sticky_name {
386            self.sticky_name = name.to_string();
387        }
388        self
389    }
390
391    pub fn with_certificate<S>(&mut self, certificate: S) -> &mut Self
392    where
393        S: ToString,
394    {
395        self.certificate = Some(certificate.to_string());
396        self
397    }
398
399    pub fn with_certificate_chain(&mut self, certificate_chain: String) -> &mut Self {
400        self.certificate = Some(certificate_chain);
401        self
402    }
403
404    pub fn with_key<S>(&mut self, key: String) -> &mut Self
405    where
406        S: ToString,
407    {
408        self.key = Some(key);
409        self
410    }
411
412    pub fn with_front_timeout(&mut self, front_timeout: Option<u32>) -> &mut Self {
413        self.front_timeout = front_timeout;
414        self
415    }
416
417    pub fn with_back_timeout(&mut self, back_timeout: Option<u32>) -> &mut Self {
418        self.back_timeout = back_timeout;
419        self
420    }
421
422    pub fn with_connect_timeout(&mut self, connect_timeout: Option<u32>) -> &mut Self {
423        self.connect_timeout = connect_timeout;
424        self
425    }
426
427    pub fn with_request_timeout(&mut self, request_timeout: Option<u32>) -> &mut Self {
428        self.request_timeout = request_timeout;
429        self
430    }
431
432    /// Get the custom HTTP answers from the file system using the provided paths
433    fn get_http_answers(&self) -> Result<Option<CustomHttpAnswers>, ConfigError> {
434        let http_answers = CustomHttpAnswers {
435            answer_301: read_http_answer_file(&self.answer_301)?,
436            answer_400: read_http_answer_file(&self.answer_400)?,
437            answer_401: read_http_answer_file(&self.answer_401)?,
438            answer_404: read_http_answer_file(&self.answer_404)?,
439            answer_408: read_http_answer_file(&self.answer_408)?,
440            answer_413: read_http_answer_file(&self.answer_413)?,
441            answer_502: read_http_answer_file(&self.answer_502)?,
442            answer_503: read_http_answer_file(&self.answer_503)?,
443            answer_504: read_http_answer_file(&self.answer_504)?,
444            answer_507: read_http_answer_file(&self.answer_507)?,
445        };
446        Ok(Some(http_answers))
447    }
448
449    /// Assign the timeouts of the config to this listener, only if timeouts did not exist
450    fn assign_config_timeouts(&mut self, config: &Config) {
451        self.front_timeout = Some(self.front_timeout.unwrap_or(config.front_timeout));
452        self.back_timeout = Some(self.back_timeout.unwrap_or(config.back_timeout));
453        self.connect_timeout = Some(self.connect_timeout.unwrap_or(config.connect_timeout));
454        self.request_timeout = Some(self.request_timeout.unwrap_or(config.request_timeout));
455    }
456
457    /// build an HTTP listener with config timeouts, using defaults if no config is provided
458    pub fn to_http(&mut self, config: Option<&Config>) -> Result<HttpListenerConfig, ConfigError> {
459        if self.protocol != Some(ListenerProtocol::Http) {
460            return Err(ConfigError::WrongListenerProtocol {
461                expected: ListenerProtocol::Http,
462                found: self.protocol.to_owned(),
463            });
464        }
465
466        if let Some(config) = config {
467            self.assign_config_timeouts(config);
468        }
469
470        let http_answers = self.get_http_answers()?;
471
472        let configuration = HttpListenerConfig {
473            address: self.address.into(),
474            public_address: self.public_address.map(|a| a.into()),
475            expect_proxy: self.expect_proxy.unwrap_or(false),
476            sticky_name: self.sticky_name.clone(),
477            front_timeout: self.front_timeout.unwrap_or(DEFAULT_FRONT_TIMEOUT),
478            back_timeout: self.back_timeout.unwrap_or(DEFAULT_BACK_TIMEOUT),
479            connect_timeout: self.connect_timeout.unwrap_or(DEFAULT_CONNECT_TIMEOUT),
480            request_timeout: self.request_timeout.unwrap_or(DEFAULT_REQUEST_TIMEOUT),
481            http_answers,
482            ..Default::default()
483        };
484
485        Ok(configuration)
486    }
487
488    /// build an HTTPS listener using defaults if no config or values were provided upstream
489    pub fn to_tls(&mut self, config: Option<&Config>) -> Result<HttpsListenerConfig, ConfigError> {
490        if self.protocol != Some(ListenerProtocol::Https) {
491            return Err(ConfigError::WrongListenerProtocol {
492                expected: ListenerProtocol::Https,
493                found: self.protocol.to_owned(),
494            });
495        }
496
497        let default_cipher_list = DEFAULT_RUSTLS_CIPHER_LIST
498            .into_iter()
499            .map(String::from)
500            .collect();
501
502        let cipher_list = self.cipher_list.clone().unwrap_or(default_cipher_list);
503
504        let default_cipher_suites = DEFAULT_CIPHER_SUITES
505            .into_iter()
506            .map(String::from)
507            .collect();
508
509        let cipher_suites = self.cipher_suites.clone().unwrap_or(default_cipher_suites);
510
511        let signature_algorithms: Vec<String> = DEFAULT_SIGNATURE_ALGORITHMS
512            .into_iter()
513            .map(String::from)
514            .collect();
515
516        let groups_list: Vec<String> = DEFAULT_GROUPS_LIST.into_iter().map(String::from).collect();
517
518        let versions = match self.tls_versions {
519            None => vec![TlsVersion::TlsV12 as i32, TlsVersion::TlsV13 as i32],
520            Some(ref v) => v.iter().map(|v| *v as i32).collect(),
521        };
522
523        let key = self.key.as_ref().and_then(|path| {
524            Config::load_file(path)
525                .map_err(|e| {
526                    error!("cannot load key at path '{}': {:?}", path, e);
527                    e
528                })
529                .ok()
530        });
531        let certificate = self.certificate.as_ref().and_then(|path| {
532            Config::load_file(path)
533                .map_err(|e| {
534                    error!("cannot load certificate at path '{}': {:?}", path, e);
535                    e
536                })
537                .ok()
538        });
539        let certificate_chain = self
540            .certificate_chain
541            .as_ref()
542            .and_then(|path| {
543                Config::load_file(path)
544                    .map_err(|e| {
545                        error!("cannot load certificate chain at path '{}': {:?}", path, e);
546                        e
547                    })
548                    .ok()
549            })
550            .map(split_certificate_chain)
551            .unwrap_or_default();
552
553        let http_answers = self.get_http_answers()?;
554
555        if let Some(config) = config {
556            self.assign_config_timeouts(config);
557        }
558
559        let https_listener_config = HttpsListenerConfig {
560            address: self.address.into(),
561            sticky_name: self.sticky_name.clone(),
562            public_address: self.public_address.map(|a| a.into()),
563            cipher_list,
564            versions,
565            expect_proxy: self.expect_proxy.unwrap_or(false),
566            key,
567            certificate,
568            certificate_chain,
569            front_timeout: self.front_timeout.unwrap_or(DEFAULT_FRONT_TIMEOUT),
570            back_timeout: self.back_timeout.unwrap_or(DEFAULT_BACK_TIMEOUT),
571            connect_timeout: self.connect_timeout.unwrap_or(DEFAULT_CONNECT_TIMEOUT),
572            request_timeout: self.request_timeout.unwrap_or(DEFAULT_REQUEST_TIMEOUT),
573            cipher_suites,
574            signature_algorithms,
575            groups_list,
576            active: false,
577            send_tls13_tickets: self
578                .send_tls13_tickets
579                .unwrap_or(DEFAULT_SEND_TLS_13_TICKETS),
580            http_answers,
581        };
582
583        Ok(https_listener_config)
584    }
585
586    /// build an HTTPS listener using defaults if no config or values were provided upstream
587    pub fn to_tcp(&mut self, config: Option<&Config>) -> Result<TcpListenerConfig, ConfigError> {
588        if self.protocol != Some(ListenerProtocol::Tcp) {
589            return Err(ConfigError::WrongListenerProtocol {
590                expected: ListenerProtocol::Tcp,
591                found: self.protocol.to_owned(),
592            });
593        }
594
595        if let Some(config) = config {
596            self.assign_config_timeouts(config);
597        }
598
599        Ok(TcpListenerConfig {
600            address: self.address.into(),
601            public_address: self.public_address.map(|a| a.into()),
602            expect_proxy: self.expect_proxy.unwrap_or(false),
603            front_timeout: self.front_timeout.unwrap_or(DEFAULT_FRONT_TIMEOUT),
604            back_timeout: self.back_timeout.unwrap_or(DEFAULT_BACK_TIMEOUT),
605            connect_timeout: self.connect_timeout.unwrap_or(DEFAULT_CONNECT_TIMEOUT),
606            active: false,
607        })
608    }
609}
610
611/// read a custom HTTP answer from a file
612fn read_http_answer_file(path: &Option<String>) -> Result<Option<String>, ConfigError> {
613    match path {
614        Some(path) => {
615            let mut content = String::new();
616            let mut file = File::open(path).map_err(|io_error| ConfigError::FileOpen {
617                path_to_open: path.to_owned(),
618                io_error,
619            })?;
620
621            file.read_to_string(&mut content)
622                .map_err(|io_error| ConfigError::FileRead {
623                    path_to_read: path.to_owned(),
624                    io_error,
625                })?;
626
627            Ok(Some(content))
628        }
629        None => Ok(None),
630    }
631}
632
633#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
634#[serde(deny_unknown_fields)]
635pub struct MetricsConfig {
636    pub address: SocketAddr,
637    #[serde(default)]
638    pub tagged_metrics: bool,
639    #[serde(default)]
640    pub prefix: Option<String>,
641}
642
643#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
644#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
645#[serde(deny_unknown_fields)]
646pub enum PathRuleType {
647    Prefix,
648    Regex,
649    Equals,
650}
651
652#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
653#[serde(deny_unknown_fields)]
654pub struct FileClusterFrontendConfig {
655    pub address: SocketAddr,
656    pub hostname: Option<String>,
657    /// creates a path routing rule where the request URL path has to match this
658    pub path: Option<String>,
659    /// declares whether the path rule is Prefix (default), Regex, or Equals
660    pub path_type: Option<PathRuleType>,
661    pub method: Option<String>,
662    pub certificate: Option<String>,
663    pub key: Option<String>,
664    pub certificate_chain: Option<String>,
665    #[serde(default)]
666    pub tls_versions: Vec<TlsVersion>,
667    #[serde(default)]
668    pub position: RulePosition,
669    pub tags: Option<BTreeMap<String, String>>,
670}
671
672impl FileClusterFrontendConfig {
673    pub fn to_tcp_front(&self) -> Result<TcpFrontendConfig, ConfigError> {
674        if self.hostname.is_some() {
675            return Err(ConfigError::InvalidFrontendConfig("hostname".to_string()));
676        }
677        if self.path.is_some() {
678            return Err(ConfigError::InvalidFrontendConfig(
679                "path_prefix".to_string(),
680            ));
681        }
682        if self.certificate.is_some() {
683            return Err(ConfigError::InvalidFrontendConfig(
684                "certificate".to_string(),
685            ));
686        }
687        if self.hostname.is_some() {
688            return Err(ConfigError::InvalidFrontendConfig("hostname".to_string()));
689        }
690        if self.certificate_chain.is_some() {
691            return Err(ConfigError::InvalidFrontendConfig(
692                "certificate_chain".to_string(),
693            ));
694        }
695
696        Ok(TcpFrontendConfig {
697            address: self.address,
698            tags: self.tags.clone(),
699        })
700    }
701
702    pub fn to_http_front(&self, _cluster_id: &str) -> Result<HttpFrontendConfig, ConfigError> {
703        let hostname = match &self.hostname {
704            Some(hostname) => hostname.to_owned(),
705            None => {
706                return Err(ConfigError::Missing(MissingKind::Field(
707                    "hostname".to_string(),
708                )))
709            }
710        };
711
712        let key_opt = match self.key.as_ref() {
713            None => None,
714            Some(path) => {
715                let key = Config::load_file(path)?;
716                Some(key)
717            }
718        };
719
720        let certificate_opt = match self.certificate.as_ref() {
721            None => None,
722            Some(path) => {
723                let certificate = Config::load_file(path)?;
724                Some(certificate)
725            }
726        };
727
728        let certificate_chain = match self.certificate_chain.as_ref() {
729            None => None,
730            Some(path) => {
731                let certificate_chain = Config::load_file(path)?;
732                Some(split_certificate_chain(certificate_chain))
733            }
734        };
735
736        let path = match (self.path.as_ref(), self.path_type.as_ref()) {
737            (None, _) => PathRule::prefix("".to_string()),
738            (Some(s), Some(PathRuleType::Prefix)) => PathRule::prefix(s.to_string()),
739            (Some(s), Some(PathRuleType::Regex)) => PathRule::regex(s.to_string()),
740            (Some(s), Some(PathRuleType::Equals)) => PathRule::equals(s.to_string()),
741            (Some(s), None) => PathRule::prefix(s.clone()),
742        };
743
744        Ok(HttpFrontendConfig {
745            address: self.address,
746            hostname,
747            certificate: certificate_opt,
748            key: key_opt,
749            certificate_chain,
750            tls_versions: self.tls_versions.clone(),
751            position: self.position,
752            path,
753            method: self.method.clone(),
754            tags: self.tags.clone(),
755        })
756    }
757}
758
759#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
760#[serde(deny_unknown_fields, rename_all = "lowercase")]
761pub enum ListenerProtocol {
762    Http,
763    Https,
764    Tcp,
765}
766
767#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
768#[serde(deny_unknown_fields, rename_all = "lowercase")]
769pub enum FileClusterProtocolConfig {
770    Http,
771    Tcp,
772}
773
774#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
775#[serde(deny_unknown_fields)]
776pub struct FileClusterConfig {
777    pub frontends: Vec<FileClusterFrontendConfig>,
778    pub backends: Vec<BackendConfig>,
779    pub protocol: FileClusterProtocolConfig,
780    pub sticky_session: Option<bool>,
781    pub https_redirect: Option<bool>,
782    #[serde(default)]
783    pub send_proxy: Option<bool>,
784    #[serde(default)]
785    pub load_balancing: LoadBalancingAlgorithms,
786    pub answer_503: Option<String>,
787    #[serde(default)]
788    pub load_metric: Option<LoadMetric>,
789}
790
791#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
792#[serde(deny_unknown_fields)]
793pub struct BackendConfig {
794    pub address: SocketAddr,
795    pub weight: Option<u8>,
796    pub sticky_id: Option<String>,
797    pub backup: Option<bool>,
798    pub backend_id: Option<String>,
799}
800
801impl FileClusterConfig {
802    pub fn to_cluster_config(
803        self,
804        cluster_id: &str,
805        expect_proxy: &HashSet<SocketAddr>,
806    ) -> Result<ClusterConfig, ConfigError> {
807        match self.protocol {
808            FileClusterProtocolConfig::Tcp => {
809                let mut has_expect_proxy = None;
810                let mut frontends = Vec::new();
811                for f in self.frontends {
812                    if expect_proxy.contains(&f.address) {
813                        match has_expect_proxy {
814                            Some(true) => {}
815                            Some(false) => {
816                                return Err(ConfigError::Incompatible {
817                                    object: ObjectKind::Cluster,
818                                    id: cluster_id.to_owned(),
819                                    kind: IncompatibilityKind::ProxyProtocol,
820                                })
821                            }
822                            None => has_expect_proxy = Some(true),
823                        }
824                    } else {
825                        match has_expect_proxy {
826                            Some(false) => {}
827                            Some(true) => {
828                                return Err(ConfigError::Incompatible {
829                                    object: ObjectKind::Cluster,
830                                    id: cluster_id.to_owned(),
831                                    kind: IncompatibilityKind::ProxyProtocol,
832                                })
833                            }
834                            None => has_expect_proxy = Some(false),
835                        }
836                    }
837                    let tcp_frontend = f.to_tcp_front()?;
838                    frontends.push(tcp_frontend);
839                }
840
841                let send_proxy = self.send_proxy.unwrap_or(false);
842                let expect_proxy = has_expect_proxy.unwrap_or(false);
843                let proxy_protocol = match (send_proxy, expect_proxy) {
844                    (true, true) => Some(ProxyProtocolConfig::RelayHeader),
845                    (true, false) => Some(ProxyProtocolConfig::SendHeader),
846                    (false, true) => Some(ProxyProtocolConfig::ExpectHeader),
847                    _ => None,
848                };
849
850                Ok(ClusterConfig::Tcp(TcpClusterConfig {
851                    cluster_id: cluster_id.to_string(),
852                    frontends,
853                    backends: self.backends,
854                    proxy_protocol,
855                    load_balancing: self.load_balancing,
856                    load_metric: self.load_metric,
857                }))
858            }
859            FileClusterProtocolConfig::Http => {
860                let mut frontends = Vec::new();
861                for frontend in self.frontends {
862                    let http_frontend = frontend.to_http_front(cluster_id)?;
863                    frontends.push(http_frontend);
864                }
865
866                let answer_503 = self.answer_503.as_ref().and_then(|path| {
867                    Config::load_file(path)
868                        .map_err(|e| {
869                            error!("cannot load 503 error page at path '{}': {:?}", path, e);
870                            e
871                        })
872                        .ok()
873                });
874
875                Ok(ClusterConfig::Http(HttpClusterConfig {
876                    cluster_id: cluster_id.to_string(),
877                    frontends,
878                    backends: self.backends,
879                    sticky_session: self.sticky_session.unwrap_or(false),
880                    https_redirect: self.https_redirect.unwrap_or(false),
881                    load_balancing: self.load_balancing,
882                    load_metric: self.load_metric,
883                    answer_503,
884                }))
885            }
886        }
887    }
888}
889
890#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
891#[serde(deny_unknown_fields)]
892pub struct HttpFrontendConfig {
893    pub address: SocketAddr,
894    pub hostname: String,
895    pub path: PathRule,
896    pub method: Option<String>,
897    pub certificate: Option<String>,
898    pub key: Option<String>,
899    pub certificate_chain: Option<Vec<String>>,
900    #[serde(default)]
901    pub tls_versions: Vec<TlsVersion>,
902    #[serde(default)]
903    pub position: RulePosition,
904    pub tags: Option<BTreeMap<String, String>>,
905}
906
907impl HttpFrontendConfig {
908    pub fn generate_requests(&self, cluster_id: &str) -> Vec<Request> {
909        let mut v = Vec::new();
910
911        let tags = self.tags.clone().unwrap_or_default();
912
913        if self.key.is_some() && self.certificate.is_some() {
914            v.push(
915                RequestType::AddCertificate(AddCertificate {
916                    address: self.address.into(),
917                    certificate: CertificateAndKey {
918                        key: self.key.clone().unwrap(),
919                        certificate: self.certificate.clone().unwrap(),
920                        certificate_chain: self.certificate_chain.clone().unwrap_or_default(),
921                        versions: self.tls_versions.iter().map(|v| *v as i32).collect(),
922                        // This field is used to override the certificate subject and san, we should not set it when
923                        // loading the configuration, as we may provide a wildcard certificate for a specific domain.
924                        // As a result, we will reject legit traffic for others domains as the certificate resolver will
925                        // not load twice the same certificate and then do not register the certificate for others domains.
926                        names: vec![],
927                    },
928                    expired_at: None,
929                })
930                .into(),
931            );
932
933            v.push(
934                RequestType::AddHttpsFrontend(RequestHttpFrontend {
935                    cluster_id: Some(cluster_id.to_string()),
936                    address: self.address.into(),
937                    hostname: self.hostname.clone(),
938                    path: self.path.clone(),
939                    method: self.method.clone(),
940                    position: self.position.into(),
941                    tags,
942                })
943                .into(),
944            );
945        } else {
946            //create the front both for HTTP and HTTPS if possible
947            v.push(
948                RequestType::AddHttpFrontend(RequestHttpFrontend {
949                    cluster_id: Some(cluster_id.to_string()),
950                    address: self.address.into(),
951                    hostname: self.hostname.clone(),
952                    path: self.path.clone(),
953                    method: self.method.clone(),
954                    position: self.position.into(),
955                    tags,
956                })
957                .into(),
958            );
959        }
960
961        v
962    }
963}
964
965#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
966#[serde(deny_unknown_fields)]
967pub struct HttpClusterConfig {
968    pub cluster_id: String,
969    pub frontends: Vec<HttpFrontendConfig>,
970    pub backends: Vec<BackendConfig>,
971    pub sticky_session: bool,
972    pub https_redirect: bool,
973    pub load_balancing: LoadBalancingAlgorithms,
974    pub load_metric: Option<LoadMetric>,
975    pub answer_503: Option<String>,
976}
977
978impl HttpClusterConfig {
979    pub fn generate_requests(&self) -> Result<Vec<Request>, ConfigError> {
980        let mut v = vec![RequestType::AddCluster(Cluster {
981            cluster_id: self.cluster_id.clone(),
982            sticky_session: self.sticky_session,
983            https_redirect: self.https_redirect,
984            proxy_protocol: None,
985            load_balancing: self.load_balancing as i32,
986            answer_503: self.answer_503.clone(),
987            load_metric: self.load_metric.map(|s| s as i32),
988        })
989        .into()];
990
991        for frontend in &self.frontends {
992            let mut orders = frontend.generate_requests(&self.cluster_id);
993            v.append(&mut orders);
994        }
995
996        for (backend_count, backend) in self.backends.iter().enumerate() {
997            let load_balancing_parameters = Some(LoadBalancingParams {
998                weight: backend.weight.unwrap_or(100) as i32,
999            });
1000
1001            v.push(
1002                RequestType::AddBackend(AddBackend {
1003                    cluster_id: self.cluster_id.clone(),
1004                    backend_id: backend.backend_id.clone().unwrap_or_else(|| {
1005                        format!("{}-{}-{}", self.cluster_id, backend_count, backend.address)
1006                    }),
1007                    address: backend.address.into(),
1008                    load_balancing_parameters,
1009                    sticky_id: backend.sticky_id.clone(),
1010                    backup: backend.backup,
1011                })
1012                .into(),
1013            );
1014        }
1015
1016        Ok(v)
1017    }
1018}
1019
1020#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
1021pub struct TcpFrontendConfig {
1022    pub address: SocketAddr,
1023    pub tags: Option<BTreeMap<String, String>>,
1024}
1025
1026#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
1027pub struct TcpClusterConfig {
1028    pub cluster_id: String,
1029    pub frontends: Vec<TcpFrontendConfig>,
1030    pub backends: Vec<BackendConfig>,
1031    #[serde(default)]
1032    pub proxy_protocol: Option<ProxyProtocolConfig>,
1033    pub load_balancing: LoadBalancingAlgorithms,
1034    pub load_metric: Option<LoadMetric>,
1035}
1036
1037impl TcpClusterConfig {
1038    pub fn generate_requests(&self) -> Result<Vec<Request>, ConfigError> {
1039        let mut v = vec![RequestType::AddCluster(Cluster {
1040            cluster_id: self.cluster_id.clone(),
1041            sticky_session: false,
1042            https_redirect: false,
1043            proxy_protocol: self.proxy_protocol.map(|s| s as i32),
1044            load_balancing: self.load_balancing as i32,
1045            load_metric: self.load_metric.map(|s| s as i32),
1046            answer_503: None,
1047        })
1048        .into()];
1049
1050        for frontend in &self.frontends {
1051            v.push(
1052                RequestType::AddTcpFrontend(RequestTcpFrontend {
1053                    cluster_id: self.cluster_id.clone(),
1054                    address: frontend.address.into(),
1055                    tags: frontend.tags.clone().unwrap_or(BTreeMap::new()),
1056                })
1057                .into(),
1058            );
1059        }
1060
1061        for (backend_count, backend) in self.backends.iter().enumerate() {
1062            let load_balancing_parameters = Some(LoadBalancingParams {
1063                weight: backend.weight.unwrap_or(100) as i32,
1064            });
1065
1066            v.push(
1067                RequestType::AddBackend(AddBackend {
1068                    cluster_id: self.cluster_id.clone(),
1069                    backend_id: backend.backend_id.clone().unwrap_or_else(|| {
1070                        format!("{}-{}-{}", self.cluster_id, backend_count, backend.address)
1071                    }),
1072                    address: backend.address.into(),
1073                    load_balancing_parameters,
1074                    sticky_id: backend.sticky_id.clone(),
1075                    backup: backend.backup,
1076                })
1077                .into(),
1078            );
1079        }
1080
1081        Ok(v)
1082    }
1083}
1084
1085#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
1086pub enum ClusterConfig {
1087    Http(HttpClusterConfig),
1088    Tcp(TcpClusterConfig),
1089}
1090
1091impl ClusterConfig {
1092    pub fn generate_requests(&self) -> Result<Vec<Request>, ConfigError> {
1093        match *self {
1094            ClusterConfig::Http(ref http) => http.generate_requests(),
1095            ClusterConfig::Tcp(ref tcp) => tcp.generate_requests(),
1096        }
1097    }
1098}
1099
1100/// Parsed from the TOML config provided by the user.
1101#[derive(Debug, Clone, PartialEq, Eq, Serialize, Default, Deserialize)]
1102pub struct FileConfig {
1103    pub command_socket: Option<String>,
1104    pub command_buffer_size: Option<u64>,
1105    pub max_command_buffer_size: Option<u64>,
1106    pub max_connections: Option<usize>,
1107    pub min_buffers: Option<u64>,
1108    pub max_buffers: Option<u64>,
1109    pub buffer_size: Option<u64>,
1110    pub saved_state: Option<String>,
1111    #[serde(default)]
1112    pub automatic_state_save: Option<bool>,
1113    pub log_level: Option<String>,
1114    pub log_target: Option<String>,
1115    #[serde(default)]
1116    pub log_colored: bool,
1117    #[serde(default)]
1118    pub access_logs_target: Option<String>,
1119    #[serde(default)]
1120    pub access_logs_format: Option<AccessLogFormat>,
1121    #[serde(default)]
1122    pub access_logs_colored: Option<bool>,
1123    pub worker_count: Option<u16>,
1124    pub worker_automatic_restart: Option<bool>,
1125    pub metrics: Option<MetricsConfig>,
1126    pub disable_cluster_metrics: Option<bool>,
1127    pub listeners: Option<Vec<ListenerBuilder>>,
1128    pub clusters: Option<HashMap<String, FileClusterConfig>>,
1129    pub handle_process_affinity: Option<bool>,
1130    pub ctl_command_timeout: Option<u64>,
1131    pub pid_file_path: Option<String>,
1132    pub activate_listeners: Option<bool>,
1133    #[serde(default)]
1134    pub front_timeout: Option<u32>,
1135    #[serde(default)]
1136    pub back_timeout: Option<u32>,
1137    #[serde(default)]
1138    pub connect_timeout: Option<u32>,
1139    #[serde(default)]
1140    pub zombie_check_interval: Option<u32>,
1141    #[serde(default)]
1142    pub accept_queue_timeout: Option<u32>,
1143    #[serde(default)]
1144    pub request_timeout: Option<u32>,
1145    #[serde(default)]
1146    pub worker_timeout: Option<u32>,
1147}
1148
1149impl FileConfig {
1150    pub fn load_from_path(path: &str) -> Result<FileConfig, ConfigError> {
1151        let data = Config::load_file(path)?;
1152
1153        let config: FileConfig = match toml::from_str(&data) {
1154            Ok(config) => config,
1155            Err(e) => {
1156                display_toml_error(&data, &e);
1157                return Err(ConfigError::DeserializeToml(e.to_string()));
1158            }
1159        };
1160
1161        let mut reserved_address: HashSet<SocketAddr> = HashSet::new();
1162
1163        if let Some(listeners) = config.listeners.as_ref() {
1164            for listener in listeners.iter() {
1165                if reserved_address.contains(&listener.address) {
1166                    return Err(ConfigError::ListenerAddressAlreadyInUse(listener.address));
1167                }
1168                reserved_address.insert(listener.address);
1169            }
1170        }
1171
1172        //FIXME: verify how clusters and listeners share addresses
1173        /*
1174        if let Some(ref clusters) = config.clusters {
1175          for (key, cluster) in clusters.iter() {
1176            if let (Some(address), Some(port)) = (cluster.ip_address.clone(), cluster.port) {
1177              let addr = (address, port);
1178              if reserved_address.contains(&addr) {
1179                println!("TCP cluster '{}' listening address ( {}:{} ) is already used in the configuration",
1180                  key, addr.0, addr.1);
1181                return Err(Error::new(
1182                  ErrorKind::InvalidData,
1183                  format!("TCP cluster '{}' listening address ( {}:{} ) is already used in the configuration",
1184                    key, addr.0, addr.1)));
1185              } else {
1186                reserved_address.insert(addr.clone());
1187              }
1188            }
1189          }
1190        }
1191        */
1192
1193        Ok(config)
1194    }
1195}
1196
1197/// A builder that converts [FileConfig] to [Config]
1198pub struct ConfigBuilder {
1199    file: FileConfig,
1200    known_addresses: HashMap<SocketAddr, ListenerProtocol>,
1201    expect_proxy_addresses: HashSet<SocketAddr>,
1202    built: Config,
1203}
1204
1205impl ConfigBuilder {
1206    /// starts building a [Config] with values from a [FileConfig], or defaults.
1207    ///
1208    /// please provide a config path, usefull for rebuilding the config later.
1209    pub fn new<S>(file_config: FileConfig, config_path: S) -> Self
1210    where
1211        S: ToString,
1212    {
1213        let built = Config {
1214            accept_queue_timeout: file_config
1215                .accept_queue_timeout
1216                .unwrap_or(DEFAULT_ACCEPT_QUEUE_TIMEOUT),
1217            activate_listeners: file_config.activate_listeners.unwrap_or(true),
1218            automatic_state_save: file_config
1219                .automatic_state_save
1220                .unwrap_or(DEFAULT_AUTOMATIC_STATE_SAVE),
1221            back_timeout: file_config.back_timeout.unwrap_or(DEFAULT_BACK_TIMEOUT),
1222            buffer_size: file_config.buffer_size.unwrap_or(DEFAULT_BUFFER_SIZE),
1223            command_buffer_size: file_config
1224                .command_buffer_size
1225                .unwrap_or(DEFAULT_COMMAND_BUFFER_SIZE),
1226            config_path: config_path.to_string(),
1227            connect_timeout: file_config
1228                .connect_timeout
1229                .unwrap_or(DEFAULT_CONNECT_TIMEOUT),
1230            ctl_command_timeout: file_config.ctl_command_timeout.unwrap_or(1_000),
1231            front_timeout: file_config.front_timeout.unwrap_or(DEFAULT_FRONT_TIMEOUT),
1232            handle_process_affinity: file_config.handle_process_affinity.unwrap_or(false),
1233            access_logs_target: file_config.access_logs_target.clone(),
1234            access_logs_format: file_config.access_logs_format.clone(),
1235            access_logs_colored: file_config.access_logs_colored,
1236            log_level: file_config
1237                .log_level
1238                .clone()
1239                .unwrap_or_else(|| String::from("info")),
1240            log_target: file_config
1241                .log_target
1242                .clone()
1243                .unwrap_or_else(|| String::from("stdout")),
1244            log_colored: file_config.log_colored,
1245            max_buffers: file_config.max_buffers.unwrap_or(DEFAULT_MAX_BUFFERS),
1246            max_command_buffer_size: file_config
1247                .max_command_buffer_size
1248                .unwrap_or(DEFAULT_MAX_COMMAND_BUFFER_SIZE),
1249            max_connections: file_config
1250                .max_connections
1251                .unwrap_or(DEFAULT_MAX_CONNECTIONS),
1252            metrics: file_config.metrics.clone(),
1253            disable_cluster_metrics: file_config
1254                .disable_cluster_metrics
1255                .unwrap_or(DEFAULT_DISABLE_CLUSTER_METRICS),
1256            min_buffers: std::cmp::min(
1257                file_config.min_buffers.unwrap_or(DEFAULT_MIN_BUFFERS),
1258                file_config.max_buffers.unwrap_or(DEFAULT_MAX_BUFFERS),
1259            ),
1260            pid_file_path: file_config.pid_file_path.clone(),
1261            request_timeout: file_config
1262                .request_timeout
1263                .unwrap_or(DEFAULT_REQUEST_TIMEOUT),
1264            saved_state: file_config.saved_state.clone(),
1265            worker_automatic_restart: file_config
1266                .worker_automatic_restart
1267                .unwrap_or(DEFAULT_WORKER_AUTOMATIC_RESTART),
1268            worker_count: file_config.worker_count.unwrap_or(DEFAULT_WORKER_COUNT),
1269            zombie_check_interval: file_config
1270                .zombie_check_interval
1271                .unwrap_or(DEFAULT_ZOMBIE_CHECK_INTERVAL),
1272            worker_timeout: file_config.worker_timeout.unwrap_or(DEFAULT_WORKER_TIMEOUT),
1273            ..Default::default()
1274        };
1275
1276        Self {
1277            file: file_config,
1278            known_addresses: HashMap::new(),
1279            expect_proxy_addresses: HashSet::new(),
1280            built,
1281        }
1282    }
1283
1284    fn push_tls_listener(&mut self, mut listener: ListenerBuilder) -> Result<(), ConfigError> {
1285        let listener = listener.to_tls(Some(&self.built))?;
1286        self.built.https_listeners.push(listener);
1287        Ok(())
1288    }
1289
1290    fn push_http_listener(&mut self, mut listener: ListenerBuilder) -> Result<(), ConfigError> {
1291        let listener = listener.to_http(Some(&self.built))?;
1292        self.built.http_listeners.push(listener);
1293        Ok(())
1294    }
1295
1296    fn push_tcp_listener(&mut self, mut listener: ListenerBuilder) -> Result<(), ConfigError> {
1297        let listener = listener.to_tcp(Some(&self.built))?;
1298        self.built.tcp_listeners.push(listener);
1299        Ok(())
1300    }
1301
1302    fn populate_listeners(&mut self, listeners: Vec<ListenerBuilder>) -> Result<(), ConfigError> {
1303        for listener in listeners.iter() {
1304            if self.known_addresses.contains_key(&listener.address) {
1305                return Err(ConfigError::ListenerAddressAlreadyInUse(listener.address));
1306            }
1307
1308            let protocol = listener
1309                .protocol
1310                .ok_or(ConfigError::Missing(MissingKind::Protocol))?;
1311
1312            self.known_addresses.insert(listener.address, protocol);
1313            if listener.expect_proxy == Some(true) {
1314                self.expect_proxy_addresses.insert(listener.address);
1315            }
1316
1317            if listener.public_address.is_some() && listener.expect_proxy == Some(true) {
1318                return Err(ConfigError::Incompatible {
1319                    object: ObjectKind::Listener,
1320                    id: listener.address.to_string(),
1321                    kind: IncompatibilityKind::PublicAddress,
1322                });
1323            }
1324
1325            match protocol {
1326                ListenerProtocol::Https => self.push_tls_listener(listener.clone())?,
1327                ListenerProtocol::Http => self.push_http_listener(listener.clone())?,
1328                ListenerProtocol::Tcp => self.push_tcp_listener(listener.clone())?,
1329            }
1330        }
1331        Ok(())
1332    }
1333
1334    fn populate_clusters(
1335        &mut self,
1336        mut file_cluster_configs: HashMap<String, FileClusterConfig>,
1337    ) -> Result<(), ConfigError> {
1338        for (id, file_cluster_config) in file_cluster_configs.drain() {
1339            let mut cluster_config =
1340                file_cluster_config.to_cluster_config(id.as_str(), &self.expect_proxy_addresses)?;
1341
1342            match cluster_config {
1343                ClusterConfig::Http(ref mut http) => {
1344                    for frontend in http.frontends.iter_mut() {
1345                        match self.known_addresses.get(&frontend.address) {
1346                            Some(ListenerProtocol::Tcp) => {
1347                                return Err(ConfigError::WrongFrontendProtocol(
1348                                    ListenerProtocol::Tcp,
1349                                ));
1350                            }
1351                            Some(ListenerProtocol::Http) => {
1352                                if frontend.certificate.is_some() {
1353                                    return Err(ConfigError::WrongFrontendProtocol(
1354                                        ListenerProtocol::Http,
1355                                    ));
1356                                }
1357                            }
1358                            Some(ListenerProtocol::Https) => {
1359                                if frontend.certificate.is_none() {
1360                                    if let Some(https_listener) =
1361                                        self.built.https_listeners.iter().find(|listener| {
1362                                            listener.address == frontend.address.into()
1363                                                && listener.certificate.is_some()
1364                                        })
1365                                    {
1366                                        //println!("using listener certificate for {:}", frontend.address);
1367                                        frontend
1368                                            .certificate
1369                                            .clone_from(&https_listener.certificate);
1370                                        frontend.certificate_chain =
1371                                            Some(https_listener.certificate_chain.clone());
1372                                        frontend.key.clone_from(&https_listener.key);
1373                                    }
1374                                    if frontend.certificate.is_none() {
1375                                        debug!("known addresses: {:?}", self.known_addresses);
1376                                        debug!("frontend: {:?}", frontend);
1377                                        return Err(ConfigError::WrongFrontendProtocol(
1378                                            ListenerProtocol::Https,
1379                                        ));
1380                                    }
1381                                }
1382                            }
1383                            None => {
1384                                // create a default listener for that front
1385                                let file_listener_protocol = if frontend.certificate.is_some() {
1386                                    self.push_tls_listener(ListenerBuilder::new(
1387                                        frontend.address.into(),
1388                                        ListenerProtocol::Https,
1389                                    ))?;
1390
1391                                    ListenerProtocol::Https
1392                                } else {
1393                                    self.push_http_listener(ListenerBuilder::new(
1394                                        frontend.address.into(),
1395                                        ListenerProtocol::Http,
1396                                    ))?;
1397
1398                                    ListenerProtocol::Http
1399                                };
1400                                self.known_addresses
1401                                    .insert(frontend.address, file_listener_protocol);
1402                            }
1403                        }
1404                    }
1405                }
1406                ClusterConfig::Tcp(ref tcp) => {
1407                    //FIXME: verify that different TCP clusters do not request the same address
1408                    for frontend in &tcp.frontends {
1409                        match self.known_addresses.get(&frontend.address) {
1410                            Some(ListenerProtocol::Http) | Some(ListenerProtocol::Https) => {
1411                                return Err(ConfigError::WrongFrontendProtocol(
1412                                    ListenerProtocol::Http,
1413                                ));
1414                            }
1415                            Some(ListenerProtocol::Tcp) => {}
1416                            None => {
1417                                // create a default listener for that front
1418                                self.push_tcp_listener(ListenerBuilder::new(
1419                                    frontend.address.into(),
1420                                    ListenerProtocol::Tcp,
1421                                ))?;
1422                                self.known_addresses
1423                                    .insert(frontend.address, ListenerProtocol::Tcp);
1424                            }
1425                        }
1426                    }
1427                }
1428            }
1429
1430            self.built.clusters.insert(id, cluster_config);
1431        }
1432        Ok(())
1433    }
1434
1435    /// Builds a [`Config`], populated with listeners and clusters
1436    pub fn into_config(&mut self) -> Result<Config, ConfigError> {
1437        if let Some(listeners) = &self.file.listeners {
1438            self.populate_listeners(listeners.clone())?;
1439        }
1440
1441        if let Some(file_cluster_configs) = &self.file.clusters {
1442            self.populate_clusters(file_cluster_configs.clone())?;
1443        }
1444
1445        let command_socket_path = self.file.command_socket.clone().unwrap_or({
1446            let mut path = env::current_dir().map_err(|e| ConfigError::Env(e.to_string()))?;
1447            path.push("sozu.sock");
1448            let verified_path = path
1449                .to_str()
1450                .ok_or(ConfigError::InvalidPath(path.clone()))?;
1451            verified_path.to_owned()
1452        });
1453
1454        if let (None, Some(true)) = (&self.file.saved_state, &self.file.automatic_state_save) {
1455            return Err(ConfigError::Missing(MissingKind::SavedState));
1456        }
1457
1458        Ok(Config {
1459            command_socket: command_socket_path,
1460            ..self.built.clone()
1461        })
1462    }
1463}
1464
1465/// Sōzu configuration, populated with clusters and listeners.
1466///
1467/// This struct is used on startup to generate `WorkerRequest`s
1468#[derive(Clone, PartialEq, Eq, Serialize, Default, Deserialize)]
1469pub struct Config {
1470    pub config_path: String,
1471    pub command_socket: String,
1472    pub command_buffer_size: u64,
1473    pub max_command_buffer_size: u64,
1474    pub max_connections: usize,
1475    pub min_buffers: u64,
1476    pub max_buffers: u64,
1477    pub buffer_size: u64,
1478    pub saved_state: Option<String>,
1479    #[serde(default)]
1480    pub automatic_state_save: bool,
1481    pub log_level: String,
1482    pub log_target: String,
1483    pub log_colored: bool,
1484    #[serde(default)]
1485    pub access_logs_target: Option<String>,
1486    pub access_logs_format: Option<AccessLogFormat>,
1487    pub access_logs_colored: Option<bool>,
1488    pub worker_count: u16,
1489    pub worker_automatic_restart: bool,
1490    pub metrics: Option<MetricsConfig>,
1491    #[serde(default = "default_disable_cluster_metrics")]
1492    pub disable_cluster_metrics: bool,
1493    pub http_listeners: Vec<HttpListenerConfig>,
1494    pub https_listeners: Vec<HttpsListenerConfig>,
1495    pub tcp_listeners: Vec<TcpListenerConfig>,
1496    pub clusters: HashMap<String, ClusterConfig>,
1497    pub handle_process_affinity: bool,
1498    pub ctl_command_timeout: u64,
1499    pub pid_file_path: Option<String>,
1500    pub activate_listeners: bool,
1501    #[serde(default = "default_front_timeout")]
1502    pub front_timeout: u32,
1503    #[serde(default = "default_back_timeout")]
1504    pub back_timeout: u32,
1505    #[serde(default = "default_connect_timeout")]
1506    pub connect_timeout: u32,
1507    #[serde(default = "default_zombie_check_interval")]
1508    pub zombie_check_interval: u32,
1509    #[serde(default = "default_accept_queue_timeout")]
1510    pub accept_queue_timeout: u32,
1511    #[serde(default = "default_request_timeout")]
1512    pub request_timeout: u32,
1513    #[serde(default = "default_worker_timeout")]
1514    pub worker_timeout: u32,
1515}
1516
1517fn default_front_timeout() -> u32 {
1518    DEFAULT_FRONT_TIMEOUT
1519}
1520
1521fn default_back_timeout() -> u32 {
1522    DEFAULT_BACK_TIMEOUT
1523}
1524
1525fn default_connect_timeout() -> u32 {
1526    DEFAULT_CONNECT_TIMEOUT
1527}
1528
1529fn default_request_timeout() -> u32 {
1530    DEFAULT_REQUEST_TIMEOUT
1531}
1532
1533fn default_zombie_check_interval() -> u32 {
1534    DEFAULT_ZOMBIE_CHECK_INTERVAL
1535}
1536
1537fn default_accept_queue_timeout() -> u32 {
1538    DEFAULT_ACCEPT_QUEUE_TIMEOUT
1539}
1540
1541fn default_disable_cluster_metrics() -> bool {
1542    DEFAULT_DISABLE_CLUSTER_METRICS
1543}
1544
1545fn default_worker_timeout() -> u32 {
1546    DEFAULT_WORKER_TIMEOUT
1547}
1548
1549impl Config {
1550    /// Parse a TOML file and build a config out of it
1551    pub fn load_from_path(path: &str) -> Result<Config, ConfigError> {
1552        let file_config = FileConfig::load_from_path(path)?;
1553
1554        let mut config = ConfigBuilder::new(file_config, path).into_config()?;
1555
1556        // replace saved_state with a verified path
1557        config.saved_state = config.saved_state_path()?;
1558
1559        Ok(config)
1560    }
1561
1562    /// yields requests intended to recreate a proxy that match the config
1563    pub fn generate_config_messages(&self) -> Result<Vec<WorkerRequest>, ConfigError> {
1564        let mut v = Vec::new();
1565        let mut count = 0u8;
1566
1567        for listener in &self.http_listeners {
1568            v.push(WorkerRequest {
1569                id: format!("CONFIG-{count}"),
1570                content: RequestType::AddHttpListener(listener.clone()).into(),
1571            });
1572            count += 1;
1573        }
1574
1575        for listener in &self.https_listeners {
1576            v.push(WorkerRequest {
1577                id: format!("CONFIG-{count}"),
1578                content: RequestType::AddHttpsListener(listener.clone()).into(),
1579            });
1580            count += 1;
1581        }
1582
1583        for listener in &self.tcp_listeners {
1584            v.push(WorkerRequest {
1585                id: format!("CONFIG-{count}"),
1586                content: RequestType::AddTcpListener(*listener).into(),
1587            });
1588            count += 1;
1589        }
1590
1591        for cluster in self.clusters.values() {
1592            let mut orders = cluster.generate_requests()?;
1593            for content in orders.drain(..) {
1594                v.push(WorkerRequest {
1595                    id: format!("CONFIG-{count}"),
1596                    content,
1597                });
1598                count += 1;
1599            }
1600        }
1601
1602        if self.activate_listeners {
1603            for listener in &self.http_listeners {
1604                v.push(WorkerRequest {
1605                    id: format!("CONFIG-{count}"),
1606                    content: RequestType::ActivateListener(ActivateListener {
1607                        address: listener.address,
1608                        proxy: ListenerType::Http.into(),
1609                        from_scm: false,
1610                    })
1611                    .into(),
1612                });
1613                count += 1;
1614            }
1615
1616            for listener in &self.https_listeners {
1617                v.push(WorkerRequest {
1618                    id: format!("CONFIG-{count}"),
1619                    content: RequestType::ActivateListener(ActivateListener {
1620                        address: listener.address,
1621                        proxy: ListenerType::Https.into(),
1622                        from_scm: false,
1623                    })
1624                    .into(),
1625                });
1626                count += 1;
1627            }
1628
1629            for listener in &self.tcp_listeners {
1630                v.push(WorkerRequest {
1631                    id: format!("CONFIG-{count}"),
1632                    content: RequestType::ActivateListener(ActivateListener {
1633                        address: listener.address,
1634                        proxy: ListenerType::Tcp.into(),
1635                        from_scm: false,
1636                    })
1637                    .into(),
1638                });
1639                count += 1;
1640            }
1641        }
1642
1643        if self.disable_cluster_metrics {
1644            v.push(WorkerRequest {
1645                id: format!("CONFIG-{count}"),
1646                content: RequestType::ConfigureMetrics(MetricsConfiguration::Disabled.into())
1647                    .into(),
1648            });
1649            // count += 1; // uncomment if code is added below
1650        }
1651
1652        Ok(v)
1653    }
1654
1655    /// Get the path of the UNIX socket used to communicate with Sōzu
1656    pub fn command_socket_path(&self) -> Result<String, ConfigError> {
1657        let config_path_buf = PathBuf::from(self.config_path.clone());
1658        let mut config_dir = config_path_buf
1659            .parent()
1660            .ok_or(ConfigError::NoFileParent(
1661                config_path_buf.to_string_lossy().to_string(),
1662            ))?
1663            .to_path_buf();
1664
1665        let socket_path = PathBuf::from(self.command_socket.clone());
1666
1667        let mut socket_parent_dir = match socket_path.parent() {
1668            // if the socket path is of the form "./sozu.sock",
1669            // then the parent is the directory where config.toml is situated
1670            None => config_dir,
1671            Some(path) => {
1672                // concatenate the config directory and the relative path of the socket
1673                config_dir.push(path);
1674                // canonicalize to remove double dots like /path/to/config/directory/../../path/to/socket/directory/
1675                config_dir.canonicalize().map_err(|io_error| {
1676                    ConfigError::SocketPathError(format!(
1677                        "Could not canonicalize path {config_dir:?}: {io_error}"
1678                    ))
1679                })?
1680            }
1681        };
1682
1683        let socket_name = socket_path
1684            .file_name()
1685            .ok_or(ConfigError::SocketPathError(format!(
1686                "could not get command socket file name from {socket_path:?}"
1687            )))?;
1688
1689        // concatenate parent directory and socket file name
1690        socket_parent_dir.push(socket_name);
1691
1692        let command_socket_path = socket_parent_dir
1693            .to_str()
1694            .ok_or(ConfigError::SocketPathError(format!(
1695                "Invalid socket path {socket_parent_dir:?}"
1696            )))?
1697            .to_string();
1698
1699        Ok(command_socket_path)
1700    }
1701
1702    /// Get the path of where the state will be saved
1703    fn saved_state_path(&self) -> Result<Option<String>, ConfigError> {
1704        let path = match self.saved_state.as_ref() {
1705            Some(path) => path,
1706            None => return Ok(None),
1707        };
1708
1709        debug!("saved_stated path in the config: {}", path);
1710        let config_path = PathBuf::from(self.config_path.clone());
1711
1712        debug!("Config path buffer: {:?}", config_path);
1713        let config_dir = config_path
1714            .parent()
1715            .ok_or(ConfigError::SaveStatePath(format!(
1716                "Could get parent directory of config file {config_path:?}"
1717            )))?;
1718
1719        debug!("Config folder: {:?}", config_dir);
1720        if !config_dir.exists() {
1721            create_dir_all(config_dir).map_err(|io_error| {
1722                ConfigError::SaveStatePath(format!(
1723                    "failed to create state parent directory '{config_dir:?}': {io_error}"
1724                ))
1725            })?;
1726        }
1727
1728        let mut saved_state_path_raw = config_dir.to_path_buf();
1729        saved_state_path_raw.push(path);
1730        debug!(
1731            "Looking for saved state on the path {:?}",
1732            saved_state_path_raw
1733        );
1734
1735        match metadata(path) {
1736            Err(err) if matches!(err.kind(), ErrorKind::NotFound) => {
1737                info!("Create an empty state file at '{}'", path);
1738                File::create(path).map_err(|io_error| {
1739                    ConfigError::SaveStatePath(format!(
1740                        "failed to create state file '{path:?}': {io_error}"
1741                    ))
1742                })?;
1743            }
1744            _ => {}
1745        }
1746
1747        saved_state_path_raw.canonicalize().map_err(|io_error| {
1748            ConfigError::SaveStatePath(format!(
1749                "could not get saved state path from config file input {path:?}: {io_error}"
1750            ))
1751        })?;
1752
1753        let stringified_path = saved_state_path_raw
1754            .to_str()
1755            .ok_or(ConfigError::SaveStatePath(format!(
1756                "Invalid path {saved_state_path_raw:?}"
1757            )))?
1758            .to_string();
1759
1760        Ok(Some(stringified_path))
1761    }
1762
1763    /// read any file to a string
1764    pub fn load_file(path: &str) -> Result<String, ConfigError> {
1765        std::fs::read_to_string(path).map_err(|io_error| ConfigError::FileRead {
1766            path_to_read: path.to_owned(),
1767            io_error,
1768        })
1769    }
1770
1771    /// read any file to bytes
1772    pub fn load_file_bytes(path: &str) -> Result<Vec<u8>, ConfigError> {
1773        std::fs::read(path).map_err(|io_error| ConfigError::FileRead {
1774            path_to_read: path.to_owned(),
1775            io_error,
1776        })
1777    }
1778}
1779
1780impl fmt::Debug for Config {
1781    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1782        f.debug_struct("Config")
1783            .field("config_path", &self.config_path)
1784            .field("command_socket", &self.command_socket)
1785            .field("command_buffer_size", &self.command_buffer_size)
1786            .field("max_command_buffer_size", &self.max_command_buffer_size)
1787            .field("max_connections", &self.max_connections)
1788            .field("min_buffers", &self.min_buffers)
1789            .field("max_buffers", &self.max_buffers)
1790            .field("buffer_size", &self.buffer_size)
1791            .field("saved_state", &self.saved_state)
1792            .field("automatic_state_save", &self.automatic_state_save)
1793            .field("log_level", &self.log_level)
1794            .field("log_target", &self.log_target)
1795            .field("access_logs_target", &self.access_logs_target)
1796            .field("access_logs_format", &self.access_logs_format)
1797            .field("worker_count", &self.worker_count)
1798            .field("worker_automatic_restart", &self.worker_automatic_restart)
1799            .field("metrics", &self.metrics)
1800            .field("disable_cluster_metrics", &self.disable_cluster_metrics)
1801            .field("handle_process_affinity", &self.handle_process_affinity)
1802            .field("ctl_command_timeout", &self.ctl_command_timeout)
1803            .field("pid_file_path", &self.pid_file_path)
1804            .field("activate_listeners", &self.activate_listeners)
1805            .field("front_timeout", &self.front_timeout)
1806            .field("back_timeout", &self.back_timeout)
1807            .field("connect_timeout", &self.connect_timeout)
1808            .field("zombie_check_interval", &self.zombie_check_interval)
1809            .field("accept_queue_timeout", &self.accept_queue_timeout)
1810            .field("request_timeout", &self.request_timeout)
1811            .field("worker_timeout", &self.worker_timeout)
1812            .finish()
1813    }
1814}
1815
1816fn display_toml_error(file: &str, error: &toml::de::Error) {
1817    println!("error parsing the configuration file '{file}': {error}");
1818    if let Some(Range { start, end }) = error.span() {
1819        print!("error parsing the configuration file '{file}' at position: {start}, {end}");
1820    }
1821}
1822
1823impl ServerConfig {
1824    /// size of the slab for the Session manager
1825    pub fn slab_capacity(&self) -> u64 {
1826        10 + 2 * self.max_connections
1827    }
1828}
1829
1830/// reduce the config to the bare minimum needed by a worker
1831impl From<&Config> for ServerConfig {
1832    fn from(config: &Config) -> Self {
1833        let metrics = config.metrics.clone().map(|m| ServerMetricsConfig {
1834            address: m.address.to_string(),
1835            tagged_metrics: m.tagged_metrics,
1836            prefix: m.prefix,
1837        });
1838        Self {
1839            max_connections: config.max_connections as u64,
1840            front_timeout: config.front_timeout,
1841            back_timeout: config.back_timeout,
1842            connect_timeout: config.connect_timeout,
1843            zombie_check_interval: config.zombie_check_interval,
1844            accept_queue_timeout: config.accept_queue_timeout,
1845            min_buffers: config.min_buffers,
1846            max_buffers: config.max_buffers,
1847            buffer_size: config.buffer_size,
1848            log_level: config.log_level.clone(),
1849            log_target: config.log_target.clone(),
1850            access_logs_target: config.access_logs_target.clone(),
1851            command_buffer_size: config.command_buffer_size,
1852            max_command_buffer_size: config.max_command_buffer_size,
1853            metrics,
1854            access_log_format: ProtobufAccessLogFormat::from(&config.access_logs_format) as i32,
1855            log_colored: config.log_colored,
1856        }
1857    }
1858}
1859
1860#[cfg(test)]
1861mod tests {
1862    use super::*;
1863    use toml::to_string;
1864
1865    #[test]
1866    fn serialize() {
1867        let http = ListenerBuilder::new(
1868            SocketAddress::new_v4(127, 0, 0, 1, 8080),
1869            ListenerProtocol::Http,
1870        )
1871        .with_answer_404_path(Some("404.html"))
1872        .to_owned();
1873        println!("http: {:?}", to_string(&http));
1874
1875        let https = ListenerBuilder::new(
1876            SocketAddress::new_v4(127, 0, 0, 1, 8443),
1877            ListenerProtocol::Https,
1878        )
1879        .with_answer_404_path(Some("404.html"))
1880        .to_owned();
1881        println!("https: {:?}", to_string(&https));
1882
1883        let listeners = vec![http, https];
1884        let config = FileConfig {
1885            command_socket: Some(String::from("./command_folder/sock")),
1886            worker_count: Some(2),
1887            worker_automatic_restart: Some(true),
1888            max_connections: Some(500),
1889            min_buffers: Some(1),
1890            max_buffers: Some(500),
1891            buffer_size: Some(16393),
1892            metrics: Some(MetricsConfig {
1893                address: "127.0.0.1:8125".parse().unwrap(),
1894                tagged_metrics: false,
1895                prefix: Some(String::from("sozu-metrics")),
1896            }),
1897            listeners: Some(listeners),
1898            ..Default::default()
1899        };
1900
1901        println!("config: {:?}", to_string(&config));
1902        let encoded = to_string(&config).unwrap();
1903        println!("conf:\n{encoded}");
1904    }
1905
1906    #[test]
1907    fn parse() {
1908        let path = "assets/config.toml";
1909        let config = Config::load_from_path(path).unwrap_or_else(|load_error| {
1910            panic!("Cannot load config from path {path}: {load_error:?}")
1911        });
1912        println!("config: {config:#?}");
1913        //panic!();
1914    }
1915}