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*/