Skip to main content

actix_settings/
lib.rs

1//! Easily manage Actix Web's settings from a TOML file and environment variables.
2//!
3//! To get started add a [`Settings::parse_toml("./Server.toml")`](Settings::parse_toml) call to the
4//! top of your main function. This will create a template file with descriptions of all the
5//! configurable settings. You can change or remove anything in that file and it will be picked up
6//! the next time you run your application.
7//!
8//! Overriding parts of the file can be done from values using [`Settings::override_field`] or from
9//! the environment using [`Settings::override_field_with_env_var`].
10//!
11//! # Examples
12//!
13//! See examples folder on GitHub for complete example.
14//!
15//! ```ignore
16//! # use actix_web::{
17//! #     get,
18//! #     middleware::{Compress, Condition, Logger},
19//! #     web, App, HttpServer,
20//! # };
21//! use actix_settings::{ApplySettings as _, Mode, Settings};
22//!
23//! #[actix_web::main]
24//! async fn main() -> std::io::Result<()> {
25//!     let mut settings = Settings::parse_toml("./Server.toml")
26//!         .expect("Failed to parse `Settings` from Server.toml");
27//!
28//!     // If the environment variable `$APPLICATION__HOSTS` is set,
29//!     // have its value override the `settings.actix.hosts` setting:
30//!     Settings::override_field_with_env_var(&mut settings.actix.hosts, "APPLICATION__HOSTS")?;
31//!
32//!     init_logger(&settings);
33//!
34//!     HttpServer::new({
35//!         // clone settings into each worker thread
36//!         let settings = settings.clone();
37//!
38//!         move || {
39//!             App::new()
40//!                 // Include this `.wrap()` call for compression settings to take effect
41//!                 .wrap(Condition::new(
42//!                     settings.actix.enable_compression,
43//!                     Compress::default(),
44//!                 ))
45//!
46//!                 // add request logger
47//!                 .wrap(Logger::default())
48//!
49//!                 // make `Settings` available to handlers
50//!                 .app_data(web::Data::new(settings.clone()))
51//!
52//!                 // add request handlers as normal
53//!                 .service(index)
54//!         }
55//!     })
56//!     // apply the `Settings` to Actix Web's `HttpServer`
57//!     .try_apply_settings(&settings)?
58//!     .run()
59//!     .await
60//! }
61//! ```
62
63#![forbid(unsafe_code)]
64#![warn(missing_docs, missing_debug_implementations)]
65#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
66#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
67#![cfg_attr(docsrs, feature(doc_cfg))]
68
69use std::{
70    env, fmt,
71    fs::File,
72    io::{Read as _, Write as _},
73    path::Path,
74    time::Duration,
75};
76
77use actix_http::{Request, Response};
78use actix_service::IntoServiceFactory;
79use actix_web::{
80    body::MessageBody,
81    dev::{AppConfig, ServiceFactory},
82    http::KeepAlive as ActixKeepAlive,
83    Error as WebError, HttpServer,
84};
85use serde::{de, Deserialize};
86
87#[macro_use]
88mod error;
89mod parse;
90mod settings;
91
92#[cfg(all(feature = "openssl", feature = "rustls-0_23", not(docsrs)))]
93compile_error!("`actix-settings` supports only one TLS backend feature at a time");
94
95#[cfg(any(feature = "openssl", feature = "rustls-0_23"))]
96pub use self::settings::Tls;
97pub use self::{
98    error::Error,
99    parse::Parse,
100    settings::{
101        ActixSettings, Address, Backlog, KeepAlive, MaxConnectionRate, MaxConnections, Mode,
102        NumWorkers, Timeout,
103    },
104};
105
106/// Convenience type alias for `Result<T, AtError>`.
107type AsResult<T> = std::result::Result<T, Error>;
108
109/// Wrapper for server and application-specific settings.
110#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize)]
111#[serde(bound = "A: Deserialize<'de>")]
112pub struct BasicSettings<A> {
113    /// Actix Web server settings.
114    pub actix: ActixSettings,
115
116    /// Application-specific settings.
117    pub application: A,
118}
119
120/// Convenience type alias for [`BasicSettings`] with no defined application-specific settings.
121pub type Settings = BasicSettings<NoSettings>;
122
123/// Marker type representing no defined application-specific settings.
124#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize)]
125#[non_exhaustive]
126pub struct NoSettings {/* NOTE: turning this into a unit struct will cause deserialization failures. */}
127
128impl<A> BasicSettings<A>
129where
130    A: de::DeserializeOwned,
131{
132    // NOTE **DO NOT** mess with the ordering of the tables in the default template.
133    //      Especially the `[application]` table needs to be last in order
134    //      for some tests to keep working.
135    /// Default settings file contents.
136    pub(crate) const DEFAULT_TOML_TEMPLATE: &'static str = include_str!("./defaults.toml");
137
138    /// Parse an instance of `Self` from a TOML file located at `filepath`.
139    ///
140    /// If the file doesn't exist, it is generated from the default TOML template, after which the
141    /// newly generated file is read in and parsed.
142    pub fn parse_toml<P>(filepath: P) -> AsResult<Self>
143    where
144        P: AsRef<Path>,
145    {
146        let filepath = filepath.as_ref();
147
148        if !filepath.exists() {
149            Self::write_toml_file(filepath)?;
150        }
151
152        let mut f = File::open(filepath)?;
153        let len_guess = f.metadata().map(|md| md.len()).unwrap_or(128);
154
155        let mut contents = String::with_capacity(len_guess as usize);
156        f.read_to_string(&mut contents)?;
157
158        Ok(toml::from_str::<Self>(&contents)?)
159    }
160
161    /// Parse an instance of `Self` straight from the default TOML template.
162    pub fn from_default_template() -> Self {
163        Self::from_template(Self::DEFAULT_TOML_TEMPLATE).unwrap()
164    }
165
166    /// Parse an instance of `Self` straight from the default TOML template.
167    pub fn from_template(template: &str) -> AsResult<Self> {
168        Ok(toml::from_str(template)?)
169    }
170
171    /// Writes the default TOML template to a new file, located at `filepath`.
172    ///
173    /// # Errors
174    ///
175    /// Returns a [`FileExists`](crate::Error::FileExists) error if a file already exists at that
176    /// location.
177    pub fn write_toml_file<P>(filepath: P) -> AsResult<()>
178    where
179        P: AsRef<Path>,
180    {
181        let filepath = filepath.as_ref();
182
183        if filepath.exists() {
184            return Err(Error::FileExists(filepath.to_path_buf()));
185        }
186
187        let mut file = File::create(filepath)?;
188        file.write_all(Self::DEFAULT_TOML_TEMPLATE.trim().as_bytes())?;
189        file.flush()?;
190
191        Ok(())
192    }
193
194    /// Attempts to parse `value` and override the referenced `field`.
195    ///
196    /// # Examples
197    /// ```
198    /// use actix_settings::{Settings, Mode};
199    ///
200    /// # fn inner() -> Result<(), actix_settings::Error> {
201    /// let mut settings = Settings::from_default_template();
202    /// assert_eq!(settings.actix.mode, Mode::Development);
203    ///
204    /// Settings::override_field(&mut settings.actix.mode, "production")?;
205    /// assert_eq!(settings.actix.mode, Mode::Production);
206    /// # Ok(()) }
207    /// ```
208    pub fn override_field<F, V>(field: &mut F, value: V) -> AsResult<()>
209    where
210        F: Parse,
211        V: AsRef<str>,
212    {
213        *field = F::parse(value.as_ref())?;
214        Ok(())
215    }
216
217    /// Attempts to read an environment variable, parse it, and override the referenced `field`.
218    ///
219    /// # Examples
220    /// ```
221    /// use actix_settings::{Settings, Mode};
222    ///
223    /// std::env::set_var("OVERRIDE__MODE", "production");
224    ///
225    /// # fn inner() -> Result<(), actix_settings::Error> {
226    /// let mut settings = Settings::from_default_template();
227    /// assert_eq!(settings.actix.mode, Mode::Development);
228    ///
229    /// Settings::override_field_with_env_var(&mut settings.actix.mode, "OVERRIDE__MODE")?;
230    /// assert_eq!(settings.actix.mode, Mode::Production);
231    /// # Ok(()) }
232    /// ```
233    pub fn override_field_with_env_var<F, N>(field: &mut F, var_name: N) -> AsResult<()>
234    where
235        F: Parse,
236        N: AsRef<str>,
237    {
238        match env::var(var_name.as_ref()) {
239            Err(env::VarError::NotPresent) => Ok((/*NOP*/)),
240            Err(var_error) => Err(Error::from(var_error)),
241            Ok(value) => Self::override_field(field, value),
242        }
243    }
244}
245
246/// Extension trait for applying parsed settings to the server object.
247pub trait ApplySettings<S>: Sized {
248    /// Applies some settings object value to `self`.
249    ///
250    /// The default implementation calls [`try_apply_settings()`].
251    ///
252    /// # Panics
253    ///
254    /// May panic if settings are invalid or cannot be applied.
255    ///
256    /// [`try_apply_settings()`]: ApplySettings::try_apply_settings().
257    #[deprecated = "Prefer `try_apply_settings()`."]
258    fn apply_settings(self, settings: &S) -> Self {
259        self.try_apply_settings(settings)
260            .expect("Could not apply settings")
261    }
262
263    /// Applies some settings object value to `self`.
264    ///
265    /// # Errors
266    ///
267    /// May return error if settings are invalid or cannot be applied.
268    fn try_apply_settings(self, settings: &S) -> AsResult<Self>;
269}
270
271impl<F, I, S, B> ApplySettings<ActixSettings> for HttpServer<F, I, S, B>
272where
273    F: Fn() -> I + Send + Clone + 'static,
274    I: IntoServiceFactory<S, Request>,
275    S: ServiceFactory<Request, Config = AppConfig> + 'static,
276    S::Error: Into<WebError> + 'static,
277    S::InitError: fmt::Debug,
278    S::Response: Into<Response<B>> + 'static,
279    S::Future: 'static,
280    B: MessageBody + 'static,
281{
282    fn apply_settings(self, settings: &ActixSettings) -> Self {
283        self.try_apply_settings(settings).unwrap()
284    }
285
286    fn try_apply_settings(mut self, settings: &ActixSettings) -> AsResult<Self> {
287        for Address { host, port } in &settings.hosts {
288            #[cfg(feature = "openssl")]
289            {
290                if settings.tls.enabled {
291                    self = self.bind_openssl(
292                        format!("{host}:{port}"),
293                        settings.tls.get_ssl_acceptor_builder()?,
294                    )?;
295                } else {
296                    self = self.bind(format!("{host}:{port}"))?;
297                }
298            }
299
300            #[cfg(feature = "rustls-0_23")]
301            {
302                if settings.tls.enabled {
303                    self = self.bind_rustls_0_23(
304                        format!("{host}:{port}"),
305                        settings.tls.get_rustls_0_23_server_config()?,
306                    )?;
307                } else {
308                    self = self.bind(format!("{host}:{port}"))?;
309                }
310            }
311
312            #[cfg(not(any(feature = "openssl", feature = "rustls-0_23")))]
313            {
314                self = self.bind(format!("{host}:{port}"))?;
315            }
316        }
317
318        self = match settings.num_workers {
319            NumWorkers::Default => self,
320            NumWorkers::Manual(n) => self.workers(n),
321        };
322
323        self = match settings.backlog {
324            Backlog::Default => self,
325            Backlog::Manual(n) => self.backlog(n as u32),
326        };
327
328        self = match settings.max_connections {
329            MaxConnections::Default => self,
330            MaxConnections::Manual(n) => self.max_connections(n),
331        };
332
333        self = match settings.max_connection_rate {
334            MaxConnectionRate::Default => self,
335            MaxConnectionRate::Manual(n) => self.max_connection_rate(n),
336        };
337
338        self = match settings.keep_alive {
339            KeepAlive::Default => self,
340            KeepAlive::Disabled => self.keep_alive(ActixKeepAlive::Disabled),
341            KeepAlive::Os => self.keep_alive(ActixKeepAlive::Os),
342            KeepAlive::Seconds(n) => self.keep_alive(Duration::from_secs(n as u64)),
343        };
344
345        self = match settings.client_timeout {
346            Timeout::Default => self,
347            Timeout::Milliseconds(n) => {
348                self.client_request_timeout(Duration::from_millis(n as u64))
349            }
350            Timeout::Seconds(n) => self.client_request_timeout(Duration::from_secs(n as u64)),
351        };
352
353        self = match settings.client_shutdown {
354            Timeout::Default => self,
355            Timeout::Milliseconds(n) => {
356                self.client_disconnect_timeout(Duration::from_millis(n as u64))
357            }
358            Timeout::Seconds(n) => self.client_disconnect_timeout(Duration::from_secs(n as u64)),
359        };
360
361        self = match settings.shutdown_timeout {
362            Timeout::Default => self,
363            Timeout::Milliseconds(_) => self.shutdown_timeout(1),
364            Timeout::Seconds(n) => self.shutdown_timeout(n as u64),
365        };
366
367        Ok(self)
368    }
369}
370
371impl<F, I, S, B, A> ApplySettings<BasicSettings<A>> for HttpServer<F, I, S, B>
372where
373    F: Fn() -> I + Send + Clone + 'static,
374    I: IntoServiceFactory<S, Request>,
375    S: ServiceFactory<Request, Config = AppConfig> + 'static,
376    S::Error: Into<WebError> + 'static,
377    S::InitError: fmt::Debug,
378    S::Response: Into<Response<B>> + 'static,
379    S::Future: 'static,
380    B: MessageBody + 'static,
381    A: de::DeserializeOwned,
382{
383    fn apply_settings(self, settings: &BasicSettings<A>) -> Self {
384        self.try_apply_settings(&settings.actix).unwrap()
385    }
386
387    fn try_apply_settings(self, settings: &BasicSettings<A>) -> AsResult<Self> {
388        self.try_apply_settings(&settings.actix)
389    }
390}
391
392#[cfg(test)]
393mod tests {
394    use actix_web::App;
395
396    use super::*;
397
398    #[test]
399    fn apply_settings() {
400        let settings = Settings::parse_toml("Server.toml").unwrap();
401        let server = HttpServer::new(App::new).try_apply_settings(&settings);
402        assert!(server.is_ok());
403    }
404
405    #[test]
406    fn override_field_hosts() {
407        let mut settings = Settings::from_default_template();
408
409        assert_eq!(
410            settings.actix.hosts,
411            vec![Address {
412                host: "0.0.0.0".into(),
413                port: 9000
414            },]
415        );
416
417        Settings::override_field(
418            &mut settings.actix.hosts,
419            r#"[
420            ["0.0.0.0",   1234],
421            ["localhost", 2345]
422        ]"#,
423        )
424        .unwrap();
425
426        assert_eq!(
427            settings.actix.hosts,
428            vec![
429                Address {
430                    host: "0.0.0.0".into(),
431                    port: 1234
432                },
433                Address {
434                    host: "localhost".into(),
435                    port: 2345
436                },
437            ]
438        );
439    }
440
441    #[test]
442    fn override_field_with_env_var_hosts() {
443        let mut settings = Settings::from_default_template();
444
445        assert_eq!(
446            settings.actix.hosts,
447            vec![Address {
448                host: "0.0.0.0".into(),
449                port: 9000
450            },]
451        );
452
453        std::env::set_var(
454            "OVERRIDE__HOSTS",
455            r#"[
456            ["0.0.0.0",   1234],
457            ["localhost", 2345]
458        ]"#,
459        );
460
461        Settings::override_field_with_env_var(&mut settings.actix.hosts, "OVERRIDE__HOSTS")
462            .unwrap();
463
464        assert_eq!(
465            settings.actix.hosts,
466            vec![
467                Address {
468                    host: "0.0.0.0".into(),
469                    port: 1234
470                },
471                Address {
472                    host: "localhost".into(),
473                    port: 2345
474                },
475            ]
476        );
477    }
478
479    #[test]
480    fn override_field_mode() {
481        let mut settings = Settings::from_default_template();
482        assert_eq!(settings.actix.mode, Mode::Development);
483        Settings::override_field(&mut settings.actix.mode, "production").unwrap();
484        assert_eq!(settings.actix.mode, Mode::Production);
485    }
486
487    #[test]
488    fn override_field_with_env_var_mode() {
489        let mut settings = Settings::from_default_template();
490        assert_eq!(settings.actix.mode, Mode::Development);
491        std::env::set_var("OVERRIDE__MODE", "production");
492        Settings::override_field_with_env_var(&mut settings.actix.mode, "OVERRIDE__MODE").unwrap();
493        assert_eq!(settings.actix.mode, Mode::Production);
494    }
495
496    #[test]
497    fn override_field_enable_compression() {
498        let mut settings = Settings::from_default_template();
499        assert!(settings.actix.enable_compression);
500        Settings::override_field(&mut settings.actix.enable_compression, "false").unwrap();
501        assert!(!settings.actix.enable_compression);
502    }
503
504    #[test]
505    fn override_field_with_env_var_enable_compression() {
506        let mut settings = Settings::from_default_template();
507        assert!(settings.actix.enable_compression);
508        std::env::set_var("OVERRIDE__ENABLE_COMPRESSION", "false");
509        Settings::override_field_with_env_var(
510            &mut settings.actix.enable_compression,
511            "OVERRIDE__ENABLE_COMPRESSION",
512        )
513        .unwrap();
514        assert!(!settings.actix.enable_compression);
515    }
516
517    #[test]
518    fn override_field_enable_log() {
519        let mut settings = Settings::from_default_template();
520        assert!(settings.actix.enable_log);
521        Settings::override_field(&mut settings.actix.enable_log, "false").unwrap();
522        assert!(!settings.actix.enable_log);
523    }
524
525    #[test]
526    fn override_field_with_env_var_enable_log() {
527        let mut settings = Settings::from_default_template();
528        assert!(settings.actix.enable_log);
529        std::env::set_var("OVERRIDE__ENABLE_LOG", "false");
530        Settings::override_field_with_env_var(
531            &mut settings.actix.enable_log,
532            "OVERRIDE__ENABLE_LOG",
533        )
534        .unwrap();
535        assert!(!settings.actix.enable_log);
536    }
537
538    #[test]
539    fn override_field_num_workers() {
540        let mut settings = Settings::from_default_template();
541        assert_eq!(settings.actix.num_workers, NumWorkers::Default);
542        Settings::override_field(&mut settings.actix.num_workers, "42").unwrap();
543        assert_eq!(settings.actix.num_workers, NumWorkers::Manual(42));
544    }
545
546    #[test]
547    fn override_field_with_env_var_num_workers() {
548        let mut settings = Settings::from_default_template();
549        assert_eq!(settings.actix.num_workers, NumWorkers::Default);
550        std::env::set_var("OVERRIDE__NUM_WORKERS", "42");
551        Settings::override_field_with_env_var(
552            &mut settings.actix.num_workers,
553            "OVERRIDE__NUM_WORKERS",
554        )
555        .unwrap();
556        assert_eq!(settings.actix.num_workers, NumWorkers::Manual(42));
557    }
558
559    #[test]
560    fn override_field_backlog() {
561        let mut settings = Settings::from_default_template();
562        assert_eq!(settings.actix.backlog, Backlog::Default);
563        Settings::override_field(&mut settings.actix.backlog, "42").unwrap();
564        assert_eq!(settings.actix.backlog, Backlog::Manual(42));
565    }
566
567    #[test]
568    fn override_field_with_env_var_backlog() {
569        let mut settings = Settings::from_default_template();
570        assert_eq!(settings.actix.backlog, Backlog::Default);
571        std::env::set_var("OVERRIDE__BACKLOG", "42");
572        Settings::override_field_with_env_var(&mut settings.actix.backlog, "OVERRIDE__BACKLOG")
573            .unwrap();
574        assert_eq!(settings.actix.backlog, Backlog::Manual(42));
575    }
576
577    #[test]
578    fn override_field_max_connections() {
579        let mut settings = Settings::from_default_template();
580        assert_eq!(settings.actix.max_connections, MaxConnections::Default);
581        Settings::override_field(&mut settings.actix.max_connections, "42").unwrap();
582        assert_eq!(settings.actix.max_connections, MaxConnections::Manual(42));
583    }
584
585    #[test]
586    fn override_field_with_env_var_max_connections() {
587        let mut settings = Settings::from_default_template();
588        assert_eq!(settings.actix.max_connections, MaxConnections::Default);
589        std::env::set_var("OVERRIDE__MAX_CONNECTIONS", "42");
590        Settings::override_field_with_env_var(
591            &mut settings.actix.max_connections,
592            "OVERRIDE__MAX_CONNECTIONS",
593        )
594        .unwrap();
595        assert_eq!(settings.actix.max_connections, MaxConnections::Manual(42));
596    }
597
598    #[test]
599    fn override_field_max_connection_rate() {
600        let mut settings = Settings::from_default_template();
601        assert_eq!(
602            settings.actix.max_connection_rate,
603            MaxConnectionRate::Default
604        );
605        Settings::override_field(&mut settings.actix.max_connection_rate, "42").unwrap();
606        assert_eq!(
607            settings.actix.max_connection_rate,
608            MaxConnectionRate::Manual(42)
609        );
610    }
611
612    #[test]
613    fn override_field_with_env_var_max_connection_rate() {
614        let mut settings = Settings::from_default_template();
615        assert_eq!(
616            settings.actix.max_connection_rate,
617            MaxConnectionRate::Default
618        );
619        std::env::set_var("OVERRIDE__MAX_CONNECTION_RATE", "42");
620        Settings::override_field_with_env_var(
621            &mut settings.actix.max_connection_rate,
622            "OVERRIDE__MAX_CONNECTION_RATE",
623        )
624        .unwrap();
625        assert_eq!(
626            settings.actix.max_connection_rate,
627            MaxConnectionRate::Manual(42)
628        );
629    }
630
631    #[test]
632    fn override_field_keep_alive() {
633        let mut settings = Settings::from_default_template();
634        assert_eq!(settings.actix.keep_alive, KeepAlive::Default);
635        Settings::override_field(&mut settings.actix.keep_alive, "42 seconds").unwrap();
636        assert_eq!(settings.actix.keep_alive, KeepAlive::Seconds(42));
637    }
638
639    #[test]
640    fn override_field_with_env_var_keep_alive() {
641        let mut settings = Settings::from_default_template();
642        assert_eq!(settings.actix.keep_alive, KeepAlive::Default);
643        std::env::set_var("OVERRIDE__KEEP_ALIVE", "42 seconds");
644        Settings::override_field_with_env_var(
645            &mut settings.actix.keep_alive,
646            "OVERRIDE__KEEP_ALIVE",
647        )
648        .unwrap();
649        assert_eq!(settings.actix.keep_alive, KeepAlive::Seconds(42));
650    }
651
652    #[test]
653    fn override_field_client_timeout() {
654        let mut settings = Settings::from_default_template();
655        assert_eq!(settings.actix.client_timeout, Timeout::Default);
656        Settings::override_field(&mut settings.actix.client_timeout, "42 seconds").unwrap();
657        assert_eq!(settings.actix.client_timeout, Timeout::Seconds(42));
658    }
659
660    #[test]
661    fn override_field_with_env_var_client_timeout() {
662        let mut settings = Settings::from_default_template();
663        assert_eq!(settings.actix.client_timeout, Timeout::Default);
664        std::env::set_var("OVERRIDE__CLIENT_TIMEOUT", "42 seconds");
665        Settings::override_field_with_env_var(
666            &mut settings.actix.client_timeout,
667            "OVERRIDE__CLIENT_TIMEOUT",
668        )
669        .unwrap();
670        assert_eq!(settings.actix.client_timeout, Timeout::Seconds(42));
671    }
672
673    #[test]
674    fn override_field_client_shutdown() {
675        let mut settings = Settings::from_default_template();
676        assert_eq!(settings.actix.client_shutdown, Timeout::Default);
677        Settings::override_field(&mut settings.actix.client_shutdown, "42 seconds").unwrap();
678        assert_eq!(settings.actix.client_shutdown, Timeout::Seconds(42));
679    }
680
681    #[test]
682    fn override_field_with_env_var_client_shutdown() {
683        let mut settings = Settings::from_default_template();
684        assert_eq!(settings.actix.client_shutdown, Timeout::Default);
685        std::env::set_var("OVERRIDE__CLIENT_SHUTDOWN", "42 seconds");
686        Settings::override_field_with_env_var(
687            &mut settings.actix.client_shutdown,
688            "OVERRIDE__CLIENT_SHUTDOWN",
689        )
690        .unwrap();
691        assert_eq!(settings.actix.client_shutdown, Timeout::Seconds(42));
692    }
693
694    #[test]
695    fn override_field_shutdown_timeout() {
696        let mut settings = Settings::from_default_template();
697        assert_eq!(settings.actix.shutdown_timeout, Timeout::Default);
698        Settings::override_field(&mut settings.actix.shutdown_timeout, "42 seconds").unwrap();
699        assert_eq!(settings.actix.shutdown_timeout, Timeout::Seconds(42));
700    }
701
702    #[test]
703    fn override_field_with_env_var_shutdown_timeout() {
704        let mut settings = Settings::from_default_template();
705        assert_eq!(settings.actix.shutdown_timeout, Timeout::Default);
706        std::env::set_var("OVERRIDE__SHUTDOWN_TIMEOUT", "42 seconds");
707        Settings::override_field_with_env_var(
708            &mut settings.actix.shutdown_timeout,
709            "OVERRIDE__SHUTDOWN_TIMEOUT",
710        )
711        .unwrap();
712        assert_eq!(settings.actix.shutdown_timeout, Timeout::Seconds(42));
713    }
714
715    #[cfg(any(feature = "openssl", feature = "rustls-0_23"))]
716    #[test]
717    fn override_field_tls_enabled() {
718        let mut settings = Settings::from_default_template();
719        assert!(!settings.actix.tls.enabled);
720        Settings::override_field(&mut settings.actix.tls.enabled, "true").unwrap();
721        assert!(settings.actix.tls.enabled);
722    }
723
724    #[cfg(any(feature = "openssl", feature = "rustls-0_23"))]
725    #[test]
726    fn override_field_with_env_var_tls_enabled() {
727        let mut settings = Settings::from_default_template();
728        assert!(!settings.actix.tls.enabled);
729        std::env::set_var("OVERRIDE__TLS_ENABLED", "true");
730        Settings::override_field_with_env_var(
731            &mut settings.actix.tls.enabled,
732            "OVERRIDE__TLS_ENABLED",
733        )
734        .unwrap();
735        assert!(settings.actix.tls.enabled);
736    }
737
738    #[cfg(any(feature = "openssl", feature = "rustls-0_23"))]
739    #[test]
740    fn override_field_tls_certificate() {
741        let mut settings = Settings::from_default_template();
742        assert_eq!(
743            settings.actix.tls.certificate,
744            Path::new("path/to/cert/cert.pem")
745        );
746        Settings::override_field(
747            &mut settings.actix.tls.certificate,
748            "/overridden/path/to/cert/cert.pem",
749        )
750        .unwrap();
751        assert_eq!(
752            settings.actix.tls.certificate,
753            Path::new("/overridden/path/to/cert/cert.pem")
754        );
755    }
756
757    #[cfg(any(feature = "openssl", feature = "rustls-0_23"))]
758    #[test]
759    fn override_field_with_env_var_tls_certificate() {
760        let mut settings = Settings::from_default_template();
761        assert_eq!(
762            settings.actix.tls.certificate,
763            Path::new("path/to/cert/cert.pem")
764        );
765        std::env::set_var(
766            "OVERRIDE__TLS_CERTIFICATE",
767            "/overridden/path/to/cert/cert.pem",
768        );
769        Settings::override_field_with_env_var(
770            &mut settings.actix.tls.certificate,
771            "OVERRIDE__TLS_CERTIFICATE",
772        )
773        .unwrap();
774        assert_eq!(
775            settings.actix.tls.certificate,
776            Path::new("/overridden/path/to/cert/cert.pem")
777        );
778    }
779
780    #[cfg(any(feature = "openssl", feature = "rustls-0_23"))]
781    #[test]
782    fn override_field_tls_private_key() {
783        let mut settings = Settings::from_default_template();
784        assert_eq!(
785            settings.actix.tls.private_key,
786            Path::new("path/to/cert/key.pem")
787        );
788        Settings::override_field(
789            &mut settings.actix.tls.private_key,
790            "/overridden/path/to/cert/key.pem",
791        )
792        .unwrap();
793        assert_eq!(
794            settings.actix.tls.private_key,
795            Path::new("/overridden/path/to/cert/key.pem")
796        );
797    }
798
799    #[cfg(any(feature = "openssl", feature = "rustls-0_23"))]
800    #[test]
801    fn override_field_with_env_var_tls_private_key() {
802        let mut settings = Settings::from_default_template();
803        assert_eq!(
804            settings.actix.tls.private_key,
805            Path::new("path/to/cert/key.pem")
806        );
807        std::env::set_var(
808            "OVERRIDE__TLS_PRIVATE_KEY",
809            "/overridden/path/to/cert/key.pem",
810        );
811        Settings::override_field_with_env_var(
812            &mut settings.actix.tls.private_key,
813            "OVERRIDE__TLS_PRIVATE_KEY",
814        )
815        .unwrap();
816        assert_eq!(
817            settings.actix.tls.private_key,
818            Path::new("/overridden/path/to/cert/key.pem")
819        );
820    }
821
822    #[test]
823    fn override_extended_field_with_custom_type() {
824        #[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
825        struct NestedSetting {
826            foo: String,
827            bar: bool,
828        }
829
830        #[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
831        #[serde(rename_all = "kebab-case")]
832        struct AppSettings {
833            example_name: String,
834            nested_field: NestedSetting,
835        }
836
837        type CustomSettings = BasicSettings<AppSettings>;
838
839        let mut settings = CustomSettings::from_template(
840            &(CustomSettings::DEFAULT_TOML_TEMPLATE.to_string()
841                // NOTE: Add these entries to the `[application]` table:
842                + "\nexample-name = \"example value\""
843                + "\nnested-field = { foo = \"foo\", bar = false }"),
844        )
845        .unwrap();
846
847        assert_eq!(
848            settings.application,
849            AppSettings {
850                example_name: "example value".into(),
851                nested_field: NestedSetting {
852                    foo: "foo".into(),
853                    bar: false,
854                },
855            }
856        );
857
858        CustomSettings::override_field(
859            &mut settings.application.example_name,
860            "/overridden/path/to/cert/key.pem",
861        )
862        .unwrap();
863
864        assert_eq!(
865            settings.application,
866            AppSettings {
867                example_name: "/overridden/path/to/cert/key.pem".into(),
868                nested_field: NestedSetting {
869                    foo: "foo".into(),
870                    bar: false,
871                },
872            }
873        );
874    }
875}