actix_web/http/header/
content_length.rs

1use std::{convert::Infallible, str};
2
3use derive_more::{Deref, DerefMut};
4
5use crate::{
6    error::ParseError,
7    http::header::{
8        from_one_raw_str, Header, HeaderName, HeaderValue, TryIntoHeaderValue, CONTENT_LENGTH,
9    },
10    HttpMessage,
11};
12
13/// `Content-Length` header, defined in [RFC 9110 §8.6].
14///
15/// The Content-Length
16///
17/// # ABNF
18///
19/// ```plain
20/// Content-Length = 1*DIGIT
21/// ```
22///
23/// # Example Values
24///
25/// - `0`
26/// - `3495`
27///
28/// # Examples
29///
30/// ```
31/// use actix_web::{http::header::ContentLength, HttpResponse};
32///
33/// let res_empty = HttpResponse::Ok()
34///     .insert_header(ContentLength(0));
35///
36/// let res_fake_cl = HttpResponse::Ok()
37///     .insert_header(ContentLength(3_495));
38/// ```
39///
40/// [RFC 9110 §8.6]: https://www.rfc-editor.org/rfc/rfc9110#name-content-length
41#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Deref, DerefMut)]
42pub struct ContentLength(pub usize);
43
44impl ContentLength {
45    /// Returns Content-Length value.
46    pub fn into_inner(&self) -> usize {
47        self.0
48    }
49}
50
51impl str::FromStr for ContentLength {
52    type Err = <usize as str::FromStr>::Err;
53
54    #[inline]
55    fn from_str(val: &str) -> Result<Self, Self::Err> {
56        let val = val.trim();
57
58        // decoder prevents this case
59        debug_assert!(!val.starts_with('+'));
60
61        val.parse().map(Self)
62    }
63}
64
65impl TryIntoHeaderValue for ContentLength {
66    type Error = Infallible;
67
68    fn try_into_value(self) -> Result<HeaderValue, Self::Error> {
69        Ok(HeaderValue::from(self.0))
70    }
71}
72
73impl Header for ContentLength {
74    fn name() -> HeaderName {
75        CONTENT_LENGTH
76    }
77
78    fn parse<M: HttpMessage>(msg: &M) -> Result<Self, ParseError> {
79        let val = from_one_raw_str(msg.headers().get(Self::name()))?;
80
81        // decoder prevents multiple CL headers
82        debug_assert_eq!(msg.headers().get_all(Self::name()).count(), 1);
83
84        Ok(val)
85    }
86}
87
88impl From<ContentLength> for usize {
89    fn from(ContentLength(len): ContentLength) -> Self {
90        len
91    }
92}
93
94impl From<usize> for ContentLength {
95    fn from(len: usize) -> Self {
96        ContentLength(len)
97    }
98}
99
100impl PartialEq<usize> for ContentLength {
101    fn eq(&self, other: &usize) -> bool {
102        self.0 == *other
103    }
104}
105
106impl PartialEq<ContentLength> for usize {
107    fn eq(&self, other: &ContentLength) -> bool {
108        *self == other.0
109    }
110}
111
112impl PartialOrd<usize> for ContentLength {
113    fn partial_cmp(&self, other: &usize) -> Option<std::cmp::Ordering> {
114        self.0.partial_cmp(other)
115    }
116}
117
118impl PartialOrd<ContentLength> for usize {
119    fn partial_cmp(&self, other: &ContentLength) -> Option<std::cmp::Ordering> {
120        self.partial_cmp(&other.0)
121    }
122}
123
124#[cfg(test)]
125mod tests {
126    use std::fmt;
127
128    use super::*;
129    use crate::{test::TestRequest, HttpRequest};
130
131    fn req_from_raw_headers<H: Header, I: IntoIterator<Item = V>, V: AsRef<[u8]>>(
132        header_lines: I,
133    ) -> HttpRequest {
134        header_lines
135            .into_iter()
136            .fold(TestRequest::default(), |req, item| {
137                req.append_header((H::name(), item.as_ref().to_vec()))
138            })
139            .to_http_request()
140    }
141
142    #[track_caller]
143    pub(crate) fn assert_parse_fail<
144        H: Header + fmt::Debug,
145        I: IntoIterator<Item = V>,
146        V: AsRef<[u8]>,
147    >(
148        headers: I,
149    ) {
150        let req = req_from_raw_headers::<H, _, _>(headers);
151        H::parse(&req).unwrap_err();
152    }
153
154    #[track_caller]
155    pub(crate) fn assert_parse_eq<
156        H: Header + fmt::Debug + PartialEq,
157        I: IntoIterator<Item = V>,
158        V: AsRef<[u8]>,
159    >(
160        headers: I,
161        expect: H,
162    ) {
163        let req = req_from_raw_headers::<H, _, _>(headers);
164        assert_eq!(H::parse(&req).unwrap(), expect);
165    }
166
167    #[test]
168    fn missing_header() {
169        assert_parse_fail::<ContentLength, _, _>([""; 0]);
170        assert_parse_fail::<ContentLength, _, _>([""]);
171    }
172
173    #[test]
174    fn bad_header() {
175        assert_parse_fail::<ContentLength, _, _>(["-123"]);
176        assert_parse_fail::<ContentLength, _, _>(["123_456"]);
177        assert_parse_fail::<ContentLength, _, _>(["123.456"]);
178
179        // too large for u64 (2^64, 2^64 + 1)
180        assert_parse_fail::<ContentLength, _, _>(["18446744073709551616"]);
181        assert_parse_fail::<ContentLength, _, _>(["18446744073709551617"]);
182
183        // hex notation
184        assert_parse_fail::<ContentLength, _, _>(["0x123"]);
185
186        // multi-value
187        assert_parse_fail::<ContentLength, _, _>(["0, 123"]);
188    }
189
190    #[test]
191    #[should_panic]
192    fn bad_header_plus() {
193        // prevented by HTTP decoder anyway
194        assert_parse_fail::<ContentLength, _, _>(["+123"]);
195    }
196
197    #[test]
198    #[should_panic]
199    fn bad_multiple_value() {
200        // prevented by HTTP decoder anyway
201        assert_parse_fail::<ContentLength, _, _>(["0", "123"]);
202    }
203
204    #[test]
205    fn good_header() {
206        assert_parse_eq::<ContentLength, _, _>(["0"], ContentLength(0));
207        assert_parse_eq::<ContentLength, _, _>(["1"], ContentLength(1));
208        assert_parse_eq::<ContentLength, _, _>(["123"], ContentLength(123));
209
210        // value that looks like octal notation is not interpreted as such
211        assert_parse_eq::<ContentLength, _, _>(["0123"], ContentLength(123));
212
213        // whitespace variations
214        assert_parse_eq::<ContentLength, _, _>([" 0"], ContentLength(0));
215        assert_parse_eq::<ContentLength, _, _>(["0 "], ContentLength(0));
216        assert_parse_eq::<ContentLength, _, _>([" 0 "], ContentLength(0));
217
218        // large value (2^64 - 1)
219        assert_parse_eq::<ContentLength, _, _>(
220            ["18446744073709551615"],
221            ContentLength(18_446_744_073_709_551_615),
222        );
223    }
224
225    #[test]
226    fn equality() {
227        assert!(ContentLength(0) == ContentLength(0));
228        assert!(ContentLength(0) == 0);
229        assert!(0 != ContentLength(123));
230    }
231
232    #[test]
233    fn ordering() {
234        assert!(ContentLength(0) < ContentLength(123));
235        assert!(ContentLength(0) < 123);
236        assert!(0 < ContentLength(123));
237    }
238}