1use 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
74pub const DEFAULT_RUSTLS_CIPHER_LIST: [&str; 9] = [
79 "TLS13_AES_256_GCM_SHA384",
81 "TLS13_AES_128_GCM_SHA256",
82 "TLS13_CHACHA20_POLY1305_SHA256",
83 "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
113pub const DEFAULT_FRONT_TIMEOUT: u32 = 60;
115
116pub const DEFAULT_BACK_TIMEOUT: u32 = 30;
118
119pub const DEFAULT_CONNECT_TIMEOUT: u32 = 3;
121
122pub const DEFAULT_REQUEST_TIMEOUT: u32 = 10;
124
125pub const DEFAULT_WORKER_TIMEOUT: u32 = 10;
127
128pub const DEFAULT_STICKY_NAME: &str = "SOZUBALANCEID";
130
131pub const DEFAULT_ZOMBIE_CHECK_INTERVAL: u32 = 1_800;
133
134pub const DEFAULT_ACCEPT_QUEUE_TIMEOUT: u32 = 60;
136
137pub const DEFAULT_WORKER_COUNT: u16 = 2;
139
140pub const DEFAULT_WORKER_AUTOMATIC_RESTART: bool = true;
142
143pub const DEFAULT_AUTOMATIC_STATE_SAVE: bool = false;
145
146pub const DEFAULT_MIN_BUFFERS: u64 = 1;
148
149pub const DEFAULT_MAX_BUFFERS: u64 = 1_000;
151
152pub const DEFAULT_BUFFER_SIZE: u64 = 16_393;
154
155pub const DEFAULT_MAX_CONNECTIONS: usize = 10_000;
157
158pub const DEFAULT_COMMAND_BUFFER_SIZE: u64 = 1_000_000;
160
161pub const DEFAULT_MAX_COMMAND_BUFFER_SIZE: u64 = 2_000_000;
163
164pub const DEFAULT_DISABLE_CLUSTER_METRICS: bool = false;
166
167pub const MAX_LOOP_ITERATIONS: usize = 100000;
168
169pub const DEFAULT_SEND_TLS_13_TICKETS: u64 = 4;
174
175pub 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#[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 pub front_timeout: Option<u32>,
264 pub back_timeout: Option<u32>,
266 pub connect_timeout: Option<u32>,
268 pub request_timeout: Option<u32>,
270 pub config: Option<Config>,
272 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 pub fn new_http(address: SocketAddress) -> ListenerBuilder {
286 Self::new(address, ListenerProtocol::Http)
287 }
288
289 pub fn new_tcp(address: SocketAddress) -> ListenerBuilder {
292 Self::new(address, ListenerProtocol::Tcp)
293 }
294
295 pub fn new_https(address: SocketAddress) -> ListenerBuilder {
298 Self::new(address, ListenerProtocol::Https)
299 }
300
301 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 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 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 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 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 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
611fn 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 pub path: Option<String>,
659 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 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 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#[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 Ok(config)
1194 }
1195}
1196
1197pub 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 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 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 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 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 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 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#[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 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 config.saved_state = config.saved_state_path()?;
1558
1559 Ok(config)
1560 }
1561
1562 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 }
1651
1652 Ok(v)
1653 }
1654
1655 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 None => config_dir,
1671 Some(path) => {
1672 config_dir.push(path);
1674 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 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 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 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 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 pub fn slab_capacity(&self) -> u64 {
1826 10 + 2 * self.max_connections
1827 }
1828}
1829
1830impl 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 }
1915}