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