1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
use hyper::header::ContentType;
use hyper::mime::{Mime, SubLevel, TopLevel};
use serde::Deserialize;
use serde_json;
use request::Request;
use plugin::{Plugin, Pluggable};
use status::StatusCode;
use std::error::Error as StdError;
use std::fmt;
use std::io::{self, ErrorKind, Read};
use typemap::Key;
use urlencoded::{self, Params};

struct BodyReader;

impl Key for BodyReader {
    type Value = String;
}

impl<'mw, 'conn, D> Plugin<Request<'mw, 'conn, D>> for BodyReader {
    type Error = io::Error;

    fn eval(req: &mut Request<D>) -> Result<String, io::Error> {
        let mut buf = String::new();
        try!(req.origin.read_to_string(&mut buf));
        Ok(buf)
    }
}

struct FormBodyParser;

impl Key for FormBodyParser {
    type Value = Params;
}

impl<'mw, 'conn, D> Plugin<Request<'mw, 'conn, D>> for FormBodyParser {
    type Error = BodyError;

    fn eval(req: &mut Request<D>) -> Result<Params, BodyError> {
        match req.origin.headers.get::<ContentType>() {
            Some(&ContentType(Mime(
                TopLevel::Application,
                SubLevel::WwwFormUrlEncoded,
                _
            ))) => {
                let body = try!(req.get_ref::<BodyReader>());
                Ok(urlencoded::parse(&*body))
            },
            _ => Err(BodyError::WrongContentType)
        }
    }
}

pub trait FormBody {
    /// Extracts URL encoded data from the request body.
    /// # Examples
    /// ```{rust}
    /// #[macro_use] extern crate nickel;
    /// use nickel::{Nickel, HttpRouter, FormBody};
    ///
    /// fn main() {
    ///     let mut server = Nickel::new();
    ///     server.post("/a", middleware! { |req, res|
    ///         let form_body = try_with!(res, req.form_body());
    ///         format!("Post: {:?}", form_body)
    ///     });
    /// }
    /// ```
    fn form_body(&mut self) -> Result<&Params, (StatusCode, BodyError)>;
}

impl<'mw, 'conn, D> FormBody for Request<'mw, 'conn, D> {
    fn form_body(&mut self) -> Result<&Params, (StatusCode, BodyError)> {
        self.get_ref::<FormBodyParser>().map_err(|e| (StatusCode::BadRequest, e))
    }
}

pub trait JsonBody {
    fn json_as<'a, T: Deserialize<'a>>(&'a mut self) -> Result<T, io::Error>;
}

impl<'mw, 'conn, D> JsonBody for Request<'mw, 'conn, D> {
    // FIXME: Update the error type.
    // Would be good to capture parsing error rather than a generic io::Error.
    // FIXME: Do the content-type check
    fn json_as<'a, T: Deserialize<'a>>(&'a mut self) -> Result<T, io::Error> {
        self.get_ref::<BodyReader>().and_then(|body|
            serde_json::from_str::<T>(&*body).map_err(|err|
                io::Error::new(ErrorKind::Other, format!("Parse error: {}", err))
            )
        )
    }
}

#[derive(Debug)]
pub enum BodyError {
    Io(io::Error),
    WrongContentType,
}

impl From<io::Error> for BodyError {
    fn from(err: io::Error) -> BodyError {
        BodyError::Io(err)
    }
}

impl StdError for BodyError {
    fn description(&self) -> &str {
        match *self {
            BodyError::Io(ref err) => err.description(),
            BodyError::WrongContentType => "Wrong content type"
        }
    }

    fn cause(&self) -> Option<&StdError> {
        match *self {
            BodyError::Io(ref err) => Some(err),
            _ => None
        }
    }
}

impl fmt::Display for BodyError {
    fn fmt(&self, out: &mut fmt::Formatter) -> fmt::Result {
        write!(out, "{}", self.description())
    }
}