bodyparser/
lib.rs

1#![crate_name = "bodyparser"]
2
3//! Body Parser Plugin for Iron
4//!
5//! This plugin parses JSON out of an incoming Request.
6
7extern crate iron;
8extern crate plugin;
9extern crate persistent;
10extern crate serde;
11extern crate serde_json;
12
13use serde::Deserialize;
14use serde_json::{from_str, from_value};
15
16use iron::mime;
17use iron::prelude::*;
18use iron::headers;
19use iron::typemap::{Key};
20use std::io::Read;
21use std::any::Any;
22use std::marker;
23
24pub use self::errors::{BodyError, BodyErrorCause};
25pub use self::limit_reader::{LimitReader};
26
27mod errors;
28mod limit_reader;
29
30fn read_body_as_utf8(req: &mut Request, limit: usize) -> Result<String, errors::BodyError> {
31    let mut bytes = Vec::new();
32    match LimitReader::new(req.body.by_ref(), limit).read_to_end(&mut bytes) {
33        Ok(_) => {
34             match String::from_utf8(bytes) {
35                Ok(e) => Ok(e),
36                Err(err) => Err(errors::BodyError {
37                    detail: "Invalid UTF-8 sequence".to_string(),
38                    cause: errors::BodyErrorCause::Utf8Error(err.utf8_error())
39                })
40            }
41        },
42        Err(err) => Err(errors::BodyError {
43            detail: "Can't read request body".to_string(),
44            cause: errors::BodyErrorCause::IoError(err)
45        })
46    }
47}
48
49/// Use this key to modify the default body limit.
50pub struct MaxBodyLength;
51impl Key for MaxBodyLength {
52    type Value = usize;
53}
54
55/// Raw is a plugin to read a request body into UTF-8 String.
56/// Doesn't read `multipart/form-data`.
57pub struct Raw;
58
59impl Key for Raw {
60    type Value = Option<String>;
61}
62
63const DEFAULT_BODY_LIMIT: usize = 1024 * 1024 * 100;
64
65impl<'a, 'b> plugin::Plugin<Request<'a, 'b>> for Raw {
66    type Error = BodyError;
67
68    fn eval(req: &mut Request) -> Result<Option<String>, BodyError> {
69        let need_read = req.headers.get::<headers::ContentType>().map(|header| {
70            match **header {
71                mime::Mime(mime::TopLevel::Multipart, mime::SubLevel::FormData, _) => false,
72                _ => true
73            }
74        }).unwrap_or(false);
75
76        if need_read {
77            let max_length = req
78                .get::<persistent::Read<MaxBodyLength>>()
79                .ok()
80                .map(|x| *x)
81                .unwrap_or(DEFAULT_BODY_LIMIT);
82
83            let body = try!(read_body_as_utf8(req, max_length));
84            Ok(Some(body))
85        } else {
86            Ok(None)
87        }
88    }
89}
90
91/// Json is a plugin to parse a request body into JSON.
92/// Uses Raw plugin to parse the body with limit.
93#[derive(Clone)]
94pub struct Json;
95impl Key for Json {
96    type Value = Option<serde_json::Value>;
97}
98
99impl<'a, 'b> plugin::Plugin<Request<'a, 'b>> for Json {
100    type Error = BodyError;
101
102    fn eval(req: &mut Request) -> Result<Option<serde_json::Value>, BodyError> {
103        req.get::<Raw>()
104            .and_then(|maybe_body| {
105                reverse_option(maybe_body.map(|body| from_str(&body)))
106                    .map_err(|err| {
107                        BodyError {
108                            detail: "Can't parse body to JSON".to_string(),
109                            cause: BodyErrorCause::JsonError(err)
110                        }
111                    })
112            })
113    }
114}
115
116/// Struct is a plugin to parse a request body into a struct.
117/// Uses Raw plugin to parse the body with limit.
118pub struct Struct<T> where T: for<'a> Deserialize<'a> {
119    marker: marker::PhantomData<T>
120}
121impl<T> Key for Struct<T> where T: for<'a> Deserialize<'a> + Any {
122    type Value = Option<T>;
123}
124
125impl<'a, 'b, T> plugin::Plugin<Request<'a, 'b>> for Struct<T>
126where T: for<'c> Deserialize<'c> + Any {
127    type Error = BodyError;
128
129    fn eval(req: &mut Request) -> Result<Option<T>, BodyError> {
130        req.get::<Json>()
131            .and_then(|maybe_body| {
132                reverse_option(maybe_body.map(|body| from_value(body)))
133                    .map_err(|err| BodyError {
134                        detail: "Can't parse body to the struct".to_string(),
135                        cause: BodyErrorCause::JsonError(err)
136                    })
137            })
138    }
139}
140
141fn reverse_option<T,E>(value: Option<Result<T, E>>) -> Result<Option<T>, E> {
142    match value {
143        Some(Ok(val)) => Ok(Some(val)),
144        Some(Err(err)) => Err(err),
145        None => Ok(None),
146    }
147}