Skip to content

Instantly share code, notes, and snippets.

@clonejo
Created January 19, 2016 19:07
Show Gist options
  • Select an option

  • Save clonejo/64c09bec272e2f85caa2 to your computer and use it in GitHub Desktop.

Select an option

Save clonejo/64c09bec272e2f85caa2 to your computer and use it in GitHub Desktop.
use std::any::Any;
use std::cmp::min;
use std::str;
use db::DbCon;
use db;
use rustc_serialize::json::{Json, Object, ToJson};
use std::sync::{Arc, Mutex};
use std::io::Read;
use model::{json_to_object, Action};
use hyper::server::{Server, Request, Response};
use hyper::header;
use hyper::uri::RequestUri;
use hyper::method::Method;
use hyper::status::StatusCode;
use hyper_router::{RouterBuilder, Route};
use route_recognizer::{Router, Match, Params};
fn check_authentication(req: &Request, password: &Option<&str>) -> bool {
match *password {
None => true,
Some(pass_str) => {
match req.headers.get::<header::Authorization<header::Basic>>() {
Some(authorization) => true,
None => false
}
}
}
}
trait Handler: Sync + Send + Any {
fn handle(&self, params: Params, req: Request, authenticated: bool, res: Response, con: Arc<Mutex<DbCon>>);
}
impl<F> Handler for F
where F: Send + Sync + Any + Fn(Params, Request, bool, Response, Arc<Mutex<DbCon>>) {
fn handle(&self, params: Params, req: Request, authenticated: bool, res: Response, con: Arc<Mutex<DbCon>>) {
(*self)(params, req, authenticated, res, con);
}
}
pub fn run(con: DbCon, listen: &str, password: Option<&str>) {
let shared_con = Arc::new(Mutex::new(con));
let mut router: Router<(Method, Box<Handler>)> = Router::new();
router.add("/api/versions", (Method::Get, Box::new(api_versions)));
router.add("/api/v0", (Method::Put, Box::new(create_action)));
router.add("/api/v0/:type", (Method::Get, Box::new(query)));
router.add("/api/v0/status/current", (Method::Get, Box::new(status_current)));
Server::http(listen).unwrap().handle(move |req: Request, res: Response| {
let match_result = {
let path_str = match req.uri {
RequestUri::AbsolutePath(ref p) => p,
_ => panic!()
};
router.recognize(&*path_str)
};
match match_result {
Ok(Match{ handler: tup, params: params }) => {
let authenticated = { check_authentication(&req, &password) };
//let authenticated = true;
let &(ref method, ref handler): &(Method, Box<Handler>) = tup;
if *method == req.method {
handler.handle(params, req, authenticated, res, shared_con.clone());
} else {
handler_405(req, res);
}
},
Err(_) =>
handler_404(req, res)
};
}).unwrap();
}
fn handler_404(_req: Request, mut res: Response) {
{
let status = res.status_mut();
*status = StatusCode::NotFound;
}
res.send(b"Not Found\n");
}
fn handler_405(_req: Request, mut res: Response) {
{
let status = res.status_mut();
*status = StatusCode::MethodNotAllowed;
}
res.send(b"Method Not Allowed\n");
}
fn send_status(mut res: Response, status: StatusCode) {
let s = res.status_mut();
*s = status;
}
fn send(mut res: Response, status: StatusCode, msg: &[u8]) {
{
let s = res.status_mut();
*s = status;
}
res.send(msg);
}
fn api_versions(_params: Params, _req: Request, authenticated: bool, mut res: Response, _shared_con: Arc<Mutex<DbCon>>) {
let mut obj = Object::new();
obj.insert("versions".into(), [0].to_json());
{
let headers = res.headers_mut();
headers.set(header::AccessControlAllowOrigin::Any);
}
let mut resp_str = obj.to_json().to_string();
resp_str.push('\n');
res.send(resp_str.as_bytes());
}
/*
* PUT
*/
fn create_action(params: Params, mut req: Request, authenticated: bool, res: Response, shared_con: Arc<Mutex<DbCon>>) {
// TODO: check auth
let mut action_buf = &mut [0; 1024];
// parse at maximum 1k bytes
let bytes_read = req.read(action_buf).unwrap();
let (action_buf, _) = action_buf.split_at(bytes_read);
let action_str = str::from_utf8(action_buf).unwrap();
match Json::from_str(action_str) {
Err(_) =>
send_status(res, StatusCode::BadRequest),
Ok(action_json) => {
match json_to_object(action_json) {
Ok(mut action) => {
let con = shared_con.lock().unwrap();
action.store(&*con);
let mut resp_str = format!("{}", action.get_base_action().id.unwrap());
resp_str.push('\n');
res.send(resp_str.as_bytes());
},
Err(msg) => {
send(res, StatusCode::BadRequest, msg.as_bytes());
}
}
}
}
}
/*
* GET
*/
fn status_current(params: Params, req: Request, authenticated: bool, res: Response, shared_con: Arc<Mutex<DbCon>>) {
//let public_api = req.headers.has("public");
let mut obj = Object::new();
let con = shared_con.lock().unwrap();
obj.insert("last".into(), db::status::get_last(&*con).unwrap().to_json());
obj.insert("changed".into(), db::status::get_last_changed(&*con).unwrap().to_json());
let mut resp_str = obj.to_json().to_string();
resp_str.push('\n');
res.send(resp_str.as_bytes());
// for non-authenticated API
//Ok(Response::with((status::Ok, resp_str, modifiers::Header(headers::AccessControlAllowOrigin::Any))))
}
fn query(params: Params, req: Request, authenticated: bool, res: Response, shared_con: Arc<Mutex<DbCon>>) {
let count = 20;
let count: u64 = min(count, 100);
let mut obj = Object::new();
let con = shared_con.lock().unwrap();
let actions = db::query(count, &*con);
obj.insert("actions".into(), actions.unwrap().to_json());
let mut resp_str = obj.to_json().to_string();
resp_str.push('\n');
res.send(resp_str.as_bytes());
}
RUST_BACKTRACE=1 cargo run -- -c example.conf
Compiling clubstatusd v0.1.0 (file:///home/clonejo/code/clubstatusd)
src/api.rs:51:35: 73:7 error: the type `[closure@src/api.rs:51:42: 73:6 router:route_recognizer::Router<(hyper::method::Method, Box<api::Handler + 'static>)>, password:core::option::Option<&str>, shared_con:alloc::arc::Arc<std::sync::mutex::Mutex<rusqlite::SqliteConnection>>]` does not fulfill the required lifetime [E0477]
src/api.rs:51 Server::http(listen).unwrap().handle(move |req: Request, res: Response| {
src/api.rs:52 let match_result = {
src/api.rs:53 let path_str = match req.uri {
src/api.rs:54 RequestUri::AbsolutePath(ref p) => p,
src/api.rs:55 _ => panic!()
src/api.rs:56 };
...
note: type must outlive the static lifetime
error: aborting due to previous error
Could not compile `clubstatusd`.
To learn more, run the command again with --verbose.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment