actix_web/http/header/
content_range.rs1use std::{
2 fmt::{self, Display, Write},
3 str::FromStr,
4};
5
6use super::{HeaderValue, InvalidHeaderValue, TryIntoHeaderValue, Writer, CONTENT_RANGE};
7use crate::error::ParseError;
8
9crate::http::header::common_header! {
10 (ContentRange, CONTENT_RANGE) => [ContentRangeSpec]
13
14 test_parse_and_format {
15 crate::http::header::common_header_test!(test_bytes,
16 [b"bytes 0-499/500"],
17 Some(ContentRange(ContentRangeSpec::Bytes {
18 range: Some((0, 499)),
19 instance_length: Some(500)
20 })));
21
22 crate::http::header::common_header_test!(test_bytes_unknown_len,
23 [b"bytes 0-499/*"],
24 Some(ContentRange(ContentRangeSpec::Bytes {
25 range: Some((0, 499)),
26 instance_length: None
27 })));
28
29 crate::http::header::common_header_test!(test_bytes_unknown_range,
30 [b"bytes */500"],
31 Some(ContentRange(ContentRangeSpec::Bytes {
32 range: None,
33 instance_length: Some(500)
34 })));
35
36 crate::http::header::common_header_test!(test_unregistered,
37 [b"seconds 1-2"],
38 Some(ContentRange(ContentRangeSpec::Unregistered {
39 unit: "seconds".to_owned(),
40 resp: "1-2".to_owned()
41 })));
42
43 crate::http::header::common_header_test!(test_no_len,
44 [b"bytes 0-499"],
45 None::<ContentRange>);
46
47 crate::http::header::common_header_test!(test_only_unit,
48 [b"bytes"],
49 None::<ContentRange>);
50
51 crate::http::header::common_header_test!(test_end_less_than_start,
52 [b"bytes 499-0/500"],
53 None::<ContentRange>);
54
55 crate::http::header::common_header_test!(test_blank,
56 [b""],
57 None::<ContentRange>);
58
59 crate::http::header::common_header_test!(test_bytes_many_spaces,
60 [b"bytes 1-2/500 3"],
61 None::<ContentRange>);
62
63 crate::http::header::common_header_test!(test_bytes_many_slashes,
64 [b"bytes 1-2/500/600"],
65 None::<ContentRange>);
66
67 crate::http::header::common_header_test!(test_bytes_many_dashes,
68 [b"bytes 1-2-3/500"],
69 None::<ContentRange>);
70 }
71}
72
73#[derive(Debug, Clone, PartialEq, Eq)]
94pub enum ContentRangeSpec {
95 Bytes {
97 range: Option<(u64, u64)>,
100
101 instance_length: Option<u64>,
103 },
104
105 Unregistered {
107 unit: String,
109
110 resp: String,
112 },
113}
114
115impl FromStr for ContentRangeSpec {
116 type Err = ParseError;
117
118 fn from_str(s: &str) -> Result<Self, ParseError> {
119 let res = match s.split_once(' ') {
120 Some(("bytes", resp)) => {
121 let (range, instance_length) = resp.split_once('/').ok_or(ParseError::Header)?;
122
123 let instance_length = if instance_length == "*" {
124 None
125 } else {
126 Some(instance_length.parse().map_err(|_| ParseError::Header)?)
127 };
128
129 let range = if range == "*" {
130 None
131 } else {
132 let (first_byte, last_byte) =
133 range.split_once('-').ok_or(ParseError::Header)?;
134 let first_byte = first_byte.parse().map_err(|_| ParseError::Header)?;
135 let last_byte = last_byte.parse().map_err(|_| ParseError::Header)?;
136 if last_byte < first_byte {
137 return Err(ParseError::Header);
138 }
139 Some((first_byte, last_byte))
140 };
141
142 ContentRangeSpec::Bytes {
143 range,
144 instance_length,
145 }
146 }
147 Some((unit, resp)) => ContentRangeSpec::Unregistered {
148 unit: unit.to_owned(),
149 resp: resp.to_owned(),
150 },
151 _ => return Err(ParseError::Header),
152 };
153 Ok(res)
154 }
155}
156
157impl Display for ContentRangeSpec {
158 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
159 match *self {
160 ContentRangeSpec::Bytes {
161 range,
162 instance_length,
163 } => {
164 f.write_str("bytes ")?;
165 match range {
166 Some((first_byte, last_byte)) => {
167 write!(f, "{}-{}", first_byte, last_byte)?;
168 }
169 None => {
170 f.write_str("*")?;
171 }
172 };
173 f.write_str("/")?;
174 if let Some(v) = instance_length {
175 write!(f, "{}", v)
176 } else {
177 f.write_str("*")
178 }
179 }
180 ContentRangeSpec::Unregistered { ref unit, ref resp } => {
181 f.write_str(unit)?;
182 f.write_str(" ")?;
183 f.write_str(resp)
184 }
185 }
186 }
187}
188
189impl TryIntoHeaderValue for ContentRangeSpec {
190 type Error = InvalidHeaderValue;
191
192 fn try_into_value(self) -> Result<HeaderValue, Self::Error> {
193 let mut writer = Writer::new();
194 let _ = write!(&mut writer, "{}", self);
195 HeaderValue::from_maybe_shared(writer.take())
196 }
197}