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;
|
2017-06-02 04:44:31 +00:00
|
|
|
use yansi::Color::*;
|
2016-04-06 10:26:43 +00:00
|
|
|
|
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
|
2017-09-23 02:04:14 +00:00
|
|
|
/// declared using the `catch` decorator, as follows:
|
2016-11-05 18:31:50 +00:00
|
|
|
///
|
|
|
|
/// ```rust
|
2017-08-29 03:14:59 +00:00
|
|
|
/// #![feature(plugin, decl_macro)]
|
2016-11-05 18:31:50 +00:00
|
|
|
/// #![plugin(rocket_codegen)]
|
|
|
|
///
|
|
|
|
/// extern crate rocket;
|
|
|
|
///
|
|
|
|
/// use rocket::Request;
|
|
|
|
///
|
2017-09-23 02:04:14 +00:00
|
|
|
/// #[catch(500)]
|
2016-11-05 18:31:50 +00:00
|
|
|
/// fn internal_error() -> &'static str {
|
|
|
|
/// "Whoops! Looks like we messed up."
|
|
|
|
/// }
|
|
|
|
///
|
2018-02-26 09:34:10 +00:00
|
|
|
/// #[catch(404)]
|
2016-11-05 18:31:50 +00:00
|
|
|
/// 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.
|
2017-09-23 02:04:14 +00:00
|
|
|
/// rocket::ignite().catch(catchers![internal_error, not_found]).launch();
|
2016-11-05 18:31:50 +00:00
|
|
|
/// # }
|
|
|
|
/// }
|
|
|
|
/// ```
|
|
|
|
///
|
2017-09-23 02:04:14 +00:00
|
|
|
/// A function decorated with `catch` can take in 0, 1, or 2 parameters:
|
2016-11-05 18:31:50 +00:00
|
|
|
/// `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};
|
2017-03-09 23:22:32 +00:00
|
|
|
/// use rocket::response::status::Custom;
|
|
|
|
/// use rocket::http::Status;
|
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> {
|
2017-05-19 10:29:08 +00:00
|
|
|
/// let res = Custom(Status::NotFound, format!("404: {}", req.uri()));
|
|
|
|
/// res.respond_to(req)
|
2016-11-05 18:31:50 +00:00
|
|
|
/// }
|
|
|
|
///
|
2017-05-19 10:29:08 +00:00
|
|
|
/// fn handle_500<'r>(_: Error, req: &'r Request) -> Result<'r> {
|
|
|
|
/// "Whoops, we messed up!".respond_to(req)
|
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 {
|
2018-07-28 16:58:10 +00:00
|
|
|
Catcher { code, handler, is_default: false }
|
2016-04-06 20:50:02 +00:00
|
|
|
}
|
|
|
|
|
2016-11-04 13:35:04 +00:00
|
|
|
#[inline(always)]
|
Overhaul URI types.
This is fairly large commit with several entangled logical changes.
The primary change in this commit is to completely overhaul how URI
handling in Rocket works. Prior to this commit, the `Uri` type acted as
an origin API. Its parser was minimal and lenient, allowing URIs that
were invalid according to RFC 7230. By contrast, the new `Uri` type
brings with it a strict RFC 7230 compliant parser. The `Uri` type now
represents any kind of valid URI, not simply `Origin` types. Three new
URI types were introduced:
* `Origin` - represents valid origin URIs
* `Absolute` - represents valid absolute URIs
* `Authority` - represents valid authority URIs
The `Origin` type replaces `Uri` in many cases:
* As fields and method inputs of `Route`
* The `&Uri` request guard is now `&Origin`
* The `uri!` macro produces an `Origin` instead of a `Uri`
The strict nature of URI parsing cascaded into the following changes:
* Several `Route` methods now `panic!` on invalid URIs
* The `Rocket::mount()` method is (correctly) stricter with URIs
* The `Redirect` constructors take a `TryInto<Uri>` type
* Dispatching of a `LocalRequest` correctly validates URIs
Overall, URIs are now properly and uniformly handled throughout Rocket's
codebase, resulting in a more reliable and correct system.
In addition to these URI changes, the following changes are also part of
this commit:
* The `LocalRequest::cloned_dispatch()` method was removed in favor of
chaining `.clone().dispatch()`.
* The entire Rocket codebase uses `crate` instead of `pub(crate)` as a
visibility modifier.
* Rocket uses the `crate_visibility_modifier` and `try_from` features.
A note on unsafety: this commit introduces many uses of `unsafe` in the
URI parser. All of these uses are a result of unsafely transforming byte
slices (`&[u8]` or similar) into strings (`&str`). The parser ensures
that these casts are safe, but of course, we must label their use
`unsafe`. The parser was written to be as generic and efficient as
possible and thus can parse directly from byte sources. Rocket, however,
does not make use of this fact and so would be able to remove all uses
of `unsafe` by parsing from an existing `&str`. This should be
considered in the future.
Fixes #443.
Resolves #263.
2018-07-29 01:26:15 +00:00
|
|
|
crate fn handle<'r>(&self, e: Error, r: &'r Request) -> response::Result<'r> {
|
2017-09-09 07:54:30 +00:00
|
|
|
(self.handler)(e, r)
|
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 {
|
2018-07-28 16:58:10 +00:00
|
|
|
Catcher { code, 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)]
|
Overhaul URI types.
This is fairly large commit with several entangled logical changes.
The primary change in this commit is to completely overhaul how URI
handling in Rocket works. Prior to this commit, the `Uri` type acted as
an origin API. Its parser was minimal and lenient, allowing URIs that
were invalid according to RFC 7230. By contrast, the new `Uri` type
brings with it a strict RFC 7230 compliant parser. The `Uri` type now
represents any kind of valid URI, not simply `Origin` types. Three new
URI types were introduced:
* `Origin` - represents valid origin URIs
* `Absolute` - represents valid absolute URIs
* `Authority` - represents valid authority URIs
The `Origin` type replaces `Uri` in many cases:
* As fields and method inputs of `Route`
* The `&Uri` request guard is now `&Origin`
* The `uri!` macro produces an `Origin` instead of a `Uri`
The strict nature of URI parsing cascaded into the following changes:
* Several `Route` methods now `panic!` on invalid URIs
* The `Rocket::mount()` method is (correctly) stricter with URIs
* The `Redirect` constructors take a `TryInto<Uri>` type
* Dispatching of a `LocalRequest` correctly validates URIs
Overall, URIs are now properly and uniformly handled throughout Rocket's
codebase, resulting in a more reliable and correct system.
In addition to these URI changes, the following changes are also part of
this commit:
* The `LocalRequest::cloned_dispatch()` method was removed in favor of
chaining `.clone().dispatch()`.
* The entire Rocket codebase uses `crate` instead of `pub(crate)` as a
visibility modifier.
* Rocket uses the `crate_visibility_modifier` and `try_from` features.
A note on unsafety: this commit introduces many uses of `unsafe` in the
URI parser. All of these uses are a result of unsafely transforming byte
slices (`&[u8]` or similar) into strings (`&str`). The parser ensures
that these casts are safe, but of course, we must label their use
`unsafe`. The parser was written to be as generic and efficient as
possible and thus can parse directly from byte sources. Rocket, however,
does not make use of this fact and so would be able to remove all uses
of `unsafe` by parsing from an existing `&str`. This should be
considered in the future.
Fixes #443.
Resolves #263.
2018-07-29 01:26:15 +00:00
|
|
|
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
|
|
|
|
2017-09-23 02:04:14 +00:00
|
|
|
macro_rules! default_catchers {
|
2016-10-08 04:31:52 +00:00
|
|
|
($($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
|
|
|
|
|
|
|
$(
|
2017-05-19 10:29:08 +00:00
|
|
|
fn $fn_name<'r>(_: Error, req: &'r Request) -> response::Result<'r> {
|
2016-12-15 08:47:31 +00:00
|
|
|
status::Custom(Status::from_code($code).unwrap(),
|
2017-07-12 22:21:45 +00:00
|
|
|
content::Html(error_page_template!($code, $name, $description))
|
2017-05-19 10:29:08 +00:00
|
|
|
).respond_to(req)
|
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> {
|
2017-09-23 02:04:14 +00:00
|
|
|
default_catchers! {
|
2016-10-08 04:31:52 +00:00
|
|
|
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,
|
2017-09-09 07:34:43 +00:00
|
|
|
403, "Forbidden", "The server refused to authorize the request.", 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,
|
2017-10-03 05:00:08 +00:00
|
|
|
504, "Gateway Timeout", "The server did not receive a timely
|
|
|
|
response from an upstream server.", handle_504,
|
2016-10-08 04:31:52 +00:00
|
|
|
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
|
|
|
|