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.
This commit is contained in:
Sergio Benitez 2016-04-06 13:50:02 -07:00
parent 33c4d89614
commit 1ef7a15bab
5 changed files with 91 additions and 20 deletions

View File

@ -19,8 +19,7 @@ fn hello(name: &str, age: u8) -> String {
} }
fn main() { fn main() {
let mut rocket = Rocket::new("localhost", 8000); Rocket::new("localhost", 8000).mount_and_launch("/hello", routes![hello]);
rocket.mount_and_launch("/hello", routes![hello]);
} }
``` ```

View File

@ -11,13 +11,12 @@ fn hello(name: &str, age: i8) -> String {
#[error(code = "404")] #[error(code = "404")]
fn not_found() -> &'static str { fn not_found() -> &'static str {
"Sorry, I couldn't find what you're looking for." "<h1>Sorry pal.</h1><p>Go to '/hello/&lt;name&gt;/&ltage&gt;' instead.</p>"
} }
fn main() { fn main() {
let mut rocket = Rocket::new("localhost", 8000); let mut rocket = Rocket::new("localhost", 8000);
rocket.mount("/", routes![hello]); rocket.mount("/", routes![hello]);
rocket.catch(errors![not_found]); rocket.catch(errors![not_found]);
// rocket.catch_and_launch(errors![not_found]);
rocket.launch(); rocket.launch();
} }

View File

@ -8,15 +8,25 @@ use term_painter::Color::*;
pub struct Catcher { pub struct Catcher {
pub code: u16, pub code: u16,
pub handler: Handler, pub handler: Handler,
is_default: bool
} }
impl Catcher { impl Catcher {
pub fn new(code: u16, handler: Handler) -> 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 { Catcher {
code: code, code: code,
handler: handler, handler: handler,
is_default: default
} }
} }
pub fn is_default(&self) -> bool {
self.is_default
}
} }
impl<'a> From<&'a StaticCatchInfo> for Catcher { impl<'a> From<&'a StaticCatchInfo> for Catcher {
@ -27,6 +37,52 @@ impl<'a> From<&'a StaticCatchInfo> for Catcher {
impl fmt::Display for Catcher { impl fmt::Display for Catcher {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 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<u16, Catcher> {
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("\
<head>\
<meta charset=\"utf-8\">\
<title>404: Not Found</title>\
</head>\
<body align=\"center\">\
<h1>404: Not Found</h1>\
<p>The page you were looking for could not be found.<p>\
<hr />\
<small>Rocket</small>\
</body>\
")
}
pub fn internal_error(_request: Request) -> Response {
// FIXME: Need a way to pass in the status code.
Response::new("\
<head>\
<meta charset=\"utf-8\">\
<title>404: Not Found</title>\
</head>\
<body align=\"center\">\
<h1>500: Internal Server Error</h1>\
<p>The server encountered a problem processing your request.<p>\
<hr />\
<small>Rocket</small>\
")
}
}

View File

View File

@ -1,8 +1,10 @@
use super::*; use super::*;
use response::{HyperResponse, HyperFresh}; use response::{HyperResponse, HyperFresh};
use request::HyperRequest; use request::HyperRequest;
use catcher;
use std::io::Read; use std::io::Read;
use std::collections::HashMap;
use term_painter::Color::*; use term_painter::Color::*;
use term_painter::ToStyle; use term_painter::ToStyle;
@ -14,37 +16,45 @@ pub struct Rocket {
address: &'static str, address: &'static str,
port: isize, port: isize,
router: Router, router: Router,
catchers: Vec<Catcher>, catchers: HashMap<u16, Catcher>,
} }
impl HyperHandler for Rocket { impl HyperHandler for Rocket {
fn handle<'a, 'k>(&'a self, mut req: HyperRequest<'a, 'k>, fn handle<'a, 'k>(&'a self, mut req: HyperRequest<'a, 'k>,
res: HyperResponse<'a, HyperFresh>) { res: HyperResponse<'a, HyperFresh>) {
println!("{} {:?} {:?}", White.paint("Incoming:"), println!("{:?} {:?}", Green.paint(&req.method), Blue.paint(&req.uri));
Green.paint(&req.method), Blue.paint(&req.uri));
let mut buf = vec![]; let mut buf = vec![];
req.read_to_end(&mut buf); // FIXME: Simple DOS attack here. req.read_to_end(&mut buf); // FIXME: Simple DOS attack here.
if let HyperRequestUri::AbsolutePath(uri_string) = req.uri { if let HyperRequestUri::AbsolutePath(uri_string) = req.uri {
if let Some(method) = Method::from_hyp(req.method) { if let Some(method) = Method::from_hyp(req.method) {
let uri_str = uri_string.as_str(); let uri_str = uri_string.as_str();
let route = self.router.route(method, uri_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 params = route.get_params(uri_str);
let request = Request::new(params, uri_str, &buf); let request = Request::new(params, uri_str, &buf);
(route.handler)(request)
});
println!("{}", Green.paint("\t=> Dispatched request.")); println!("{}", Green.paint("\t=> Dispatching request."));
return response.respond(res); // 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!("{}", Yellow.paint("\t=> Debug: Method::from_hyp failed!"));
} }
println!("{}", Red.paint("\t=> Dispatch failed. Returning 404.")); println!("{}", Red.paint("\t=> Internal failure. Bad method or path."));
Response::not_found().respond(res); Response::server_error().respond(res);
} }
} }
@ -54,7 +64,7 @@ impl Rocket {
address: address, address: address,
port: port, port: port,
router: Router::new(), router: Router::new(),
catchers: Vec::new(), catchers: catcher::defaults::get(),
} }
} }
@ -73,9 +83,16 @@ impl Rocket {
pub fn catch(&mut self, catchers: Vec<Catcher>) -> &mut Self { pub fn catch(&mut self, catchers: Vec<Catcher>) -> &mut Self {
println!("👾 {}:", Magenta.paint("Catchers")); println!("👾 {}:", Magenta.paint("Catchers"));
for catcher in catchers { for c in catchers {
println!("\t* {}", catcher); if self.catchers.contains_key(&c.code) &&
self.catchers.push(catcher); !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 self