http_box/util/
field.rs

1// +-----------------------------------------------------------------------------------------------+
2// | Copyright 2016 Sean Kerr                                                                      |
3// |                                                                                               |
4// | Licensed under the Apache License, Version 2.0 (the "License");                               |
5// | you may not use this file except in compliance with the License.                              |
6// | You may obtain a copy of the License at                                                       |
7// |                                                                                               |
8// |  http://www.apache.org/licenses/LICENSE-2.0                                                   |
9// |                                                                                               |
10// | Unless required by applicable law or agreed to in writing, software                           |
11// | distributed under the License is distributed on an "AS IS" BASIS,                             |
12// | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.                      |
13// | See the License for the specific language governing permissions and                           |
14// | limitations under the License.                                                                |
15// +-----------------------------------------------------------------------------------------------+
16
17use byte::{ is_header_field, is_quoted_header_field, is_token };
18
19use byte_slice::ByteStream;
20use std::fmt;
21
22/// Field errors.
23pub enum FieldError {
24    /// Invalid field name.
25    Name(u8),
26
27    /// Invalid field value.
28    Value(u8)
29}
30
31impl FieldError {
32    /// Format this for debug and display purposes.
33    fn format(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
34        match *self {
35            FieldError::Name(x) => {
36                write!(
37                    formatter,
38                    "<FieldError::Name: Invalid field name on byte {}>",
39                    x
40                )
41            },
42            FieldError::Value(x) => {
43                write!(
44                    formatter,
45                    "<FieldError::Value: Invalid field value on byte {}>",
46                    x
47                )
48            }
49        }
50    }
51}
52
53impl fmt::Debug for FieldError {
54    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
55        self.format(formatter)
56    }
57}
58
59impl fmt::Display for FieldError {
60    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
61        self.format(formatter)
62    }
63}
64
65// -------------------------------------------------------------------------------------------------
66
67/// Header field iterator.
68///
69/// This allows you to iterate over a header field to retrieve `(name, value)` pairs.
70///
71/// # Errors
72///
73/// - [`FieldError::Name`](enum.FieldError.html#variant.Name)
74/// - [`FieldError::Value`](enum.FieldError.html#variant.Value)
75///
76/// ```rust
77/// extern crate http_box;
78///
79/// use http_box::util::{ FieldError, FieldIterator };
80///
81/// fn main() {
82///     // notice the upper-cased parameter names that are normalized to lower-case thanks
83///     // to the third parameter being `true`
84///     let field = b"COMPRESSION=bzip; BOUNDARY=\"longrandomboundarystring\"";
85///
86///     for (n, (name, value)) in FieldIterator::new(field, b';', true).enumerate() {
87///         if n == 0 {
88///             assert_eq!(
89///                 name,
90///                 "compression"
91///             );
92///
93///             assert_eq!(
94///                 value.unwrap(),
95///                 "bzip"
96///             );
97///         } else if n == 1 {
98///             assert_eq!(
99///                 name,
100///                 "boundary"
101///             );
102///
103///             assert_eq!(
104///                 value.unwrap(),
105///                 "longrandomboundarystring"
106///             );
107///         }
108///     }
109/// }
110/// ```
111pub struct FieldIterator<'a> {
112    context:   ByteStream<'a>,
113    delimiter: u8,
114    name:      Vec<u8>,
115    normalize: bool,
116    on_error:  Box<FnMut(FieldError) + 'a>,
117    value:     Vec<u8>
118}
119
120impl<'a> FieldIterator<'a> {
121    /// Create a new `FieldIterator`.
122    ///
123    /// # Arguments
124    ///
125    /// **`field`**
126    ///
127    /// The header field.
128    ///
129    /// **`delimiter`**
130    ///
131    /// The field delimiter.
132    ///
133    /// **`normalize`**
134    ///
135    /// Indicates that field names should be normalized to lower-case.
136    pub fn new(field: &'a [u8], delimiter: u8, normalize: bool) -> FieldIterator<'a> {
137        FieldIterator{
138            context:   ByteStream::new(field),
139            delimiter: delimiter,
140            name:      Vec::new(),
141            normalize: normalize,
142            on_error:  Box::new(|_|{}),
143            value:     Vec::new()
144        }
145    }
146
147    /// Set the on error callback.
148    ///
149    /// # Arguments
150    ///
151    /// **`on_error`**
152    ///
153    /// The callback.
154    pub fn on_error<F>(&mut self, on_error: F) -> &mut Self
155    where F : FnMut(FieldError) + 'a {
156        self.on_error = Box::new(on_error);
157        self
158    }
159}
160
161impl<'a> Iterator for FieldIterator<'a> {
162    type Item = (String, Option<String>);
163
164    fn next(&mut self) -> Option<(String, Option<String>)> {
165        if bs_available!(self.context) == 0 {
166            return None;
167        }
168
169        self.name.clear();
170        self.value.clear();
171
172        loop {
173            // parsing name
174            consume_linear_space!(
175                self.context,
176
177                // on end-of-stream
178                return None
179            );
180
181            bs_replay!(self.context);
182
183            bs_mark!(self.context);
184
185            collect_tokens!(
186                self.context,
187
188                // stop on these bytes
189                   self.context.byte == b'='
190                || self.context.byte == self.delimiter
191                || self.context.byte == b'/'
192                || (self.normalize && self.context.byte > 0x40 && self.context.byte < 0x5B),
193
194                // on end-of-stream
195                {
196                    // name without a value
197                    if bs_slice_length!(self.context) > 0 {
198                        self.name.extend_from_slice(bs_slice!(self.context));
199                    }
200
201                    submit_name!(self);
202                }
203            );
204
205            self.name.extend_from_slice(bs_slice_ignore!(self.context));
206
207            match self.context.byte {
208                b'=' => {
209                    consume_linear_space!(
210                        self.context,
211
212                        // on end-of-stream
213                        submit_name!(self)
214                    );
215
216                    if self.context.byte == b'"' {
217                        // quoted value
218                        loop {
219                            bs_mark!(self.context);
220
221                            collect_quoted_field!(
222                                self.context,
223
224                                // on end-of-stream
225                                // didn't find an ending quote
226                                submit_error!(self, FieldError::Value)
227                            );
228
229                            if self.context.byte == b'"' {
230                                // found end quote
231                                self.value.extend_from_slice(bs_slice_ignore!(self.context));
232
233                                consume_linear_space!(
234                                    self.context,
235
236                                    // on end-of-stream
237                                    submit_name_value!(self)
238                                );
239
240                                if bs_available!(self.context) == 0 {
241                                    submit_name_value!(self);
242                                }
243
244                                bs_next!(self.context);
245
246                                if self.context.byte == self.delimiter {
247                                    submit_name_value!(self);
248                                }
249
250                                // expected a semicolon to end the value
251                                submit_error!(self, FieldError::Value);
252                            } else if self.context.byte == b'\\' {
253                                // found backslash
254                                if bs_is_eos!(self.context) {
255                                    submit_error!(self, FieldError::Name);
256                                }
257
258                                self.value.extend_from_slice(bs_slice_ignore!(self.context));
259
260                                bs_next!(self.context);
261
262                                self.value.push(self.context.byte);
263                            } else {
264                                bs_jump!(self.context, bs_available!(self.context));
265
266                                (*self.on_error)(FieldError::Value(self.context.byte));
267
268                                return None;
269                            }
270                        }
271                    } else {
272                        // unquoted value
273                        bs_replay!(self.context);
274                        bs_mark!(self.context);
275
276                        collect_field!(
277                            self.context,
278
279                            // stop on these bytes
280                            self.context.byte == self.delimiter,
281
282                            // on end-of-stream
283                            {
284                                if bs_slice_length!(self.context) > 0 {
285                                    self.value.extend_from_slice(bs_slice!(self.context));
286                                }
287
288                                submit_name_value!(self);
289                            }
290                        );
291
292                        if bs_slice_length!(self.context) == 0 {
293                            // name without a value
294                            submit_name!(self);
295                        }
296
297                        if self.context.byte == self.delimiter {
298                            submit_name_value!(self.name, bs_slice_ignore!(self.context));
299                        } else {
300                            bs_jump!(self.context, bs_available!(self.context));
301
302                            (*self.on_error)(FieldError::Value(self.context.byte));
303
304                            return None;
305                        }
306                    }
307                },
308                b'/' => {
309                    // this isn't allowed as a token, but since it's a name-only field, it's allowed
310                    self.name.push(b'/');
311                },
312                byte if byte == self.delimiter => {
313                    // name without a value
314                    submit_name!(self);
315                },
316                byte if byte > 0x40 && byte < 0x5B => {
317                    // upper-cased byte, let's lower-case it
318                    self.name.push(self.context.byte + 0x20);
319                },
320                _ => {
321                    bs_jump!(self.context, bs_available!(self.context));
322
323                    (*self.on_error)(FieldError::Name(self.context.byte));
324
325                    return None;
326                }
327            }
328        }
329    }
330}