rocket_http/parse/
media_type.rs

1use std::borrow::Cow;
2
3use pear::input::Extent;
4use pear::combinators::{prefixed_series, surrounded};
5use pear::macros::{parser, switch, parse};
6use pear::parsers::*;
7
8use crate::header::{MediaType, Source};
9use crate::parse::checkers::{is_whitespace, is_valid_token};
10
11type Input<'a> = pear::input::Pear<pear::input::Cursor<&'a str>>;
12type Result<'a, T> = pear::input::Result<T, Input<'a>>;
13
14#[parser]
15fn quoted_string<'a>(input: &mut Input<'a>) -> Result<'a, Extent<&'a str>> {
16    eat('"')?;
17
18    let mut is_escaped = false;
19    let inner = take_while(|&c| {
20        if is_escaped { is_escaped = false; return true; }
21        if c == '\\' { is_escaped = true; return true; }
22        c != '"'
23    })?;
24
25    eat('"')?;
26    inner
27}
28
29#[parser]
30fn media_param<'a>(input: &mut Input<'a>) -> Result<'a, (Extent<&'a str>, Extent<&'a str>)> {
31    let key = (take_some_while_until(is_valid_token, '=')?, eat('=')?).0;
32    let value = switch! {
33        peek('"') => quoted_string()?,
34        _ => take_some_while_until(is_valid_token, ';')?
35    };
36
37    (key, value)
38}
39
40#[parser]
41pub fn media_type<'a>(input: &mut Input<'a>) -> Result<'a, MediaType> {
42    let (top, sub, params) = {
43        let top = (take_some_while_until(is_valid_token, '/')?, eat('/')?).0;
44        let sub = take_some_while_until(is_valid_token, ';')?;
45        let params = prefixed_series(';', |i| {
46            let param = surrounded(i, media_param, is_whitespace)?;
47            Ok((param.0.into(), param.1.into()))
48        }, ';')?;
49
50        (top, sub, params)
51    };
52
53    MediaType {
54        params,
55        source: Source::Custom(Cow::Owned(input.start.to_string())),
56        top: top.into(),
57        sub: sub.into(),
58    }
59}
60
61pub fn parse_media_type(input: &str) -> Result<'_, MediaType> {
62    parse!(media_type: Input::new(input))
63}
64
65#[cfg(test)]
66mod test {
67    use crate::MediaType;
68    use super::parse_media_type;
69
70    macro_rules! assert_no_parse {
71        ($string:expr) => ({
72            let result: Result<_, _> = parse_media_type($string).into();
73            if result.is_ok() {
74                panic!("{:?} parsed unexpectedly.", $string)
75            }
76        });
77    }
78
79    macro_rules! assert_parse {
80        ($string:expr) => ({
81            match parse_media_type($string) {
82                Ok(media_type) => media_type,
83                Err(e) => panic!("{:?} failed to parse: {}", $string, e)
84            }
85        });
86    }
87
88    macro_rules! assert_parse_eq {
89        (@full $string:expr, $result:expr, $(($k:expr, $v:expr)),*) => ({
90            let result = assert_parse!($string);
91            assert_eq!(result, $result);
92
93            let expected_params: Vec<(&str, &str)> = vec![$(($k, $v)),*];
94            if expected_params.len() > 0 {
95                assert_eq!(result.params().count(), expected_params.len());
96                let all_params = result.params().zip(expected_params.iter());
97                for ((key, val), &(ekey, eval)) in all_params {
98                    assert_eq!(key, ekey);
99                    assert_eq!(val, eval);
100                }
101            }
102        });
103
104        (from: $string:expr, into: $result:expr)
105            => (assert_parse_eq!(@full $string, $result, ));
106        (from: $string:expr, into: $result:expr, params: $(($key:expr, $val:expr)),*)
107            => (assert_parse_eq!(@full $string, $result, $(($key, $val)),*));
108    }
109
110    #[test]
111    fn check_does_parse() {
112        assert_parse!("text/html");
113        assert_parse!("a/b");
114        assert_parse!("*/*");
115    }
116
117    #[test]
118    fn check_parse_eq() {
119        assert_parse_eq!(from: "text/html", into: MediaType::HTML);
120        assert_parse_eq!(from: "text/html; charset=utf-8", into: MediaType::HTML);
121        assert_parse_eq!(from: "text/html", into: MediaType::HTML);
122
123        assert_parse_eq!(from: "a/b", into: MediaType::new("a", "b"));
124        assert_parse_eq!(from: "*/*", into: MediaType::Any);
125        assert_parse_eq!(from: "application/pdf", into: MediaType::PDF);
126        assert_parse_eq!(from: "application/json", into: MediaType::JSON);
127        assert_parse_eq!(from: "image/svg+xml", into: MediaType::SVG);
128
129        assert_parse_eq!(from: "*/json", into: MediaType::new("*", "json"));
130        assert_parse_eq! {
131            from: "application/*; param=1",
132            into: MediaType::new("application", "*")
133        };
134    }
135
136    #[test]
137    fn check_param_eq() {
138        assert_parse_eq! {
139            from: "text/html; a=b; b=c; c=d",
140            into: MediaType::new("text", "html"),
141            params: ("a", "b"), ("b", "c"), ("c", "d")
142        };
143
144        assert_parse_eq! {
145            from: "text/html;a=b;b=c;     c=d; d=e",
146            into: MediaType::new("text", "html"),
147            params: ("a", "b"), ("b", "c"), ("c", "d"), ("d", "e")
148        };
149
150        assert_parse_eq! {
151            from: "text/html; charset=utf-8",
152            into: MediaType::new("text", "html"),
153            params: ("charset", "utf-8")
154        };
155
156        assert_parse_eq! {
157            from: "application/*; param=1",
158            into: MediaType::new("application", "*"),
159            params: ("param", "1")
160        };
161
162        assert_parse_eq! {
163            from: "*/*;q=0.5;b=c;c=d",
164            into: MediaType::Any,
165            params: ("q", "0.5"), ("b", "c"), ("c", "d")
166        };
167
168        assert_parse_eq! {
169            from: "multipart/form-data; boundary=----WebKitFormBoundarypRshfItmvaC3aEuq",
170            into: MediaType::FormData,
171            params: ("boundary", "----WebKitFormBoundarypRshfItmvaC3aEuq")
172        };
173
174        assert_parse_eq! {
175            from: r#"*/*; a="hello, world!@#$%^&*();;hi""#,
176            into: MediaType::Any,
177            params: ("a", "hello, world!@#$%^&*();;hi")
178        };
179
180        assert_parse_eq! {
181            from: r#"application/json; a=";,;""#,
182            into: MediaType::JSON,
183            params: ("a", ";,;")
184        };
185
186        assert_parse_eq! {
187            from: r#"application/json; a=";,;"; b=c"#,
188            into: MediaType::JSON,
189            params: ("a", ";,;"), ("b", "c")
190        };
191
192        assert_parse_eq! {
193            from: r#"application/json; b=c; a=";.,.;""#,
194            into: MediaType::JSON,
195            params: ("b", "c"), ("a", ";.,.;")
196        };
197
198        assert_parse_eq! {
199            from: r#"*/*; a="a"; b="b"; a=a; b=b; c=c"#,
200            into: MediaType::Any,
201            params: ("a", "a"), ("b", "b"), ("a", "a"), ("b", "b"), ("c", "c")
202        };
203    }
204
205    #[test]
206    fn check_params_do_parse() {
207        assert_parse!("*/*; q=1; q=2");
208        assert_parse!("*/*; q=1;q=2;q=3;a=v;c=1;da=1;sdlkldsadasd=uhisdcb89");
209        assert_parse!("*/*; q=1; q=2");
210        assert_parse!("*/*; q=1; q=2; a=b;c=d;    e=f; a=s;a=e");
211        assert_parse!("*/*; q=1; q=2 ; a=b");
212        assert_parse!("*/*; q=1; q=2; hello=\"world !\"");
213    }
214
215    #[test]
216    fn test_bad_parses() {
217        assert_no_parse!("*&_/*)()");
218        assert_no_parse!("/json");
219        assert_no_parse!("text/");
220        assert_no_parse!("text//");
221        assert_no_parse!("/");
222        assert_no_parse!("*/");
223        assert_no_parse!("/*");
224        assert_no_parse!("///");
225        assert_no_parse!("application//json");
226        assert_no_parse!("application///json");
227        assert_no_parse!("a/b;");
228        assert_no_parse!("*/*; a=b;;");
229        assert_no_parse!("*/*; a=b;a");
230        assert_no_parse!("*/*; a=b; ");
231        assert_no_parse!("*/*; a=b;");
232        assert_no_parse!("*/*; a = b");
233        assert_no_parse!("*/*; a= b");
234        assert_no_parse!("*/*; a =b");
235        assert_no_parse!(r#"*/*; a="b"#);
236        assert_no_parse!(r#"*/*; a="b; c=d"#);
237        assert_no_parse!(r#"*/*; a="b; c=d"#);
238        assert_no_parse!("*/*;a=@#$%^&*()");
239        assert_no_parse!("*/*;;");
240        assert_no_parse!("*/*;=;");
241        assert_no_parse!("*/*=;");
242        assert_no_parse!("*/*=;=");
243    }
244}