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
129
extern crate url;

pub use hyper::header as header;
use header::{Cookie as CookieHeader, ContentType};
pub use header::CookiePair as Cookie;
pub use hyper::status::StatusCode as Status;

use hyper::{Headers, Method};
use hyper::uri::RequestUri::{AbsolutePath, Star};
use hyper::mime::{Mime, TopLevel, SubLevel};
use hyper::server::Request as HttpRequest;

use std::collections::BTreeMap;
use std::io::{Error, ErrorKind, Result};
use std::result::Result as StdResult;
use std::slice::Iter;

use buffer::Buffer;

use url::{ParseError, Url};

/// A request, with a path, query, and fragment (accessor methods not yet implemented for the last two).
///
/// Can be queried for the parameters that were matched by the router.
pub struct Request {
    inner: HttpRequest,
    path: Vec<String>,
    query: Option<String>,
    fragment: Option<String>,

    params: Option<BTreeMap<String, String>>,
    body: Option<Buffer>
}

pub fn new(inner: HttpRequest) -> StdResult<Request, ParseError> {
    let (path, query, fragment) = match *inner.uri() {
        AbsolutePath(ref path) => {
            let base = Url::parse("http://localhost").unwrap();
            match Url::options().base_url(Some(&base)).parse(path) {
                Ok(url) => (url.path_segments().unwrap().map(|s| s.to_string()).collect(),
                    url.query().map(|s| s.to_string()),
                    url.fragment().map(|s| s.to_string())),
                Err(e) => return Err(e)
            }
        },
        Star => (vec!["*".to_owned()], None, None),
        _ => panic!("unsupported request URI")
    };

    Ok(Request {
        inner: inner,
        path: path,
        query: query,
        fragment: fragment,
        params: None,
        body: None})
}

pub fn set_body(request: Option<&mut Request>, body: Option<Buffer>) {
    if let Some(req) = request {
        req.body = body;
    }
}

impl Request {
    /// Returns this request's body as a vector of bytes.
    pub fn body(&self) -> Result<&[u8]> {
        match self.body {
            Some(ref buffer) => Ok(buffer.as_ref()),
            None => Err(Error::new(ErrorKind::UnexpectedEof, "empty body"))
        }
    }

    /// Returns an iterator over the cookies of this request.
    pub fn cookies(&self) -> Iter<Cookie> {
        self.headers().get::<CookieHeader>().map_or([].iter(),
            |&CookieHeader(ref cookies)| cookies.iter()
        )
    }

    /// Reads the body of this request, parses it as an application/x-www-form-urlencoded format,
    /// and returns it as a vector of (name, value) pairs.
    pub fn form(&mut self) -> Result<Vec<(String, String)>> {
        let body = try!(self.body());

        match self.headers().get::<ContentType>() {
            Some(&ContentType(Mime(TopLevel::Application, SubLevel::WwwFormUrlEncoded, _))) => {
                let parse = url::form_urlencoded::parse(body);
                Ok(parse.into_owned().collect())
            }
            Some(_) => Err(Error::new(ErrorKind::InvalidInput, "invalid Content-Type, expected application/x-www-form-urlencoded")),
            None => Err(Error::new(ErrorKind::InvalidInput, "missing Content-Type header"))
        }
    }

    /// Returns the method
    pub fn method(&self) -> &Method {
        self.inner.method()
    }

    /// Returns headers
    #[inline]
    pub fn headers(&self) -> &Headers { self.inner.headers() }

    /// Returns the parameter with the given name declared by the route that matched the URL of this request (if any).
    pub fn param(&self, key: &str) -> Option<&str> {
        self.params.as_ref().map_or(None, |map| map.get(key).map(String::as_str))
    }

    /// Returns the path of this request, i.e. the list of segments of the URL.
    pub fn path(&self) -> &[String] {
        &self.path
    }

    /// Returns the query of this request (if any).
    pub fn query(&self) -> Option<&str> {
        self.query.as_ref().map(String::as_str)
    }

    /// Returns the fragment of this request (if any).
    pub fn fragment(&self) -> Option<&str> {
        self.fragment.as_ref().map(String::as_str)
    }
}

/// Sets the parameters declared by the route that matched the URL of this request.
pub fn set_params(request: &mut Request, params: BTreeMap<String, String>) {
    request.params = Some(params);
}