use std::collections::{HashMap,HashSet};
use std::fs::{File};
use std::io::{BufReader, Read};
use std::path::PathBuf;
use futures::future::ok;
use hyper::{Get, Post, Put, Delete, StatusCode, Error};
use hyper::server::{Service, Request, Response};
use hyper::header::{ContentLength};
use super::Callback;
pub struct Pony {
pub gets: HashMap<String, Callback>,
pub posts: HashMap<String, Callback>,
pub puts: HashMap<String, Callback>,
pub deletes: HashMap<String, Callback>,
pub static_path: String,
pub static_enabled: bool,
pub not_found_path: String,
pub custom_not_found: bool,
pub known_extensions: HashSet<String>
}
impl Pony {
fn get(&self, req: Request) -> super::HyperResult {
match self.gets.get(req.path()) {
Some(cb) => {
cb(req)
},
None => {
if self.static_enabled {
self.static_file(req.path())
} else {
self.not_found()
}
},
}
}
fn static_file(&self, path: &str) -> super::HyperResult {
let mut incoming = String::from(path);
if incoming.ends_with('/') {
incoming += "index.html";
} else if !self.check_for_known_ext(&incoming) {
incoming += "/index.html";
}
if self.static_path.ends_with('/') && incoming.starts_with('/') {
incoming.remove(0);
}
let static_path = self.static_path.clone() + &incoming;
let pb = PathBuf::from(static_path);
let file = if let Ok(f) = File::open(pb) {
f
} else {
return self.not_found()
};
let mut reader = BufReader::new(file);
let mut contents: Vec<u8> = vec!();
if let Ok(_) = reader.read_to_end(&mut contents) {
Box::new(
ok(
Response::new()
.with_body(contents)
)
)
} else {
self.not_found()
}
}
fn check_for_known_ext(&self, path: &str) -> bool {
if path.ends_with("/") {
return false;
}
let ext = path.split('.').last().expect("failed to get last item in path");
self.known_extensions.contains(ext)
}
}
impl Service for Pony {
type Request = Request;
type Response = Response;
type Error = Error;
type Future = super::HyperResult;
fn call(&self, req: Request) -> Self::Future {
match req.method() {
&Get => {
self.get(req)
},
&Post => {
match self.posts.get(req.path()) {
Some(cb) => cb(req),
None => self.not_found(),
}
},
&Put => {
match self.puts.get(req.path()) {
Some(cb) => cb(req),
None => self.not_found(),
}
},
&Delete => {
match self.deletes.get(req.path()) {
Some(cb) => cb(req),
None => self.not_found(),
}
}
_ => {
self.not_found()
}
}
}
}
impl Pony {
fn not_found(&self) -> super::HyperResult {
if self.custom_not_found {
let path = PathBuf::from(&self.not_found_path);
let file = if let Ok(f) = File::open(path) {
f
} else {
panic!(format!("Unable to find file 404 file: {:?}", self.not_found_path));
};
let mut reader = BufReader::new(file);
let mut bytes: Vec<u8> = vec!();
if let Ok(size) = reader.read_to_end(&mut bytes) {
Box::new(
ok(
Response::new()
.with_status(StatusCode::Ok)
.with_header(ContentLength(size as u64))
.with_body(bytes)
)
)
} else {
Pony::default_not_found()
}
} else {
Pony::default_not_found()
}
}
fn default_not_found() -> super::HyperResult {
Box::new(
ok(
Response::new()
.with_status(StatusCode::NotFound)
)
)
}
}
#[cfg(test)]
mod tests {
}