+use std::fmt;
use failure::*;
use futures::future::*;
use crate::json_schema::*;
use serde_json::{Value};
-use hyper::{Body, Response};
+use hyper::{Body, Response, StatusCode};
use std::collections::HashMap;
pub async_handler: fn(Value, &ApiMethod) -> Box<Future<Item = Response<Body>, Error = Error> + Send>
}
+#[derive(Debug, Fail)]
+pub struct ApiError {
+ pub code: StatusCode,
+ pub message: String,
+}
+
+impl ApiError {
+ pub fn new(code: StatusCode, message: String) -> Self {
+ ApiError { code, message }
+ }
+}
+
+impl fmt::Display for ApiError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "Error {}: {}", self.code, self.message)
+ }
+}
+
pub struct MethodInfo {
pub get: Option<ApiMethod>,
pub put: Option<ApiMethod>,
use failure::*;
use std::collections::HashMap;
//use std::io;
-use std::fs;
-use std::path::{Path,PathBuf};
+//use std::fs;
+use std::path::{PathBuf};
//use std::collections::HashMap;
use lazy_static::lazy_static;
//use serde_derive::{Serialize, Deserialize};
use serde_json::{json, Value};
-use futures::future::*;
+use futures::future::{self, Either};
//use tokio::prelude::*;
//use tokio::timer::Delay;
use tokio::fs::File;
use hyper::http::request::Parts;
use hyper::{Method, Body, Request, Response, Server, StatusCode};
use hyper::rt::{Future, Stream};
-use hyper::service::service_fn;
+//use hyper::service::service_fn;
use hyper::header;
//use std::time::{Duration, Instant};
macro_rules! http_error_future {
($status:ident, $msg:expr) => {{
let resp = error_response!($status, $msg);
- return Box::new(ok(resp));
+ return Box::new(future::ok(resp));
}}
}
) -> Box<Future<Item = Value, Error = failure::Error> + Send + 'a>
{
let resp = req_body
- .map_err(|err| format_err!("Promlems reading request body: {}", err))
+ .map_err(|err| Error::from(ApiError::new(StatusCode::BAD_REQUEST, format!("Promlems reading request body: {}", err))))
.fold(Vec::new(), |mut acc, chunk| {
if acc.len() + chunk.len() < 64*1024 { //fimxe: max request body size?
acc.extend_from_slice(&*chunk);
- ok(acc)
- } else {
- err(format_err!("Request body too large"))
+ future::ok(acc)
}
+ //else { ok(acc) } //FIXMEEEEE
+ else { future::err(Error::from(ApiError::new(StatusCode::BAD_REQUEST, format!("Request body too large")))) }
})
.and_then(move |body| {
fn simple_static_file_download(filename: PathBuf) -> BoxFut {
Box::new(File::open(filename)
- .map_err(|err| format_err!("File open failed: {}", err))
+ .map_err(|err| Error::from(ApiError::new(StatusCode::BAD_REQUEST, format!("File open failed: {}", err))))
.and_then(|file| {
let buf: Vec<u8> = Vec::new();
tokio::io::read_to_end(file, buf)
- .map_err(|err| format_err!("File read failed: {}", err))
+ .map_err(|err| Error::from(ApiError::new(StatusCode::BAD_REQUEST, format!("File read failed: {}", err))))
.and_then(|data| Ok(Response::new(data.1.into())))
}))
}
fn chuncked_static_file_download(filename: PathBuf) -> BoxFut {
Box::new(File::open(filename)
- .map_err(|err| format_err!("File open failed: {}", err))
+ .map_err(|err| Error::from(ApiError::new(StatusCode::BAD_REQUEST, format!("File open failed: {}", err))))
.and_then(|file| {
let payload = tokio_codec::FramedRead::new(file, tokio_codec::BytesCodec::new()).
map(|bytes| {
fn handle_static_file_download(filename: PathBuf) -> BoxFut {
let response = tokio::fs::metadata(filename.clone())
- .map_err(|err| format_err!("File access problems: {}", err))
+ .map_err(|err| Error::from(ApiError::new(StatusCode::BAD_REQUEST, format!("File access problems: {}", err))))
.and_then(|metadata| {
if metadata.len() < 1024*32 {
Either::A(simple_static_file_download(filename))
basedirs
}
+struct ApiServer {
+ basedir: PathBuf,
+}
+
+impl hyper::service::Service for ApiServer {
+ type ReqBody = Body;
+ type ResBody = Body;
+ type Error = String;
+ type Future = Box<Future<Item = Response<Body>, Error = String> + Send>;
+
+ fn call(&mut self, req: Request<Self::ReqBody>) -> Self::Future {
+
+ Box::new(handle_request(req).then(|result| {
+ match result {
+ Ok(res) => Ok(res),
+ Err(err) => {
+ if let Some(apierr) = err.downcast_ref::<ApiError>() {
+ let mut resp = Response::new(Body::from(apierr.message.clone()));
+ *resp.status_mut() = apierr.code;
+ Ok(resp)
+ } else {
+ Ok(error_response!(BAD_REQUEST, err.to_string()))
+ }
+ }
+ }
+ }))
+ }
+}
+
lazy_static!{
static ref DIR_ALIASES: HashMap<String, PathBuf> = initialize_directory_aliases();
}
let addr = ([127, 0, 0, 1], 8007).into();
let new_svc = || {
- service_fn(|req| {
- // clumsy way to convert failure::Error to Response
- handle_request(req).then(|result| -> Result<Response<Body>, String> {
- match result {
- Ok(res) => Ok(res),
- Err(err) => Ok(error_response!(BAD_REQUEST, err.to_string())),
- }
- })
- })
+ let service = ApiServer { basedir: "/var/www".into() };
+ future::ok::<_,String>(service)
};
let server = Server::bind(&addr)