extern crate iron;
extern crate url;
use std::io::Read;
use std::sync::Arc;
use iron::prelude::*;
use std::collections::HashMap;
use std::collections::hash_map::Entry::*;
use url::form_urlencoded;
use std::str::FromStr;
pub trait RequestExt {
fn params<'c, 'd>(&'c self, name: &'d str) -> Option<&'c Vec<String>>;
fn param<'c, 'd, F: FromStr>(&'c self, name: &'d str) -> Option<F>;
fn body<'c, 'd>(&'c self) -> Option<&'c String>;
}
impl<'a, 'b> RequestExt for Request<'a, 'b> {
fn params<'c, 'd>(&'c self, name: &'d str) -> Option<&'c Vec<String>> {
if let Some(map) = self.extensions.get::<Params>() {
return map.get(name);
}
None
}
fn param<'c, 'd, F: FromStr>(&'c self, name: &'d str) -> Option<F> {
match self.params(name) {
Some(vec) if vec.len() > 0 => vec[0].clone().parse().ok(),
_ => None,
}
}
fn body<'c, 'd>(&'c self) -> Option<&'c String> {
use std::borrow::Borrow;
self.extensions.get::<Body>().map(|v| v.borrow())
}
}
pub struct Params;
impl iron::typemap::Key for Params {
type Value = Arc<HashMap<String, Vec<String>>>;
}
pub struct Body;
impl iron::typemap::Key for Body {
type Value = Arc<String>;
}
impl iron::BeforeMiddleware for Params {
fn before(&self, req: &mut Request) -> IronResult<()> {
let (body, map) = combine(req);
req.extensions.insert::<Params>(Arc::new(map));
req.extensions.insert::<Body>(Arc::new(body));
Ok(())
}
}
fn combine(req: &mut Request) -> (String, HashMap<String, Vec<String>>) {
let mut map = HashMap::<String, Vec<String>>::new();
let url = req.url.clone();
let query = url.query();
if let Some(query) = query {
combine_duplicates(form_urlencoded::parse(&String::from(query).into_bytes()), &mut map);
}
let mut reader = &mut req.body;
let mut buf = vec![0;300];
let mut vec: Vec<u8> = Vec::new();
while let Ok(n) = reader.read(&mut buf) {
if n <= 0 {
break;
}
vec.extend_from_slice(&buf[0..n]);
}
let body = String::from_utf8(vec.clone()).unwrap_or("".into());
combine_duplicates(form_urlencoded::parse(&vec), &mut map);
(body, map)
}
fn combine_duplicates(q: url::form_urlencoded::Parse, map: &mut HashMap<String, Vec<String>>) {
for (k, v) in q.into_iter() {
let (k, v) = (k.into_owned(), v.into_owned());
match map.entry(k) {
Occupied(entry) => {
entry.into_mut().push(v);
}
Vacant(entry) => {
entry.insert(vec![v]);
}
};
}
}