2016-12-15 08:47:31 +00:00
|
|
|
use response;
|
2016-08-26 08:55:11 +00:00
|
|
|
use handler::ErrorHandler;
|
2016-04-06 10:26:43 +00:00
|
|
|
use codegen::StaticCatchInfo;
|
2016-08-26 08:55:11 +00:00
|
|
|
use error::Error;
|
|
|
|
use request::Request;
|
2016-04-06 10:26:43 +00:00
|
|
|
|
|
|
|
use std::fmt;
|
|
|
|
use term_painter::ToStyle;
|
|
|
|
use term_painter::Color::*;
|
|
|
|
|
2016-11-04 13:35:04 +00:00
|
|
|
/// An error catching route.
|
2016-11-05 18:31:50 +00:00
|
|
|
///
|
|
|
|
/// 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.
|
2016-04-06 10:26:43 +00:00
|
|
|
pub struct Catcher {
|
2016-11-05 18:31:50 +00:00
|
|
|
/// The HTTP status code to match against.
|
2016-04-06 10:26:43 +00:00
|
|
|
pub code: u16,
|
2016-08-26 08:55:11 +00:00
|
|
|
handler: ErrorHandler,
|
2016-09-09 08:00:51 +00:00
|
|
|
is_default: bool,
|
2016-04-06 10:26:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Catcher {
|
2016-11-04 13:35:04 +00:00
|
|
|
/// Creates a catcher for the given status code using the given error
|
2016-11-05 18:31:50 +00:00
|
|
|
/// handler. This should only be used when routing manually.
|
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
/// ```rust
|
2017-02-02 10:16:21 +00:00
|
|
|
/// # #![allow(unused_variables)]
|
2016-12-15 08:47:31 +00:00
|
|
|
/// use rocket::{Catcher, Request, Error};
|
|
|
|
/// use rocket::response::{Result, Responder};
|
2016-11-05 18:31:50 +00:00
|
|
|
///
|
2016-12-16 11:07:23 +00:00
|
|
|
/// fn handle_404<'r>(_: Error, req: &'r Request) -> Result<'r> {
|
2016-12-15 08:47:31 +00:00
|
|
|
/// format!("Couldn't find: {}", req.uri()).respond()
|
2016-11-05 18:31:50 +00:00
|
|
|
/// }
|
|
|
|
///
|
2016-12-16 11:07:23 +00:00
|
|
|
/// fn handle_500<'r>(_: Error, _: &'r Request) -> Result<'r> {
|
2016-12-15 08:47:31 +00:00
|
|
|
/// "Whoops, we messed up!".respond()
|
2016-11-05 18:31:50 +00:00
|
|
|
/// }
|
|
|
|
///
|
|
|
|
/// let not_found_catcher = Catcher::new(404, handle_404);
|
|
|
|
/// let internal_server_error_catcher = Catcher::new(500, handle_500);
|
|
|
|
/// ```
|
2016-11-04 13:35:04 +00:00
|
|
|
#[inline(always)]
|
2016-08-26 08:55:11 +00:00
|
|
|
pub fn new(code: u16, handler: ErrorHandler) -> Catcher {
|
2016-10-08 04:31:52 +00:00
|
|
|
Catcher { code: code, handler: handler, is_default: false }
|
2016-04-06 20:50:02 +00:00
|
|
|
}
|
|
|
|
|
2016-11-04 13:35:04 +00:00
|
|
|
#[inline(always)]
|
2017-02-03 10:16:46 +00:00
|
|
|
pub(crate) fn handle<'r>(&self, err: Error, req: &'r Request)
|
|
|
|
-> response::Result<'r> {
|
2016-12-15 08:47:31 +00:00
|
|
|
(self.handler)(err, req)
|
2016-07-16 04:09:08 +00:00
|
|
|
}
|
|
|
|
|
2016-11-04 13:35:04 +00:00
|
|
|
#[inline(always)]
|
2016-10-08 04:31:52 +00:00
|
|
|
fn new_default(code: u16, handler: ErrorHandler) -> Catcher {
|
|
|
|
Catcher { code: code, handler: handler, is_default: true, }
|
2016-04-06 10:26:43 +00:00
|
|
|
}
|
2016-04-06 20:50:02 +00:00
|
|
|
|
2016-11-04 13:35:04 +00:00
|
|
|
#[inline(always)]
|
2017-02-03 10:16:46 +00:00
|
|
|
pub(crate) fn is_default(&self) -> bool {
|
2016-04-06 20:50:02 +00:00
|
|
|
self.is_default
|
|
|
|
}
|
2016-04-06 10:26:43 +00:00
|
|
|
}
|
|
|
|
|
2016-11-04 13:35:04 +00:00
|
|
|
#[doc(hidden)]
|
2016-04-06 10:26:43 +00:00
|
|
|
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 {
|
2016-04-06 20:50:02 +00:00
|
|
|
write!(f, "{}", Blue.paint(&self.code))
|
2016-04-06 10:26:43 +00:00
|
|
|
}
|
|
|
|
}
|
2016-04-06 20:50:02 +00:00
|
|
|
|
2016-10-08 04:31:52 +00:00
|
|
|
macro_rules! error_page_template {
|
|
|
|
($code:expr, $name:expr, $description:expr) => (
|
|
|
|
concat!(r#"
|
2016-09-09 08:00:51 +00:00
|
|
|
<!DOCTYPE html>
|
|
|
|
<html>
|
|
|
|
<head>
|
|
|
|
<meta charset="utf-8">
|
2016-10-08 04:31:52 +00:00
|
|
|
<title>"#, $code, " ", $name, r#"</title>
|
2016-09-09 08:00:51 +00:00
|
|
|
</head>
|
|
|
|
<body align="center">
|
|
|
|
<div align="center">
|
2016-10-08 06:20:49 +00:00
|
|
|
<h1>"#, $code, ": ", $name, r#"</h1>
|
2016-10-08 04:31:52 +00:00
|
|
|
<p>"#, $description, r#"</p>
|
2016-09-09 08:00:51 +00:00
|
|
|
<hr />
|
|
|
|
<small>Rocket</small>
|
|
|
|
</div>
|
|
|
|
</body>
|
|
|
|
</html>
|
2016-10-08 04:31:52 +00:00
|
|
|
"#
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
2016-04-06 20:50:02 +00:00
|
|
|
|
2016-10-08 04:31:52 +00:00
|
|
|
macro_rules! default_errors {
|
|
|
|
($($code:expr, $name:expr, $description:expr, $fn_name:ident),+) => (
|
2016-09-09 08:00:51 +00:00
|
|
|
let mut map = HashMap::new();
|
2016-10-08 04:31:52 +00:00
|
|
|
|
|
|
|
$(
|
2016-12-15 08:47:31 +00:00
|
|
|
fn $fn_name<'r>(_: Error, _r: &'r Request) -> response::Result<'r> {
|
|
|
|
status::Custom(Status::from_code($code).unwrap(),
|
2016-10-25 11:03:50 +00:00
|
|
|
content::HTML(error_page_template!($code, $name, $description))
|
2016-12-15 08:47:31 +00:00
|
|
|
).respond()
|
2016-10-08 04:31:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
map.insert($code, Catcher::new_default($code, $fn_name));
|
|
|
|
)+
|
|
|
|
|
2016-09-09 08:00:51 +00:00
|
|
|
map
|
2016-10-08 04:31:52 +00:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub mod defaults {
|
|
|
|
use super::Catcher;
|
|
|
|
|
|
|
|
use std::collections::HashMap;
|
|
|
|
|
|
|
|
use request::Request;
|
2016-12-15 08:47:31 +00:00
|
|
|
use response::{self, content, status, Responder};
|
|
|
|
use http::Status;
|
2016-10-08 04:31:52 +00:00
|
|
|
use error::Error;
|
|
|
|
|
|
|
|
pub fn get() -> HashMap<u16, Catcher> {
|
|
|
|
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,
|
2016-11-05 18:31:50 +00:00
|
|
|
403, "Forbidden", "The request was forbidden by the server.
|
|
|
|
Check authentication.", handle_403,
|
2016-10-08 04:31:52 +00:00
|
|
|
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,
|
2017-01-03 20:06:22 +00:00
|
|
|
422, "Unprocessable Entity", "The request was well-formed but was unable to
|
|
|
|
be followed due to semantic errors.", handle_422,
|
2016-10-08 04:31:52 +00:00
|
|
|
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
|
|
|
|
}
|
2016-07-16 04:09:08 +00:00
|
|
|
}
|
|
|
|
}
|
2016-10-08 04:31:52 +00:00
|
|
|
|