hyper/body/
length.rs
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 = std::u64::MAX - 2;
19
20impl 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)]
26 pub(crate) fn new(len: u64) -> Self {
27 debug_assert!(len <= MAX_LEN);
28 DecodedLength(len)
29 }
30
31 #[inline]
36 #[cfg(feature = "http1")]
37 pub(crate) fn danger_len(self) -> u64 {
38 debug_assert!(self.0 < Self::CHUNKED.0);
39 self.0
40 }
41
42 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 #[cfg(any(feature = "http1", feature = "http2"))]
52 pub(crate) fn checked_new(len: u64) -> Result<Self, crate::error::Parse> {
53 use tracing::warn;
54
55 if len <= MAX_LEN {
56 Ok(DecodedLength(len))
57 } else {
58 warn!("content-length bigger than maximum: {} > {}", len, MAX_LEN);
59 Err(crate::error::Parse::TooLarge)
60 }
61 }
62
63 pub(crate) fn sub_if(&mut self, amt: u64) {
64 match *self {
65 DecodedLength::CHUNKED | DecodedLength::CLOSE_DELIMITED => (),
66 DecodedLength(ref mut known) => {
67 *known -= amt;
68 }
69 }
70 }
71
72 #[cfg(feature = "http2")]
78 pub(crate) fn is_exact(&self) -> bool {
79 self.0 <= MAX_LEN
80 }
81}
82
83impl fmt::Debug for DecodedLength {
84 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
85 match *self {
86 DecodedLength::CLOSE_DELIMITED => f.write_str("CLOSE_DELIMITED"),
87 DecodedLength::CHUNKED => f.write_str("CHUNKED"),
88 DecodedLength(n) => f.debug_tuple("DecodedLength").field(&n).finish(),
89 }
90 }
91}
92
93impl fmt::Display for DecodedLength {
94 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
95 match *self {
96 DecodedLength::CLOSE_DELIMITED => f.write_str("close-delimited"),
97 DecodedLength::CHUNKED => f.write_str("chunked encoding"),
98 DecodedLength::ZERO => f.write_str("empty"),
99 DecodedLength(n) => write!(f, "content-length ({} bytes)", n),
100 }
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107
108 #[test]
109 fn sub_if_known() {
110 let mut len = DecodedLength::new(30);
111 len.sub_if(20);
112
113 assert_eq!(len.0, 10);
114 }
115
116 #[test]
117 fn sub_if_chunked() {
118 let mut len = DecodedLength::CHUNKED;
119 len.sub_if(20);
120
121 assert_eq!(len, DecodedLength::CHUNKED);
122 }
123}