actix_http/
config.rs

1use std::{
2    net,
3    rc::Rc,
4    time::{Duration, Instant},
5};
6
7use bytes::BytesMut;
8
9use crate::{date::DateService, KeepAlive};
10
11/// HTTP service configuration.
12#[derive(Debug, Clone)]
13pub struct ServiceConfig(Rc<Inner>);
14
15#[derive(Debug)]
16struct Inner {
17    keep_alive: KeepAlive,
18    client_request_timeout: Duration,
19    client_disconnect_timeout: Duration,
20    secure: bool,
21    local_addr: Option<std::net::SocketAddr>,
22    date_service: DateService,
23}
24
25impl Default for ServiceConfig {
26    fn default() -> Self {
27        Self::new(
28            KeepAlive::default(),
29            Duration::from_secs(5),
30            Duration::ZERO,
31            false,
32            None,
33        )
34    }
35}
36
37impl ServiceConfig {
38    /// Create instance of `ServiceConfig`.
39    pub fn new(
40        keep_alive: KeepAlive,
41        client_request_timeout: Duration,
42        client_disconnect_timeout: Duration,
43        secure: bool,
44        local_addr: Option<net::SocketAddr>,
45    ) -> ServiceConfig {
46        ServiceConfig(Rc::new(Inner {
47            keep_alive: keep_alive.normalize(),
48            client_request_timeout,
49            client_disconnect_timeout,
50            secure,
51            local_addr,
52            date_service: DateService::new(),
53        }))
54    }
55
56    /// Returns `true` if connection is secure (i.e., using TLS / HTTPS).
57    #[inline]
58    pub fn secure(&self) -> bool {
59        self.0.secure
60    }
61
62    /// Returns the local address that this server is bound to.
63    ///
64    /// Returns `None` for connections via UDS (Unix Domain Socket).
65    #[inline]
66    pub fn local_addr(&self) -> Option<net::SocketAddr> {
67        self.0.local_addr
68    }
69
70    /// Connection keep-alive setting.
71    #[inline]
72    pub fn keep_alive(&self) -> KeepAlive {
73        self.0.keep_alive
74    }
75
76    /// Creates a time object representing the deadline for this connection's keep-alive period, if
77    /// enabled.
78    ///
79    /// When [`KeepAlive::Os`] or [`KeepAlive::Disabled`] is set, this will return `None`.
80    pub fn keep_alive_deadline(&self) -> Option<Instant> {
81        match self.keep_alive() {
82            KeepAlive::Timeout(dur) => Some(self.now() + dur),
83            KeepAlive::Os => None,
84            KeepAlive::Disabled => None,
85        }
86    }
87
88    /// Creates a time object representing the deadline for the client to finish sending the head of
89    /// its first request.
90    ///
91    /// Returns `None` if this `ServiceConfig was` constructed with `client_request_timeout: 0`.
92    pub fn client_request_deadline(&self) -> Option<Instant> {
93        let timeout = self.0.client_request_timeout;
94        (timeout != Duration::ZERO).then(|| self.now() + timeout)
95    }
96
97    /// Creates a time object representing the deadline for the client to disconnect.
98    pub fn client_disconnect_deadline(&self) -> Option<Instant> {
99        let timeout = self.0.client_disconnect_timeout;
100        (timeout != Duration::ZERO).then(|| self.now() + timeout)
101    }
102
103    pub(crate) fn now(&self) -> Instant {
104        self.0.date_service.now()
105    }
106
107    /// Writes date header to `dst` buffer.
108    ///
109    /// Low-level method that utilizes the built-in efficient date service, requiring fewer syscalls
110    /// than normal. Note that a CRLF (`\r\n`) is included in what is written.
111    #[doc(hidden)]
112    pub fn write_date_header(&self, dst: &mut BytesMut, camel_case: bool) {
113        let mut buf: [u8; 37] = [0; 37];
114
115        buf[..6].copy_from_slice(if camel_case { b"Date: " } else { b"date: " });
116
117        self.0
118            .date_service
119            .with_date(|date| buf[6..35].copy_from_slice(&date.bytes));
120
121        buf[35..].copy_from_slice(b"\r\n");
122        dst.extend_from_slice(&buf);
123    }
124
125    #[allow(unused)] // used with `http2` feature flag
126    pub(crate) fn write_date_header_value(&self, dst: &mut BytesMut) {
127        self.0
128            .date_service
129            .with_date(|date| dst.extend_from_slice(&date.bytes));
130    }
131}
132
133#[cfg(test)]
134mod tests {
135    use actix_rt::{
136        task::yield_now,
137        time::{sleep, sleep_until},
138    };
139    use memchr::memmem;
140
141    use super::*;
142    use crate::{date::DATE_VALUE_LENGTH, notify_on_drop};
143
144    #[actix_rt::test]
145    async fn test_date_service_update() {
146        let settings =
147            ServiceConfig::new(KeepAlive::Os, Duration::ZERO, Duration::ZERO, false, None);
148
149        yield_now().await;
150
151        let mut buf1 = BytesMut::with_capacity(DATE_VALUE_LENGTH + 10);
152        settings.write_date_header(&mut buf1, false);
153        let now1 = settings.now();
154
155        sleep_until((Instant::now() + Duration::from_secs(2)).into()).await;
156        yield_now().await;
157
158        let now2 = settings.now();
159        let mut buf2 = BytesMut::with_capacity(DATE_VALUE_LENGTH + 10);
160        settings.write_date_header(&mut buf2, false);
161
162        assert_ne!(now1, now2);
163
164        assert_ne!(buf1, buf2);
165
166        drop(settings);
167
168        // Ensure the task will drop eventually
169        let mut times = 0;
170        while !notify_on_drop::is_dropped() {
171            sleep(Duration::from_millis(100)).await;
172            times += 1;
173            assert!(times < 10, "Timeout waiting for task drop");
174        }
175    }
176
177    #[actix_rt::test]
178    async fn test_date_service_drop() {
179        let service = Rc::new(DateService::new());
180
181        // yield so date service have a chance to register the spawned timer update task.
182        yield_now().await;
183
184        let clone1 = service.clone();
185        let clone2 = service.clone();
186        let clone3 = service.clone();
187
188        drop(clone1);
189        assert!(!notify_on_drop::is_dropped());
190        drop(clone2);
191        assert!(!notify_on_drop::is_dropped());
192        drop(clone3);
193        assert!(!notify_on_drop::is_dropped());
194
195        drop(service);
196
197        // Ensure the task will drop eventually
198        let mut times = 0;
199        while !notify_on_drop::is_dropped() {
200            sleep(Duration::from_millis(100)).await;
201            times += 1;
202            assert!(times < 10, "Timeout waiting for task drop");
203        }
204    }
205
206    #[test]
207    fn test_date_len() {
208        assert_eq!(DATE_VALUE_LENGTH, "Sun, 06 Nov 1994 08:49:37 GMT".len());
209    }
210
211    #[actix_rt::test]
212    async fn test_date() {
213        let settings = ServiceConfig::default();
214
215        let mut buf1 = BytesMut::with_capacity(DATE_VALUE_LENGTH + 10);
216        settings.write_date_header(&mut buf1, false);
217
218        let mut buf2 = BytesMut::with_capacity(DATE_VALUE_LENGTH + 10);
219        settings.write_date_header(&mut buf2, false);
220
221        assert_eq!(buf1, buf2);
222    }
223
224    #[actix_rt::test]
225    async fn test_date_camel_case() {
226        let settings = ServiceConfig::default();
227
228        let mut buf = BytesMut::with_capacity(DATE_VALUE_LENGTH + 10);
229        settings.write_date_header(&mut buf, false);
230        assert!(memmem::find(&buf, b"date:").is_some());
231
232        let mut buf = BytesMut::with_capacity(DATE_VALUE_LENGTH + 10);
233        settings.write_date_header(&mut buf, true);
234        assert!(memmem::find(&buf, b"Date:").is_some());
235    }
236}