Skip to main content

ntex/http/
config.rs

1use std::{cell::Cell, cell::RefCell, rc::Rc, time};
2
3use crate::http::header::HeaderValue;
4use crate::io::{IoRef, cfg::FrameReadRate};
5use crate::service::cfg::{CfgContext, Configuration};
6use crate::time::{Millis, Seconds, sleep};
7use crate::{channel::oneshot, util::BytePages, util::Bytes, util::BytesMut, util::HashSet};
8
9#[derive(Debug, PartialEq, Eq, Clone, Copy)]
10/// Server keep-alive behavior.
11pub enum KeepAlive {
12    /// Close an idle connection after this timeout.
13    Timeout(Seconds),
14    /// Keep the connection open until the peer or operating system closes it.
15    Os,
16    /// Disable persistent connections.
17    Disabled,
18}
19
20impl From<usize> for KeepAlive {
21    fn from(keepalive: usize) -> Self {
22        KeepAlive::Timeout(Seconds(keepalive as u16))
23    }
24}
25
26impl From<Seconds> for KeepAlive {
27    fn from(keepalive: Seconds) -> Self {
28        KeepAlive::Timeout(keepalive)
29    }
30}
31
32impl From<Option<usize>> for KeepAlive {
33    fn from(keepalive: Option<usize>) -> Self {
34        if let Some(keepalive) = keepalive {
35            KeepAlive::Timeout(Seconds(keepalive as u16))
36        } else {
37            KeepAlive::Disabled
38        }
39    }
40}
41
42#[derive(Debug)]
43#[allow(clippy::struct_excessive_bools)]
44/// Configuration shared by HTTP/1 and HTTP/2 server services.
45///
46/// The default configuration enables persistent HTTP/1 connections with a
47/// five-second idle timeout, allows 96 headers, limits the request or status
48/// line to 16 KiB and the message-head buffer to 64 KiB, and applies a
49/// one-second initial request-header timeout.
50pub struct HttpServiceConfig {
51    pub(super) keep_alive: Seconds,
52    pub(super) ka_enabled: bool,
53    pub(super) headers_vec: bool,
54    pub(super) validate_host: bool,
55    pub(super) max_headers: u16,
56    pub(super) max_buf_size: usize,
57    pub(super) max_start_line_size: usize,
58    pub(super) headers_read_rate: Option<FrameReadRate>,
59    pub(super) payload_read_rate: Option<FrameReadRate>,
60    pub(super) write_timeout: Seconds,
61    pub(super) half_close: bool,
62
63    config: CfgContext,
64}
65
66impl Default for HttpServiceConfig {
67    fn default() -> Self {
68        HttpServiceConfig::new()
69    }
70}
71
72impl Configuration for HttpServiceConfig {
73    const NAME: &str = "Http service configuration";
74
75    fn ctx(&self) -> &CfgContext {
76        &self.config
77    }
78
79    fn set_ctx(&mut self, ctx: CfgContext) {
80        self.config = ctx;
81    }
82}
83
84impl HttpServiceConfig {
85    #[must_use]
86    /// Creates an HTTP service configuration with default settings.
87    pub fn new() -> HttpServiceConfig {
88        Self::new_inner(KeepAlive::Timeout(Seconds(5)), Seconds::ONE)
89    }
90
91    fn new_inner(keep_alive: KeepAlive, client_timeout: Seconds) -> HttpServiceConfig {
92        let (keep_alive, ka_enabled) = match keep_alive {
93            KeepAlive::Timeout(val) => (val, true),
94            KeepAlive::Os => (Seconds::ZERO, true),
95            KeepAlive::Disabled => (Seconds::ZERO, false),
96        };
97        let keep_alive = if ka_enabled { keep_alive } else { Seconds::ZERO };
98
99        HttpServiceConfig {
100            keep_alive,
101            ka_enabled,
102            headers_read_rate: Some(FrameReadRate {
103                rate: 256,
104                timeout: client_timeout,
105                max_timeout: client_timeout + Seconds(15),
106            }),
107            max_headers: 96,
108            max_buf_size: 64 * 1024,
109            max_start_line_size: 16 * 1024,
110            headers_vec: false,
111            validate_host: true,
112            payload_read_rate: None,
113            write_timeout: Seconds::ZERO,
114            half_close: false,
115            config: CfgContext::default(),
116        }
117    }
118
119    #[must_use]
120    /// Sets the maximum number of headers in a message.
121    ///
122    /// Every header line counts, including repeated header names.
123    ///
124    /// Requests exceeding this limit are rejected with
125    /// `431 Request Header Fields Too Large`. The default is 96.
126    pub fn set_max_headers(mut self, val: u16) -> Self {
127        self.max_headers = val;
128        self
129    }
130
131    #[must_use]
132    /// Sets the maximum cumulative size of an HTTP message head.
133    ///
134    /// The request or response line, headers, and terminating empty line may
135    /// occupy up to and including this number of bytes. Larger message heads
136    /// are rejected. The default is 64 KiB.
137    pub fn set_max_buf_size(mut self, val: usize) -> Self {
138        self.max_buf_size = val;
139        self
140    }
141
142    #[must_use]
143    /// Sets the maximum size of an HTTP/1 request or status line.
144    ///
145    /// The line, including its line end, may occupy up to and including this
146    /// number of bytes. Requests with a longer request line are rejected with
147    /// `414 URI Too Long`. The line is also limited by
148    /// [`set_max_buf_size`](Self::set_max_buf_size). The default is 16 KiB.
149    pub fn set_max_start_line_size(mut self, val: usize) -> Self {
150        self.max_start_line_size = val;
151        self
152    }
153
154    #[must_use]
155    /// Sets the server keep-alive behavior.
156    ///
157    /// By default, idle persistent connections are closed after five seconds.
158    /// The keep-alive timeout does not apply before the first request. If
159    /// request-head timing is disabled, it also bounds a partially received
160    /// request head after the first request.
161    pub fn set_keepalive<W: Into<KeepAlive>>(mut self, val: W) -> Self {
162        let (keep_alive, ka_enabled) = match val.into() {
163            KeepAlive::Timeout(val) => (val, true),
164            KeepAlive::Os => (Seconds::ZERO, true),
165            KeepAlive::Disabled => (Seconds::ZERO, false),
166        };
167        let keep_alive = if ka_enabled { keep_alive } else { Seconds::ZERO };
168
169        self.keep_alive = keep_alive;
170        self.ka_enabled = ka_enabled;
171        self
172    }
173
174    #[must_use]
175    /// Sets the keep-alive timeout.
176    ///
177    /// A zero duration disables persistent connections rather than selecting
178    /// an unlimited timeout. Use [`KeepAlive::Os`] with
179    /// [`set_keepalive`](Self::set_keepalive) to leave connection lifetime to
180    /// the peer or operating system. The default is five seconds.
181    pub fn set_keepalive_timeout(mut self, timeout: Seconds) -> Self {
182        self.keep_alive = timeout;
183        self.ka_enabled = !timeout.is_zero();
184        self
185    }
186
187    #[must_use]
188    /// Sets the initial timeout for reading request headers.
189    ///
190    /// A new connection must send the first byte of its first request within
191    /// this period, otherwise it is rejected with `408 Request Timeout`. The
192    /// request-head read rate starts with that byte, and this period is also
193    /// its measurement interval. A zero duration disables header-read timing.
194    /// A new connection can then wait indefinitely for its first request,
195    /// while on a persistent connection the keep-alive timeout bounds both
196    /// waiting for the next request and reading its head. The default is one
197    /// second.
198    ///
199    /// HTTP/1 timers have one-second resolution, a timeout can expire up to
200    /// one second later than configured.
201    ///
202    /// This sets the measurement interval of the request-head read rate. The
203    /// cumulative limit and required rate configured by
204    /// [`set_headers_read_rate`](Self::set_headers_read_rate) are kept. If
205    /// header-read timing was disabled, it is enabled again with the default
206    /// rate of 256 bytes and a cumulative limit of `timeout` plus 15 seconds.
207    pub fn set_client_timeout(mut self, timeout: Seconds) -> Self {
208        if timeout.is_zero() {
209            self.headers_read_rate = None;
210        } else {
211            let mut rate = self.headers_read_rate.unwrap_or(FrameReadRate {
212                rate: 256,
213                timeout,
214                max_timeout: timeout + Seconds(15),
215            });
216            rate.timeout = timeout;
217            self.headers_read_rate = Some(rate);
218        }
219        self
220    }
221
222    #[must_use]
223    /// Sets the HTTP/1 write backpressure timeout.
224    ///
225    /// Write backpressure is enabled when outstanding output reaches the I/O
226    /// write buffer high watermark, and disabled once the peer has accepted
227    /// enough of it. If backpressure is still enabled when the timeout
228    /// expires, the connection is closed, and the HTTP/1 control service
229    /// receives a peer-gone event with an [`io::ErrorKind::TimedOut`](std::io::ErrorKind::TimedOut)
230    /// error. An unfinished request payload stream receives a
231    /// [`PayloadError::Io`](crate::http::error::PayloadError::Io) with the
232    /// same error kind. Each backpressure period starts a fresh timeout.
233    ///
234    /// Without a write timeout, a client that stops reading responses can hold
235    /// the connection open indefinitely. Payload read-rate timing is paused
236    /// during write backpressure and resumes once it is disabled.
237    ///
238    /// Timers have one-second resolution, the timeout can expire up to one
239    /// second later than configured. A zero duration disables the timeout. It
240    /// is disabled by default.
241    pub fn set_write_timeout(mut self, timeout: Seconds) -> Self {
242        self.write_timeout = timeout;
243        self
244    }
245
246    #[must_use]
247    /// Keeps streaming an HTTP/1 response after the client half-closes the
248    /// connection.
249    ///
250    /// A client that closes its side of the connection is treated as gone:
251    /// when the read side reaches EOF, all buffered requests are handled and
252    /// the response body has no data ready, the dispatcher drops the body and
253    /// closes the connection. Otherwise an idle streaming response, e.g.
254    /// server-sent events, would hold the connection until the body produces
255    /// its next chunk.
256    ///
257    /// Enable this for clients that shut down their write side after sending
258    /// the request and still expect the full response. Such a response then
259    /// ends only when the body completes or a write fails. Responses that are
260    /// ready are sent either way. This setting does not affect HTTP/2. It is
261    /// disabled by default.
262    pub fn set_half_close(mut self, enabled: bool) -> Self {
263        self.half_close = enabled;
264        self
265    }
266
267    #[must_use]
268    /// Preserves headers in their original order and casing.
269    ///
270    /// When enabled, decoded headers are additionally copied into
271    /// [`RequestHead::headers_vec`](crate::http::RequestHead::headers_vec) or
272    /// [`ResponseHead::headers_vec`](crate::http::ResponseHead::headers_vec).
273    /// The normal header map remains populated. This is disabled by default.
274    pub fn set_headers_vec(mut self, enabled: bool) -> Self {
275        self.headers_vec = enabled;
276        self
277    }
278
279    #[must_use]
280    /// Enables validation of the HTTP/1 `Host` request header.
281    ///
282    /// When enabled, requests are rejected with `400 Bad Request` if they
283    /// contain more than one `Host` header or a `Host` value that is not a
284    /// valid host and optional port. HTTP/1.1 requests without a `Host`
285    /// header are rejected as well, see
286    /// [RFC 9112 section 3.2](https://www.rfc-editor.org/rfc/rfc9112#section-3.2).
287    /// An empty `Host` value is accepted. This setting does not affect HTTP/2.
288    /// It is enabled by default.
289    pub fn set_host_validation(mut self, enabled: bool) -> Self {
290        self.validate_host = enabled;
291        self
292    }
293
294    #[must_use]
295    /// Sets read-rate limits for request headers.
296    ///
297    /// This setting protects HTTP/1 connections from clients that send a
298    /// request line or headers too slowly. The timer starts when the first
299    /// bytes of a request head arrive, on a new connection as well as on a
300    /// persistent one. Until the first byte of the first request arrives, a
301    /// new connection waits for at most one `timeout` interval, the
302    /// [client timeout](Self::set_client_timeout), without rate extension.
303    ///
304    /// `timeout` is the duration of one measurement interval. When an interval
305    /// expires, the dispatcher grants another interval only if more than
306    /// `rate` new bytes were received. The request head must complete before
307    /// the cumulative `max_timeout` is exhausted. All newly received
308    /// request-head bytes count toward progress, including request-line and
309    /// header bytes that the incremental parser has already consumed.
310    ///
311    /// A zero `timeout` disables request-head timing. The first request of a
312    /// connection is then unbounded, and the keep-alive timeout bounds waiting
313    /// for and reading each following request head. A zero `max_timeout`
314    /// removes the cumulative limit, allowing the deadline to be extended
315    /// indefinitely while the required read rate is maintained. When
316    /// `max_timeout` is not an exact multiple of `timeout`, the final
317    /// measurement interval is shortened so the cumulative limit is not
318    /// exceeded. Intervals have one-second resolution and can expire up to one
319    /// second later than configured.
320    ///
321    /// If the request head misses its deadline, the HTTP/1 control service
322    /// receives
323    /// [`ProtocolError::SlowRequestTimeout`](crate::http::h1::ProtocolError::SlowRequestTimeout).
324    /// The default control service responds with `408 Request Timeout` and
325    /// closes the connection.
326    ///
327    /// By default, the timeout is 1 second and the maximum timeout is 16
328    /// seconds, with more than 256 bytes required for each extension.
329    ///
330    /// # Example
331    ///
332    /// ```rust
333    /// use ntex::http::HttpServiceConfig;
334    /// use ntex::time::Seconds;
335    ///
336    /// let config = HttpServiceConfig::new().set_headers_read_rate(
337    ///     Seconds(2),  // measurement interval
338    ///     Seconds(10), // maximum time for one request head
339    ///     512,         // bytes required to extend the deadline for next 2 seconds
340    /// );
341    /// ```
342    pub fn set_headers_read_rate(
343        mut self,
344        timeout: Seconds,
345        max_timeout: Seconds,
346        rate: u32,
347    ) -> Self {
348        if timeout.is_zero() {
349            self.headers_read_rate = None;
350        } else {
351            self.headers_read_rate = Some(FrameReadRate {
352                rate,
353                timeout,
354                max_timeout,
355            });
356        }
357        self
358    }
359
360    #[must_use]
361    /// Sets read-rate limits for request payloads.
362    ///
363    /// This setting protects HTTP/1 connections from clients that send a
364    /// request body too slowly. The timer starts when the dispatcher begins
365    /// decoding a request payload. For a request with `Expect: 100-continue`,
366    /// it starts only once `100 Continue` has been sent, the request has been
367    /// passed to the application, or a response has been sent and the rest of
368    /// the payload is read, because the client does not send the body before
369    /// that. At the end of each `timeout` interval, another interval is
370    /// granted only if more than `rate` bytes were decoded.
371    ///
372    /// The timer runs only while the dispatcher can read and forward payload
373    /// data. It is paused while application payload backpressure or response
374    /// write backpressure prevents further reads, so those conditions are not
375    /// treated as a slow network peer. Write backpressure is bounded by the
376    /// [write timeout](Self::set_write_timeout). Pausing preserves the unused
377    /// portion of the current measurement interval, and resuming continues
378    /// that interval rather than starting a new one, so the bytes decoded
379    /// before and after the pause are measured together. If the interval
380    /// expired before the pause, already received payload data is decoded
381    /// on resume and then the read rate is checked. The timer stops when the
382    /// complete payload has been decoded.
383    ///
384    /// A zero `timeout` disables payload timing. A zero `max_timeout` removes
385    /// the cumulative limit, allowing the deadline to be extended indefinitely
386    /// while the required read rate is maintained. When `max_timeout` is not
387    /// an exact multiple of `timeout`, the final measurement interval is
388    /// shortened so the cumulative limit is not exceeded. Intervals have
389    /// one-second resolution and can expire up to one second later than
390    /// configured.
391    ///
392    /// If the payload misses its deadline, its stream receives a timed-out
393    /// [`PayloadError`](crate::http::error::PayloadError), and the HTTP/1
394    /// control service receives
395    /// [`ProtocolError::SlowPayloadTimeout`](crate::http::h1::ProtocolError::SlowPayloadTimeout).
396    /// The default control service responds with `408 Request Timeout` and
397    /// closes the connection.
398    ///
399    /// Payload read-rate limiting is disabled by default.
400    ///
401    /// # Example
402    ///
403    /// ```rust
404    /// use ntex::http::HttpServiceConfig;
405    /// use ntex::time::Seconds;
406    ///
407    /// let config = HttpServiceConfig::new().set_payload_read_rate(
408    ///     Seconds(2),  // measurement interval
409    ///     Seconds(30), // maximum time for one request payload
410    ///     1024,        // bytes required to extend the deadline
411    /// );
412    /// ```
413    pub fn set_payload_read_rate(
414        mut self,
415        timeout: Seconds,
416        max_timeout: Seconds,
417        rate: u32,
418    ) -> Self {
419        if timeout.is_zero() {
420            self.payload_read_rate = None;
421        } else {
422            self.payload_read_rate = Some(FrameReadRate {
423                rate,
424                timeout,
425                max_timeout,
426            });
427        }
428        self
429    }
430}
431
432bitflags::bitflags! {
433    #[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
434    struct Flags: u8 {
435        /// Shutdown service
436        const SHUTDOWN   = 0b0000_0010;
437    }
438}
439
440#[derive(Clone)]
441pub(super) struct DispatcherConfig(Rc<DispatcherConfigInner>);
442
443struct DispatcherConfigInner {
444    flags: Cell<Flags>,
445    idx: Cell<usize>,
446    pub(super) inflight: RefCell<HashSet<IoRef>>,
447    rx: Cell<Option<oneshot::Receiver<()>>>,
448    tx: Cell<Option<oneshot::Sender<()>>>,
449}
450
451impl Default for DispatcherConfig {
452    fn default() -> Self {
453        let (tx, rx) = oneshot::channel();
454
455        DispatcherConfig(Rc::new(DispatcherConfigInner {
456            idx: Cell::new(0),
457            flags: Cell::new(Flags::empty()),
458            rx: Cell::new(Some(rx)),
459            tx: Cell::new(Some(tx)),
460            inflight: RefCell::new(HashSet::default()),
461        }))
462    }
463}
464
465impl DispatcherConfig {
466    /// Get connection id
467    pub(super) fn next_id(&self) -> usize {
468        let id = self.0.idx.get();
469        self.0.idx.set(id + 1);
470        id
471    }
472
473    /// Registers an in-flight connection.
474    ///
475    /// Returns the guard that unregisters the connection when dropped, and
476    /// the number of in-flight connections.
477    pub(super) fn insert_io(&self, io: &IoRef) -> (InflightGuard, usize) {
478        let mut inflight = self.0.inflight.borrow_mut();
479        inflight.insert(io.clone());
480        let guard = InflightGuard {
481            config: self.clone(),
482            io: io.clone(),
483        };
484        (guard, inflight.len())
485    }
486
487    /// Service is shutting down
488    pub(super) fn is_shutdown(&self) -> bool {
489        self.0.flags.get().contains(Flags::SHUTDOWN)
490    }
491
492    pub(super) fn shutdown(&self) -> usize {
493        ntex_h2::ServiceConfig::shutdown();
494
495        let mut flags = self.0.flags.get();
496        flags.insert(Flags::SHUTDOWN);
497        self.0.flags.set(flags);
498
499        let inflight = self.0.inflight.borrow();
500        for io in inflight.iter() {
501            io.notify_dispatcher();
502        }
503        inflight.len()
504    }
505
506    pub(super) async fn wait_shutdown(&self) {
507        if let Some(rx) = self.0.rx.take() {
508            let _ = rx.await;
509        }
510    }
511
512    pub(super) fn notify_shutdown(&self) {
513        if let Some(tx) = self.0.tx.take() {
514            let _ = tx.send(());
515        }
516    }
517}
518
519/// Unregisters an in-flight connection when dropped.
520///
521/// The connection is unregistered even if its future is dropped before it
522/// completes, a pending shutdown is notified once no connections are left.
523pub(super) struct InflightGuard {
524    config: DispatcherConfig,
525    io: IoRef,
526}
527
528impl Drop for InflightGuard {
529    fn drop(&mut self) {
530        let inflight = {
531            let mut inflight = self.config.0.inflight.borrow_mut();
532            inflight.remove(&self.io);
533            inflight.len()
534        };
535        if inflight == 0 && self.config.is_shutdown() {
536            self.config.notify_shutdown();
537        }
538    }
539}
540
541const DATE_VALUE_LENGTH_HDR: usize = 39;
542const DATE_VALUE_DEFAULT: [u8; DATE_VALUE_LENGTH_HDR] =
543    *b"date: 00000000000000000000000000000\r\n\r\n";
544
545#[derive(Debug, Copy, Clone)]
546/// Generates the cached HTTP `Date` header used by the protocol encoders.
547///
548/// The cached value is refreshed periodically and avoids formatting the
549/// current system time for every response. Applications normally do not need
550/// to use this type directly.
551pub struct DateService;
552
553thread_local! {
554    static DATE: DateServiceInner = DateServiceInner::new();
555}
556
557#[derive(Debug)]
558struct DateServiceInner {
559    current: Cell<bool>,
560    current_time: Cell<time::Instant>,
561    current_date: Cell<[u8; DATE_VALUE_LENGTH_HDR]>,
562    current_value: RefCell<Option<HeaderValue>>,
563}
564
565impl DateServiceInner {
566    fn new() -> Self {
567        DateServiceInner {
568            current: Cell::new(false),
569            current_time: Cell::new(time::Instant::now()),
570            current_date: Cell::new(DATE_VALUE_DEFAULT),
571            current_value: RefCell::new(None),
572        }
573    }
574
575    fn update(&self) {
576        self.current.set(true);
577        self.current_time.set(time::Instant::now());
578
579        let mut bytes = DATE_VALUE_DEFAULT;
580        let dt = httpdate::HttpDate::from(time::SystemTime::now()).to_string();
581        bytes[6..35].copy_from_slice(dt.as_ref());
582        self.current_date.set(bytes);
583        *self.current_value.borrow_mut() = None;
584    }
585}
586
587impl DateService {
588    fn check_date() {
589        DATE.with(|date| {
590            if !date.current.get() {
591                date.update();
592
593                // periodic date update
594                crate::rt::spawn(async move {
595                    sleep(Millis(500)).await;
596                    DATE.with(|date| {
597                        date.current.set(false);
598                    });
599                });
600            }
601        });
602    }
603
604    /// Returns the current date as a `Date` header value.
605    ///
606    /// The value is shared until the next date update.
607    pub(super) fn header_value() -> HeaderValue {
608        DateService::check_date();
609        DATE.with(|date| {
610            date.current_value
611                .borrow_mut()
612                .get_or_insert_with(|| {
613                    let bytes = Bytes::copy_from_slice(&date.current_date.get()[6..35]);
614                    // SAFETY: the formatted date is visible ASCII
615                    unsafe { HeaderValue::from_shared_unchecked(bytes) }
616                })
617                .clone()
618        })
619    }
620
621    #[doc(hidden)]
622    pub fn set_date_header(&self, dst: &mut BytesMut) {
623        DateService::check_date();
624        DATE.with(|date| {
625            dst.extend_from_slice(unsafe { date.current_date.as_ptr().as_ref().unwrap() });
626        });
627    }
628
629    #[doc(hidden)]
630    pub fn set_date_header2(&self, dst: &mut BytePages) {
631        DateService::check_date();
632        DATE.with(|date| {
633            dst.extend_from_slice(unsafe { date.current_date.as_ptr().as_ref().unwrap() });
634        });
635    }
636
637    #[doc(hidden)]
638    pub fn bset_date_header(&self, dst: &mut BytesMut) {
639        DateService::check_date();
640        DATE.with(|date| {
641            dst.extend_from_slice(unsafe { date.current_date.as_ptr().as_ref().unwrap() });
642        });
643    }
644}
645
646#[cfg(test)]
647mod tests {
648    use super::*;
649
650    #[crate::rt_test]
651    async fn test_date() {
652        let mut buf1 = BytesMut::with_capacity(DATE_VALUE_LENGTH_HDR);
653        DateService.set_date_header(&mut buf1);
654        let mut buf2 = BytesMut::with_capacity(DATE_VALUE_LENGTH_HDR);
655        DateService.set_date_header(&mut buf2);
656        assert_eq!(buf1, buf2);
657
658        let mut buf1 = BytesMut::with_capacity(DATE_VALUE_LENGTH_HDR);
659        DateService.bset_date_header(&mut buf1);
660        let mut buf2 = BytesMut::with_capacity(DATE_VALUE_LENGTH_HDR);
661        DateService.bset_date_header(&mut buf2);
662        assert_eq!(buf1, buf2);
663
664        // the header value is the cached date
665        let value = DateService::header_value();
666        assert_eq!(value.as_bytes(), &buf1[6..35]);
667        assert_eq!(DateService::header_value(), value);
668        DATE.with(DateServiceInner::update);
669        assert!(DATE.with(|date| date.current_value.borrow().is_none()));
670        assert_eq!(DateService::header_value().len(), 29);
671    }
672
673    #[test]
674    fn keep_alive() {
675        assert_eq!(KeepAlive::Disabled, Option::<usize>::None.into());
676        assert_eq!(
677            KeepAlive::Timeout(Seconds(10)),
678            Option::<usize>::Some(10).into()
679        );
680    }
681
682    #[test]
683    fn keep_alive_settings() {
684        let cfg = HttpServiceConfig::new().set_keepalive(KeepAlive::Os);
685        assert_eq!(cfg.keep_alive, Seconds::ZERO);
686        assert!(cfg.ka_enabled);
687
688        let cfg = HttpServiceConfig::new().set_keepalive(KeepAlive::Disabled);
689        assert_eq!(cfg.keep_alive, Seconds::ZERO);
690        assert!(!cfg.ka_enabled);
691
692        let cfg = HttpServiceConfig::new().set_keepalive_timeout(Seconds(30));
693        assert_eq!(cfg.keep_alive, Seconds(30));
694        assert!(cfg.ka_enabled);
695
696        let cfg = HttpServiceConfig::new().set_keepalive_timeout(Seconds::ZERO);
697        assert_eq!(cfg.keep_alive, Seconds::ZERO);
698        assert!(!cfg.ka_enabled);
699    }
700
701    #[test]
702    fn read_rate_settings() {
703        let cfg = HttpServiceConfig::new()
704            .set_headers_read_rate(Seconds(1), Seconds(5), 128)
705            .set_payload_read_rate(Seconds(1), Seconds(5), 128);
706        assert!(cfg.headers_read_rate.is_some());
707        assert!(cfg.payload_read_rate.is_some());
708
709        let cfg = cfg
710            .set_headers_read_rate(Seconds::ZERO, Seconds(5), 128)
711            .set_payload_read_rate(Seconds::ZERO, Seconds(5), 128);
712        assert!(cfg.headers_read_rate.is_none());
713        assert!(cfg.payload_read_rate.is_none());
714    }
715}