actix_web_lab/
content_length.rs

1//! Content-Length typed header.
2//!
3//! See [`ContentLength`] docs.
4
5use std::{convert::Infallible, str};
6
7use actix_web::{
8    error::ParseError,
9    http::header::{
10        from_one_raw_str, Header, HeaderName, HeaderValue, TryIntoHeaderValue, CONTENT_LENGTH,
11    },
12    HttpMessage,
13};
14
15/// `Content-Length` header, defined in [RFC 9110 §8.6].
16///
17/// The "Content-Length" header field indicates the associated representation's data length as a
18/// decimal non-negative integer number of octets.
19///
20/// # ABNF
21/// ```plain
22/// Content-Length = 1*DIGIT
23/// ```
24///
25/// [RFC 9110 §8.6]: https://www.rfc-editor.org/rfc/rfc9110#name-content-length
26#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
27pub struct ContentLength(usize);
28
29impl ContentLength {
30    /// Returns Content-Length value.
31    pub fn into_inner(&self) -> usize {
32        self.0
33    }
34}
35
36impl str::FromStr for ContentLength {
37    type Err = <usize as str::FromStr>::Err;
38
39    #[inline]
40    fn from_str(val: &str) -> Result<Self, Self::Err> {
41        let val = val.trim();
42
43        // decoder prevents this case
44        debug_assert!(!val.starts_with('+'));
45
46        val.parse().map(Self)
47    }
48}
49
50impl TryIntoHeaderValue for ContentLength {
51    type Error = Infallible;
52
53    fn try_into_value(self) -> Result<HeaderValue, Self::Error> {
54        Ok(HeaderValue::from(self.0))
55    }
56}
57
58impl Header for ContentLength {
59    fn name() -> HeaderName {
60        CONTENT_LENGTH
61    }
62
63    fn parse<M: HttpMessage>(msg: &M) -> Result<Self, ParseError> {
64        let val = from_one_raw_str(msg.headers().get(Self::name()))?;
65
66        // decoder prevents multiple CL headers
67        debug_assert_eq!(msg.headers().get_all(Self::name()).count(), 1);
68
69        Ok(val)
70    }
71}
72
73impl From<ContentLength> for usize {
74    fn from(ContentLength(len): ContentLength) -> Self {
75        len
76    }
77}
78
79impl From<usize> for ContentLength {
80    fn from(len: usize) -> Self {
81        ContentLength(len)
82    }
83}
84
85impl PartialEq<usize> for ContentLength {
86    fn eq(&self, other: &usize) -> bool {
87        self.0 == *other
88    }
89}
90
91impl PartialEq<ContentLength> for usize {
92    fn eq(&self, other: &ContentLength) -> bool {
93        *self == other.0
94    }
95}
96
97impl PartialOrd<usize> for ContentLength {
98    fn partial_cmp(&self, other: &usize) -> Option<std::cmp::Ordering> {
99        self.0.partial_cmp(other)
100    }
101}
102
103impl PartialOrd<ContentLength> for usize {
104    fn partial_cmp(&self, other: &ContentLength) -> Option<std::cmp::Ordering> {
105        self.partial_cmp(&other.0)
106    }
107}
108
109#[cfg(test)]
110mod tests {
111    use super::*;
112    use crate::header::{assert_parse_eq, assert_parse_fail};
113
114    #[test]
115    fn missing_header() {
116        assert_parse_fail::<ContentLength, _, _>([""; 0]);
117        assert_parse_fail::<ContentLength, _, _>([""]);
118    }
119
120    #[test]
121    fn bad_header() {
122        assert_parse_fail::<ContentLength, _, _>(["-123"]);
123        assert_parse_fail::<ContentLength, _, _>(["123_456"]);
124        assert_parse_fail::<ContentLength, _, _>(["123.456"]);
125
126        // too large for u64 (2^64, 2^64 + 1)
127        assert_parse_fail::<ContentLength, _, _>(["18446744073709551616"]);
128        assert_parse_fail::<ContentLength, _, _>(["18446744073709551617"]);
129
130        // hex notation
131        assert_parse_fail::<ContentLength, _, _>(["0x123"]);
132
133        // multi-value
134        assert_parse_fail::<ContentLength, _, _>(["0, 123"]);
135    }
136
137    #[test]
138    #[should_panic]
139    fn bad_header_plus() {
140        // prevented by decoder anyway
141        assert_parse_fail::<ContentLength, _, _>(["+123"]);
142    }
143
144    #[test]
145    #[should_panic]
146    fn bad_multiple_value() {
147        assert_parse_fail::<ContentLength, _, _>(["0", "123"]);
148    }
149
150    #[test]
151    fn good_header() {
152        assert_parse_eq::<ContentLength, _, _>(["0"], ContentLength(0));
153        assert_parse_eq::<ContentLength, _, _>(["1"], ContentLength(1));
154        assert_parse_eq::<ContentLength, _, _>(["123"], ContentLength(123));
155
156        // value that looks like octal notation is not interpreted as such
157        assert_parse_eq::<ContentLength, _, _>(["0123"], ContentLength(123));
158
159        // whitespace variations
160        assert_parse_eq::<ContentLength, _, _>([" 0"], ContentLength(0));
161        assert_parse_eq::<ContentLength, _, _>(["0 "], ContentLength(0));
162        assert_parse_eq::<ContentLength, _, _>([" 0 "], ContentLength(0));
163
164        // large value (2^64 - 1)
165        assert_parse_eq::<ContentLength, _, _>(
166            ["18446744073709551615"],
167            ContentLength(18_446_744_073_709_551_615),
168        );
169    }
170
171    #[test]
172    fn equality() {
173        assert!(ContentLength(0) == ContentLength(0));
174        assert!(ContentLength(0) == 0);
175        assert!(0 != ContentLength(123));
176    }
177
178    #[test]
179    fn ordering() {
180        assert!(ContentLength(0) < ContentLength(123));
181        assert!(ContentLength(0) < 123);
182        assert!(0 < ContentLength(123));
183    }
184}