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 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 sub_if(&mut self, amt: u64)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 73 impl fmt::Debug for DecodedLength { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result74 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 75 match *self { 76 DecodedLength::CLOSE_DELIMITED => f.write_str("CLOSE_DELIMITED"), 77 DecodedLength::CHUNKED => f.write_str("CHUNKED"), 78 DecodedLength(n) => f.debug_tuple("DecodedLength").field(&n).finish(), 79 } 80 } 81 } 82 83 impl fmt::Display for DecodedLength { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result84 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 encoding"), 88 DecodedLength::ZERO => f.write_str("empty"), 89 DecodedLength(n) => write!(f, "content-length ({} bytes)", n), 90 } 91 } 92 } 93 94 #[cfg(test)] 95 mod tests { 96 use super::*; 97 98 #[test] sub_if_known()99 fn sub_if_known() { 100 let mut len = DecodedLength::new(30); 101 len.sub_if(20); 102 103 assert_eq!(len.0, 10); 104 } 105 106 #[test] sub_if_chunked()107 fn sub_if_chunked() { 108 let mut len = DecodedLength::CHUNKED; 109 len.sub_if(20); 110 111 assert_eq!(len, DecodedLength::CHUNKED); 112 } 113 } 114