| 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 { |
| 8 | 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(default:DecodedLength::CHUNKED) |
| 14 | } |
| 15 | } |
| 16 | |
| 17 | #[cfg (any(feature = "http1" , feature = "http2" , test))] |
| 18 | const MAX_LEN: u64 = u64::MAX - 2; |
| 19 | |
| 20 | impl 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 | /// 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 (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 | /// Converts to an Option<u64> representing a Known or Unknown length. |
| 43 | #[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 | /// Checks the `u64` is within the maximum allowed for content-length. |
| 55 | #[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 | /// Returns whether this represents an exact length. |
| 79 | /// |
| 80 | /// This includes 0, which of course is an exact known length. |
| 81 | /// |
| 82 | /// It would return false if "chunked" or otherwise size-unknown. |
| 83 | #[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 | |
| 89 | impl fmt::Debug for DecodedLength { |
| 90 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 91 | match *self { |
| 92 | DecodedLength::CLOSE_DELIMITED => f.write_str(data:"CLOSE_DELIMITED" ), |
| 93 | DecodedLength::CHUNKED => f.write_str(data:"CHUNKED" ), |
| 94 | DecodedLength(n: u64) => f.debug_tuple(name:"DecodedLength" ).field(&n).finish(), |
| 95 | } |
| 96 | } |
| 97 | } |
| 98 | |
| 99 | impl fmt::Display for DecodedLength { |
| 100 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 101 | match *self { |
| 102 | DecodedLength::CLOSE_DELIMITED => f.write_str(data:"close-delimited" ), |
| 103 | DecodedLength::CHUNKED => f.write_str(data:"chunked encoding" ), |
| 104 | DecodedLength::ZERO => f.write_str(data:"empty" ), |
| 105 | DecodedLength(n: u64) => write!(f, "content-length ( {} bytes)" , n), |
| 106 | } |
| 107 | } |
| 108 | } |
| 109 | |
| 110 | #[cfg (test)] |
| 111 | mod 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 | } |
| 130 | |