1use std::{
51 collections::{BTreeMap, HashMap, HashSet},
52 env, fmt,
53 fs::{File, create_dir_all, metadata},
54 io::{ErrorKind, Read},
55 net::SocketAddr,
56 ops::Range,
57 path::PathBuf,
58};
59
60use crate::{
61 ObjectKind,
62 certificate::split_certificate_chain,
63 logging::AccessLogFormat,
64 proto::command::{
65 ActivateListener, AddBackend, AddCertificate, CertificateAndKey, Cluster,
66 CustomHttpAnswers, HttpListenerConfig, HttpsListenerConfig, ListenerType,
67 LoadBalancingAlgorithms, LoadBalancingParams, LoadMetric, MetricsConfiguration, PathRule,
68 ProtobufAccessLogFormat, ProxyProtocolConfig, Request, RequestHttpFrontend,
69 RequestTcpFrontend, RulePosition, ServerConfig, ServerMetricsConfig, SocketAddress,
70 TcpListenerConfig, TlsVersion, WorkerRequest, request::RequestType,
71 },
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(
206 "the field {kind:?} of {object:?} with id or address {id} is incompatible with the rest of the options"
207 )]
208 Incompatible {
209 kind: IncompatibilityKind,
210 object: ObjectKind,
211 id: String,
212 },
213 #[error("Invalid '{0}' field for a TCP frontend")]
214 InvalidFrontendConfig(String),
215 #[error("invalid path {0:?}")]
216 InvalidPath(PathBuf),
217 #[error("listening address {0:?} is already used in the configuration")]
218 ListenerAddressAlreadyInUse(SocketAddr),
219 #[error("missing {0:?}")]
220 Missing(MissingKind),
221 #[error("could not get parent directory for file {0}")]
222 NoFileParent(String),
223 #[error("Could not get the path of the saved state")]
224 SaveStatePath(String),
225 #[error("Can not determine path to sozu socket: {0}")]
226 SocketPathError(String),
227 #[error("toml decoding error: {0}")]
228 DeserializeToml(String),
229 #[error("Can not set this frontend on a {0:?} listener")]
230 WrongFrontendProtocol(ListenerProtocol),
231 #[error("Can not build a {expected:?} listener from a {found:?} config")]
232 WrongListenerProtocol {
233 expected: ListenerProtocol,
234 found: Option<ListenerProtocol>,
235 },
236}
237
238#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
240#[serde(deny_unknown_fields)]
241pub struct ListenerBuilder {
242 pub address: SocketAddr,
243 pub protocol: Option<ListenerProtocol>,
244 pub public_address: Option<SocketAddr>,
245 pub answer_301: Option<String>,
246 pub answer_400: Option<String>,
247 pub answer_401: Option<String>,
248 pub answer_404: Option<String>,
249 pub answer_408: Option<String>,
250 pub answer_413: Option<String>,
251 pub answer_502: Option<String>,
252 pub answer_503: Option<String>,
253 pub answer_504: Option<String>,
254 pub answer_507: Option<String>,
255 pub tls_versions: Option<Vec<TlsVersion>>,
256 pub cipher_list: Option<Vec<String>>,
257 pub cipher_suites: Option<Vec<String>>,
258 pub expect_proxy: Option<bool>,
259 #[serde(default = "default_sticky_name")]
260 pub sticky_name: String,
261 pub certificate: Option<String>,
262 pub certificate_chain: Option<String>,
263 pub key: Option<String>,
264 pub front_timeout: Option<u32>,
266 pub back_timeout: Option<u32>,
268 pub connect_timeout: Option<u32>,
270 pub request_timeout: Option<u32>,
272 pub config: Option<Config>,
274 pub send_tls13_tickets: Option<u64>,
278}
279
280pub fn default_sticky_name() -> String {
281 DEFAULT_STICKY_NAME.to_string()
282}
283
284impl ListenerBuilder {
285 pub fn new_http(address: SocketAddress) -> ListenerBuilder {
288 Self::new(address, ListenerProtocol::Http)
289 }
290
291 pub fn new_tcp(address: SocketAddress) -> ListenerBuilder {
294 Self::new(address, ListenerProtocol::Tcp)
295 }
296
297 pub fn new_https(address: SocketAddress) -> ListenerBuilder {
300 Self::new(address, ListenerProtocol::Https)
301 }
302
303 fn new(address: SocketAddress, protocol: ListenerProtocol) -> ListenerBuilder {
305 ListenerBuilder {
306 address: address.into(),
307 answer_301: None,
308 answer_401: None,
309 answer_400: None,
310 answer_404: None,
311 answer_408: None,
312 answer_413: None,
313 answer_502: None,
314 answer_503: None,
315 answer_504: None,
316 answer_507: None,
317 back_timeout: None,
318 certificate_chain: None,
319 certificate: None,
320 cipher_list: None,
321 cipher_suites: None,
322 config: None,
323 connect_timeout: None,
324 expect_proxy: None,
325 front_timeout: None,
326 key: None,
327 protocol: Some(protocol),
328 public_address: None,
329 request_timeout: None,
330 send_tls13_tickets: None,
331 sticky_name: DEFAULT_STICKY_NAME.to_string(),
332 tls_versions: None,
333 }
334 }
335
336 pub fn with_public_address(&mut self, public_address: Option<SocketAddr>) -> &mut Self {
337 if let Some(address) = public_address {
338 self.public_address = Some(address);
339 }
340 self
341 }
342
343 pub fn with_answer_404_path<S>(&mut self, answer_404_path: Option<S>) -> &mut Self
344 where
345 S: ToString,
346 {
347 if let Some(path) = answer_404_path {
348 self.answer_404 = Some(path.to_string());
349 }
350 self
351 }
352
353 pub fn with_answer_503_path<S>(&mut self, answer_503_path: Option<S>) -> &mut Self
354 where
355 S: ToString,
356 {
357 if let Some(path) = answer_503_path {
358 self.answer_503 = Some(path.to_string());
359 }
360 self
361 }
362
363 pub fn with_tls_versions(&mut self, tls_versions: Vec<TlsVersion>) -> &mut Self {
364 self.tls_versions = Some(tls_versions);
365 self
366 }
367
368 pub fn with_cipher_list(&mut self, cipher_list: Option<Vec<String>>) -> &mut Self {
369 self.cipher_list = cipher_list;
370 self
371 }
372
373 pub fn with_cipher_suites(&mut self, cipher_suites: Option<Vec<String>>) -> &mut Self {
374 self.cipher_suites = cipher_suites;
375 self
376 }
377
378 pub fn with_expect_proxy(&mut self, expect_proxy: bool) -> &mut Self {
379 self.expect_proxy = Some(expect_proxy);
380 self
381 }
382
383 pub fn with_sticky_name<S>(&mut self, sticky_name: Option<S>) -> &mut Self
384 where
385 S: ToString,
386 {
387 if let Some(name) = sticky_name {
388 self.sticky_name = name.to_string();
389 }
390 self
391 }
392
393 pub fn with_certificate<S>(&mut self, certificate: S) -> &mut Self
394 where
395 S: ToString,
396 {
397 self.certificate = Some(certificate.to_string());
398 self
399 }
400
401 pub fn with_certificate_chain(&mut self, certificate_chain: String) -> &mut Self {
402 self.certificate = Some(certificate_chain);
403 self
404 }
405
406 pub fn with_key<S>(&mut self, key: String) -> &mut Self
407 where
408 S: ToString,
409 {
410 self.key = Some(key);
411 self
412 }
413
414 pub fn with_front_timeout(&mut self, front_timeout: Option<u32>) -> &mut Self {
415 self.front_timeout = front_timeout;
416 self
417 }
418
419 pub fn with_back_timeout(&mut self, back_timeout: Option<u32>) -> &mut Self {
420 self.back_timeout = back_timeout;
421 self
422 }
423
424 pub fn with_connect_timeout(&mut self, connect_timeout: Option<u32>) -> &mut Self {
425 self.connect_timeout = connect_timeout;
426 self
427 }
428
429 pub fn with_request_timeout(&mut self, request_timeout: Option<u32>) -> &mut Self {
430 self.request_timeout = request_timeout;
431 self
432 }
433
434 fn get_http_answers(&self) -> Result<Option<CustomHttpAnswers>, ConfigError> {
436 let http_answers = CustomHttpAnswers {
437 answer_301: read_http_answer_file(&self.answer_301)?,
438 answer_400: read_http_answer_file(&self.answer_400)?,
439 answer_401: read_http_answer_file(&self.answer_401)?,
440 answer_404: read_http_answer_file(&self.answer_404)?,
441 answer_408: read_http_answer_file(&self.answer_408)?,
442 answer_413: read_http_answer_file(&self.answer_413)?,
443 answer_502: read_http_answer_file(&self.answer_502)?,
444 answer_503: read_http_answer_file(&self.answer_503)?,
445 answer_504: read_http_answer_file(&self.answer_504)?,
446 answer_507: read_http_answer_file(&self.answer_507)?,
447 };
448 Ok(Some(http_answers))
449 }
450
451 fn assign_config_timeouts(&mut self, config: &Config) {
453 self.front_timeout = Some(self.front_timeout.unwrap_or(config.front_timeout));
454 self.back_timeout = Some(self.back_timeout.unwrap_or(config.back_timeout));
455 self.connect_timeout = Some(self.connect_timeout.unwrap_or(config.connect_timeout));
456 self.request_timeout = Some(self.request_timeout.unwrap_or(config.request_timeout));
457 }
458
459 pub fn to_http(&mut self, config: Option<&Config>) -> Result<HttpListenerConfig, ConfigError> {
461 if self.protocol != Some(ListenerProtocol::Http) {
462 return Err(ConfigError::WrongListenerProtocol {
463 expected: ListenerProtocol::Http,
464 found: self.protocol.to_owned(),
465 });
466 }
467
468 if let Some(config) = config {
469 self.assign_config_timeouts(config);
470 }
471
472 let http_answers = self.get_http_answers()?;
473
474 let configuration = HttpListenerConfig {
475 address: self.address.into(),
476 public_address: self.public_address.map(|a| a.into()),
477 expect_proxy: self.expect_proxy.unwrap_or(false),
478 sticky_name: self.sticky_name.clone(),
479 front_timeout: self.front_timeout.unwrap_or(DEFAULT_FRONT_TIMEOUT),
480 back_timeout: self.back_timeout.unwrap_or(DEFAULT_BACK_TIMEOUT),
481 connect_timeout: self.connect_timeout.unwrap_or(DEFAULT_CONNECT_TIMEOUT),
482 request_timeout: self.request_timeout.unwrap_or(DEFAULT_REQUEST_TIMEOUT),
483 http_answers,
484 ..Default::default()
485 };
486
487 Ok(configuration)
488 }
489
490 pub fn to_tls(&mut self, config: Option<&Config>) -> Result<HttpsListenerConfig, ConfigError> {
492 if self.protocol != Some(ListenerProtocol::Https) {
493 return Err(ConfigError::WrongListenerProtocol {
494 expected: ListenerProtocol::Https,
495 found: self.protocol.to_owned(),
496 });
497 }
498
499 let default_cipher_list = DEFAULT_RUSTLS_CIPHER_LIST
500 .into_iter()
501 .map(String::from)
502 .collect();
503
504 let cipher_list = self.cipher_list.clone().unwrap_or(default_cipher_list);
505
506 let default_cipher_suites = DEFAULT_CIPHER_SUITES
507 .into_iter()
508 .map(String::from)
509 .collect();
510
511 let cipher_suites = self.cipher_suites.clone().unwrap_or(default_cipher_suites);
512
513 let signature_algorithms: Vec<String> = DEFAULT_SIGNATURE_ALGORITHMS
514 .into_iter()
515 .map(String::from)
516 .collect();
517
518 let groups_list: Vec<String> = DEFAULT_GROUPS_LIST.into_iter().map(String::from).collect();
519
520 let versions = match self.tls_versions {
521 None => vec![TlsVersion::TlsV12 as i32, TlsVersion::TlsV13 as i32],
522 Some(ref v) => v.iter().map(|v| *v as i32).collect(),
523 };
524
525 let key = self.key.as_ref().and_then(|path| {
526 Config::load_file(path)
527 .map_err(|e| {
528 error!("cannot load key at path '{}': {:?}", path, e);
529 e
530 })
531 .ok()
532 });
533 let certificate = self.certificate.as_ref().and_then(|path| {
534 Config::load_file(path)
535 .map_err(|e| {
536 error!("cannot load certificate at path '{}': {:?}", path, e);
537 e
538 })
539 .ok()
540 });
541 let certificate_chain = self
542 .certificate_chain
543 .as_ref()
544 .and_then(|path| {
545 Config::load_file(path)
546 .map_err(|e| {
547 error!("cannot load certificate chain at path '{}': {:?}", path, e);
548 e
549 })
550 .ok()
551 })
552 .map(split_certificate_chain)
553 .unwrap_or_default();
554
555 let http_answers = self.get_http_answers()?;
556
557 if let Some(config) = config {
558 self.assign_config_timeouts(config);
559 }
560
561 let https_listener_config = HttpsListenerConfig {
562 address: self.address.into(),
563 sticky_name: self.sticky_name.clone(),
564 public_address: self.public_address.map(|a| a.into()),
565 cipher_list,
566 versions,
567 expect_proxy: self.expect_proxy.unwrap_or(false),
568 key,
569 certificate,
570 certificate_chain,
571 front_timeout: self.front_timeout.unwrap_or(DEFAULT_FRONT_TIMEOUT),
572 back_timeout: self.back_timeout.unwrap_or(DEFAULT_BACK_TIMEOUT),
573 connect_timeout: self.connect_timeout.unwrap_or(DEFAULT_CONNECT_TIMEOUT),
574 request_timeout: self.request_timeout.unwrap_or(DEFAULT_REQUEST_TIMEOUT),
575 cipher_suites,
576 signature_algorithms,
577 groups_list,
578 active: false,
579 send_tls13_tickets: self
580 .send_tls13_tickets
581 .unwrap_or(DEFAULT_SEND_TLS_13_TICKETS),
582 http_answers,
583 };
584
585 Ok(https_listener_config)
586 }
587
588 pub fn to_tcp(&mut self, config: Option<&Config>) -> Result<TcpListenerConfig, ConfigError> {
590 if self.protocol != Some(ListenerProtocol::Tcp) {
591 return Err(ConfigError::WrongListenerProtocol {
592 expected: ListenerProtocol::Tcp,
593 found: self.protocol.to_owned(),
594 });
595 }
596
597 if let Some(config) = config {
598 self.assign_config_timeouts(config);
599 }
600
601 Ok(TcpListenerConfig {
602 address: self.address.into(),
603 public_address: self.public_address.map(|a| a.into()),
604 expect_proxy: self.expect_proxy.unwrap_or(false),
605 front_timeout: self.front_timeout.unwrap_or(DEFAULT_FRONT_TIMEOUT),
606 back_timeout: self.back_timeout.unwrap_or(DEFAULT_BACK_TIMEOUT),
607 connect_timeout: self.connect_timeout.unwrap_or(DEFAULT_CONNECT_TIMEOUT),
608 active: false,
609 })
610 }
611}
612
613fn read_http_answer_file(path: &Option<String>) -> Result<Option<String>, ConfigError> {
615 match path {
616 Some(path) => {
617 let mut content = String::new();
618 let mut file = File::open(path).map_err(|io_error| ConfigError::FileOpen {
619 path_to_open: path.to_owned(),
620 io_error,
621 })?;
622
623 file.read_to_string(&mut content)
624 .map_err(|io_error| ConfigError::FileRead {
625 path_to_read: path.to_owned(),
626 io_error,
627 })?;
628
629 Ok(Some(content))
630 }
631 None => Ok(None),
632 }
633}
634
635#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
636#[serde(deny_unknown_fields)]
637pub struct MetricsConfig {
638 pub address: SocketAddr,
639 #[serde(default)]
640 pub tagged_metrics: bool,
641 #[serde(default)]
642 pub prefix: Option<String>,
643}
644
645#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
646#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
647#[serde(deny_unknown_fields)]
648pub enum PathRuleType {
649 Prefix,
650 Regex,
651 Equals,
652}
653
654#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
655#[serde(deny_unknown_fields)]
656pub struct FileClusterFrontendConfig {
657 pub address: SocketAddr,
658 pub hostname: Option<String>,
659 pub path: Option<String>,
661 pub path_type: Option<PathRuleType>,
663 pub method: Option<String>,
664 pub certificate: Option<String>,
665 pub key: Option<String>,
666 pub certificate_chain: Option<String>,
667 #[serde(default)]
668 pub tls_versions: Vec<TlsVersion>,
669 #[serde(default)]
670 pub position: RulePosition,
671 pub tags: Option<BTreeMap<String, String>>,
672}
673
674impl FileClusterFrontendConfig {
675 pub fn to_tcp_front(&self) -> Result<TcpFrontendConfig, ConfigError> {
676 if self.hostname.is_some() {
677 return Err(ConfigError::InvalidFrontendConfig("hostname".to_string()));
678 }
679 if self.path.is_some() {
680 return Err(ConfigError::InvalidFrontendConfig(
681 "path_prefix".to_string(),
682 ));
683 }
684 if self.certificate.is_some() {
685 return Err(ConfigError::InvalidFrontendConfig(
686 "certificate".to_string(),
687 ));
688 }
689 if self.hostname.is_some() {
690 return Err(ConfigError::InvalidFrontendConfig("hostname".to_string()));
691 }
692 if self.certificate_chain.is_some() {
693 return Err(ConfigError::InvalidFrontendConfig(
694 "certificate_chain".to_string(),
695 ));
696 }
697
698 Ok(TcpFrontendConfig {
699 address: self.address,
700 tags: self.tags.clone(),
701 })
702 }
703
704 pub fn to_http_front(&self, _cluster_id: &str) -> Result<HttpFrontendConfig, ConfigError> {
705 let hostname = match &self.hostname {
706 Some(hostname) => hostname.to_owned(),
707 None => {
708 return Err(ConfigError::Missing(MissingKind::Field(
709 "hostname".to_string(),
710 )));
711 }
712 };
713
714 let key_opt = match self.key.as_ref() {
715 None => None,
716 Some(path) => {
717 let key = Config::load_file(path)?;
718 Some(key)
719 }
720 };
721
722 let certificate_opt = match self.certificate.as_ref() {
723 None => None,
724 Some(path) => {
725 let certificate = Config::load_file(path)?;
726 Some(certificate)
727 }
728 };
729
730 let certificate_chain = match self.certificate_chain.as_ref() {
731 None => None,
732 Some(path) => {
733 let certificate_chain = Config::load_file(path)?;
734 Some(split_certificate_chain(certificate_chain))
735 }
736 };
737
738 let path = match (self.path.as_ref(), self.path_type.as_ref()) {
739 (None, _) => PathRule::prefix("".to_string()),
740 (Some(s), Some(PathRuleType::Prefix)) => PathRule::prefix(s.to_string()),
741 (Some(s), Some(PathRuleType::Regex)) => PathRule::regex(s.to_string()),
742 (Some(s), Some(PathRuleType::Equals)) => PathRule::equals(s.to_string()),
743 (Some(s), None) => PathRule::prefix(s.clone()),
744 };
745
746 Ok(HttpFrontendConfig {
747 address: self.address,
748 hostname,
749 certificate: certificate_opt,
750 key: key_opt,
751 certificate_chain,
752 tls_versions: self.tls_versions.clone(),
753 position: self.position,
754 path,
755 method: self.method.clone(),
756 tags: self.tags.clone(),
757 })
758 }
759}
760
761#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
762#[serde(deny_unknown_fields, rename_all = "lowercase")]
763pub enum ListenerProtocol {
764 Http,
765 Https,
766 Tcp,
767}
768
769#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
770#[serde(deny_unknown_fields, rename_all = "lowercase")]
771pub enum FileClusterProtocolConfig {
772 Http,
773 Tcp,
774}
775
776#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
777#[serde(deny_unknown_fields)]
778pub struct FileClusterConfig {
779 pub frontends: Vec<FileClusterFrontendConfig>,
780 pub backends: Vec<BackendConfig>,
781 pub protocol: FileClusterProtocolConfig,
782 pub sticky_session: Option<bool>,
783 pub https_redirect: Option<bool>,
784 #[serde(default)]
785 pub send_proxy: Option<bool>,
786 #[serde(default)]
787 pub load_balancing: LoadBalancingAlgorithms,
788 pub answer_503: Option<String>,
789 #[serde(default)]
790 pub load_metric: Option<LoadMetric>,
791}
792
793#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
794#[serde(deny_unknown_fields)]
795pub struct BackendConfig {
796 pub address: SocketAddr,
797 pub weight: Option<u8>,
798 pub sticky_id: Option<String>,
799 pub backup: Option<bool>,
800 pub backend_id: Option<String>,
801}
802
803impl FileClusterConfig {
804 pub fn to_cluster_config(
805 self,
806 cluster_id: &str,
807 expect_proxy: &HashSet<SocketAddr>,
808 ) -> Result<ClusterConfig, ConfigError> {
809 match self.protocol {
810 FileClusterProtocolConfig::Tcp => {
811 let mut has_expect_proxy = None;
812 let mut frontends = Vec::new();
813 for f in self.frontends {
814 if expect_proxy.contains(&f.address) {
815 match has_expect_proxy {
816 Some(true) => {}
817 Some(false) => {
818 return Err(ConfigError::Incompatible {
819 object: ObjectKind::Cluster,
820 id: cluster_id.to_owned(),
821 kind: IncompatibilityKind::ProxyProtocol,
822 });
823 }
824 None => has_expect_proxy = Some(true),
825 }
826 } else {
827 match has_expect_proxy {
828 Some(false) => {}
829 Some(true) => {
830 return Err(ConfigError::Incompatible {
831 object: ObjectKind::Cluster,
832 id: cluster_id.to_owned(),
833 kind: IncompatibilityKind::ProxyProtocol,
834 });
835 }
836 None => has_expect_proxy = Some(false),
837 }
838 }
839 let tcp_frontend = f.to_tcp_front()?;
840 frontends.push(tcp_frontend);
841 }
842
843 let send_proxy = self.send_proxy.unwrap_or(false);
844 let expect_proxy = has_expect_proxy.unwrap_or(false);
845 let proxy_protocol = match (send_proxy, expect_proxy) {
846 (true, true) => Some(ProxyProtocolConfig::RelayHeader),
847 (true, false) => Some(ProxyProtocolConfig::SendHeader),
848 (false, true) => Some(ProxyProtocolConfig::ExpectHeader),
849 _ => None,
850 };
851
852 Ok(ClusterConfig::Tcp(TcpClusterConfig {
853 cluster_id: cluster_id.to_string(),
854 frontends,
855 backends: self.backends,
856 proxy_protocol,
857 load_balancing: self.load_balancing,
858 load_metric: self.load_metric,
859 }))
860 }
861 FileClusterProtocolConfig::Http => {
862 let mut frontends = Vec::new();
863 for frontend in self.frontends {
864 let http_frontend = frontend.to_http_front(cluster_id)?;
865 frontends.push(http_frontend);
866 }
867
868 let answer_503 = self.answer_503.as_ref().and_then(|path| {
869 Config::load_file(path)
870 .map_err(|e| {
871 error!("cannot load 503 error page at path '{}': {:?}", path, e);
872 e
873 })
874 .ok()
875 });
876
877 Ok(ClusterConfig::Http(HttpClusterConfig {
878 cluster_id: cluster_id.to_string(),
879 frontends,
880 backends: self.backends,
881 sticky_session: self.sticky_session.unwrap_or(false),
882 https_redirect: self.https_redirect.unwrap_or(false),
883 load_balancing: self.load_balancing,
884 load_metric: self.load_metric,
885 answer_503,
886 }))
887 }
888 }
889 }
890}
891
892#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
893#[serde(deny_unknown_fields)]
894pub struct HttpFrontendConfig {
895 pub address: SocketAddr,
896 pub hostname: String,
897 pub path: PathRule,
898 pub method: Option<String>,
899 pub certificate: Option<String>,
900 pub key: Option<String>,
901 pub certificate_chain: Option<Vec<String>>,
902 #[serde(default)]
903 pub tls_versions: Vec<TlsVersion>,
904 #[serde(default)]
905 pub position: RulePosition,
906 pub tags: Option<BTreeMap<String, String>>,
907}
908
909impl HttpFrontendConfig {
910 pub fn generate_requests(&self, cluster_id: &str) -> Vec<Request> {
911 let mut v = Vec::new();
912
913 let tags = self.tags.clone().unwrap_or_default();
914
915 if self.key.is_some() && self.certificate.is_some() {
916 v.push(
917 RequestType::AddCertificate(AddCertificate {
918 address: self.address.into(),
919 certificate: CertificateAndKey {
920 key: self.key.clone().unwrap(),
921 certificate: self.certificate.clone().unwrap(),
922 certificate_chain: self.certificate_chain.clone().unwrap_or_default(),
923 versions: self.tls_versions.iter().map(|v| *v as i32).collect(),
924 names: vec![],
929 },
930 expired_at: None,
931 })
932 .into(),
933 );
934
935 v.push(
936 RequestType::AddHttpsFrontend(RequestHttpFrontend {
937 cluster_id: Some(cluster_id.to_string()),
938 address: self.address.into(),
939 hostname: self.hostname.clone(),
940 path: self.path.clone(),
941 method: self.method.clone(),
942 position: self.position.into(),
943 tags,
944 })
945 .into(),
946 );
947 } else {
948 v.push(
950 RequestType::AddHttpFrontend(RequestHttpFrontend {
951 cluster_id: Some(cluster_id.to_string()),
952 address: self.address.into(),
953 hostname: self.hostname.clone(),
954 path: self.path.clone(),
955 method: self.method.clone(),
956 position: self.position.into(),
957 tags,
958 })
959 .into(),
960 );
961 }
962
963 v
964 }
965}
966
967#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
968#[serde(deny_unknown_fields)]
969pub struct HttpClusterConfig {
970 pub cluster_id: String,
971 pub frontends: Vec<HttpFrontendConfig>,
972 pub backends: Vec<BackendConfig>,
973 pub sticky_session: bool,
974 pub https_redirect: bool,
975 pub load_balancing: LoadBalancingAlgorithms,
976 pub load_metric: Option<LoadMetric>,
977 pub answer_503: Option<String>,
978}
979
980impl HttpClusterConfig {
981 pub fn generate_requests(&self) -> Result<Vec<Request>, ConfigError> {
982 let mut v = vec![
983 RequestType::AddCluster(Cluster {
984 cluster_id: self.cluster_id.clone(),
985 sticky_session: self.sticky_session,
986 https_redirect: self.https_redirect,
987 proxy_protocol: None,
988 load_balancing: self.load_balancing as i32,
989 answer_503: self.answer_503.clone(),
990 load_metric: self.load_metric.map(|s| s as i32),
991 })
992 .into(),
993 ];
994
995 for frontend in &self.frontends {
996 let mut orders = frontend.generate_requests(&self.cluster_id);
997 v.append(&mut orders);
998 }
999
1000 for (backend_count, backend) in self.backends.iter().enumerate() {
1001 let load_balancing_parameters = Some(LoadBalancingParams {
1002 weight: backend.weight.unwrap_or(100) as i32,
1003 });
1004
1005 v.push(
1006 RequestType::AddBackend(AddBackend {
1007 cluster_id: self.cluster_id.clone(),
1008 backend_id: backend.backend_id.clone().unwrap_or_else(|| {
1009 format!("{}-{}-{}", self.cluster_id, backend_count, backend.address)
1010 }),
1011 address: backend.address.into(),
1012 load_balancing_parameters,
1013 sticky_id: backend.sticky_id.clone(),
1014 backup: backend.backup,
1015 })
1016 .into(),
1017 );
1018 }
1019
1020 Ok(v)
1021 }
1022}
1023
1024#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
1025pub struct TcpFrontendConfig {
1026 pub address: SocketAddr,
1027 pub tags: Option<BTreeMap<String, String>>,
1028}
1029
1030#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
1031pub struct TcpClusterConfig {
1032 pub cluster_id: String,
1033 pub frontends: Vec<TcpFrontendConfig>,
1034 pub backends: Vec<BackendConfig>,
1035 #[serde(default)]
1036 pub proxy_protocol: Option<ProxyProtocolConfig>,
1037 pub load_balancing: LoadBalancingAlgorithms,
1038 pub load_metric: Option<LoadMetric>,
1039}
1040
1041impl TcpClusterConfig {
1042 pub fn generate_requests(&self) -> Result<Vec<Request>, ConfigError> {
1043 let mut v = vec![
1044 RequestType::AddCluster(Cluster {
1045 cluster_id: self.cluster_id.clone(),
1046 sticky_session: false,
1047 https_redirect: false,
1048 proxy_protocol: self.proxy_protocol.map(|s| s as i32),
1049 load_balancing: self.load_balancing as i32,
1050 load_metric: self.load_metric.map(|s| s as i32),
1051 answer_503: None,
1052 })
1053 .into(),
1054 ];
1055
1056 for frontend in &self.frontends {
1057 v.push(
1058 RequestType::AddTcpFrontend(RequestTcpFrontend {
1059 cluster_id: self.cluster_id.clone(),
1060 address: frontend.address.into(),
1061 tags: frontend.tags.clone().unwrap_or(BTreeMap::new()),
1062 })
1063 .into(),
1064 );
1065 }
1066
1067 for (backend_count, backend) in self.backends.iter().enumerate() {
1068 let load_balancing_parameters = Some(LoadBalancingParams {
1069 weight: backend.weight.unwrap_or(100) as i32,
1070 });
1071
1072 v.push(
1073 RequestType::AddBackend(AddBackend {
1074 cluster_id: self.cluster_id.clone(),
1075 backend_id: backend.backend_id.clone().unwrap_or_else(|| {
1076 format!("{}-{}-{}", self.cluster_id, backend_count, backend.address)
1077 }),
1078 address: backend.address.into(),
1079 load_balancing_parameters,
1080 sticky_id: backend.sticky_id.clone(),
1081 backup: backend.backup,
1082 })
1083 .into(),
1084 );
1085 }
1086
1087 Ok(v)
1088 }
1089}
1090
1091#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
1092pub enum ClusterConfig {
1093 Http(HttpClusterConfig),
1094 Tcp(TcpClusterConfig),
1095}
1096
1097impl ClusterConfig {
1098 pub fn generate_requests(&self) -> Result<Vec<Request>, ConfigError> {
1099 match *self {
1100 ClusterConfig::Http(ref http) => http.generate_requests(),
1101 ClusterConfig::Tcp(ref tcp) => tcp.generate_requests(),
1102 }
1103 }
1104}
1105
1106#[derive(Debug, Clone, PartialEq, Eq, Serialize, Default, Deserialize)]
1108pub struct FileConfig {
1109 pub command_socket: Option<String>,
1110 pub command_buffer_size: Option<u64>,
1111 pub max_command_buffer_size: Option<u64>,
1112 pub max_connections: Option<usize>,
1113 pub min_buffers: Option<u64>,
1114 pub max_buffers: Option<u64>,
1115 pub buffer_size: Option<u64>,
1116 pub saved_state: Option<String>,
1117 #[serde(default)]
1118 pub automatic_state_save: Option<bool>,
1119 pub log_level: Option<String>,
1120 pub log_target: Option<String>,
1121 #[serde(default)]
1122 pub log_colored: bool,
1123 #[serde(default)]
1124 pub access_logs_target: Option<String>,
1125 #[serde(default)]
1126 pub access_logs_format: Option<AccessLogFormat>,
1127 #[serde(default)]
1128 pub access_logs_colored: Option<bool>,
1129 pub worker_count: Option<u16>,
1130 pub worker_automatic_restart: Option<bool>,
1131 pub metrics: Option<MetricsConfig>,
1132 pub disable_cluster_metrics: Option<bool>,
1133 pub listeners: Option<Vec<ListenerBuilder>>,
1134 pub clusters: Option<HashMap<String, FileClusterConfig>>,
1135 pub handle_process_affinity: Option<bool>,
1136 pub ctl_command_timeout: Option<u64>,
1137 pub pid_file_path: Option<String>,
1138 pub activate_listeners: Option<bool>,
1139 #[serde(default)]
1140 pub front_timeout: Option<u32>,
1141 #[serde(default)]
1142 pub back_timeout: Option<u32>,
1143 #[serde(default)]
1144 pub connect_timeout: Option<u32>,
1145 #[serde(default)]
1146 pub zombie_check_interval: Option<u32>,
1147 #[serde(default)]
1148 pub accept_queue_timeout: Option<u32>,
1149 #[serde(default)]
1150 pub request_timeout: Option<u32>,
1151 #[serde(default)]
1152 pub worker_timeout: Option<u32>,
1153}
1154
1155impl FileConfig {
1156 pub fn load_from_path(path: &str) -> Result<FileConfig, ConfigError> {
1157 let data = Config::load_file(path)?;
1158
1159 let config: FileConfig = match toml::from_str(&data) {
1160 Ok(config) => config,
1161 Err(e) => {
1162 display_toml_error(&data, &e);
1163 return Err(ConfigError::DeserializeToml(e.to_string()));
1164 }
1165 };
1166
1167 let mut reserved_address: HashSet<SocketAddr> = HashSet::new();
1168
1169 if let Some(listeners) = config.listeners.as_ref() {
1170 for listener in listeners.iter() {
1171 if reserved_address.contains(&listener.address) {
1172 return Err(ConfigError::ListenerAddressAlreadyInUse(listener.address));
1173 }
1174 reserved_address.insert(listener.address);
1175 }
1176 }
1177
1178 Ok(config)
1200 }
1201}
1202
1203pub struct ConfigBuilder {
1205 file: FileConfig,
1206 known_addresses: HashMap<SocketAddr, ListenerProtocol>,
1207 expect_proxy_addresses: HashSet<SocketAddr>,
1208 built: Config,
1209}
1210
1211impl ConfigBuilder {
1212 pub fn new<S>(file_config: FileConfig, config_path: S) -> Self
1216 where
1217 S: ToString,
1218 {
1219 let built = Config {
1220 accept_queue_timeout: file_config
1221 .accept_queue_timeout
1222 .unwrap_or(DEFAULT_ACCEPT_QUEUE_TIMEOUT),
1223 activate_listeners: file_config.activate_listeners.unwrap_or(true),
1224 automatic_state_save: file_config
1225 .automatic_state_save
1226 .unwrap_or(DEFAULT_AUTOMATIC_STATE_SAVE),
1227 back_timeout: file_config.back_timeout.unwrap_or(DEFAULT_BACK_TIMEOUT),
1228 buffer_size: file_config.buffer_size.unwrap_or(DEFAULT_BUFFER_SIZE),
1229 command_buffer_size: file_config
1230 .command_buffer_size
1231 .unwrap_or(DEFAULT_COMMAND_BUFFER_SIZE),
1232 config_path: config_path.to_string(),
1233 connect_timeout: file_config
1234 .connect_timeout
1235 .unwrap_or(DEFAULT_CONNECT_TIMEOUT),
1236 ctl_command_timeout: file_config.ctl_command_timeout.unwrap_or(1_000),
1237 front_timeout: file_config.front_timeout.unwrap_or(DEFAULT_FRONT_TIMEOUT),
1238 handle_process_affinity: file_config.handle_process_affinity.unwrap_or(false),
1239 access_logs_target: file_config.access_logs_target.clone(),
1240 access_logs_format: file_config.access_logs_format.clone(),
1241 access_logs_colored: file_config.access_logs_colored,
1242 log_level: file_config
1243 .log_level
1244 .clone()
1245 .unwrap_or_else(|| String::from("info")),
1246 log_target: file_config
1247 .log_target
1248 .clone()
1249 .unwrap_or_else(|| String::from("stdout")),
1250 log_colored: file_config.log_colored,
1251 max_buffers: file_config.max_buffers.unwrap_or(DEFAULT_MAX_BUFFERS),
1252 max_command_buffer_size: file_config
1253 .max_command_buffer_size
1254 .unwrap_or(DEFAULT_MAX_COMMAND_BUFFER_SIZE),
1255 max_connections: file_config
1256 .max_connections
1257 .unwrap_or(DEFAULT_MAX_CONNECTIONS),
1258 metrics: file_config.metrics.clone(),
1259 disable_cluster_metrics: file_config
1260 .disable_cluster_metrics
1261 .unwrap_or(DEFAULT_DISABLE_CLUSTER_METRICS),
1262 min_buffers: std::cmp::min(
1263 file_config.min_buffers.unwrap_or(DEFAULT_MIN_BUFFERS),
1264 file_config.max_buffers.unwrap_or(DEFAULT_MAX_BUFFERS),
1265 ),
1266 pid_file_path: file_config.pid_file_path.clone(),
1267 request_timeout: file_config
1268 .request_timeout
1269 .unwrap_or(DEFAULT_REQUEST_TIMEOUT),
1270 saved_state: file_config.saved_state.clone(),
1271 worker_automatic_restart: file_config
1272 .worker_automatic_restart
1273 .unwrap_or(DEFAULT_WORKER_AUTOMATIC_RESTART),
1274 worker_count: file_config.worker_count.unwrap_or(DEFAULT_WORKER_COUNT),
1275 zombie_check_interval: file_config
1276 .zombie_check_interval
1277 .unwrap_or(DEFAULT_ZOMBIE_CHECK_INTERVAL),
1278 worker_timeout: file_config.worker_timeout.unwrap_or(DEFAULT_WORKER_TIMEOUT),
1279 ..Default::default()
1280 };
1281
1282 Self {
1283 file: file_config,
1284 known_addresses: HashMap::new(),
1285 expect_proxy_addresses: HashSet::new(),
1286 built,
1287 }
1288 }
1289
1290 fn push_tls_listener(&mut self, mut listener: ListenerBuilder) -> Result<(), ConfigError> {
1291 let listener = listener.to_tls(Some(&self.built))?;
1292 self.built.https_listeners.push(listener);
1293 Ok(())
1294 }
1295
1296 fn push_http_listener(&mut self, mut listener: ListenerBuilder) -> Result<(), ConfigError> {
1297 let listener = listener.to_http(Some(&self.built))?;
1298 self.built.http_listeners.push(listener);
1299 Ok(())
1300 }
1301
1302 fn push_tcp_listener(&mut self, mut listener: ListenerBuilder) -> Result<(), ConfigError> {
1303 let listener = listener.to_tcp(Some(&self.built))?;
1304 self.built.tcp_listeners.push(listener);
1305 Ok(())
1306 }
1307
1308 fn populate_listeners(&mut self, listeners: Vec<ListenerBuilder>) -> Result<(), ConfigError> {
1309 for listener in listeners.iter() {
1310 if self.known_addresses.contains_key(&listener.address) {
1311 return Err(ConfigError::ListenerAddressAlreadyInUse(listener.address));
1312 }
1313
1314 let protocol = listener
1315 .protocol
1316 .ok_or(ConfigError::Missing(MissingKind::Protocol))?;
1317
1318 self.known_addresses.insert(listener.address, protocol);
1319 if listener.expect_proxy == Some(true) {
1320 self.expect_proxy_addresses.insert(listener.address);
1321 }
1322
1323 if listener.public_address.is_some() && listener.expect_proxy == Some(true) {
1324 return Err(ConfigError::Incompatible {
1325 object: ObjectKind::Listener,
1326 id: listener.address.to_string(),
1327 kind: IncompatibilityKind::PublicAddress,
1328 });
1329 }
1330
1331 match protocol {
1332 ListenerProtocol::Https => self.push_tls_listener(listener.clone())?,
1333 ListenerProtocol::Http => self.push_http_listener(listener.clone())?,
1334 ListenerProtocol::Tcp => self.push_tcp_listener(listener.clone())?,
1335 }
1336 }
1337 Ok(())
1338 }
1339
1340 fn populate_clusters(
1341 &mut self,
1342 mut file_cluster_configs: HashMap<String, FileClusterConfig>,
1343 ) -> Result<(), ConfigError> {
1344 for (id, file_cluster_config) in file_cluster_configs.drain() {
1345 let mut cluster_config =
1346 file_cluster_config.to_cluster_config(id.as_str(), &self.expect_proxy_addresses)?;
1347
1348 match cluster_config {
1349 ClusterConfig::Http(ref mut http) => {
1350 for frontend in http.frontends.iter_mut() {
1351 match self.known_addresses.get(&frontend.address) {
1352 Some(ListenerProtocol::Tcp) => {
1353 return Err(ConfigError::WrongFrontendProtocol(
1354 ListenerProtocol::Tcp,
1355 ));
1356 }
1357 Some(ListenerProtocol::Http) => {
1358 if frontend.certificate.is_some() {
1359 return Err(ConfigError::WrongFrontendProtocol(
1360 ListenerProtocol::Http,
1361 ));
1362 }
1363 }
1364 Some(ListenerProtocol::Https) => {
1365 if frontend.certificate.is_none() {
1366 if let Some(https_listener) =
1367 self.built.https_listeners.iter().find(|listener| {
1368 listener.address == frontend.address.into()
1369 && listener.certificate.is_some()
1370 })
1371 {
1372 frontend
1374 .certificate
1375 .clone_from(&https_listener.certificate);
1376 frontend.certificate_chain =
1377 Some(https_listener.certificate_chain.clone());
1378 frontend.key.clone_from(&https_listener.key);
1379 }
1380 if frontend.certificate.is_none() {
1381 debug!("known addresses: {:?}", self.known_addresses);
1382 debug!("frontend: {:?}", frontend);
1383 return Err(ConfigError::WrongFrontendProtocol(
1384 ListenerProtocol::Https,
1385 ));
1386 }
1387 }
1388 }
1389 None => {
1390 let file_listener_protocol = if frontend.certificate.is_some() {
1392 self.push_tls_listener(ListenerBuilder::new(
1393 frontend.address.into(),
1394 ListenerProtocol::Https,
1395 ))?;
1396
1397 ListenerProtocol::Https
1398 } else {
1399 self.push_http_listener(ListenerBuilder::new(
1400 frontend.address.into(),
1401 ListenerProtocol::Http,
1402 ))?;
1403
1404 ListenerProtocol::Http
1405 };
1406 self.known_addresses
1407 .insert(frontend.address, file_listener_protocol);
1408 }
1409 }
1410 }
1411 }
1412 ClusterConfig::Tcp(ref tcp) => {
1413 for frontend in &tcp.frontends {
1415 match self.known_addresses.get(&frontend.address) {
1416 Some(ListenerProtocol::Http) | Some(ListenerProtocol::Https) => {
1417 return Err(ConfigError::WrongFrontendProtocol(
1418 ListenerProtocol::Http,
1419 ));
1420 }
1421 Some(ListenerProtocol::Tcp) => {}
1422 None => {
1423 self.push_tcp_listener(ListenerBuilder::new(
1425 frontend.address.into(),
1426 ListenerProtocol::Tcp,
1427 ))?;
1428 self.known_addresses
1429 .insert(frontend.address, ListenerProtocol::Tcp);
1430 }
1431 }
1432 }
1433 }
1434 }
1435
1436 self.built.clusters.insert(id, cluster_config);
1437 }
1438 Ok(())
1439 }
1440
1441 pub fn into_config(&mut self) -> Result<Config, ConfigError> {
1443 if let Some(listeners) = &self.file.listeners {
1444 self.populate_listeners(listeners.clone())?;
1445 }
1446
1447 if let Some(file_cluster_configs) = &self.file.clusters {
1448 self.populate_clusters(file_cluster_configs.clone())?;
1449 }
1450
1451 let command_socket_path = self.file.command_socket.clone().unwrap_or({
1452 let mut path = env::current_dir().map_err(|e| ConfigError::Env(e.to_string()))?;
1453 path.push("sozu.sock");
1454 let verified_path = path
1455 .to_str()
1456 .ok_or(ConfigError::InvalidPath(path.clone()))?;
1457 verified_path.to_owned()
1458 });
1459
1460 if let (None, Some(true)) = (&self.file.saved_state, &self.file.automatic_state_save) {
1461 return Err(ConfigError::Missing(MissingKind::SavedState));
1462 }
1463
1464 Ok(Config {
1465 command_socket: command_socket_path,
1466 ..self.built.clone()
1467 })
1468 }
1469}
1470
1471#[derive(Clone, PartialEq, Eq, Serialize, Default, Deserialize)]
1475pub struct Config {
1476 pub config_path: String,
1477 pub command_socket: String,
1478 pub command_buffer_size: u64,
1479 pub max_command_buffer_size: u64,
1480 pub max_connections: usize,
1481 pub min_buffers: u64,
1482 pub max_buffers: u64,
1483 pub buffer_size: u64,
1484 pub saved_state: Option<String>,
1485 #[serde(default)]
1486 pub automatic_state_save: bool,
1487 pub log_level: String,
1488 pub log_target: String,
1489 pub log_colored: bool,
1490 #[serde(default)]
1491 pub access_logs_target: Option<String>,
1492 pub access_logs_format: Option<AccessLogFormat>,
1493 pub access_logs_colored: Option<bool>,
1494 pub worker_count: u16,
1495 pub worker_automatic_restart: bool,
1496 pub metrics: Option<MetricsConfig>,
1497 #[serde(default = "default_disable_cluster_metrics")]
1498 pub disable_cluster_metrics: bool,
1499 pub http_listeners: Vec<HttpListenerConfig>,
1500 pub https_listeners: Vec<HttpsListenerConfig>,
1501 pub tcp_listeners: Vec<TcpListenerConfig>,
1502 pub clusters: HashMap<String, ClusterConfig>,
1503 pub handle_process_affinity: bool,
1504 pub ctl_command_timeout: u64,
1505 pub pid_file_path: Option<String>,
1506 pub activate_listeners: bool,
1507 #[serde(default = "default_front_timeout")]
1508 pub front_timeout: u32,
1509 #[serde(default = "default_back_timeout")]
1510 pub back_timeout: u32,
1511 #[serde(default = "default_connect_timeout")]
1512 pub connect_timeout: u32,
1513 #[serde(default = "default_zombie_check_interval")]
1514 pub zombie_check_interval: u32,
1515 #[serde(default = "default_accept_queue_timeout")]
1516 pub accept_queue_timeout: u32,
1517 #[serde(default = "default_request_timeout")]
1518 pub request_timeout: u32,
1519 #[serde(default = "default_worker_timeout")]
1520 pub worker_timeout: u32,
1521}
1522
1523fn default_front_timeout() -> u32 {
1524 DEFAULT_FRONT_TIMEOUT
1525}
1526
1527fn default_back_timeout() -> u32 {
1528 DEFAULT_BACK_TIMEOUT
1529}
1530
1531fn default_connect_timeout() -> u32 {
1532 DEFAULT_CONNECT_TIMEOUT
1533}
1534
1535fn default_request_timeout() -> u32 {
1536 DEFAULT_REQUEST_TIMEOUT
1537}
1538
1539fn default_zombie_check_interval() -> u32 {
1540 DEFAULT_ZOMBIE_CHECK_INTERVAL
1541}
1542
1543fn default_accept_queue_timeout() -> u32 {
1544 DEFAULT_ACCEPT_QUEUE_TIMEOUT
1545}
1546
1547fn default_disable_cluster_metrics() -> bool {
1548 DEFAULT_DISABLE_CLUSTER_METRICS
1549}
1550
1551fn default_worker_timeout() -> u32 {
1552 DEFAULT_WORKER_TIMEOUT
1553}
1554
1555impl Config {
1556 pub fn load_from_path(path: &str) -> Result<Config, ConfigError> {
1558 let file_config = FileConfig::load_from_path(path)?;
1559
1560 let mut config = ConfigBuilder::new(file_config, path).into_config()?;
1561
1562 config.saved_state = config.saved_state_path()?;
1564
1565 Ok(config)
1566 }
1567
1568 pub fn generate_config_messages(&self) -> Result<Vec<WorkerRequest>, ConfigError> {
1570 let mut v = Vec::new();
1571 let mut count = 0u8;
1572
1573 for listener in &self.http_listeners {
1574 v.push(WorkerRequest {
1575 id: format!("CONFIG-{count}"),
1576 content: RequestType::AddHttpListener(listener.clone()).into(),
1577 });
1578 count += 1;
1579 }
1580
1581 for listener in &self.https_listeners {
1582 v.push(WorkerRequest {
1583 id: format!("CONFIG-{count}"),
1584 content: RequestType::AddHttpsListener(listener.clone()).into(),
1585 });
1586 count += 1;
1587 }
1588
1589 for listener in &self.tcp_listeners {
1590 v.push(WorkerRequest {
1591 id: format!("CONFIG-{count}"),
1592 content: RequestType::AddTcpListener(*listener).into(),
1593 });
1594 count += 1;
1595 }
1596
1597 for cluster in self.clusters.values() {
1598 let mut orders = cluster.generate_requests()?;
1599 for content in orders.drain(..) {
1600 v.push(WorkerRequest {
1601 id: format!("CONFIG-{count}"),
1602 content,
1603 });
1604 count += 1;
1605 }
1606 }
1607
1608 if self.activate_listeners {
1609 for listener in &self.http_listeners {
1610 v.push(WorkerRequest {
1611 id: format!("CONFIG-{count}"),
1612 content: RequestType::ActivateListener(ActivateListener {
1613 address: listener.address,
1614 proxy: ListenerType::Http.into(),
1615 from_scm: false,
1616 })
1617 .into(),
1618 });
1619 count += 1;
1620 }
1621
1622 for listener in &self.https_listeners {
1623 v.push(WorkerRequest {
1624 id: format!("CONFIG-{count}"),
1625 content: RequestType::ActivateListener(ActivateListener {
1626 address: listener.address,
1627 proxy: ListenerType::Https.into(),
1628 from_scm: false,
1629 })
1630 .into(),
1631 });
1632 count += 1;
1633 }
1634
1635 for listener in &self.tcp_listeners {
1636 v.push(WorkerRequest {
1637 id: format!("CONFIG-{count}"),
1638 content: RequestType::ActivateListener(ActivateListener {
1639 address: listener.address,
1640 proxy: ListenerType::Tcp.into(),
1641 from_scm: false,
1642 })
1643 .into(),
1644 });
1645 count += 1;
1646 }
1647 }
1648
1649 if self.disable_cluster_metrics {
1650 v.push(WorkerRequest {
1651 id: format!("CONFIG-{count}"),
1652 content: RequestType::ConfigureMetrics(MetricsConfiguration::Disabled.into())
1653 .into(),
1654 });
1655 }
1657
1658 Ok(v)
1659 }
1660
1661 pub fn command_socket_path(&self) -> Result<String, ConfigError> {
1663 let config_path_buf = PathBuf::from(self.config_path.clone());
1664 let mut config_dir = config_path_buf
1665 .parent()
1666 .ok_or(ConfigError::NoFileParent(
1667 config_path_buf.to_string_lossy().to_string(),
1668 ))?
1669 .to_path_buf();
1670
1671 let socket_path = PathBuf::from(self.command_socket.clone());
1672
1673 let mut socket_parent_dir = match socket_path.parent() {
1674 None => config_dir,
1677 Some(path) => {
1678 config_dir.push(path);
1680 config_dir.canonicalize().map_err(|io_error| {
1682 ConfigError::SocketPathError(format!(
1683 "Could not canonicalize path {config_dir:?}: {io_error}"
1684 ))
1685 })?
1686 }
1687 };
1688
1689 let socket_name = socket_path
1690 .file_name()
1691 .ok_or(ConfigError::SocketPathError(format!(
1692 "could not get command socket file name from {socket_path:?}"
1693 )))?;
1694
1695 socket_parent_dir.push(socket_name);
1697
1698 let command_socket_path = socket_parent_dir
1699 .to_str()
1700 .ok_or(ConfigError::SocketPathError(format!(
1701 "Invalid socket path {socket_parent_dir:?}"
1702 )))?
1703 .to_string();
1704
1705 Ok(command_socket_path)
1706 }
1707
1708 fn saved_state_path(&self) -> Result<Option<String>, ConfigError> {
1710 let path = match self.saved_state.as_ref() {
1711 Some(path) => path,
1712 None => return Ok(None),
1713 };
1714
1715 debug!("saved_stated path in the config: {}", path);
1716 let config_path = PathBuf::from(self.config_path.clone());
1717
1718 debug!("Config path buffer: {:?}", config_path);
1719 let config_dir = config_path
1720 .parent()
1721 .ok_or(ConfigError::SaveStatePath(format!(
1722 "Could get parent directory of config file {config_path:?}"
1723 )))?;
1724
1725 debug!("Config folder: {:?}", config_dir);
1726 if !config_dir.exists() {
1727 create_dir_all(config_dir).map_err(|io_error| {
1728 ConfigError::SaveStatePath(format!(
1729 "failed to create state parent directory '{config_dir:?}': {io_error}"
1730 ))
1731 })?;
1732 }
1733
1734 let mut saved_state_path_raw = config_dir.to_path_buf();
1735 saved_state_path_raw.push(path);
1736 debug!(
1737 "Looking for saved state on the path {:?}",
1738 saved_state_path_raw
1739 );
1740
1741 match metadata(path) {
1742 Err(err) if matches!(err.kind(), ErrorKind::NotFound) => {
1743 info!("Create an empty state file at '{}'", path);
1744 File::create(path).map_err(|io_error| {
1745 ConfigError::SaveStatePath(format!(
1746 "failed to create state file '{path:?}': {io_error}"
1747 ))
1748 })?;
1749 }
1750 _ => {}
1751 }
1752
1753 saved_state_path_raw.canonicalize().map_err(|io_error| {
1754 ConfigError::SaveStatePath(format!(
1755 "could not get saved state path from config file input {path:?}: {io_error}"
1756 ))
1757 })?;
1758
1759 let stringified_path = saved_state_path_raw
1760 .to_str()
1761 .ok_or(ConfigError::SaveStatePath(format!(
1762 "Invalid path {saved_state_path_raw:?}"
1763 )))?
1764 .to_string();
1765
1766 Ok(Some(stringified_path))
1767 }
1768
1769 pub fn load_file(path: &str) -> Result<String, ConfigError> {
1771 std::fs::read_to_string(path).map_err(|io_error| ConfigError::FileRead {
1772 path_to_read: path.to_owned(),
1773 io_error,
1774 })
1775 }
1776
1777 pub fn load_file_bytes(path: &str) -> Result<Vec<u8>, ConfigError> {
1779 std::fs::read(path).map_err(|io_error| ConfigError::FileRead {
1780 path_to_read: path.to_owned(),
1781 io_error,
1782 })
1783 }
1784}
1785
1786impl fmt::Debug for Config {
1787 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1788 f.debug_struct("Config")
1789 .field("config_path", &self.config_path)
1790 .field("command_socket", &self.command_socket)
1791 .field("command_buffer_size", &self.command_buffer_size)
1792 .field("max_command_buffer_size", &self.max_command_buffer_size)
1793 .field("max_connections", &self.max_connections)
1794 .field("min_buffers", &self.min_buffers)
1795 .field("max_buffers", &self.max_buffers)
1796 .field("buffer_size", &self.buffer_size)
1797 .field("saved_state", &self.saved_state)
1798 .field("automatic_state_save", &self.automatic_state_save)
1799 .field("log_level", &self.log_level)
1800 .field("log_target", &self.log_target)
1801 .field("access_logs_target", &self.access_logs_target)
1802 .field("access_logs_format", &self.access_logs_format)
1803 .field("worker_count", &self.worker_count)
1804 .field("worker_automatic_restart", &self.worker_automatic_restart)
1805 .field("metrics", &self.metrics)
1806 .field("disable_cluster_metrics", &self.disable_cluster_metrics)
1807 .field("handle_process_affinity", &self.handle_process_affinity)
1808 .field("ctl_command_timeout", &self.ctl_command_timeout)
1809 .field("pid_file_path", &self.pid_file_path)
1810 .field("activate_listeners", &self.activate_listeners)
1811 .field("front_timeout", &self.front_timeout)
1812 .field("back_timeout", &self.back_timeout)
1813 .field("connect_timeout", &self.connect_timeout)
1814 .field("zombie_check_interval", &self.zombie_check_interval)
1815 .field("accept_queue_timeout", &self.accept_queue_timeout)
1816 .field("request_timeout", &self.request_timeout)
1817 .field("worker_timeout", &self.worker_timeout)
1818 .finish()
1819 }
1820}
1821
1822fn display_toml_error(file: &str, error: &toml::de::Error) {
1823 println!("error parsing the configuration file '{file}': {error}");
1824 if let Some(Range { start, end }) = error.span() {
1825 print!("error parsing the configuration file '{file}' at position: {start}, {end}");
1826 }
1827}
1828
1829impl ServerConfig {
1830 pub fn slab_capacity(&self) -> u64 {
1832 10 + 2 * self.max_connections
1833 }
1834}
1835
1836impl From<&Config> for ServerConfig {
1838 fn from(config: &Config) -> Self {
1839 let metrics = config.metrics.clone().map(|m| ServerMetricsConfig {
1840 address: m.address.to_string(),
1841 tagged_metrics: m.tagged_metrics,
1842 prefix: m.prefix,
1843 });
1844 Self {
1845 max_connections: config.max_connections as u64,
1846 front_timeout: config.front_timeout,
1847 back_timeout: config.back_timeout,
1848 connect_timeout: config.connect_timeout,
1849 zombie_check_interval: config.zombie_check_interval,
1850 accept_queue_timeout: config.accept_queue_timeout,
1851 min_buffers: config.min_buffers,
1852 max_buffers: config.max_buffers,
1853 buffer_size: config.buffer_size,
1854 log_level: config.log_level.clone(),
1855 log_target: config.log_target.clone(),
1856 access_logs_target: config.access_logs_target.clone(),
1857 command_buffer_size: config.command_buffer_size,
1858 max_command_buffer_size: config.max_command_buffer_size,
1859 metrics,
1860 access_log_format: ProtobufAccessLogFormat::from(&config.access_logs_format) as i32,
1861 log_colored: config.log_colored,
1862 }
1863 }
1864}
1865
1866#[cfg(test)]
1867mod tests {
1868 use toml::to_string;
1869
1870 use super::*;
1871
1872 #[test]
1873 fn serialize() {
1874 let http = ListenerBuilder::new(
1875 SocketAddress::new_v4(127, 0, 0, 1, 8080),
1876 ListenerProtocol::Http,
1877 )
1878 .with_answer_404_path(Some("404.html"))
1879 .to_owned();
1880 println!("http: {:?}", to_string(&http));
1881
1882 let https = ListenerBuilder::new(
1883 SocketAddress::new_v4(127, 0, 0, 1, 8443),
1884 ListenerProtocol::Https,
1885 )
1886 .with_answer_404_path(Some("404.html"))
1887 .to_owned();
1888 println!("https: {:?}", to_string(&https));
1889
1890 let listeners = vec![http, https];
1891 let config = FileConfig {
1892 command_socket: Some(String::from("./command_folder/sock")),
1893 worker_count: Some(2),
1894 worker_automatic_restart: Some(true),
1895 max_connections: Some(500),
1896 min_buffers: Some(1),
1897 max_buffers: Some(500),
1898 buffer_size: Some(16393),
1899 metrics: Some(MetricsConfig {
1900 address: "127.0.0.1:8125".parse().unwrap(),
1901 tagged_metrics: false,
1902 prefix: Some(String::from("sozu-metrics")),
1903 }),
1904 listeners: Some(listeners),
1905 ..Default::default()
1906 };
1907
1908 println!("config: {:?}", to_string(&config));
1909 let encoded = to_string(&config).unwrap();
1910 println!("conf:\n{encoded}");
1911 }
1912
1913 #[test]
1914 fn parse() {
1915 let path = "assets/config.toml";
1916 let config = Config::load_from_path(path).unwrap_or_else(|load_error| {
1917 panic!("Cannot load config from path {path}: {load_error:?}")
1918 });
1919 println!("config: {config:#?}");
1920 }
1922}