use std::collections;
use serialize::json;
use typemap;
use queryst;
use valico::MutableJson;
use super::{ApiHandler};
use super::api;
use super::super::backend;
use super::super::errors;
use super::super::server::status;
pub struct Application {
pub ext: typemap::TypeMap,
pub root_api: api::Api,
}
unsafe impl Send for Application {}
unsafe impl Sync for Application {}
impl Application {
pub fn new(root_api: api::Api) -> Application {
Application {
root_api: root_api,
ext: typemap::TypeMap::new()
}
}
fn call_internal<'a>(&self, req: &'a mut (backend::Request + 'a)) -> backend::HandleResult<backend::Response> {
let mut params = json::Json::Object(collections::BTreeMap::new());
let parse_result = parse_request(req, &mut params);
parse_result.and_then(|_| {
self.root_api.api_call((req.url().path().connect("/").as_slice()).as_slice(), &mut params, req, &mut super::CallInfo::new(self))
})
}
pub fn call<'a>(&self, req: &'a mut (backend::Request + 'a)) -> backend::HandleResultStrict<backend::Response> {
self.call_internal(req)
.map_err(|error_response| {
if error_response.response.is_some() {
let errors::ErrorResponse{error, response} = error_response;
errors::StrictErrorResponse{
error: error,
response: response.unwrap()
}
} else {
let errors::ErrorResponse{error, ..} = error_response;
let response = if error.is::<errors::NotMatch>() {
backend::Response::new(
status::StatusCode::NotFound
)
} else if error.is::<errors::Validation>() {
backend::Response::new(
status::StatusCode::BadRequest
)
} else {
backend::Response::new(
status::StatusCode::InternalServerError
)
};
errors::StrictErrorResponse {
error: error,
response: response
}
}
})
}
}
fn parse_query(query_str: &str, params: &mut json::Json) -> backend::HandleSuccessResult {
let maybe_query_params = queryst::parse(query_str);
match maybe_query_params {
Ok(query_params) => {
let params = params.as_object_mut().expect("Params must be an object");
for (key, value) in query_params.as_object().expect("Query params must be an object").iter() {
if !params.contains_key(key) {
params.insert(key.to_string(), value.clone());
}
}
},
Err(_) => {
return Err(error_response!(errors::QueryString));
}
}
Ok(())
}
fn parse_utf8(req: &mut backend::Request) -> backend::HandleResult<String> {
match req.body_mut().read_to_end() {
Ok(bytes) => {
match String::from_utf8(bytes) {
Ok(e) => Ok(e),
Err(_) => Err(error_response!(
errors::Body::new("Invalid UTF-8 sequence".to_string())
)),
}
},
Err(_) => Err(error_response!(
errors::Body::new("Invalid request body".to_string())
)),
}
}
fn parse_json_body(req: &mut backend::Request, params: &mut json::Json) -> backend::HandleSuccessResult {
let utf8_string_body = try!(parse_utf8(req));
if utf8_string_body.len() > 0 {
let maybe_json_body = utf8_string_body.parse::<json::Json>();
match maybe_json_body {
Ok(json_body) => {
let params = params.as_object_mut().expect("Params must be object");
if json_body.is_object() {
for (key, value) in json_body.as_object().unwrap().iter() {
if !params.contains_key(key) {
params.insert(key.to_string(), value.clone());
}
}
} else {
params.insert("body".to_string(), json_body);
}
},
Err(e) => return Err(error_response!(errors::Body::new(format!("Invalid JSON: {}", e))))
}
}
Ok(())
}
fn parse_urlencoded_body(req: &mut backend::Request, params: &mut json::Json) -> backend::HandleSuccessResult {
let utf8_string_body = try!(parse_utf8(req));
if utf8_string_body.len() > 0 {
let maybe_json_body = queryst::parse(utf8_string_body.as_slice());
match maybe_json_body {
Ok(json_body) => {
let params = params.as_object_mut().expect("Params must be object");
if json_body.is_object() {
for (key, value) in json_body.as_object().unwrap().iter() {
if !params.contains_key(key) {
params.insert(key.to_string(), value.clone());
}
}
} else {
params.insert("body".to_string(), json_body);
}
},
Err(_) => return Err(error_response!(errors::Body::new(format!("Invalid encoded data"))))
}
}
Ok(())
}
fn parse_request(req: &mut backend::Request, params: &mut json::Json) -> backend::HandleSuccessResult {
if req.url().query().is_some() {
try!(parse_query(req.url().query().as_ref().unwrap().as_slice(), params));
}
if req.is_json_body() {
try!(parse_json_body(req, params));
} else if req.is_urlencoded_body() {
try!(parse_urlencoded_body(req, params));
}
Ok(())
}