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}