use response; use handler::ErrorHandler; use codegen::StaticCatchInfo; use error::Error; use request::Request; use std::fmt; use term_painter::ToStyle; use term_painter::Color::*; /// An error catching route. /// /// Catchers are routes that run when errors occur. They correspond directly /// with the HTTP error status code they will be handling and are registered /// with Rocket via the [Rocket::catch](/rocket/struct.Rocket.html#method.catch) /// method. For example, to handle "404 not found" errors, a catcher for the /// "404" status code is registered. /// /// Because error handlers are only called when all routes are exhausted, they /// should not fail nor forward. If an error catcher fails, the user will /// receive no response. If an error catcher forwards, Rocket will respond with /// an internal server error. /// /// # Built-In Catchers /// /// Rocket has many built-in, pre-registered default catchers. In particular, /// Rocket has catchers for all of the following status codes: 400, 401, 402, /// 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, /// 418, 421, 426, 428, 429, 431, 451, 500, 501, 503, and 510. As such, catchers /// only need to be registered if an error needs to be handled in a custom /// fashion. /// /// # Code Generation /// /// Catchers should rarely be used directly. Instead, they are typically /// declared using the `error` decorator, as follows: /// /// ```rust /// #![feature(plugin)] /// #![plugin(rocket_codegen)] /// /// extern crate rocket; /// /// use rocket::Request; /// /// #[error(500)] /// fn internal_error() -> &'static str { /// "Whoops! Looks like we messed up." /// } /// /// #[error(400)] /// fn not_found(req: &Request) -> String { /// format!("I couldn't find '{}'. Try something else?", req.uri()) /// } /// /// fn main() { /// # if false { // We don't actually want to launch the server in an example. /// rocket::ignite().catch(errors![internal_error, not_found]).launch() /// # } /// } /// ``` /// /// A function decorated with `error` can take in 0, 1, or 2 parameters: /// `Error`, `&Request`, or both, as desired. pub struct Catcher { /// The HTTP status code to match against. pub code: u16, handler: ErrorHandler, is_default: bool, } impl Catcher { /// Creates a catcher for the given status code using the given error /// handler. This should only be used when routing manually. /// /// # Examples /// /// ```rust /// use rocket::{Catcher, Request, Error}; /// use rocket::response::{Result, Responder}; /// /// fn handle_404<'r>(_: Error, req: &'r Request) -> Result<'r> { /// format!("Couldn't find: {}", req.uri()).respond() /// } /// /// fn handle_500<'r>(_: Error, _: &'r Request) -> Result<'r> { /// "Whoops, we messed up!".respond() /// } /// /// let not_found_catcher = Catcher::new(404, handle_404); /// let internal_server_error_catcher = Catcher::new(500, handle_500); /// ``` #[inline(always)] pub fn new(code: u16, handler: ErrorHandler) -> Catcher { Catcher { code: code, handler: handler, is_default: false } } #[doc(hidden)] #[inline(always)] pub fn handle<'r>(&self, err: Error, req: &'r Request) -> response::Result<'r> { (self.handler)(err, req) } #[inline(always)] fn new_default(code: u16, handler: ErrorHandler) -> Catcher { Catcher { code: code, handler: handler, is_default: true, } } #[doc(hidden)] #[inline(always)] pub fn is_default(&self) -> bool { self.is_default } } #[doc(hidden)] impl<'a> From<&'a StaticCatchInfo> for Catcher { fn from(info: &'a StaticCatchInfo) -> Catcher { Catcher::new(info.code, info.handler) } } impl fmt::Display for Catcher { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", Blue.paint(&self.code)) } } macro_rules! error_page_template { ($code:expr, $name:expr, $description:expr) => ( concat!(r#" "#, $code, " ", $name, r#"

"#, $code, ": ", $name, r#"

"#, $description, r#"


Rocket
"# ) ) } macro_rules! default_errors { ($($code:expr, $name:expr, $description:expr, $fn_name:ident),+) => ( let mut map = HashMap::new(); $( fn $fn_name<'r>(_: Error, _r: &'r Request) -> response::Result<'r> { status::Custom(Status::from_code($code).unwrap(), content::HTML(error_page_template!($code, $name, $description)) ).respond() } map.insert($code, Catcher::new_default($code, $fn_name)); )+ map ) } pub mod defaults { use super::Catcher; use std::collections::HashMap; use request::Request; use response::{self, content, status, Responder}; use http::Status; use error::Error; pub fn get() -> HashMap { default_errors! { 400, "Bad Request", "The request could not be understood by the server due to malformed syntax.", handle_400, 401, "Unauthorized", "The request requires user authentication.", handle_401, 402, "Payment Required", "The request could not be processed due to lack of payment.", handle_402, 403, "Forbidden", "The request was forbidden by the server. Check authentication.", handle_403, 404, "Not Found", "The requested resource could not be found.", handle_404, 405, "Method Not Allowed", "The request method is not supported for the requested resource.", handle_405, 406, "Not Acceptable", "The requested resource is capable of generating only content not acceptable according to the Accept headers sent in the request.", handle_406, 407, "Proxy Authentication Required", "Authentication with the proxy is required.", handle_407, 408, "Request Timeout", "The server timed out waiting for the request.", handle_408, 409, "Conflict", "The request could not be processed because of a conflict in the request.", handle_409, 410, "Gone", "The resource requested is no longer available and will not be available again.", handle_410, 411, "Length Required", "The request did not specify the length of its content, which is required by the requested resource.", handle_411, 412, "Precondition Failed", "The server does not meet one of the preconditions specified in the request.", handle_412, 413, "Payload Too Large", "The request is larger than the server is willing or able to process.", handle_413, 414, "URI Too Long", "The URI provided was too long for the server to process.", handle_414, 415, "Unsupported Media Type", "The request entity has a media type which the server or resource does not support.", handle_415, 416, "Range Not Satisfiable", "The portion of the requested file cannot be supplied by the server.", handle_416, 417, "Expectation Failed", "The server cannot meet the requirements of the Expect request-header field.", handle_417, 418, "I'm a teapot", "I was requested to brew coffee, and I am a teapot.", handle_418, 421, "Misdirected Request", "The server cannot produce a response for this request.", handle_421, 426, "Upgrade Required", "Switching to the protocol in the Upgrade header field is required.", handle_426, 428, "Precondition Required", "The server requires the request to be conditional.", handle_428, 429, "Too Many Requests", "Too many requests have been received recently.", handle_429, 431, "Request Header Fields Too Large", "The server is unwilling to process the request because either an individual header field, or all the header fields collectively, are too large.", handle_431, 451, "Unavailable For Legal Reasons", "The requested resource is unavailable due to a legal demand to deny access to this resource.", handle_451, 500, "Internal Server Error", "The server encountered an internal error while processing this request.", handle_500, 501, "Not Implemented", "The server either does not recognize the request method, or it lacks the ability to fulfill the request.", handle_501, 503, "Service Unavailable", "The server is currently unavailable.", handle_503, 510, "Not Extended", "Further extensions to the request are required for the server to fulfill it.", handle_510 } } }