      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100

    use std::fmt;

    #[derive(Clone, Copy, PartialEq, Eq)]
    pub(crate) struct DecodedLength(u64);

    #[cfg(any(feature = "http1", feature = "http2", test))]
    const MAX_LEN: u64 = std::u64::MAX - 2;

    impl DecodedLength {
        pub(crate) const CLOSE_DELIMITED: DecodedLength = DecodedLength(::std::u64::MAX);
        pub(crate) const CHUNKED: DecodedLength = DecodedLength(::std::u64::MAX - 1);
        pub(crate) const ZERO: DecodedLength = DecodedLength(0);

        #[cfg(test)]
        pub(crate) fn new(len: u64) -> Self {
            debug_assert!(len <= MAX_LEN);
            DecodedLength(len)
        }

        /// Takes the length as a content-length without other checks.
        ///
        /// Should only be called if previously confirmed this isn't
        /// CLOSE_DELIMITED or CHUNKED.
        #[inline]
        #[cfg(feature = "http1")]
        pub(crate) fn danger_len(self) -> u64 {
            debug_assert!(self.0 < Self::CHUNKED.0);
            self.0
        }

        /// Converts to an Option<u64> representing a Known or Unknown length.
        pub(crate) fn into_opt(self) -> Option<u64> {
            match self {
                DecodedLength::CHUNKED | DecodedLength::CLOSE_DELIMITED => None,
                DecodedLength(known) => Some(known),
            }
        }

        /// Checks the `u64` is within the maximum allowed for content-length.
        #[cfg(any(feature = "http1", feature = "http2"))]
        pub(crate) fn checked_new(len: u64) -> Result<Self, crate::error::Parse> {
            if len <= MAX_LEN {
                Ok(DecodedLength(len))
            } else {
                warn!("content-length bigger than maximum: {} > {}", len, MAX_LEN);
                Err(crate::error::Parse::TooLarge)
            }
        }

        pub(crate) fn sub_if(&mut self, amt: u64) {
            match *self {
                DecodedLength::CHUNKED | DecodedLength::CLOSE_DELIMITED => (),
                DecodedLength(ref mut known) => {
                    *known -= amt;
                }
            }
        }
    }

    impl fmt::Debug for DecodedLength {
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
            match *self {
                DecodedLength::CLOSE_DELIMITED => f.write_str("CLOSE_DELIMITED"),
                DecodedLength::CHUNKED => f.write_str("CHUNKED"),
                DecodedLength(n) => f.debug_tuple("DecodedLength").field(&n).finish(),
            }
        }
    }

    impl fmt::Display for DecodedLength {
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
            match *self {
                DecodedLength::CLOSE_DELIMITED => f.write_str("close-delimited"),
                DecodedLength::CHUNKED => f.write_str("chunked encoding"),
                DecodedLength::ZERO => f.write_str("empty"),
                DecodedLength(n) => write!(f, "content-length ({} bytes)", n),
            }
        }
    }

    #[cfg(test)]
    mod tests {
        use super::*;

        #[test]
        fn sub_if_known() {
            let mut len = DecodedLength::new(30);
            len.sub_if(20);

            assert_eq!(len.0, 10);
        }

        #[test]
        fn sub_if_chunked() {
            let mut len = DecodedLength::CHUNKED;
            len.sub_if(20);

            assert_eq!(len, DecodedLength::CHUNKED);
        }
    }