Skip to content

Commit 05c3199

Browse files
committed
feat(headers): add Range header
1 parent febf303 commit 05c3199

File tree

2 files changed

+233
-0
lines changed

2 files changed

+233
-0
lines changed

src/header/common/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ pub use self::if_range::IfRange;
4040
pub use self::last_modified::LastModified;
4141
pub use self::location::Location;
4242
pub use self::pragma::Pragma;
43+
pub use self::range::{Range, RangeSpec};
4344
pub use self::referer::Referer;
4445
pub use self::server::Server;
4546
pub use self::set_cookie::SetCookie;
@@ -349,6 +350,7 @@ mod if_unmodified_since;
349350
mod last_modified;
350351
mod location;
351352
mod pragma;
353+
mod range;
352354
mod referer;
353355
mod server;
354356
mod set_cookie;

src/header/common/range.rs

Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
use std::fmt::{self, Display};
2+
use std::str::FromStr;
3+
4+
use header::{Header, HeaderFormat, RangeUnit};
5+
use header::parsing::{from_one_raw_str, from_one_comma_delimited};
6+
7+
/// `Range` header, defined in [RFC7233](https://tools.ietf.org/html/rfc7233#section-3.1)
8+
///
9+
/// The "Range" header field on a GET request modifies the method
10+
/// semantics to request transfer of only one or more subranges of the
11+
/// selected representation data, rather than the entire selected
12+
/// representation data.
13+
///
14+
/// # ABNF
15+
/// ```plain
16+
/// Range = byte-ranges-specifier / other-ranges-specifier
17+
/// other-ranges-specifier = other-range-unit "=" other-range-set
18+
/// other-range-set = 1*VCHAR
19+
/// ```
20+
///
21+
/// # Example values
22+
/// * `bytes=1000-`
23+
/// * `bytes=-2000`
24+
/// * `bytes=0-1,30-40`
25+
/// * `custom_unit=0-123,-200`
26+
///
27+
/// # Examples
28+
/// ```
29+
/// use hyper::header::{Headers, Range, RangeSpec, RangeUnit};
30+
///
31+
/// let mut headers = Headers::new();
32+
///
33+
/// headers.set(Range {
34+
/// unit: RangeUnit::Bytes,
35+
/// ranges: vec![RangeSpec::FromTo(1, 100), RangeSpec::AllFrom(200)]
36+
/// });
37+
/// ```
38+
/// ```
39+
/// use hyper::header::{Headers, Range};
40+
///
41+
/// let mut headers = Headers::new();
42+
/// headers.set(Range::bytes(1, 100));
43+
/// ```
44+
#[derive(PartialEq, Clone, Debug)]
45+
pub struct Range {
46+
/// Unit of the Range i.e. bytes
47+
pub unit: RangeUnit,
48+
/// Set of ranges as defined in the HTTP spec
49+
pub ranges: Vec<RangeSpec>,
50+
}
51+
52+
/// Each 'Range' header can contain one or more RangeSpecs.
53+
/// Each RangeSpec defines a range of units to fetch
54+
#[derive(PartialEq, Clone, Debug)]
55+
pub enum RangeSpec {
56+
/// Get all bytes between x and y ("x-y")
57+
FromTo(u64, u64),
58+
/// Get all bytes starting from x ("x-")
59+
AllFrom(u64),
60+
/// Get last x bytes ("-x")
61+
Last(u64)
62+
}
63+
64+
impl Range {
65+
/// Get the most common byte range header ("bytes=from-to")
66+
pub fn bytes(from: u64, to: u64) -> Range {
67+
Range {
68+
unit: RangeUnit::Bytes,
69+
ranges: vec![RangeSpec::FromTo(from, to)],
70+
}
71+
}
72+
}
73+
74+
75+
impl fmt::Display for RangeSpec {
76+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
77+
match *self {
78+
RangeSpec::FromTo(from, to) => write!(f, "{}-{}", from, to),
79+
RangeSpec::Last(pos) => write!(f, "-{}", pos),
80+
RangeSpec::AllFrom(pos) => write!(f, "{}-", pos),
81+
}
82+
}
83+
}
84+
85+
86+
impl fmt::Display for Range {
87+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
88+
try!(write!(f, "{}=", self.unit));
89+
90+
for (i, range) in self.ranges.iter().enumerate() {
91+
if i != 0 {
92+
try!(f.write_str(","));
93+
}
94+
try!(Display::fmt(range, f));
95+
}
96+
Ok(())
97+
}
98+
}
99+
100+
impl FromStr for Range {
101+
type Err = ::Error;
102+
103+
fn from_str(s: &str) -> ::Result<Range> {
104+
let mut iter = s.splitn(2, "=");
105+
106+
match (iter.next(), iter.next()) {
107+
(Some(unit), Some(ranges)) => {
108+
match (RangeUnit::from_str(unit), from_one_comma_delimited(ranges.as_bytes())) {
109+
(Ok(unit), Ok(ranges)) => {
110+
if ranges.is_empty() {
111+
return Err(::Error::Header);
112+
}
113+
Ok(Range{unit: unit, ranges: ranges})
114+
},
115+
_ => Err(::Error::Header)
116+
}
117+
}
118+
_ => Err(::Error::Header)
119+
}
120+
}
121+
}
122+
123+
impl FromStr for RangeSpec {
124+
type Err = ::Error;
125+
126+
fn from_str(s: &str) -> ::Result<RangeSpec> {
127+
let mut parts = s.splitn(2, "-");
128+
129+
match (parts.next(), parts.next()) {
130+
(Some(""), Some(end)) => {
131+
end.parse().or(Err(::Error::Header)).map(|end| RangeSpec::Last(end))
132+
},
133+
(Some(start), Some("")) => {
134+
start.parse().or(Err(::Error::Header)).map(|start| RangeSpec::AllFrom(start))
135+
},
136+
(Some(start), Some(end)) => {
137+
match (start.parse(), end.parse()) {
138+
(Ok(start), Ok(end)) if start <= end => Ok(RangeSpec::FromTo(start, end)),
139+
_ => Err(::Error::Header)
140+
}
141+
},
142+
_ => Err(::Error::Header)
143+
}
144+
}
145+
}
146+
147+
impl Header for Range {
148+
149+
fn header_name() -> &'static str {
150+
"Range"
151+
}
152+
153+
fn parse_header(raw: &[Vec<u8>]) -> ::Result<Range> {
154+
from_one_raw_str(raw)
155+
}
156+
}
157+
158+
impl HeaderFormat for Range {
159+
160+
fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result {
161+
Display::fmt(self, f)
162+
}
163+
164+
}
165+
166+
#[test]
167+
fn test_parse_valid() {
168+
let r: Range = Header::parse_header(&[b"bytes=1-100".to_vec()]).unwrap();
169+
let r2: Range = Header::parse_header(&[b"bytes=1-100,-".to_vec()]).unwrap();
170+
let r3 = Range::bytes(1, 100);
171+
assert_eq!(r, r2);
172+
assert_eq!(r2, r3);
173+
174+
let r: Range = Header::parse_header(&[b"bytes=1-100,200-".to_vec()]).unwrap();
175+
let r2: Range = Header::parse_header(&[b"bytes= 1-100 , 101-xxx, 200- ".to_vec()]).unwrap();
176+
let r3 = Range {
177+
unit: RangeUnit::Bytes,
178+
ranges: vec![RangeSpec::FromTo(1, 100), RangeSpec::AllFrom(200)]
179+
};
180+
assert_eq!(r, r2);
181+
assert_eq!(r2, r3);
182+
183+
let r: Range = Header::parse_header(&[b"custom=1-100,-100".to_vec()]).unwrap();
184+
let r2: Range = Header::parse_header(&[b"custom=1-100, ,,-100".to_vec()]).unwrap();
185+
let r3 = Range {
186+
unit: RangeUnit::Unregistered("custom".to_owned()),
187+
ranges: vec![RangeSpec::FromTo(1, 100), RangeSpec::Last(100)]
188+
};
189+
assert_eq!(r, r2);
190+
assert_eq!(r2, r3);
191+
}
192+
193+
#[test]
194+
fn test_parse_invalid() {
195+
let r: ::Result<Range> = Header::parse_header(&[b"bytes=1-a,-".to_vec()]);
196+
assert_eq!(r.ok(), None);
197+
198+
let r: ::Result<Range> = Header::parse_header(&[b"bytes=1-2-3".to_vec()]);
199+
assert_eq!(r.ok(), None);
200+
201+
let r: ::Result<Range> = Header::parse_header(&[b"abc".to_vec()]);
202+
assert_eq!(r.ok(), None);
203+
204+
let r: ::Result<Range> = Header::parse_header(&[b"bytes=1-100=".to_vec()]);
205+
assert_eq!(r.ok(), None);
206+
207+
let r: ::Result<Range> = Header::parse_header(&[b"bytes=".to_vec()]);
208+
assert_eq!(r.ok(), None);
209+
}
210+
211+
#[test]
212+
fn test_fmt() {
213+
use header::Headers;
214+
215+
let range_header = Range {
216+
unit: RangeUnit::Bytes,
217+
ranges: vec![RangeSpec::FromTo(0, 1000), RangeSpec::AllFrom(2000)],
218+
};
219+
let mut headers = Headers::new();
220+
headers.set(range_header);
221+
222+
assert_eq!(&headers.to_string(), "Range: bytes=0-1000,2000-\r\n");
223+
224+
headers.clear();
225+
headers.set(Range {unit: RangeUnit::Bytes, ranges: vec![]});
226+
227+
assert_eq!(&headers.to_string(), "Range: bytes=\r\n");
228+
}
229+
230+
bench_header!(bytes_multi, Range, { vec![b"bytes=1-1001,2001-3001,10001-".to_vec()]});
231+
bench_header!(custom_unit, Range, { vec![b"custom_unit=0-100000".to_vec()]});

0 commit comments

Comments
 (0)