actori_http/header/common/
content_range.rs

1use std::fmt::{self, Display, Write};
2use std::str::FromStr;
3
4use crate::error::ParseError;
5use crate::header::{
6    HeaderValue, IntoHeaderValue, InvalidHeaderValue, Writer, CONTENT_RANGE,
7};
8
9header! {
10    /// `Content-Range` header, defined in
11    /// [RFC7233](http://tools.ietf.org/html/rfc7233#section-4.2)
12    (ContentRange, CONTENT_RANGE) => [ContentRangeSpec]
13
14    test_content_range {
15        test_header!(test_bytes,
16            vec![b"bytes 0-499/500"],
17            Some(ContentRange(ContentRangeSpec::Bytes {
18                range: Some((0, 499)),
19                instance_length: Some(500)
20            })));
21
22        test_header!(test_bytes_unknown_len,
23            vec![b"bytes 0-499/*"],
24            Some(ContentRange(ContentRangeSpec::Bytes {
25                range: Some((0, 499)),
26                instance_length: None
27            })));
28
29        test_header!(test_bytes_unknown_range,
30            vec![b"bytes */500"],
31            Some(ContentRange(ContentRangeSpec::Bytes {
32                range: None,
33                instance_length: Some(500)
34            })));
35
36        test_header!(test_unregistered,
37            vec![b"seconds 1-2"],
38            Some(ContentRange(ContentRangeSpec::Unregistered {
39                unit: "seconds".to_owned(),
40                resp: "1-2".to_owned()
41            })));
42
43        test_header!(test_no_len,
44            vec![b"bytes 0-499"],
45            None::<ContentRange>);
46
47        test_header!(test_only_unit,
48            vec![b"bytes"],
49            None::<ContentRange>);
50
51        test_header!(test_end_less_than_start,
52            vec![b"bytes 499-0/500"],
53            None::<ContentRange>);
54
55        test_header!(test_blank,
56            vec![b""],
57            None::<ContentRange>);
58
59        test_header!(test_bytes_many_spaces,
60            vec![b"bytes 1-2/500 3"],
61            None::<ContentRange>);
62
63        test_header!(test_bytes_many_slashes,
64            vec![b"bytes 1-2/500/600"],
65            None::<ContentRange>);
66
67        test_header!(test_bytes_many_dashes,
68            vec![b"bytes 1-2-3/500"],
69            None::<ContentRange>);
70
71    }
72}
73
74/// Content-Range, described in [RFC7233](https://tools.ietf.org/html/rfc7233#section-4.2)
75///
76/// # ABNF
77///
78/// ```text
79/// Content-Range       = byte-content-range
80///                     / other-content-range
81///
82/// byte-content-range  = bytes-unit SP
83///                       ( byte-range-resp / unsatisfied-range )
84///
85/// byte-range-resp     = byte-range "/" ( complete-length / "*" )
86/// byte-range          = first-byte-pos "-" last-byte-pos
87/// unsatisfied-range   = "*/" complete-length
88///
89/// complete-length     = 1*DIGIT
90///
91/// other-content-range = other-range-unit SP other-range-resp
92/// other-range-resp    = *CHAR
93/// ```
94#[derive(PartialEq, Clone, Debug)]
95pub enum ContentRangeSpec {
96    /// Byte range
97    Bytes {
98        /// First and last bytes of the range, omitted if request could not be
99        /// satisfied
100        range: Option<(u64, u64)>,
101
102        /// Total length of the instance, can be omitted if unknown
103        instance_length: Option<u64>,
104    },
105
106    /// Custom range, with unit not registered at IANA
107    Unregistered {
108        /// other-range-unit
109        unit: String,
110
111        /// other-range-resp
112        resp: String,
113    },
114}
115
116fn split_in_two(s: &str, separator: char) -> Option<(&str, &str)> {
117    let mut iter = s.splitn(2, separator);
118    match (iter.next(), iter.next()) {
119        (Some(a), Some(b)) => Some((a, b)),
120        _ => None,
121    }
122}
123
124impl FromStr for ContentRangeSpec {
125    type Err = ParseError;
126
127    fn from_str(s: &str) -> Result<Self, ParseError> {
128        let res = match split_in_two(s, ' ') {
129            Some(("bytes", resp)) => {
130                let (range, instance_length) =
131                    split_in_two(resp, '/').ok_or(ParseError::Header)?;
132
133                let instance_length = if instance_length == "*" {
134                    None
135                } else {
136                    Some(instance_length.parse().map_err(|_| ParseError::Header)?)
137                };
138
139                let range = if range == "*" {
140                    None
141                } else {
142                    let (first_byte, last_byte) =
143                        split_in_two(range, '-').ok_or(ParseError::Header)?;
144                    let first_byte =
145                        first_byte.parse().map_err(|_| ParseError::Header)?;
146                    let last_byte = last_byte.parse().map_err(|_| ParseError::Header)?;
147                    if last_byte < first_byte {
148                        return Err(ParseError::Header);
149                    }
150                    Some((first_byte, last_byte))
151                };
152
153                ContentRangeSpec::Bytes {
154                    range,
155                    instance_length,
156                }
157            }
158            Some((unit, resp)) => ContentRangeSpec::Unregistered {
159                unit: unit.to_owned(),
160                resp: resp.to_owned(),
161            },
162            _ => return Err(ParseError::Header),
163        };
164        Ok(res)
165    }
166}
167
168impl Display for ContentRangeSpec {
169    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
170        match *self {
171            ContentRangeSpec::Bytes {
172                range,
173                instance_length,
174            } => {
175                f.write_str("bytes ")?;
176                match range {
177                    Some((first_byte, last_byte)) => {
178                        write!(f, "{}-{}", first_byte, last_byte)?;
179                    }
180                    None => {
181                        f.write_str("*")?;
182                    }
183                };
184                f.write_str("/")?;
185                if let Some(v) = instance_length {
186                    write!(f, "{}", v)
187                } else {
188                    f.write_str("*")
189                }
190            }
191            ContentRangeSpec::Unregistered { ref unit, ref resp } => {
192                f.write_str(unit)?;
193                f.write_str(" ")?;
194                f.write_str(resp)
195            }
196        }
197    }
198}
199
200impl IntoHeaderValue for ContentRangeSpec {
201    type Error = InvalidHeaderValue;
202
203    fn try_into(self) -> Result<HeaderValue, Self::Error> {
204        let mut writer = Writer::new();
205        let _ = write!(&mut writer, "{}", self);
206        HeaderValue::from_maybe_shared(writer.take())
207    }
208}