headers/common/
content_range.rs

1use std::fmt;
2use std::ops::{Bound, RangeBounds};
3
4use {util, HeaderValue};
5
6/// Content-Range, described in [RFC7233](https://tools.ietf.org/html/rfc7233#section-4.2)
7///
8/// # ABNF
9///
10/// ```text
11/// Content-Range       = byte-content-range
12///                     / other-content-range
13///
14/// byte-content-range  = bytes-unit SP
15///                       ( byte-range-resp / unsatisfied-range )
16///
17/// byte-range-resp     = byte-range "/" ( complete-length / "*" )
18/// byte-range          = first-byte-pos "-" last-byte-pos
19/// unsatisfied-range   = "*/" complete-length
20///
21/// complete-length     = 1*DIGIT
22///
23/// other-content-range = other-range-unit SP other-range-resp
24/// other-range-resp    = *CHAR
25/// ```
26///
27/// # Example
28///
29/// ```
30/// # extern crate headers;
31/// use headers::ContentRange;
32///
33/// // 100 bytes (included byte 199), with a full length of 3,400
34/// let cr = ContentRange::bytes(100..200, 3400).unwrap();
35/// ```
36//NOTE: only supporting bytes-content-range, YAGNI the extension
37#[derive(Clone, Debug, PartialEq)]
38pub struct ContentRange {
39    /// First and last bytes of the range, omitted if request could not be
40    /// satisfied
41    range: Option<(u64, u64)>,
42
43    /// Total length of the instance, can be omitted if unknown
44    complete_length: Option<u64>,
45}
46
47error_type!(InvalidContentRange);
48
49impl ContentRange {
50    /// Construct a new `Content-Range: bytes ..` header.
51    pub fn bytes(
52        range: impl RangeBounds<u64>,
53        complete_length: impl Into<Option<u64>>,
54    ) -> Result<ContentRange, InvalidContentRange> {
55        let complete_length = complete_length.into();
56
57        let start = match range.start_bound() {
58            Bound::Included(&s) => s,
59            Bound::Excluded(&s) => s + 1,
60            Bound::Unbounded => 0,
61        };
62
63        let end = match range.end_bound() {
64            Bound::Included(&e) => e,
65            Bound::Excluded(&e) => e - 1,
66            Bound::Unbounded => match complete_length {
67                Some(max) => max - 1,
68                None => return Err(InvalidContentRange { _inner: () }),
69            },
70        };
71
72        Ok(ContentRange {
73            range: Some((start, end)),
74            complete_length,
75        })
76    }
77
78    /// Create a new `ContentRange` stating the range could not be satisfied.
79    ///
80    /// The passed argument is the complete length of the entity.
81    pub fn unsatisfied_bytes(complete_length: u64) -> Self {
82        ContentRange {
83            range: None,
84            complete_length: Some(complete_length),
85        }
86    }
87
88    /// Get the byte range if satisified.
89    ///
90    /// Note that these byte ranges are inclusive on both ends.
91    pub fn bytes_range(&self) -> Option<(u64, u64)> {
92        self.range
93    }
94
95    /// Get the bytes complete length if available.
96    pub fn bytes_len(&self) -> Option<u64> {
97        self.complete_length
98    }
99}
100
101impl ::Header for ContentRange {
102    fn name() -> &'static ::HeaderName {
103        &::http::header::CONTENT_RANGE
104    }
105
106    fn decode<'i, I: Iterator<Item = &'i HeaderValue>>(values: &mut I) -> Result<Self, ::Error> {
107        values
108            .next()
109            .and_then(|v| v.to_str().ok())
110            .and_then(|s| split_in_two(s, ' '))
111            .and_then(|(unit, spec)| {
112                if unit != "bytes" {
113                    // For now, this only supports bytes-content-range. nani?
114                    return None;
115                }
116
117                let (range, complete_length) = split_in_two(spec, '/')?;
118
119                let complete_length = if complete_length == "*" {
120                    None
121                } else {
122                    Some(complete_length.parse().ok()?)
123                };
124
125                let range = if range == "*" {
126                    None
127                } else {
128                    let (first_byte, last_byte) = split_in_two(range, '-')?;
129                    let first_byte = first_byte.parse().ok()?;
130                    let last_byte = last_byte.parse().ok()?;
131                    if last_byte < first_byte {
132                        return None;
133                    }
134                    Some((first_byte, last_byte))
135                };
136
137                Some(ContentRange {
138                    range,
139                    complete_length,
140                })
141            })
142            .ok_or_else(::Error::invalid)
143    }
144
145    fn encode<E: Extend<::HeaderValue>>(&self, values: &mut E) {
146        struct Adapter<'a>(&'a ContentRange);
147
148        impl<'a> fmt::Display for Adapter<'a> {
149            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
150                f.write_str("bytes ")?;
151
152                if let Some((first_byte, last_byte)) = self.0.range {
153                    write!(f, "{}-{}", first_byte, last_byte)?;
154                } else {
155                    f.write_str("*")?;
156                }
157
158                f.write_str("/")?;
159
160                if let Some(v) = self.0.complete_length {
161                    write!(f, "{}", v)
162                } else {
163                    f.write_str("*")
164                }
165            }
166        }
167
168        values.extend(::std::iter::once(util::fmt(Adapter(self))));
169    }
170}
171
172fn split_in_two(s: &str, separator: char) -> Option<(&str, &str)> {
173    let mut iter = s.splitn(2, separator);
174    match (iter.next(), iter.next()) {
175        (Some(a), Some(b)) => Some((a, b)),
176        _ => None,
177    }
178}
179
180/*
181test_header!(test_bytes,
182    vec![b"bytes 0-499/500"],
183    Some(ContentRange(ContentRangeSpec::Bytes {
184        range: Some((0, 499)),
185        complete_length: Some(500)
186    })));
187
188test_header!(test_bytes_unknown_len,
189    vec![b"bytes 0-499/*"],
190    Some(ContentRange(ContentRangeSpec::Bytes {
191        range: Some((0, 499)),
192        complete_length: None
193    })));
194
195test_header!(test_bytes_unknown_range,
196    vec![b"bytes */
197500"],
198            Some(ContentRange(ContentRangeSpec::Bytes {
199                range: None,
200                complete_length: Some(500)
201            })));
202
203        test_header!(test_unregistered,
204            vec![b"seconds 1-2"],
205            Some(ContentRange(ContentRangeSpec::Unregistered {
206                unit: "seconds".to_owned(),
207                resp: "1-2".to_owned()
208            })));
209
210        test_header!(test_no_len,
211            vec![b"bytes 0-499"],
212            None::<ContentRange>);
213
214        test_header!(test_only_unit,
215            vec![b"bytes"],
216            None::<ContentRange>);
217
218        test_header!(test_end_less_than_start,
219            vec![b"bytes 499-0/500"],
220            None::<ContentRange>);
221
222        test_header!(test_blank,
223            vec![b""],
224            None::<ContentRange>);
225
226        test_header!(test_bytes_many_spaces,
227            vec![b"bytes 1-2/500 3"],
228            None::<ContentRange>);
229
230        test_header!(test_bytes_many_slashes,
231            vec![b"bytes 1-2/500/600"],
232            None::<ContentRange>);
233
234        test_header!(test_bytes_many_dashes,
235            vec![b"bytes 1-2-3/500"],
236            None::<ContentRange>);
237*/