tower_web/extract/
serde.rs

1//! Types used to extract Serde values from an HTTP request.
2
3use codegen::CallSite;
4use extract::{Context, Error, Extract, ExtractFuture};
5use http::status::StatusCode;
6use util::buf_stream::{self, BufStream};
7
8use futures::{Future, Poll};
9use headers::{ContentType, HeaderMapExt};
10use mime::Mime;
11use serde::de::DeserializeOwned;
12use serde_urlencoded;
13use serde_json;
14
15/*
16 * # TODO: Move this module to `codegen`?
17 */
18
19/// Extract a value using Serde
20#[derive(Debug)]
21pub struct SerdeFuture<T, B> {
22    state: State<T, B>,
23    is_json: bool,
24}
25
26#[derive(Debug)]
27enum State<T, B> {
28    Complete(Result<T, Option<Error>>),
29    Body(buf_stream::Collect<B, Vec<u8>>),
30}
31
32impl<B: BufStream> Extract<B> for serde_json::Value {
33    type Future = SerdeFuture<Self, B>;
34
35    fn extract(ctx: &Context) -> Self::Future {
36        Self::Future::new_extract(ctx)
37    }
38
39    fn extract_body(ctx: &Context, body: B) -> Self::Future {
40        Self::Future::new_extract_body(ctx, body)
41    }
42
43    fn requires_body(callsite: &CallSite) -> bool {
44        self::requires_body(callsite)
45    }
46}
47
48#[doc(hidden)]
49pub fn requires_body(callsite: &CallSite) -> bool {
50    use codegen::Source::Body;
51
52    match callsite.source() {
53        Body => true,
54        _ => false,
55    }
56}
57
58impl<T, B> SerdeFuture<T, B>
59where T: DeserializeOwned,
60      B: BufStream,
61{
62    /// Immediately extract a value using only the HTTP request head
63    pub fn new_extract(ctx: &Context) -> Self {
64        use codegen::Source::*;
65
66        match ctx.callsite().source() {
67            Capture(_) => {
68                unimplemented!();
69            }
70            Header(_) => {
71                unimplemented!();
72            }
73            QueryString => {
74                let query = ctx.request().uri()
75                    .path_and_query()
76                    .and_then(|path_and_query| path_and_query.query())
77                    .unwrap_or("");
78
79                let res = serde_urlencoded::from_str(query)
80                    .map_err(|err| {
81                        use std::error::Error as E;
82
83                        if query.is_empty() {
84                            Some(Error::missing_argument())
85                        } else {
86                            Some(Error::invalid_argument(&err.description()))
87                        }
88                    });
89
90                let state = State::Complete(res);
91
92                SerdeFuture { state, is_json: false }
93            }
94            Body => {
95                unimplemented!();
96            }
97            Unknown => {
98                unimplemented!();
99            }
100        }
101    }
102
103    /// Extract a value using the HTTP request head and body
104    pub fn new_extract_body(ctx: &Context, body: B) -> Self {
105        use codegen::Source::*;
106
107        match ctx.callsite().source() {
108            Capture(_) => {
109                unimplemented!("Capture");
110            }
111            Header(_) => {
112                unimplemented!("Header");
113            }
114            QueryString => {
115                unimplemented!("QueryString");
116            }
117            Body => {
118                if let Some(value) = ctx.request().headers().typed_get::<ContentType>() {
119                    let mime = Mime::from(value);
120
121                    match (mime.type_().as_str(), mime.subtype().as_str()) {
122                        ("application", "json") => {
123                            let state = State::Body(body.collect());
124
125                            SerdeFuture { state, is_json: true }
126                        }
127                        ("application", "x-www-form-urlencoded") => {
128                            let state = State::Body(body.collect());
129
130                            SerdeFuture { state, is_json: false }
131                        }
132                        _ => {
133                            let err = ::Error::from(StatusCode::BAD_REQUEST).into();
134                            let state = State::Complete(Err(Some(err)));
135
136                            SerdeFuture { state, is_json: false }
137                        }
138                    }
139                } else {
140                    let err = ::Error::from(StatusCode::BAD_REQUEST).into();
141                    let state = State::Complete(Err(Some(err)));
142
143                    SerdeFuture { state, is_json: false }
144                }
145            }
146            Unknown => {
147                unimplemented!("Unknown");
148            }
149        }
150    }
151}
152
153impl<T, B> ExtractFuture for SerdeFuture<T, B>
154where T: DeserializeOwned,
155      B: BufStream,
156{
157    type Item = T;
158
159    fn poll(&mut self) -> Poll<(), Error> {
160        use self::State::*;
161
162        loop {
163            let res = match self.state {
164                Complete(Err(ref mut e)) => {
165                    return Err(e.take().unwrap());
166                }
167                Complete(Ok(_)) => {
168                    return Ok(().into());
169                }
170                Body(ref mut collect) => {
171                    let res = collect.poll()
172                        // TODO: Is there a better way to handle errors?
173                        .map_err(|_| Error::internal_error());
174
175                    let res = try_ready!(res);
176                    
177                    if self.is_json == true {
178                        ::serde_json::from_slice(&res[..])
179                            .map_err(|_| {
180                                // TODO: Handle error better
181                                Some(Error::internal_error())
182                        })
183                    } else {
184                        ::serde_urlencoded::from_bytes(&res[..])
185                            .map_err(|_| {
186                                // TODO: Handle error better
187                                Some(Error::internal_error())
188                        })
189                    }
190                }
191            };
192
193            self.state = State::Complete(res);
194        }
195    }
196
197    fn extract(self) -> T {
198        use self::State::Complete;
199
200        match self.state {
201            Complete(Ok(res)) => res,
202            _ => panic!("invalid state"),
203        }
204    }
205}