conduit_utils/
lib.rs

1#![cfg_attr(test, deny(warnings))]
2
3extern crate semver;
4extern crate conduit;
5
6use std::iter;
7use std::io::prelude::*;
8use std::net::SocketAddr;
9use std::collections::hash_map::{HashMap, Iter};
10
11use conduit::{Method, Scheme, Host, Extensions, Headers, Request};
12
13pub trait RequestDelegator {
14    fn request(&self) -> &Request;
15    fn mut_request(&mut self) -> &mut Request;
16
17    fn http_version(&self) -> semver::Version {
18        self.request().http_version()
19    }
20
21    fn conduit_version(&self) -> semver::Version {
22        self.request().conduit_version()
23    }
24
25    fn method(&self) -> Method {
26        self.request().method()
27    }
28
29    fn scheme(&self) -> Scheme {
30        self.request().scheme()
31    }
32
33    fn host(&self) -> Host {
34        self.request().host()
35    }
36
37    fn virtual_root(&self) -> Option<&str> {
38        self.request().virtual_root()
39    }
40
41    fn path(&self) -> &str {
42        self.request().path()
43    }
44
45    fn query_string(&self) -> Option<&str> {
46        self.request().query_string()
47    }
48
49    fn remote_addr(&self) -> SocketAddr {
50        self.request().remote_addr()
51    }
52
53    fn content_length(&self) -> Option<u64> {
54        self.request().content_length()
55    }
56
57    fn headers(&self) -> &Headers {
58        self.request().headers()
59    }
60
61    fn body(&mut self) -> &mut Read {
62        self.mut_request().body()
63    }
64
65    fn extensions(&self) -> &Extensions {
66        self.request().extensions()
67    }
68
69    fn mut_extensions(&mut self) -> &mut Extensions {
70        self.mut_request().mut_extensions()
71    }
72}
73
74impl<'a> Request for &'a mut (RequestDelegator + 'a) {
75    fn http_version(&self) -> semver::Version {
76        (**self).http_version()
77    }
78
79    fn conduit_version(&self) -> semver::Version {
80        (**self).conduit_version()
81    }
82
83    fn method(&self) -> Method {
84        (**self).method()
85    }
86
87    fn scheme(&self) -> Scheme {
88        (**self).scheme()
89    }
90
91    fn host(&self) -> Host {
92        (**self).host()
93    }
94
95    fn virtual_root(&self) -> Option<&str> {
96        (**self).virtual_root()
97    }
98
99    fn path(&self) -> &str {
100        (**self).path()
101    }
102
103    fn query_string(&self) -> Option<&str> {
104        (**self).query_string()
105    }
106
107    fn remote_addr(&self) -> SocketAddr {
108        (**self).remote_addr()
109    }
110
111    fn content_length(&self) -> Option<u64> {
112        (**self).content_length()
113    }
114
115    fn headers(&self) -> &Headers {
116        (**self).headers()
117    }
118
119    fn body(&mut self) -> &mut Read {
120        (**self).body()
121    }
122
123    fn extensions(&self) -> &Extensions {
124        (**self).extensions()
125    }
126
127    fn mut_extensions(&mut self) -> &mut Extensions {
128        (**self).mut_extensions()
129    }
130}
131
132type RawHeaders = HashMap<String, Vec<String>>;
133pub type InHeader<'a> = (&'a String, &'a Vec<String>);
134pub type OutHeader<'a> = (String, &'a Vec<String>);
135
136#[derive(PartialEq, Clone, Debug)]
137pub struct HeaderMap(HashMap<String, Vec<String>>);
138
139impl HeaderMap {
140    pub fn normalize(headers: HashMap<String, Vec<String>>) -> HeaderMap {
141        let headers = headers.into_iter().map(|(k,v)| (to_lower(&k), v)).collect();
142        HeaderMap(headers)
143    }
144
145    pub fn iter(&self) -> iter::Map<Iter<String, Vec<String>>,
146                                for<'a> fn(InHeader<'a>) -> OutHeader<'a>> {
147        fn foo<'a>((k, v): InHeader<'a>) -> OutHeader<'a> { (to_lower(k), v) }
148        let f: for<'a> fn(InHeader<'a>) -> OutHeader<'a> = foo;
149        self.as_ref().iter().map(f)
150    }
151
152    fn as_ref(&self) -> &HashMap<String, Vec<String>> {
153        match *self {
154            HeaderMap(ref map) => map
155        }
156    }
157
158    fn as_mut(&mut self) -> &mut HashMap<String, Vec<String>> {
159        match *self {
160            HeaderMap(ref mut map) => map
161        }
162    }
163
164    pub fn len(&self) -> usize {
165        self.as_ref().len()
166    }
167    pub fn clear(&mut self) {
168        self.as_mut().clear()
169    }
170    pub fn find(&self, key: &str) -> Option<&Vec<String>> {
171        self.as_ref().get(&to_lower(key))
172    }
173    pub fn insert(&mut self, k: &str, v: Vec<String>) -> Option<Vec<String>> {
174        self.as_mut().insert(to_lower(&k), v)
175    }
176    pub fn remove(&mut self, k: &str) -> Option<Vec<String>> {
177        self.as_mut().remove(&to_lower(k))
178    }
179
180    pub fn find_mut(&mut self, k: &str) -> Option<&mut Vec<String>> {
181        self.as_mut().get_mut(&to_lower(k))
182    }
183}
184
185fn to_lower(string: &str) -> String {
186    string.chars().flat_map(|c| c.to_lowercase()).collect()
187}
188
189#[cfg(test)]
190mod tests {
191    extern crate conduit_test as test;
192
193    use {RequestDelegator, HeaderMap};
194
195    use std::collections::HashMap;
196    use conduit::{Request, Method};
197
198    struct OverrideRequest<'a> {
199        request: &'a mut (Request + 'a),
200    }
201
202    impl<'a> RequestDelegator for OverrideRequest<'a> {
203        fn request(&self) -> &Request {
204            let req: &Request = self.request; req
205        }
206
207        fn mut_request(&mut self) -> &mut Request {
208            let req: &mut Request = self.request; req
209        }
210
211        fn method(&self) -> Method {
212            Method::Get
213        }
214    }
215
216    #[test]
217    fn test_delegate() {
218        let request = &mut test::MockRequest::new(Method::Head, "/hello");
219        let new = OverrideRequest { request: request };
220
221        assert_eq!(new.method(), Method::Get);
222        assert_eq!(new.path(), "/hello");
223    }
224
225    #[test]
226    fn test_header_map() {
227        let mut map = HeaderMap(HashMap::new());
228        map.insert("Content-Type", vec!("text/html".to_string()));
229        map.insert("location", vec!("http://example.com".to_string()));
230
231        assert_eq!(map.find(&"content-type".to_string()), Some(&vec!("text/html".to_string())));
232        assert_eq!(map.find(&"Location".to_string()), Some(&vec!("http://example.com".to_string())));
233        assert_eq!(map.find(&"content-type"), Some(&vec!("text/html".to_string())));
234        assert_eq!(map.find(&"Location"), Some(&vec!("http://example.com".to_string())));
235    }
236
237    #[test]
238    fn test_header_map_with_static_inserts() {
239        let mut map = HeaderMap(HashMap::new());
240        map.insert("Content-Type", vec!("text/html".to_string()));
241        map.insert("location", vec!("http://example.com".to_string()));
242
243        assert_eq!(map.find(&"content-type".to_string()), Some(&vec!("text/html".to_string())));
244        assert_eq!(map.find(&"Location".to_string()), Some(&vec!("http://example.com".to_string())));
245        assert_eq!(map.find(&"content-type"), Some(&vec!("text/html".to_string())));
246        assert_eq!(map.find(&"Location"), Some(&vec!("http://example.com".to_string())));
247    }
248
249    #[test]
250    fn test_normalize() {
251        let mut map = HashMap::new();
252        map.insert("Content-Type".to_string(), vec!("text/html".to_string()));
253
254        let headers = HeaderMap::normalize(map);
255        assert_eq!(headers.find(&"Content-Type".to_string()), Some(&vec!("text/html".to_string())));
256        assert_eq!(headers.find(&"Content-Type"), Some(&vec!("text/html".to_string())));
257        assert_eq!(headers.find(&"content-type".to_string()), Some(&vec!("text/html".to_string())));
258        assert_eq!(headers.find(&"content-type"), Some(&vec!("text/html".to_string())));
259    }
260
261    #[test]
262    fn test_iterate() {
263        let mut headers = HeaderMap(HashMap::new());
264        headers.insert("Content-Type", vec!("text/html".to_string()));
265        headers.insert("location", vec!("http://example.com".to_string()));
266
267        assert!(headers.iter().any(|t| {
268            t.0 == "content-type" &&
269            &t.1[..] == ["text/html".to_string()]
270        }));
271        assert!(headers.iter().any(|t| {
272            t.0 == "location" &&
273            &t.1[..] == ["http://example.com".to_string()]
274        }));
275        assert!(headers.iter().count() == 2);
276    }
277}