hyper_sync/header/common/
content_range.rs

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