actix_settings/settings/
keep_alive.rs

1use std::fmt;
2
3use once_cell::sync::Lazy;
4use regex::Regex;
5use serde::de;
6
7use crate::{AsResult, Error, Parse};
8
9/// The server keep-alive preference.
10///
11/// By default keep alive is set to 5 seconds. Takes a string value: Either "default", "disabled",
12/// "os", or a string of the format "N seconds" where N is an integer > 0 e.g. "6 seconds".
13#[derive(Debug, Clone, PartialEq, Eq, Hash)]
14pub enum KeepAlive {
15    /// The default keep-alive as defined by Actix Web.
16    Default,
17
18    /// Disable keep-alive.
19    Disabled,
20
21    /// Let the OS determine keep-alive duration.
22    ///
23    /// Note: this is usually quite long.
24    Os,
25
26    /// A specific keep-alive duration (in seconds).
27    Seconds(usize),
28}
29
30impl Parse for KeepAlive {
31    fn parse(string: &str) -> AsResult<Self> {
32        pub(crate) static FMT: Lazy<Regex> =
33            Lazy::new(|| Regex::new(r"^\d+ seconds$").expect("Failed to compile regex: FMT"));
34
35        pub(crate) static DIGITS: Lazy<Regex> =
36            Lazy::new(|| Regex::new(r"^\d+").expect("Failed to compile regex: FMT"));
37
38        macro_rules! invalid_value {
39            ($got:expr) => {
40                Err(InvalidValue! {
41                    expected: "a string of the format \"N seconds\" where N is an integer > 0",
42                    got: $got,
43                })
44            };
45        }
46
47        let digits_in = |m: regex::Match<'_>| &string[m.start()..m.end()];
48        match string {
49            "default" => Ok(KeepAlive::Default),
50            "disabled" => Ok(KeepAlive::Disabled),
51            "OS" | "os" => Ok(KeepAlive::Os),
52            string if !FMT.is_match(string) => invalid_value!(string),
53            string => match DIGITS.find(string) {
54                None => invalid_value!(string),
55                Some(mat) => match digits_in(mat).parse() {
56                    Ok(val) => Ok(KeepAlive::Seconds(val)),
57                    Err(_) => invalid_value!(string),
58                },
59            },
60        }
61    }
62}
63
64impl<'de> de::Deserialize<'de> for KeepAlive {
65    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
66    where
67        D: de::Deserializer<'de>,
68    {
69        struct KeepAliveVisitor;
70
71        impl de::Visitor<'_> for KeepAliveVisitor {
72            type Value = KeepAlive;
73
74            fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75                let msg = "Either \"default\", \"disabled\", \"os\", or a string of the format \"N seconds\" where N is an integer > 0";
76                f.write_str(msg)
77            }
78
79            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
80            where
81                E: de::Error,
82            {
83                match KeepAlive::parse(value) {
84                    Ok(keep_alive) => Ok(keep_alive),
85                    Err(Error::InvalidValue { expected, got, .. }) => Err(
86                        de::Error::invalid_value(de::Unexpected::Str(&got), &expected),
87                    ),
88                    Err(_) => unreachable!(),
89                }
90            }
91        }
92
93        deserializer.deserialize_string(KeepAliveVisitor)
94    }
95}