mirror of https://github.com/rwf2/Rocket.git
Initial brush
This commit is contained in:
parent
6857b82ec4
commit
56e7fa6e59
|
@ -34,15 +34,25 @@ pub fn _catch(
|
|||
.map(|ty| ty.span())
|
||||
.unwrap_or_else(Span::call_site);
|
||||
|
||||
// TODO: how to handle request?
|
||||
// - Right now: (), (&Req), (Status, &Req) allowed
|
||||
// - New: (), (&E), (&Req, &E), (Status, &Req, &E)
|
||||
// Set the `req` and `status` spans to that of their respective function
|
||||
// arguments for a more correct `wrong type` error span. `rev` to be cute.
|
||||
let codegen_args = &[__req, __status];
|
||||
let codegen_args = &[__req, __status, __error];
|
||||
let inputs = catch.function.sig.inputs.iter().rev()
|
||||
.zip(codegen_args.iter())
|
||||
.map(|(fn_arg, codegen_arg)| match fn_arg {
|
||||
syn::FnArg::Receiver(_) => codegen_arg.respanned(fn_arg.span()),
|
||||
syn::FnArg::Typed(a) => codegen_arg.respanned(a.ty.span())
|
||||
}).rev();
|
||||
let make_error = if let Some(arg) = catch.function.sig.inputs.iter().rev().next() {
|
||||
quote_spanned!(arg.span() =>
|
||||
// let
|
||||
)
|
||||
} else {
|
||||
quote! {}
|
||||
};
|
||||
|
||||
// We append `.await` to the function call if this is `async`.
|
||||
let dot_await = catch.function.sig.asyncness
|
||||
|
@ -68,9 +78,11 @@ pub fn _catch(
|
|||
fn into_info(self) -> #_catcher::StaticInfo {
|
||||
fn monomorphized_function<'__r>(
|
||||
#__status: #Status,
|
||||
#__req: &'__r #Request<'_>
|
||||
#__req: &'__r #Request<'_>,
|
||||
__error_init: &#ErasedErrorRef<'__r>,
|
||||
) -> #_catcher::BoxFuture<'__r> {
|
||||
#_Box::pin(async move {
|
||||
#make_error
|
||||
let __response = #catcher_response;
|
||||
#Response::build()
|
||||
.status(#__status)
|
||||
|
|
|
@ -125,7 +125,7 @@ fn query_decls(route: &Route) -> Option<TokenStream> {
|
|||
fn request_guard_decl(guard: &Guard) -> TokenStream {
|
||||
let (ident, ty) = (guard.fn_ident.rocketized(), &guard.ty);
|
||||
define_spanned_export!(ty.span() =>
|
||||
__req, __data, _request, display_hack, FromRequest, Outcome
|
||||
__req, __data, _request, display_hack, FromRequest, Outcome, ErrorResolver, ErrorDefault
|
||||
);
|
||||
|
||||
quote_spanned! { ty.span() =>
|
||||
|
@ -150,11 +150,13 @@ fn request_guard_decl(guard: &Guard) -> TokenStream {
|
|||
target: concat!("rocket::codegen::route::", module_path!()),
|
||||
parameter = stringify!(#ident),
|
||||
type_name = stringify!(#ty),
|
||||
reason = %#display_hack!(__e),
|
||||
reason = %#display_hack!(&__e),
|
||||
"request guard failed"
|
||||
);
|
||||
|
||||
return #Outcome::Error(__c);
|
||||
#[allow(unused)]
|
||||
use #ErrorDefault;
|
||||
return #Outcome::Error((__c, #ErrorResolver::new(__e).cast()));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -219,7 +221,7 @@ fn param_guard_decl(guard: &Guard) -> TokenStream {
|
|||
|
||||
fn data_guard_decl(guard: &Guard) -> TokenStream {
|
||||
let (ident, ty) = (guard.fn_ident.rocketized(), &guard.ty);
|
||||
define_spanned_export!(ty.span() => __req, __data, display_hack, FromData, Outcome);
|
||||
define_spanned_export!(ty.span() => __req, __data, display_hack, FromData, Outcome, ErrorResolver, ErrorDefault);
|
||||
|
||||
quote_spanned! { ty.span() =>
|
||||
let #ident: #ty = match <#ty as #FromData>::from_data(#__req, #__data).await {
|
||||
|
@ -243,11 +245,13 @@ fn data_guard_decl(guard: &Guard) -> TokenStream {
|
|||
target: concat!("rocket::codegen::route::", module_path!()),
|
||||
parameter = stringify!(#ident),
|
||||
type_name = stringify!(#ty),
|
||||
reason = %#display_hack!(__e),
|
||||
reason = %#display_hack!(&__e),
|
||||
"data guard failed"
|
||||
);
|
||||
|
||||
return #Outcome::Error(__c);
|
||||
#[allow(unused)]
|
||||
use #ErrorDefault;
|
||||
return #Outcome::Error((__c, #ErrorResolver::new(__e).cast()));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -102,6 +102,9 @@ define_exported_paths! {
|
|||
Route => ::rocket::Route,
|
||||
Catcher => ::rocket::Catcher,
|
||||
Status => ::rocket::http::Status,
|
||||
ErrorResolver => ::rocket::catcher::resolution::Resolve,
|
||||
ErrorDefault => ::rocket::catcher::resolution::DefaultTypeErase,
|
||||
ErasedErrorRef => ::rocket::catcher::ErasedErrorRef,
|
||||
}
|
||||
|
||||
macro_rules! define_spanned_export {
|
||||
|
|
|
@ -74,6 +74,7 @@ tokio-stream = { version = "0.1.6", features = ["signal", "time"] }
|
|||
cookie = { version = "0.18", features = ["percent-encode"] }
|
||||
futures = { version = "0.3.30", default-features = false, features = ["std"] }
|
||||
state = "0.6"
|
||||
transient = { version = "0.2.0", path = "../../../transient" }
|
||||
|
||||
# tracing
|
||||
tracing = { version = "0.1.40", default-features = false, features = ["std", "attributes"] }
|
||||
|
|
|
@ -8,6 +8,8 @@ use crate::request::Request;
|
|||
use crate::http::{Status, ContentType, uri};
|
||||
use crate::catcher::{Handler, BoxFuture};
|
||||
|
||||
use super::ErasedErrorRef;
|
||||
|
||||
/// An error catching route.
|
||||
///
|
||||
/// Catchers are routes that run when errors are produced by the application.
|
||||
|
@ -147,20 +149,20 @@ impl Catcher {
|
|||
///
|
||||
/// ```rust
|
||||
/// use rocket::request::Request;
|
||||
/// use rocket::catcher::{Catcher, BoxFuture};
|
||||
/// use rocket::catcher::{Catcher, BoxFuture, ErasedErrorRef};
|
||||
/// use rocket::response::Responder;
|
||||
/// use rocket::http::Status;
|
||||
///
|
||||
/// fn handle_404<'r>(status: Status, req: &'r Request<'_>) -> BoxFuture<'r> {
|
||||
/// fn handle_404<'r>(status: Status, req: &'r Request<'_>, _e: &ErasedErrorRef<'r>) -> BoxFuture<'r> {
|
||||
/// let res = (status, format!("404: {}", req.uri()));
|
||||
/// Box::pin(async move { res.respond_to(req) })
|
||||
/// }
|
||||
///
|
||||
/// fn handle_500<'r>(_: Status, req: &'r Request<'_>) -> BoxFuture<'r> {
|
||||
/// fn handle_500<'r>(_: Status, req: &'r Request<'_>, _e: &ErasedErrorRef<'r>) -> BoxFuture<'r> {
|
||||
/// Box::pin(async move{ "Whoops, we messed up!".respond_to(req) })
|
||||
/// }
|
||||
///
|
||||
/// fn handle_default<'r>(status: Status, req: &'r Request<'_>) -> BoxFuture<'r> {
|
||||
/// fn handle_default<'r>(status: Status, req: &'r Request<'_>, _e: &ErasedErrorRef<'r>) -> BoxFuture<'r> {
|
||||
/// let res = (status, format!("{}: {}", status, req.uri()));
|
||||
/// Box::pin(async move { res.respond_to(req) })
|
||||
/// }
|
||||
|
@ -199,11 +201,11 @@ impl Catcher {
|
|||
///
|
||||
/// ```rust
|
||||
/// use rocket::request::Request;
|
||||
/// use rocket::catcher::{Catcher, BoxFuture};
|
||||
/// use rocket::catcher::{Catcher, BoxFuture, ErasedErrorRef};
|
||||
/// use rocket::response::Responder;
|
||||
/// use rocket::http::Status;
|
||||
///
|
||||
/// fn handle_404<'r>(status: Status, req: &'r Request<'_>) -> BoxFuture<'r> {
|
||||
/// fn handle_404<'r>(status: Status, req: &'r Request<'_>, _e: &ErasedErrorRef<'r>) -> BoxFuture<'r> {
|
||||
/// let res = (status, format!("404: {}", req.uri()));
|
||||
/// Box::pin(async move { res.respond_to(req) })
|
||||
/// }
|
||||
|
@ -225,12 +227,12 @@ impl Catcher {
|
|||
///
|
||||
/// ```rust
|
||||
/// use rocket::request::Request;
|
||||
/// use rocket::catcher::{Catcher, BoxFuture};
|
||||
/// use rocket::catcher::{Catcher, BoxFuture, ErasedErrorRef};
|
||||
/// use rocket::response::Responder;
|
||||
/// use rocket::http::Status;
|
||||
/// # use rocket::uri;
|
||||
///
|
||||
/// fn handle_404<'r>(status: Status, req: &'r Request<'_>) -> BoxFuture<'r> {
|
||||
/// fn handle_404<'r>(status: Status, req: &'r Request<'_>, _e: &ErasedErrorRef<'r>) -> BoxFuture<'r> {
|
||||
/// let res = (status, format!("404: {}", req.uri()));
|
||||
/// Box::pin(async move { res.respond_to(req) })
|
||||
/// }
|
||||
|
@ -279,11 +281,11 @@ impl Catcher {
|
|||
///
|
||||
/// ```rust
|
||||
/// use rocket::request::Request;
|
||||
/// use rocket::catcher::{Catcher, BoxFuture};
|
||||
/// use rocket::catcher::{Catcher, BoxFuture, ErasedErrorRef};
|
||||
/// use rocket::response::Responder;
|
||||
/// use rocket::http::Status;
|
||||
///
|
||||
/// fn handle_404<'r>(status: Status, req: &'r Request<'_>) -> BoxFuture<'r> {
|
||||
/// fn handle_404<'r>(status: Status, req: &'r Request<'_>, _e: &ErasedErrorRef<'r>) -> BoxFuture<'r> {
|
||||
/// let res = (status, format!("404: {}", req.uri()));
|
||||
/// Box::pin(async move { res.respond_to(req) })
|
||||
/// }
|
||||
|
@ -313,7 +315,7 @@ impl Catcher {
|
|||
|
||||
impl Default for Catcher {
|
||||
fn default() -> Self {
|
||||
fn handler<'r>(s: Status, req: &'r Request<'_>) -> BoxFuture<'r> {
|
||||
fn handler<'r>(s: Status, req: &'r Request<'_>, _e: &ErasedErrorRef<'r>) -> BoxFuture<'r> {
|
||||
Box::pin(async move { Ok(default_handler(s, req)) })
|
||||
}
|
||||
|
||||
|
@ -331,7 +333,7 @@ pub struct StaticInfo {
|
|||
/// The catcher's status code.
|
||||
pub code: Option<u16>,
|
||||
/// The catcher's handler, i.e, the annotated function.
|
||||
pub handler: for<'r> fn(Status, &'r Request<'_>) -> BoxFuture<'r>,
|
||||
pub handler: for<'r> fn(Status, &'r Request<'_>, &ErasedErrorRef<'r>) -> BoxFuture<'r>,
|
||||
/// The file, line, and column where the catcher was defined.
|
||||
pub location: (&'static str, u32, u32),
|
||||
}
|
||||
|
@ -418,7 +420,7 @@ macro_rules! default_handler_fn {
|
|||
|
||||
pub(crate) fn default_handler<'r>(
|
||||
status: Status,
|
||||
req: &'r Request<'_>
|
||||
req: &'r Request<'_>,
|
||||
) -> Response<'r> {
|
||||
let preferred = req.accept().map(|a| a.preferred());
|
||||
let (mime, text) = if preferred.map_or(false, |a| a.is_json()) {
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
use crate::{Request, Response};
|
||||
use crate::http::Status;
|
||||
|
||||
use super::ErasedErrorRef;
|
||||
|
||||
/// Type alias for the return type of a [`Catcher`](crate::Catcher)'s
|
||||
/// [`Handler::handle()`].
|
||||
pub type Result<'r> = std::result::Result<Response<'r>, crate::http::Status>;
|
||||
|
@ -29,7 +31,7 @@ pub type BoxFuture<'r, T = Result<'r>> = futures::future::BoxFuture<'r, T>;
|
|||
/// and used as follows:
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// use rocket::{Request, Catcher, catcher};
|
||||
/// use rocket::{Request, Catcher, catcher::{self, ErasedErrorRef}};
|
||||
/// use rocket::response::{Response, Responder};
|
||||
/// use rocket::http::Status;
|
||||
///
|
||||
|
@ -45,7 +47,7 @@ pub type BoxFuture<'r, T = Result<'r>> = futures::future::BoxFuture<'r, T>;
|
|||
///
|
||||
/// #[rocket::async_trait]
|
||||
/// impl catcher::Handler for CustomHandler {
|
||||
/// async fn handle<'r>(&self, status: Status, req: &'r Request<'_>) -> catcher::Result<'r> {
|
||||
/// async fn handle<'r>(&self, status: Status, req: &'r Request<'_>, _e: &ErasedErrorRef<'r>) -> catcher::Result<'r> {
|
||||
/// let inner = match self.0 {
|
||||
/// Kind::Simple => "simple".respond_to(req)?,
|
||||
/// Kind::Intermediate => "intermediate".respond_to(req)?,
|
||||
|
@ -97,30 +99,32 @@ pub trait Handler: Cloneable + Send + Sync + 'static {
|
|||
/// Nevertheless, failure is allowed, both for convenience and necessity. If
|
||||
/// an error handler fails, Rocket's default `500` catcher is invoked. If it
|
||||
/// succeeds, the returned `Response` is used to respond to the client.
|
||||
async fn handle<'r>(&self, status: Status, req: &'r Request<'_>) -> Result<'r>;
|
||||
async fn handle<'r>(&self, status: Status, req: &'r Request<'_>, error: &ErasedErrorRef<'r>) -> Result<'r>;
|
||||
}
|
||||
|
||||
// We write this manually to avoid double-boxing.
|
||||
impl<F: Clone + Sync + Send + 'static> Handler for F
|
||||
where for<'x> F: Fn(Status, &'x Request<'_>) -> BoxFuture<'x>,
|
||||
where for<'x> F: Fn(Status, &'x Request<'_>, &ErasedErrorRef<'x>) -> BoxFuture<'x>,
|
||||
{
|
||||
fn handle<'r, 'life0, 'life1, 'async_trait>(
|
||||
fn handle<'r, 'life0, 'life1, 'life2, 'async_trait>(
|
||||
&'life0 self,
|
||||
status: Status,
|
||||
req: &'r Request<'life1>,
|
||||
error: &'life2 ErasedErrorRef<'r>,
|
||||
) -> BoxFuture<'r>
|
||||
where 'r: 'async_trait,
|
||||
'life0: 'async_trait,
|
||||
'life1: 'async_trait,
|
||||
'life2: 'async_trait,
|
||||
Self: 'async_trait,
|
||||
{
|
||||
self(status, req)
|
||||
self(status, req, error)
|
||||
}
|
||||
}
|
||||
|
||||
// Used in tests! Do not use, please.
|
||||
#[doc(hidden)]
|
||||
pub fn dummy_handler<'r>(_: Status, _: &'r Request<'_>) -> BoxFuture<'r> {
|
||||
pub fn dummy_handler<'r>(_: Status, _: &'r Request<'_>, _: &ErasedErrorRef<'r>) -> BoxFuture<'r> {
|
||||
Box::pin(async move { Ok(Response::new()) })
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
mod catcher;
|
||||
mod handler;
|
||||
mod types;
|
||||
|
||||
pub use catcher::*;
|
||||
pub use handler::*;
|
||||
pub use types::*;
|
||||
|
|
|
@ -0,0 +1,110 @@
|
|||
use transient::{Any, CanRecoverFrom, Co, Transient, Downcast};
|
||||
|
||||
pub type ErasedError<'r> = Box<dyn Any<Co<'r>> + Send + Sync + 'r>;
|
||||
pub type ErasedErrorRef<'r> = dyn Any<Co<'r>> + Send + Sync + 'r;
|
||||
|
||||
pub fn default_error_type<'r>() -> ErasedError<'r> {
|
||||
Box::new(())
|
||||
}
|
||||
|
||||
pub fn downcast<'a, 'r, T: Transient + 'r>(v: &'a ErasedErrorRef<'r>) -> Option<&'a T>
|
||||
where T::Transience: CanRecoverFrom<Co<'r>>
|
||||
{
|
||||
v.downcast_ref()
|
||||
}
|
||||
|
||||
// /// Chosen not to expose this macro, since it's pretty short and sweet
|
||||
// #[doc(hidden)]
|
||||
// #[macro_export]
|
||||
// macro_rules! resolve_typed_catcher {
|
||||
// ($T:expr) => ({
|
||||
// #[allow(unused_imports)]
|
||||
// use $crate::catcher::types::Resolve;
|
||||
//
|
||||
// Resolve::new($T).cast()
|
||||
// })
|
||||
// }
|
||||
|
||||
// pub use resolve_typed_catcher;
|
||||
|
||||
pub mod resolution {
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use transient::{CanTranscendTo, Transient};
|
||||
|
||||
use super::*;
|
||||
|
||||
/// The *magic*.
|
||||
///
|
||||
/// `Resolve<T>::item` for `T: Transient` is `<T as Transient>::item`.
|
||||
/// `Resolve<T>::item` for `T: !Transient` is `DefaultTypeErase::item`.
|
||||
///
|
||||
/// This _must_ be used as `Resolve::<T>:item` for resolution to work. This
|
||||
/// is a fun, static dispatch hack for "specialization" that works because
|
||||
/// Rust prefers inherent methods over blanket trait impl methods.
|
||||
pub struct Resolve<'r, T: 'r>(T, PhantomData<&'r ()>);
|
||||
|
||||
impl<'r, T: 'r> Resolve<'r, T> {
|
||||
pub fn new(val: T) -> Self {
|
||||
Self(val, PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
/// Fallback trait "implementing" `Transient` for all types. This is what
|
||||
/// Rust will resolve `Resolve<T>::item` to when `T: !Transient`.
|
||||
pub trait DefaultTypeErase<'r>: Sized {
|
||||
const SPECIALIZED: bool = false;
|
||||
|
||||
fn cast(self) -> ErasedError<'r> { Box::new(()) }
|
||||
}
|
||||
|
||||
impl<'r, T: 'r> DefaultTypeErase<'r> for Resolve<'r, T> {}
|
||||
|
||||
/// "Specialized" "implementation" of `Transient` for `T: Transient`. This is
|
||||
/// what Rust will resolve `Resolve<T>::item` to when `T: Transient`.
|
||||
impl<'r, T: Transient + Send + Sync + 'r> Resolve<'r, T>
|
||||
where T::Transience: CanTranscendTo<Co<'r>>
|
||||
{
|
||||
pub const SPECIALIZED: bool = true;
|
||||
|
||||
pub fn cast(self) -> ErasedError<'r> { Box::new(self.0) }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
// use std::any::TypeId;
|
||||
|
||||
use transient::{Transient, TypeId};
|
||||
|
||||
use super::resolution::{Resolve, DefaultTypeErase};
|
||||
|
||||
struct NotAny;
|
||||
#[derive(Transient)]
|
||||
struct YesAny;
|
||||
|
||||
#[test]
|
||||
fn check_can_determine() {
|
||||
let not_any = Resolve::new(NotAny).cast();
|
||||
assert_eq!(not_any.type_id(), TypeId::of::<()>());
|
||||
|
||||
let yes_any = Resolve::new(YesAny).cast();
|
||||
assert_ne!(yes_any.type_id(), TypeId::of::<()>());
|
||||
}
|
||||
|
||||
// struct HasSentinel<T>(T);
|
||||
|
||||
// #[test]
|
||||
// fn parent_works() {
|
||||
// let child = resolve!(YesASentinel, HasSentinel<YesASentinel>);
|
||||
// assert!(child.type_name.ends_with("YesASentinel"));
|
||||
// assert_eq!(child.parent.unwrap(), TypeId::of::<HasSentinel<YesASentinel>>());
|
||||
// assert!(child.specialized);
|
||||
|
||||
// let not_a_direct_sentinel = resolve!(HasSentinel<YesASentinel>);
|
||||
// assert!(not_a_direct_sentinel.type_name.contains("HasSentinel"));
|
||||
// assert!(not_a_direct_sentinel.type_name.contains("YesASentinel"));
|
||||
// assert!(not_a_direct_sentinel.parent.is_none());
|
||||
// assert!(!not_a_direct_sentinel.specialized);
|
||||
// }
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
use futures::future::{FutureExt, Future};
|
||||
|
||||
use crate::catcher::{default_error_type, ErasedError, ErasedErrorRef};
|
||||
use crate::trace::Trace;
|
||||
use crate::util::Formatter;
|
||||
use crate::data::IoHandler;
|
||||
|
@ -108,12 +109,12 @@ impl Rocket<Orbit> {
|
|||
request._set_method(Method::Get);
|
||||
match self.route(request, data).await {
|
||||
Outcome::Success(response) => response,
|
||||
Outcome::Error(status) => self.dispatch_error(status, request).await,
|
||||
Outcome::Forward((_, status)) => self.dispatch_error(status, request).await,
|
||||
Outcome::Error((status, error)) => self.dispatch_error(status, request, error).await,
|
||||
Outcome::Forward((_, status)) => self.dispatch_error(status, request, default_error_type()).await,
|
||||
}
|
||||
}
|
||||
Outcome::Forward((_, status)) => self.dispatch_error(status, request).await,
|
||||
Outcome::Error(status) => self.dispatch_error(status, request).await,
|
||||
Outcome::Forward((_, status)) => self.dispatch_error(status, request, default_error_type()).await,
|
||||
Outcome::Error((status, error)) => self.dispatch_error(status, request, error).await,
|
||||
};
|
||||
|
||||
// Set the cookies. Note that error responses will only include cookies
|
||||
|
@ -204,7 +205,7 @@ impl Rocket<Orbit> {
|
|||
|
||||
let name = route.name.as_deref();
|
||||
let outcome = catch_handle(name, || route.handler.handle(request, data)).await
|
||||
.unwrap_or(Outcome::Error(Status::InternalServerError));
|
||||
.unwrap_or(Outcome::error(Status::InternalServerError));
|
||||
|
||||
// Check if the request processing completed (Some) or if the
|
||||
// request needs to be forwarded. If it does, continue the loop
|
||||
|
@ -229,14 +230,15 @@ impl Rocket<Orbit> {
|
|||
pub(crate) async fn dispatch_error<'r, 's: 'r>(
|
||||
&'s self,
|
||||
mut status: Status,
|
||||
req: &'r Request<'s>
|
||||
req: &'r Request<'s>,
|
||||
error: ErasedError<'r>,
|
||||
) -> Response<'r> {
|
||||
// We may wish to relax this in the future.
|
||||
req.cookies().reset_delta();
|
||||
|
||||
loop {
|
||||
// Dispatch to the `status` catcher.
|
||||
match self.invoke_catcher(status, req).await {
|
||||
match self.invoke_catcher(status, error.as_ref(), req).await {
|
||||
Ok(r) => return r,
|
||||
// If the catcher failed, try `500` catcher, unless this is it.
|
||||
Err(e) if status.code != 500 => {
|
||||
|
@ -265,11 +267,12 @@ impl Rocket<Orbit> {
|
|||
async fn invoke_catcher<'s, 'r: 's>(
|
||||
&'s self,
|
||||
status: Status,
|
||||
error: &ErasedErrorRef<'r>,
|
||||
req: &'r Request<'s>
|
||||
) -> Result<Response<'r>, Option<Status>> {
|
||||
if let Some(catcher) = self.router.catch(status, req) {
|
||||
catcher.trace_info();
|
||||
catch_handle(catcher.name.as_deref(), || catcher.handler.handle(status, req)).await
|
||||
catch_handle(catcher.name.as_deref(), || catcher.handler.handle(status, req, error)).await
|
||||
.map(|result| result.map_err(Some))
|
||||
.unwrap_or_else(|| Err(None))
|
||||
} else {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use std::fmt;
|
||||
|
||||
use crate::catcher::default_error_type;
|
||||
use crate::{Request, Data};
|
||||
use crate::http::{Status, Method};
|
||||
use crate::http::uri::Origin;
|
||||
|
@ -86,7 +87,7 @@ impl<'c> LocalRequest<'c> {
|
|||
if self.inner().uri() == invalid {
|
||||
error!("invalid request URI: {:?}", invalid.path());
|
||||
return LocalResponse::new(self.request, move |req| {
|
||||
rocket.dispatch_error(Status::BadRequest, req)
|
||||
rocket.dispatch_error(Status::BadRequest, req, default_error_type())
|
||||
}).await
|
||||
}
|
||||
}
|
||||
|
|
|
@ -86,6 +86,7 @@
|
|||
//! a type of `Option<S>`. If an `Outcome` is a `Forward`, the `Option` will be
|
||||
//! `None`.
|
||||
|
||||
use crate::catcher::default_error_type;
|
||||
use crate::{route, request, response};
|
||||
use crate::data::{self, Data, FromData};
|
||||
use crate::http::Status;
|
||||
|
@ -796,7 +797,7 @@ impl<'r, 'o: 'r> IntoOutcome<route::Outcome<'r>> for response::Result<'o> {
|
|||
fn or_error(self, _: ()) -> route::Outcome<'r> {
|
||||
match self {
|
||||
Ok(val) => Success(val),
|
||||
Err(status) => Error(status),
|
||||
Err(status) => Error((status, default_error_type())),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
use crate::catcher::ErasedError;
|
||||
use crate::{Request, Data};
|
||||
use crate::response::{Response, Responder};
|
||||
use crate::http::Status;
|
||||
|
||||
/// Type alias for the return type of a [`Route`](crate::Route)'s
|
||||
/// [`Handler::handle()`].
|
||||
pub type Outcome<'r> = crate::outcome::Outcome<Response<'r>, Status, (Data<'r>, Status)>;
|
||||
pub type Outcome<'r> = crate::outcome::Outcome<Response<'r>, (Status, ErasedError<'r>), (Data<'r>, Status)>;
|
||||
|
||||
/// Type alias for the return type of a _raw_ [`Route`](crate::Route)'s
|
||||
/// [`Handler`].
|
||||
|
@ -187,7 +188,7 @@ impl<'r, 'o: 'r> Outcome<'o> {
|
|||
pub fn from<R: Responder<'r, 'o>>(req: &'r Request<'_>, responder: R) -> Outcome<'r> {
|
||||
match responder.respond_to(req) {
|
||||
Ok(response) => Outcome::Success(response),
|
||||
Err(status) => Outcome::Error(status)
|
||||
Err(status) => Outcome::Error((status, Box::new(()))),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -213,12 +214,12 @@ impl<'r, 'o: 'r> Outcome<'o> {
|
|||
let responder = result.map_err(crate::response::Debug);
|
||||
match responder.respond_to(req) {
|
||||
Ok(response) => Outcome::Success(response),
|
||||
Err(status) => Outcome::Error(status)
|
||||
Err(status) => Outcome::Error((status, Box::new(()))),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return an `Outcome` of `Error` with the status code `code`. This is
|
||||
/// equivalent to `Outcome::Error(code)`.
|
||||
/// equivalent to `Outcome::error_val(code, ())`.
|
||||
///
|
||||
/// This method exists to be used during manual routing.
|
||||
///
|
||||
|
@ -234,7 +235,27 @@ impl<'r, 'o: 'r> Outcome<'o> {
|
|||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn error(code: Status) -> Outcome<'r> {
|
||||
Outcome::Error(code)
|
||||
Outcome::Error((code, Box::new(())))
|
||||
}
|
||||
/// Return an `Outcome` of `Error` with the status code `code`. This adds
|
||||
/// the
|
||||
///
|
||||
/// This method exists to be used during manual routing.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use rocket::{Request, Data, route};
|
||||
/// use rocket::http::Status;
|
||||
///
|
||||
/// fn bad_req_route<'r>(_: &'r Request, _: Data<'r>) -> route::Outcome<'r> {
|
||||
/// route::Outcome::error_val(Status::BadRequest, "Some data to go with")
|
||||
/// }
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn error_val<T: 'r>(code: Status, val: T) -> Outcome<'r> {
|
||||
use crate::catcher::resolution::{Resolve, DefaultTypeErase};
|
||||
Outcome::Error((code, Resolve::new(val).cast()))
|
||||
}
|
||||
|
||||
/// Return an `Outcome` of `Forward` with the data `data` and status
|
||||
|
|
|
@ -9,6 +9,7 @@ use hyper_util::server::conn::auto::Builder;
|
|||
use futures::{Future, TryFutureExt};
|
||||
use tokio::io::{AsyncRead, AsyncWrite};
|
||||
|
||||
use crate::catcher::default_error_type;
|
||||
use crate::{Ignite, Orbit, Request, Rocket};
|
||||
use crate::request::ConnectionMeta;
|
||||
use crate::erased::{ErasedRequest, ErasedResponse, ErasedIoHandler};
|
||||
|
@ -45,7 +46,7 @@ impl Rocket<Orbit> {
|
|||
|rocket, request, data| Box::pin(rocket.preprocess(request, data)),
|
||||
|token, rocket, request, data| Box::pin(async move {
|
||||
if !request.errors.is_empty() {
|
||||
return rocket.dispatch_error(Status::BadRequest, request).await;
|
||||
return rocket.dispatch_error(Status::BadRequest, request, default_error_type()).await;
|
||||
}
|
||||
|
||||
rocket.dispatch(token, request, data).await
|
||||
|
|
|
@ -244,7 +244,7 @@ impl Trace for route::Outcome<'_> {
|
|||
},
|
||||
status = match self {
|
||||
Self::Success(r) => r.status().code,
|
||||
Self::Error(s) => s.code,
|
||||
Self::Error((s, _)) => s.code,
|
||||
Self::Forward((_, s)) => s.code,
|
||||
},
|
||||
)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#[macro_use] extern crate rocket;
|
||||
|
||||
use rocket::catcher::ErasedErrorRef;
|
||||
use rocket::{Request, Rocket, Route, Catcher, Build, route, catcher};
|
||||
use rocket::data::Data;
|
||||
use rocket::http::{Method, Status};
|
||||
|
@ -73,7 +74,7 @@ fn catches_early_route_panic() {
|
|||
|
||||
#[test]
|
||||
fn catches_early_catcher_panic() {
|
||||
fn pre_future_catcher<'r>(_: Status, _: &'r Request<'_>) -> catcher::BoxFuture<'r> {
|
||||
fn pre_future_catcher<'r>(_: Status, _: &'r Request<'_>, _: &ErasedErrorRef<'r>) -> catcher::BoxFuture<'r> {
|
||||
panic!("a panicking pre-future catcher")
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue