typed_headers/
lib.rs

1//! Typed HTTP header serialization and deserialization.
2//!
3//! This crate is still in its early, experimental stages. It currently takes a fairly pedantic view of parsing, and
4//! tries to support exactly what's specified in the HTTP RFCs.
5//!
6//! The `HeaderMapExt` extension trait provides new methods on the `http::HeaderMap` type to insert, retrieve, and
7//! remove headers in a typed manner.
8#![doc(html_root_url = "https://docs.rs/typed-headers/0.1")]
9
10extern crate base64;
11extern crate bytes;
12extern crate chrono;
13
14pub extern crate http;
15pub extern crate mime;
16
17use http::header::{self, HeaderMap, HeaderName, HeaderValue};
18use std::error;
19use std::fmt;
20use std::mem;
21
22pub use impls::*;
23
24mod impls;
25pub mod util;
26
27pub trait Header {
28    /// Returns the name of this header.
29    ///
30    /// The `http` crate provides constants for all standard header names. Implementations for
31    /// nonstandard headers can use the `lazy_static` crate to obtain a static reference to a
32    /// `HeaderName`.
33    fn name() -> &'static HeaderName;
34
35    /// Parses the header from the raw value bytes.
36    ///
37    /// The iterator may be empty, which indicates that the header was not present, and `Ok(None)`
38    /// should be returned.
39    ///
40    /// If the iterator is not exhausted when this function returns, it will be treated as a parse
41    /// error.
42    fn from_values<'a>(
43        values: &mut header::ValueIter<'a, HeaderValue>,
44    ) -> Result<Option<Self>, Error>
45    where
46        Self: Sized;
47
48    /// Serializes the header to raw values.
49    ///
50    /// Each call to `values.append` adds a header entry. Almost all headers should only append a
51    /// single value. `Set-Cookie` is a rare exception.
52    ///
53    /// This method is infallible. Header implementations should ensure at construction time that
54    /// they will be able to successfully serialize.
55    fn to_values(&self, values: &mut ToValues);
56}
57
58#[derive(Debug)]
59enum ErrorKind {
60    InvalidValue,
61    TooFewValues,
62    TooManyValues,
63}
64
65/// An error serializing or deserializing a header.
66#[derive(Debug)]
67pub struct Error(ErrorKind);
68
69impl Error {
70    #[inline]
71    pub fn invalid_value() -> Error {
72        Error(ErrorKind::InvalidValue)
73    }
74
75    #[inline]
76    pub fn too_few_values() -> Error {
77        Error(ErrorKind::TooFewValues)
78    }
79
80    #[inline]
81    pub fn too_many_values() -> Error {
82        Error(ErrorKind::TooManyValues)
83    }
84}
85
86impl fmt::Display for Error {
87    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
88        let s = match self.0 {
89            ErrorKind::InvalidValue => "invalid header value",
90            ErrorKind::TooFewValues => "too few header values",
91            ErrorKind::TooManyValues => "too many header values",
92        };
93        fmt.write_str(s)
94    }
95}
96
97impl error::Error for Error {
98    fn description(&self) -> &str {
99        "header error"
100    }
101}
102
103enum ToValuesState<'a> {
104    First(header::Entry<'a, HeaderValue>),
105    Latter(header::OccupiedEntry<'a, HeaderValue>),
106    Tmp,
107}
108
109pub struct ToValues<'a>(ToValuesState<'a>);
110
111impl<'a> ToValues<'a> {
112    pub fn append(&mut self, value: HeaderValue) {
113        let entry = match mem::replace(&mut self.0, ToValuesState::Tmp) {
114            ToValuesState::First(header::Entry::Occupied(mut e)) => {
115                e.insert(value);
116                e
117            }
118            ToValuesState::First(header::Entry::Vacant(e)) => e.insert_entry(value),
119            ToValuesState::Latter(mut e) => {
120                e.append(value);
121                e
122            }
123            ToValuesState::Tmp => unreachable!(),
124        };
125        self.0 = ToValuesState::Latter(entry);
126    }
127}
128
129/// An extension trait adding typed getters and setters to `HeaderMap`.
130pub trait HeaderMapExt {
131    /// Retrieves the specified header from the map, if present.
132    fn typed_get<H>(&self) -> Result<Option<H>, Error>
133    where
134        H: Header;
135
136    /// Inserts the provided header into the map.
137    ///
138    /// This overwrites any existing entries for that header.
139    fn typed_insert<H>(&mut self, header: &H)
140    where
141        H: Header;
142
143    /// Removes and returns the specified header from the map, if present.
144    ///
145    /// The header will be removed even if it doesn't successfully parse.
146    fn typed_remove<H>(&mut self) -> Result<Option<H>, Error>
147    where
148        H: Header;
149}
150
151impl HeaderMapExt for HeaderMap {
152    fn typed_get<H>(&self) -> Result<Option<H>, Error>
153    where
154        H: Header,
155    {
156        let mut values = self.get_all(H::name()).iter();
157        match H::from_values(&mut values) {
158            Ok(header) => match values.next() {
159                Some(_) => Err(Error::too_many_values()),
160                None => Ok(header),
161            },
162            Err(e) => Err(e),
163        }
164    }
165
166    fn typed_insert<H>(&mut self, header: &H)
167    where
168        H: Header,
169    {
170        let entry = self.entry(H::name());
171        let mut values = ToValues(ToValuesState::First(entry));
172        header.to_values(&mut values);
173    }
174
175    fn typed_remove<H>(&mut self) -> Result<Option<H>, Error>
176    where
177        H: Header,
178    {
179        match self.entry(H::name()) {
180            header::Entry::Occupied(entry) => {
181                let r = H::from_values(&mut entry.iter());
182                entry.remove();
183                r
184            }
185            header::Entry::Vacant(_) => Ok(None),
186        }
187    }
188}