From 1ef7a15bab6e9048b98799ade4b91546f040ad10 Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Wed, 6 Apr 2016 13:50:02 -0700 Subject: [PATCH] Working error handling! Not quite there yet though. A few important things needs to get this to be 'right': 1a. Have a way to return a response with a status code. 1b. Use that mechanism in the default catchers. 2. Automatically fill in that code from the #[error] handler. 3. Have a way for a responder to say if responding succeeded. 4. Try next highest ranking route if responding with one handler fails. --- README.md | 3 +- examples/errors/src/main.rs | 3 +- lib/src/catcher.rs | 58 ++++++++++++++++++++++++++++++++- lib/src/response/with_status.rs | 0 lib/src/rocket.rs | 47 +++++++++++++++++--------- 5 files changed, 91 insertions(+), 20 deletions(-) create mode 100644 lib/src/response/with_status.rs diff --git a/README.md b/README.md index d4b5324a..c259654e 100644 --- a/README.md +++ b/README.md @@ -19,8 +19,7 @@ fn hello(name: &str, age: u8) -> String { } fn main() { - let mut rocket = Rocket::new("localhost", 8000); - rocket.mount_and_launch("/hello", routes![hello]); + Rocket::new("localhost", 8000).mount_and_launch("/hello", routes![hello]); } ``` diff --git a/examples/errors/src/main.rs b/examples/errors/src/main.rs index a55d7793..5c668e61 100644 --- a/examples/errors/src/main.rs +++ b/examples/errors/src/main.rs @@ -11,13 +11,12 @@ fn hello(name: &str, age: i8) -> String { #[error(code = "404")] fn not_found() -> &'static str { - "Sorry, I couldn't find what you're looking for." + "

Sorry pal.

Go to '/hello/<name>/<age>' instead.

" } fn main() { let mut rocket = Rocket::new("localhost", 8000); rocket.mount("/", routes![hello]); rocket.catch(errors![not_found]); - // rocket.catch_and_launch(errors![not_found]); rocket.launch(); } diff --git a/lib/src/catcher.rs b/lib/src/catcher.rs index d4252bac..95e0151b 100644 --- a/lib/src/catcher.rs +++ b/lib/src/catcher.rs @@ -8,15 +8,25 @@ use term_painter::Color::*; pub struct Catcher { pub code: u16, pub handler: Handler, + is_default: bool } impl Catcher { pub fn new(code: u16, handler: Handler) -> Catcher { + Catcher::new_with_default(code, handler, false) + } + + fn new_with_default(code: u16, handler: Handler, default: bool) -> Catcher { Catcher { code: code, handler: handler, + is_default: default } } + + pub fn is_default(&self) -> bool { + self.is_default + } } impl<'a> From<&'a StaticCatchInfo> for Catcher { @@ -27,6 +37,52 @@ impl<'a> From<&'a StaticCatchInfo> for Catcher { impl fmt::Display for Catcher { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{} {}", Blue.paint(&self.code), Blue.paint("catcher.")) + write!(f, "{}", Blue.paint(&self.code)) } } + +pub mod defaults { + use request::Request; + use response::Response; + use super::Catcher; + use std::collections::HashMap; + + pub fn get() -> HashMap { + let mut map = HashMap::new(); + map.insert(404, Catcher::new_with_default(404, not_found, true)); + map.insert(500, Catcher::new_with_default(500, internal_error, true)); + map + } + + pub fn not_found(_request: Request) -> Response { + // FIXME: Need a way to pass in the status code. + Response::new("\ + \ + \ + 404: Not Found\ + \ + \ +

404: Not Found

\ +

The page you were looking for could not be found.

\ +


\ + Rocket\ + \ + ") + } + + pub fn internal_error(_request: Request) -> Response { + // FIXME: Need a way to pass in the status code. + Response::new("\ + \ + \ + 404: Not Found\ + \ + \ +

500: Internal Server Error

\ +

The server encountered a problem processing your request.

\ +


\ + Rocket\ + ") + } +} + diff --git a/lib/src/response/with_status.rs b/lib/src/response/with_status.rs new file mode 100644 index 00000000..e69de29b diff --git a/lib/src/rocket.rs b/lib/src/rocket.rs index 86a91564..5f5bb9c3 100644 --- a/lib/src/rocket.rs +++ b/lib/src/rocket.rs @@ -1,8 +1,10 @@ use super::*; use response::{HyperResponse, HyperFresh}; use request::HyperRequest; +use catcher; use std::io::Read; +use std::collections::HashMap; use term_painter::Color::*; use term_painter::ToStyle; @@ -14,37 +16,45 @@ pub struct Rocket { address: &'static str, port: isize, router: Router, - catchers: Vec, + catchers: HashMap, } impl HyperHandler for Rocket { fn handle<'a, 'k>(&'a self, mut req: HyperRequest<'a, 'k>, res: HyperResponse<'a, HyperFresh>) { - println!("{} {:?} {:?}", White.paint("Incoming:"), - Green.paint(&req.method), Blue.paint(&req.uri)); + println!("{:?} {:?}", Green.paint(&req.method), Blue.paint(&req.uri)); let mut buf = vec![]; req.read_to_end(&mut buf); // FIXME: Simple DOS attack here. if let HyperRequestUri::AbsolutePath(uri_string) = req.uri { if let Some(method) = Method::from_hyp(req.method) { - let uri_str = uri_string.as_str(); let route = self.router.route(method, uri_str); - let mut response = route.map_or(Response::not_found(), |route| { + + if route.is_some() { + let route = route.unwrap(); let params = route.get_params(uri_str); let request = Request::new(params, uri_str, &buf); - (route.handler)(request) - }); - println!("{}", Green.paint("\t=> Dispatched request.")); - return response.respond(res); + println!("{}", Green.paint("\t=> Dispatching request.")); + // FIXME: Responder should be able to say it didn't work. + return (route.handler)(request).respond(res); + } else { + // FIXME: Try next highest ranking route, not just 404. + let request = Request::new(vec![], uri_str, &buf); + let handler_404 = self.catchers.get(&404).unwrap().handler; + + let msg = "\t=> Dispatch failed. Returning 404."; + println!("{}", Red.paint(msg)); + return handler_404(request).respond(res); + } } println!("{}", Yellow.paint("\t=> Debug: Method::from_hyp failed!")); } - println!("{}", Red.paint("\t=> Dispatch failed. Returning 404.")); - Response::not_found().respond(res); + println!("{}", Red.paint("\t=> Internal failure. Bad method or path.")); + Response::server_error().respond(res); } } @@ -54,7 +64,7 @@ impl Rocket { address: address, port: port, router: Router::new(), - catchers: Vec::new(), + catchers: catcher::defaults::get(), } } @@ -73,9 +83,16 @@ impl Rocket { pub fn catch(&mut self, catchers: Vec) -> &mut Self { println!("👾 {}:", Magenta.paint("Catchers")); - for catcher in catchers { - println!("\t* {}", catcher); - self.catchers.push(catcher); + for c in catchers { + if self.catchers.contains_key(&c.code) && + !self.catchers.get(&c.code).unwrap().is_default() { + let msg = format!("warning: overrides {} catcher!", c.code); + println!("\t* {} ({})", c, Yellow.paint(msg.as_str())); + } else { + println!("\t* {}", c); + } + + self.catchers.insert(c.code, c); } self