1use std::fmt;
2
3#[derive(Clone, Copy, PartialEq, Eq)]
4pub(crate) struct DecodedLength(u64);
5
6#[cfg(any(feature = "http1", feature = "http2"))]
7impl From<Option<u64>> for DecodedLength {
8 fn from(len: Option<u64>) -> Self {
9 len.and_then(|len| {
10 Self::checked_new(len).ok()
12 })
13 .unwrap_or(DecodedLength::CHUNKED)
14 }
15}
16
17#[cfg(any(feature = "http1", feature = "http2", test))]
18const MAX_LEN: u64 = u64::MAX - 2;
19
20impl DecodedLength {
21 pub(crate) const CLOSE_DELIMITED: DecodedLength = DecodedLength(u64::MAX);
22 pub(crate) const CHUNKED: DecodedLength = DecodedLength(u64::MAX - 1);
23 pub(crate) const ZERO: DecodedLength = DecodedLength(0);
24
25 #[cfg(test)]
26 pub(crate) fn new(len: u64) -> Self {
27 debug_assert!(len <= MAX_LEN);
28 DecodedLength(len)
29 }
30
31 #[inline]
36 #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
37 pub(crate) fn danger_len(self) -> u64 {
38 debug_assert!(self.0 < Self::CHUNKED.0);
39 self.0
40 }
41
42 #[cfg(all(
44 any(feature = "http1", feature = "http2"),
45 any(feature = "client", feature = "server")
46 ))]
47 pub(crate) fn into_opt(self) -> Option<u64> {
48 match self {
49 DecodedLength::CHUNKED | DecodedLength::CLOSE_DELIMITED => None,
50 DecodedLength(known) => Some(known),
51 }
52 }
53
54 #[cfg(any(feature = "http1", feature = "http2"))]
56 pub(crate) fn checked_new(len: u64) -> Result<Self, crate::error::Parse> {
57 if len <= MAX_LEN {
58 Ok(DecodedLength(len))
59 } else {
60 warn!("content-length bigger than maximum: {} > {}", len, MAX_LEN);
61 Err(crate::error::Parse::TooLarge)
62 }
63 }
64
65 #[cfg(all(
66 any(feature = "http1", feature = "http2"),
67 any(feature = "client", feature = "server")
68 ))]
69 pub(crate) fn sub_if(&mut self, amt: u64) {
70 match *self {
71 DecodedLength::CHUNKED | DecodedLength::CLOSE_DELIMITED => (),
72 DecodedLength(ref mut known) => {
73 *known -= amt;
74 }
75 }
76 }
77
78 #[cfg(all(any(feature = "client", feature = "server"), feature = "http2"))]
84 pub(crate) fn is_exact(&self) -> bool {
85 self.0 <= MAX_LEN
86 }
87}
88
89impl fmt::Debug for DecodedLength {
90 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91 match *self {
92 DecodedLength::CLOSE_DELIMITED => f.write_str("CLOSE_DELIMITED"),
93 DecodedLength::CHUNKED => f.write_str("CHUNKED"),
94 DecodedLength(n) => f.debug_tuple("DecodedLength").field(&n).finish(),
95 }
96 }
97}
98
99impl fmt::Display for DecodedLength {
100 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101 match *self {
102 DecodedLength::CLOSE_DELIMITED => f.write_str("close-delimited"),
103 DecodedLength::CHUNKED => f.write_str("chunked encoding"),
104 DecodedLength::ZERO => f.write_str("empty"),
105 DecodedLength(n) => write!(f, "content-length ({} bytes)", n),
106 }
107 }
108}
109
110#[cfg(test)]
111mod tests {
112 use super::*;
113
114 #[test]
115 fn sub_if_known() {
116 let mut len = DecodedLength::new(30);
117 len.sub_if(20);
118
119 assert_eq!(len.0, 10);
120 }
121
122 #[test]
123 fn sub_if_chunked() {
124 let mut len = DecodedLength::CHUNKED;
125 len.sub_if(20);
126
127 assert_eq!(len, DecodedLength::CHUNKED);
128 }
129}