headers_ext/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_ext as 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(range: impl RangeBounds<u64>, complete_length: impl Into<Option<u64>>) -> Result<ContentRange, InvalidContentRange> {
52 let complete_length = complete_length.into();
53
54 let start = match range.start_bound() {
55 Bound::Included(&s) => s,
56 Bound::Excluded(&s) => s + 1,
57 Bound::Unbounded => 0,
58 };
59
60 let end = match range.end_bound() {
61 Bound::Included(&e) => e,
62 Bound::Excluded(&e) => e - 1,
63 Bound::Unbounded => match complete_length {
64 Some(max) => max -1,
65 None => return Err(InvalidContentRange { _inner: () }),
66 },
67 };
68
69 Ok(ContentRange {
70 range: Some((start, end)),
71 complete_length,
72 })
73 }
74
75 /// Create a new `ContentRange` stating the range could not be satisfied.
76 ///
77 /// The passed argument is the complete length of the entity.
78 pub fn unsatisfied_bytes(complete_length: u64) -> Self {
79 ContentRange {
80 range: None,
81 complete_length: Some(complete_length),
82 }
83 }
84
85 /// Get the byte range if satisified.
86 ///
87 /// Note that these byte ranges are inclusive on both ends.
88 pub fn bytes_range(&self) -> Option<(u64, u64)> {
89 self.range
90 }
91
92 /// Get the bytes complete length if available.
93 pub fn bytes_len(&self) -> Option<u64> {
94 self.complete_length
95 }
96}
97
98impl ::Header for ContentRange {
99 const NAME: &'static ::HeaderName = &::http::header::CONTENT_RANGE;
100
101 fn decode<'i, I: Iterator<Item = &'i HeaderValue>>(values: &mut I) -> Result<Self, ::Error> {
102 values
103 .next()
104 .and_then(|v| v.to_str().ok())
105 .and_then(|s| split_in_two(s, ' '))
106 .and_then(|(unit, spec)| {
107 if unit != "bytes" {
108 // For now, this only supports bytes-content-range. nani?
109 return None;
110 }
111
112 let (range, complete_length) = split_in_two(spec, '/')?;
113
114 let complete_length = if complete_length == "*" {
115 None
116 } else {
117 Some(complete_length.parse().ok()?)
118 };
119
120 let range = if range == "*" {
121 None
122 } else {
123 let (first_byte, last_byte) = split_in_two(range, '-')?;
124 let first_byte = first_byte.parse().ok()?;
125 let last_byte = last_byte.parse().ok()?;
126 if last_byte < first_byte {
127 return None;
128 }
129 Some((first_byte, last_byte))
130 };
131
132 Some(ContentRange {
133 range,
134 complete_length,
135 })
136 })
137 .ok_or_else(::Error::invalid)
138 }
139
140 fn encode<E: Extend<::HeaderValue>>(&self, values: &mut E) {
141 struct Adapter<'a>(&'a ContentRange);
142
143 impl<'a> fmt::Display for Adapter<'a> {
144 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
145 f.write_str("bytes ")?;
146
147 if let Some((first_byte, last_byte)) = self.0.range {
148 write!(f, "{}-{}", first_byte, last_byte)?;
149 } else {
150 f.write_str("*")?;
151 }
152
153 f.write_str("/")?;
154
155 if let Some(v) = self.0.complete_length {
156 write!(f, "{}", v)
157 } else {
158 f.write_str("*")
159 }
160 }
161 }
162
163 values.extend(::std::iter::once(util::fmt(Adapter(self))));
164 }
165}
166
167fn split_in_two(s: &str, separator: char) -> Option<(&str, &str)> {
168 let mut iter = s.splitn(2, separator);
169 match (iter.next(), iter.next()) {
170 (Some(a), Some(b)) => Some((a, b)),
171 _ => None
172 }
173}
174
175/*
176 test_header!(test_bytes,
177 vec![b"bytes 0-499/500"],
178 Some(ContentRange(ContentRangeSpec::Bytes {
179 range: Some((0, 499)),
180 complete_length: Some(500)
181 })));
182
183 test_header!(test_bytes_unknown_len,
184 vec![b"bytes 0-499/*"],
185 Some(ContentRange(ContentRangeSpec::Bytes {
186 range: Some((0, 499)),
187 complete_length: None
188 })));
189
190 test_header!(test_bytes_unknown_range,
191 vec![b"bytes */500"],
192 Some(ContentRange(ContentRangeSpec::Bytes {
193 range: None,
194 complete_length: Some(500)
195 })));
196
197 test_header!(test_unregistered,
198 vec![b"seconds 1-2"],
199 Some(ContentRange(ContentRangeSpec::Unregistered {
200 unit: "seconds".to_owned(),
201 resp: "1-2".to_owned()
202 })));
203
204 test_header!(test_no_len,
205 vec![b"bytes 0-499"],
206 None::<ContentRange>);
207
208 test_header!(test_only_unit,
209 vec![b"bytes"],
210 None::<ContentRange>);
211
212 test_header!(test_end_less_than_start,
213 vec![b"bytes 499-0/500"],
214 None::<ContentRange>);
215
216 test_header!(test_blank,
217 vec![b""],
218 None::<ContentRange>);
219
220 test_header!(test_bytes_many_spaces,
221 vec![b"bytes 1-2/500 3"],
222 None::<ContentRange>);
223
224 test_header!(test_bytes_many_slashes,
225 vec![b"bytes 1-2/500/600"],
226 None::<ContentRange>);
227
228 test_header!(test_bytes_many_dashes,
229 vec![b"bytes 1-2-3/500"],
230 None::<ContentRange>);
231*/