1 use std::fmt;
2 
3 #[derive(Clone, Copy, PartialEq, Eq)]
4 pub(crate) struct DecodedLength(u64);
5 
6 #[cfg(any(feature = "http1", feature = "http2"))]
7 impl From<Option<u64>> for DecodedLength {
from(len: Option<u64>) -> Self8     fn from(len: Option<u64>) -> Self {
9         len.and_then(|len| {
10             // If the length is u64::MAX, oh well, just reported chunked.
11             Self::checked_new(len).ok()
12         })
13         .unwrap_or(DecodedLength::CHUNKED)
14     }
15 }
16 
17 #[cfg(any(feature = "http1", feature = "http2", test))]
18 const MAX_LEN: u64 = std::u64::MAX - 2;
19 
20 impl DecodedLength {
21     pub(crate) const CLOSE_DELIMITED: DecodedLength = DecodedLength(::std::u64::MAX);
22     pub(crate) const CHUNKED: DecodedLength = DecodedLength(::std::u64::MAX - 1);
23     pub(crate) const ZERO: DecodedLength = DecodedLength(0);
24 
25     #[cfg(test)]
new(len: u64) -> Self26     pub(crate) fn new(len: u64) -> Self {
27         debug_assert!(len <= MAX_LEN);
28         DecodedLength(len)
29     }
30 
31     /// Takes the length as a content-length without other checks.
32     ///
33     /// Should only be called if previously confirmed this isn't
34     /// CLOSE_DELIMITED or CHUNKED.
35     #[inline]
36     #[cfg(feature = "http1")]
danger_len(self) -> u6437     pub(crate) fn danger_len(self) -> u64 {
38         debug_assert!(self.0 < Self::CHUNKED.0);
39         self.0
40     }
41 
42     /// Converts to an Option<u64> representing a Known or Unknown length.
into_opt(self) -> Option<u64>43     pub(crate) fn into_opt(self) -> Option<u64> {
44         match self {
45             DecodedLength::CHUNKED | DecodedLength::CLOSE_DELIMITED => None,
46             DecodedLength(known) => Some(known),
47         }
48     }
49 
50     /// Checks the `u64` is within the maximum allowed for content-length.
51     #[cfg(any(feature = "http1", feature = "http2"))]
checked_new(len: u64) -> Result<Self, crate::error::Parse>52     pub(crate) fn checked_new(len: u64) -> Result<Self, crate::error::Parse> {
53         if len <= MAX_LEN {
54             Ok(DecodedLength(len))
55         } else {
56             warn!("content-length bigger than maximum: {} > {}", len, MAX_LEN);
57             Err(crate::error::Parse::TooLarge)
58         }
59     }
60 
sub_if(&mut self, amt: u64)61     pub(crate) fn sub_if(&mut self, amt: u64) {
62         match *self {
63             DecodedLength::CHUNKED | DecodedLength::CLOSE_DELIMITED => (),
64             DecodedLength(ref mut known) => {
65                 *known -= amt;
66             }
67         }
68     }
69 }
70 
71 impl fmt::Debug for DecodedLength {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result72     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
73         match *self {
74             DecodedLength::CLOSE_DELIMITED => f.write_str("CLOSE_DELIMITED"),
75             DecodedLength::CHUNKED => f.write_str("CHUNKED"),
76             DecodedLength(n) => f.debug_tuple("DecodedLength").field(&n).finish(),
77         }
78     }
79 }
80 
81 impl fmt::Display for DecodedLength {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result82     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
83         match *self {
84             DecodedLength::CLOSE_DELIMITED => f.write_str("close-delimited"),
85             DecodedLength::CHUNKED => f.write_str("chunked encoding"),
86             DecodedLength::ZERO => f.write_str("empty"),
87             DecodedLength(n) => write!(f, "content-length ({} bytes)", n),
88         }
89     }
90 }
91 
92 #[cfg(test)]
93 mod tests {
94     use super::*;
95 
96     #[test]
sub_if_known()97     fn sub_if_known() {
98         let mut len = DecodedLength::new(30);
99         len.sub_if(20);
100 
101         assert_eq!(len.0, 10);
102     }
103 
104     #[test]
sub_if_chunked()105     fn sub_if_chunked() {
106         let mut len = DecodedLength::CHUNKED;
107         len.sub_if(20);
108 
109         assert_eq!(len, DecodedLength::CHUNKED);
110     }
111 }
112