mirror of https://github.com/rwf2/Rocket.git
Improve forwarding status code precision.
Previously, the `NotFound` status code was used to signal many kinds of recoverable, forwarding errors. This included validation errors, incorrect Content-Type errors, and more. This commit modifies the status code used to forward in these instances to more precisely indicate the forwarding condition. In particular: * Parameter `FromParam` errors now forward as 422 (`UnprocessableEntity`). * Query paramater errors now forward as 422 (`UnprocessableEntity`). * Use of incorrect form content-type forwards as 413 (`UnsupportedMediaType`). * `WebSocket` guard now forwards as 400 (`BadRequest`). * `&Host`, `&Accept`, `&ContentType`, `IpAddr`, and `SocketAddr` all forward with a 500 (`InternalServerError`). Additionally, the `IntoOutcome` trait was overhauled to support functionality previously offered by methods on `Outcome`. The `Outcome::forward()` method now requires a status code to use for the forwarding outcome. Finally, logging of `Outcome`s now includes the relevant status code. Resolves #2626.
This commit is contained in:
parent
b6430e9970
commit
186ec84f5b
|
@ -210,7 +210,7 @@ impl<'r, K: 'static, C: Poolable> FromRequest<'r> for Connection<K, C> {
|
|||
#[inline]
|
||||
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, ()> {
|
||||
match request.rocket().state::<ConnectionPool<K, C>>() {
|
||||
Some(c) => c.get().await.into_outcome((Status::ServiceUnavailable, ())),
|
||||
Some(c) => c.get().await.or_error((Status::ServiceUnavailable, ())),
|
||||
None => {
|
||||
error_!("Missing database fairing for `{}`", std::any::type_name::<K>());
|
||||
Outcome::Error((Status::InternalServerError, ()))
|
||||
|
|
|
@ -30,7 +30,7 @@ use crate::result::{Result, Error};
|
|||
/// ### Forwarding
|
||||
///
|
||||
/// If the incoming request is not a valid WebSocket request, the guard
|
||||
/// forwards. The guard never fails.
|
||||
/// forwards with a status of `BadRequest`. The guard never fails.
|
||||
pub struct WebSocket {
|
||||
config: Config,
|
||||
key: String,
|
||||
|
@ -203,7 +203,7 @@ impl<'r> FromRequest<'r> for WebSocket {
|
|||
let key = headers.get_one("Sec-WebSocket-Key").map(|k| derive_accept_key(k.as_bytes()));
|
||||
match key {
|
||||
Some(key) if is_upgrade && is_ws && is_13 => Outcome::Success(WebSocket::new(key)),
|
||||
Some(_) | None => Outcome::Forward(Status::NotFound)
|
||||
Some(_) | None => Outcome::Forward(Status::BadRequest)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -105,7 +105,7 @@ fn query_decls(route: &Route) -> Option<TokenStream> {
|
|||
if !__e.is_empty() {
|
||||
#_log::warn_!("Query string failed to match route declaration.");
|
||||
for _err in __e { #_log::warn_!("{}", _err); }
|
||||
return #Outcome::Forward((#__data, #Status::NotFound));
|
||||
return #Outcome::Forward((#__data, #Status::UnprocessableEntity));
|
||||
}
|
||||
|
||||
(#(#ident.unwrap()),*)
|
||||
|
@ -146,7 +146,7 @@ fn param_guard_decl(guard: &Guard) -> TokenStream {
|
|||
#_log::warn_!("Parameter guard `{}: {}` is forwarding: {:?}.",
|
||||
#name, stringify!(#ty), __error);
|
||||
|
||||
#Outcome::Forward((#__data, #Status::NotFound))
|
||||
#Outcome::Forward((#__data, #Status::UnprocessableEntity))
|
||||
});
|
||||
|
||||
// All dynamic parameters should be found if this function is being called;
|
||||
|
|
|
@ -269,7 +269,7 @@ fn test_query_collection() {
|
|||
|
||||
let colors = &["red"];
|
||||
let dog = &["name=Fido"];
|
||||
assert_eq!(run(&client, colors, dog).0, Status::NotFound);
|
||||
assert_eq!(run(&client, colors, dog).0, Status::UnprocessableEntity);
|
||||
|
||||
let colors = &["red"];
|
||||
let dog = &["name=Fido", "age=2"];
|
||||
|
|
|
@ -112,7 +112,7 @@ impl<'r> Data<'r> {
|
|||
///
|
||||
/// async fn from_data(r: &'r Request<'_>, mut data: Data<'r>) -> Outcome<'r, Self> {
|
||||
/// if data.peek(2).await != b"hi" {
|
||||
/// return Outcome::Forward((data, Status::NotFound))
|
||||
/// return Outcome::Forward((data, Status::BadRequest))
|
||||
/// }
|
||||
///
|
||||
/// /* .. */
|
||||
|
|
|
@ -9,27 +9,6 @@ use crate::outcome::{self, IntoOutcome, try_outcome, Outcome::*};
|
|||
pub type Outcome<'r, T, E = <T as FromData<'r>>::Error>
|
||||
= outcome::Outcome<T, (Status, E), (Data<'r>, Status)>;
|
||||
|
||||
impl<'r, S, E> IntoOutcome<S, (Status, E), (Data<'r>, Status)> for Result<S, E> {
|
||||
type Error = Status;
|
||||
type Forward = (Data<'r>, Status);
|
||||
|
||||
#[inline]
|
||||
fn into_outcome(self, status: Status) -> Outcome<'r, S, E> {
|
||||
match self {
|
||||
Ok(val) => Success(val),
|
||||
Err(err) => Error((status, err))
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn or_forward(self, (data, status): (Data<'r>, Status)) -> Outcome<'r, S, E> {
|
||||
match self {
|
||||
Ok(val) => Success(val),
|
||||
Err(_) => Forward((data, status))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait implemented by data guards to derive a value from request body data.
|
||||
///
|
||||
/// # Data Guards
|
||||
|
@ -271,7 +250,7 @@ impl<'r, S, E> IntoOutcome<S, (Status, E), (Data<'r>, Status)> for Result<S, E>
|
|||
/// // Ensure the content type is correct before opening the data.
|
||||
/// let person_ct = ContentType::new("application", "x-person");
|
||||
/// if req.content_type() != Some(&person_ct) {
|
||||
/// return Outcome::Forward((data, Status::NotFound));
|
||||
/// return Outcome::Forward((data, Status::UnsupportedMediaType));
|
||||
/// }
|
||||
///
|
||||
/// // Use a configured limit with name 'person' or fallback to default.
|
||||
|
@ -343,7 +322,7 @@ impl<'r> FromData<'r> for Capped<String> {
|
|||
|
||||
async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self> {
|
||||
let limit = req.limits().get("string").unwrap_or(Limits::STRING);
|
||||
data.open(limit).into_string().await.into_outcome(Status::BadRequest)
|
||||
data.open(limit).into_string().await.or_error(Status::BadRequest)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -406,7 +385,7 @@ impl<'r> FromData<'r> for Capped<Vec<u8>> {
|
|||
|
||||
async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self> {
|
||||
let limit = req.limits().get("bytes").unwrap_or(Limits::BYTES);
|
||||
data.open(limit).into_bytes().await.into_outcome(Status::BadRequest)
|
||||
data.open(limit).into_bytes().await.or_error(Status::BadRequest)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ impl<'r, 'i> Parser<'r, 'i> {
|
|||
let parser = match req.content_type() {
|
||||
Some(c) if c.is_form() => Self::from_form(req, data).await,
|
||||
Some(c) if c.is_form_data() => Self::from_multipart(req, data).await,
|
||||
_ => return Outcome::Forward((data, Status::NotFound)),
|
||||
_ => return Outcome::Forward((data, Status::UnsupportedMediaType)),
|
||||
};
|
||||
|
||||
match parser {
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
use std::path::{PathBuf, Path};
|
||||
|
||||
use crate::{Request, Data};
|
||||
use crate::http::{Method, uri::Segments, ext::IntoOwned};
|
||||
use crate::http::{Method, Status, uri::Segments, ext::IntoOwned};
|
||||
use crate::route::{Route, Handler, Outcome};
|
||||
use crate::response::Redirect;
|
||||
use crate::response::{Redirect, Responder};
|
||||
use crate::outcome::IntoOutcome;
|
||||
use crate::fs::NamedFile;
|
||||
|
||||
/// Custom handler for serving static files.
|
||||
|
@ -204,10 +205,10 @@ impl Handler for FileServer {
|
|||
};
|
||||
|
||||
if segments.is_empty() {
|
||||
let file = NamedFile::open(&self.root).await.ok();
|
||||
return Outcome::from_or_forward(req, data, file);
|
||||
let file = NamedFile::open(&self.root).await;
|
||||
return file.respond_to(req).or_forward((data, Status::NotFound));
|
||||
} else {
|
||||
return Outcome::forward(data);
|
||||
return Outcome::forward(data, Status::NotFound);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -225,18 +226,23 @@ impl Handler for FileServer {
|
|||
.expect("adding a trailing slash to a known good path => valid path")
|
||||
.into_owned();
|
||||
|
||||
return Outcome::from_or_forward(req, data, Redirect::permanent(normal));
|
||||
return Redirect::permanent(normal)
|
||||
.respond_to(req)
|
||||
.or_forward((data, Status::InternalServerError));
|
||||
}
|
||||
|
||||
if !options.contains(Options::Index) {
|
||||
return Outcome::forward(data);
|
||||
return Outcome::forward(data, Status::NotFound);
|
||||
}
|
||||
|
||||
let index = NamedFile::open(p.join("index.html")).await.ok();
|
||||
Outcome::from_or_forward(req, data, index)
|
||||
let index = NamedFile::open(p.join("index.html")).await;
|
||||
index.respond_to(req).or_forward((data, Status::NotFound))
|
||||
},
|
||||
Some(p) => Outcome::from_or_forward(req, data, NamedFile::open(p).await.ok()),
|
||||
None => Outcome::forward(data),
|
||||
Some(p) => {
|
||||
let file = NamedFile::open(p).await;
|
||||
file.respond_to(req).or_forward((data, Status::NotFound))
|
||||
}
|
||||
None => Outcome::forward(data, Status::NotFound),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -543,7 +543,7 @@ impl<'r> FromData<'r> for Capped<TempFile<'_>> {
|
|||
}
|
||||
|
||||
TempFile::from(req, data, None, req.content_type().cloned()).await
|
||||
.into_outcome(Status::BadRequest)
|
||||
.or_error(Status::BadRequest)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,6 @@ impl<'r> FromRequest<'r> for Certificate<'r> {
|
|||
async fn from_request(req: &'r Request<'_>) -> Outcome<Self, Self::Error> {
|
||||
let certs = req.connection.client_certificates.as_ref().or_forward(Status::Unauthorized);
|
||||
let data = try_outcome!(try_outcome!(certs).chain_data().or_forward(Status::Unauthorized));
|
||||
Certificate::parse(data).into_outcome(Status::Unauthorized)
|
||||
Certificate::parse(data).or_error(Status::Unauthorized)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -90,6 +90,10 @@ use std::fmt;
|
|||
|
||||
use yansi::{Paint, Color};
|
||||
|
||||
use crate::{route, request, response};
|
||||
use crate::data::{self, Data, FromData};
|
||||
use crate::http::Status;
|
||||
|
||||
use self::Outcome::*;
|
||||
|
||||
/// An enum representing success (`Success`), error (`Error`), or forwarding
|
||||
|
@ -107,46 +111,6 @@ pub enum Outcome<S, E, F> {
|
|||
Forward(F),
|
||||
}
|
||||
|
||||
/// Conversion trait from some type into an Outcome type.
|
||||
pub trait IntoOutcome<S, E, F> {
|
||||
/// The type to use when returning an `Outcome::Error`.
|
||||
type Error: Sized;
|
||||
|
||||
/// The type to use when returning an `Outcome::Forward`.
|
||||
type Forward: Sized;
|
||||
|
||||
/// Converts `self` into an `Outcome`. If `self` represents a success, an
|
||||
/// `Outcome::Success` is returned. Otherwise, an `Outcome::Error` is
|
||||
/// returned with `error` as the inner value.
|
||||
fn into_outcome(self, error: Self::Error) -> Outcome<S, E, F>;
|
||||
|
||||
/// Converts `self` into an `Outcome`. If `self` represents a success, an
|
||||
/// `Outcome::Success` is returned. Otherwise, an `Outcome::Forward` is
|
||||
/// returned with `forward` as the inner value.
|
||||
fn or_forward(self, forward: Self::Forward) -> Outcome<S, E, F>;
|
||||
}
|
||||
|
||||
impl<S, E, F> IntoOutcome<S, E, F> for Option<S> {
|
||||
type Error = E;
|
||||
type Forward = F;
|
||||
|
||||
#[inline]
|
||||
fn into_outcome(self, error: E) -> Outcome<S, E, F> {
|
||||
match self {
|
||||
Some(val) => Success(val),
|
||||
None => Error(error)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn or_forward(self, forward: F) -> Outcome<S, E, F> {
|
||||
match self {
|
||||
Some(val) => Success(val),
|
||||
None => Forward(forward)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, E, F> Outcome<S, E, F> {
|
||||
/// Unwraps the Outcome, yielding the contents of a Success.
|
||||
///
|
||||
|
@ -651,15 +615,6 @@ impl<S, E, F> Outcome<S, E, F> {
|
|||
Outcome::Forward(v) => Err(v),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn formatting(&self) -> (Color, &'static str) {
|
||||
match *self {
|
||||
Success(..) => (Color::Green, "Success"),
|
||||
Error(..) => (Color::Red, "Error"),
|
||||
Forward(..) => (Color::Yellow, "Forward"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, S: Send + 'a, E: Send + 'a, F: Send + 'a> Outcome<S, E, F> {
|
||||
|
@ -755,15 +710,158 @@ crate::export! {
|
|||
}
|
||||
}
|
||||
|
||||
impl<S, E, F> Outcome<S, E, F> {
|
||||
#[inline]
|
||||
fn dbg_str(&self) -> &'static str {
|
||||
match self {
|
||||
Success(..) => "Success",
|
||||
Error(..) => "Error",
|
||||
Forward(..) => "Forward",
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn color(&self) -> Color {
|
||||
match self {
|
||||
Success(..) => Color::Green,
|
||||
Error(..) => Color::Red,
|
||||
Forward(..) => Color::Yellow,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct Display<'a, 'r>(&'a route::Outcome<'r>);
|
||||
|
||||
impl<'r> route::Outcome<'r> {
|
||||
pub(crate) fn log_display(&self) -> Display<'_, 'r> {
|
||||
impl fmt::Display for Display<'_, '_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", "Outcome: ".primary().bold())?;
|
||||
|
||||
let color = self.0.color();
|
||||
match self.0 {
|
||||
Success(r) => write!(f, "{}({})", "Success".paint(color), r.status().primary()),
|
||||
Error(s) => write!(f, "{}({})", "Error".paint(color), s.primary()),
|
||||
Forward((_, s)) => write!(f, "{}({})", "Forward".paint(color), s.primary()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Display(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, E, F> fmt::Debug for Outcome<S, E, F> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "Outcome::{}", self.formatting().1)
|
||||
write!(f, "Outcome::{}", self.dbg_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, E, F> fmt::Display for Outcome<S, E, F> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let (color, string) = self.formatting();
|
||||
write!(f, "{}", string.paint(color))
|
||||
write!(f, "{}", self.dbg_str().paint(self.color()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Conversion trait from some type into an Outcome type.
|
||||
pub trait IntoOutcome<Outcome> {
|
||||
/// The type to use when returning an `Outcome::Error`.
|
||||
type Error: Sized;
|
||||
|
||||
/// The type to use when returning an `Outcome::Forward`.
|
||||
type Forward: Sized;
|
||||
|
||||
/// Converts `self` into an `Outcome`. If `self` represents a success, an
|
||||
/// `Outcome::Success` is returned. Otherwise, an `Outcome::Error` is
|
||||
/// returned with `error` as the inner value.
|
||||
fn or_error(self, error: Self::Error) -> Outcome;
|
||||
|
||||
/// Converts `self` into an `Outcome`. If `self` represents a success, an
|
||||
/// `Outcome::Success` is returned. Otherwise, an `Outcome::Forward` is
|
||||
/// returned with `forward` as the inner value.
|
||||
fn or_forward(self, forward: Self::Forward) -> Outcome;
|
||||
}
|
||||
|
||||
impl<S, E, F> IntoOutcome<Outcome<S, E, F>> for Option<S> {
|
||||
type Error = E;
|
||||
type Forward = F;
|
||||
|
||||
#[inline]
|
||||
fn or_error(self, error: E) -> Outcome<S, E, F> {
|
||||
match self {
|
||||
Some(val) => Success(val),
|
||||
None => Error(error)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn or_forward(self, forward: F) -> Outcome<S, E, F> {
|
||||
match self {
|
||||
Some(val) => Success(val),
|
||||
None => Forward(forward)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r, T: FromData<'r>> IntoOutcome<data::Outcome<'r, T>> for Result<T, T::Error> {
|
||||
type Error = Status;
|
||||
type Forward = (Data<'r>, Status);
|
||||
|
||||
#[inline]
|
||||
fn or_error(self, error: Status) -> data::Outcome<'r, T> {
|
||||
match self {
|
||||
Ok(val) => Success(val),
|
||||
Err(err) => Error((error, err))
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn or_forward(self, (data, forward): (Data<'r>, Status)) -> data::Outcome<'r, T> {
|
||||
match self {
|
||||
Ok(val) => Success(val),
|
||||
Err(_) => Forward((data, forward))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, E> IntoOutcome<request::Outcome<S, E>> for Result<S, E> {
|
||||
type Error = Status;
|
||||
type Forward = Status;
|
||||
|
||||
#[inline]
|
||||
fn or_error(self, error: Status) -> request::Outcome<S, E> {
|
||||
match self {
|
||||
Ok(val) => Success(val),
|
||||
Err(err) => Error((error, err))
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn or_forward(self, status: Status) -> request::Outcome<S, E> {
|
||||
match self {
|
||||
Ok(val) => Success(val),
|
||||
Err(_) => Forward(status)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r, 'o: 'r> IntoOutcome<route::Outcome<'r>> for response::Result<'o> {
|
||||
type Error = ();
|
||||
type Forward = (Data<'r>, Status);
|
||||
|
||||
#[inline]
|
||||
fn or_error(self, _: ()) -> route::Outcome<'r> {
|
||||
match self {
|
||||
Ok(val) => Success(val),
|
||||
Err(status) => Error(status),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn or_forward(self, (data, forward): (Data<'r>, Status)) -> route::Outcome<'r> {
|
||||
match self {
|
||||
Ok(val) => Success(val),
|
||||
Err(_) => Forward((data, forward))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,36 +3,14 @@ use std::fmt::Debug;
|
|||
use std::net::{IpAddr, SocketAddr};
|
||||
|
||||
use crate::{Request, Route};
|
||||
use crate::outcome::{self, IntoOutcome};
|
||||
use crate::outcome::Outcome::*;
|
||||
use crate::outcome::{self, Outcome::*};
|
||||
|
||||
use crate::http::{Status, ContentType, Accept, Method, CookieJar};
|
||||
use crate::http::uri::{Host, Origin};
|
||||
use crate::http::{Status, ContentType, Accept, Method, CookieJar};
|
||||
|
||||
/// Type alias for the `Outcome` of a `FromRequest` conversion.
|
||||
pub type Outcome<S, E> = outcome::Outcome<S, (Status, E), Status>;
|
||||
|
||||
impl<S, E> IntoOutcome<S, (Status, E), Status> for Result<S, E> {
|
||||
type Error = Status;
|
||||
type Forward = Status;
|
||||
|
||||
#[inline]
|
||||
fn into_outcome(self, status: Status) -> Outcome<S, E> {
|
||||
match self {
|
||||
Ok(val) => Success(val),
|
||||
Err(err) => Error((status, err))
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn or_forward(self, status: Status) -> Outcome<S, E> {
|
||||
match self {
|
||||
Ok(val) => Success(val),
|
||||
Err(_) => Forward(status)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait implemented by request guards to derive a value from incoming
|
||||
/// requests.
|
||||
///
|
||||
|
@ -136,7 +114,8 @@ impl<S, E> IntoOutcome<S, (Status, E), Status> for Result<S, E> {
|
|||
/// * **&Host**
|
||||
///
|
||||
/// Extracts the [`Host`] from the incoming request, if it exists. See
|
||||
/// [`Request::host()`] for details.
|
||||
/// [`Request::host()`] for details. If it does not exist, the request is
|
||||
/// forwarded with a 500 Internal Server Error status.
|
||||
///
|
||||
/// * **&Route**
|
||||
///
|
||||
|
@ -162,23 +141,30 @@ impl<S, E> IntoOutcome<S, (Status, E), Status> for Result<S, E> {
|
|||
///
|
||||
/// _This implementation always returns successfully._
|
||||
///
|
||||
/// * **ContentType**
|
||||
/// * **&ContentType**
|
||||
///
|
||||
/// Extracts the [`ContentType`] from the incoming request via
|
||||
/// [`Request::content_type()`]. If the request didn't specify a
|
||||
/// Content-Type, the request is forwarded with a 404 Not Found status.
|
||||
/// Content-Type, the request is forwarded with a 500 Internal Server Error
|
||||
/// status.
|
||||
///
|
||||
/// * **IpAddr**
|
||||
/// * **&ContentType**
|
||||
///
|
||||
/// Extracts the [`Accept`] from the incoming request via
|
||||
/// [`Request::accept()`]. If the request didn't specify an `Accept`, the
|
||||
/// request is forwarded with a 500 Internal Server Error status.
|
||||
///
|
||||
/// * ***IpAddr**
|
||||
///
|
||||
/// Extracts the client ip address of the incoming request as an [`IpAddr`]
|
||||
/// via [`Request::client_ip()`]. If the client's IP address is not known,
|
||||
/// the request is forwarded with a 404 Not Found status.
|
||||
/// the request is forwarded with a 500 Internal Server Error status.
|
||||
///
|
||||
/// * **SocketAddr**
|
||||
///
|
||||
/// Extracts the remote address of the incoming request as a [`SocketAddr`]
|
||||
/// via [`Request::remote()`]. If the remote address is not known, the
|
||||
/// request is forwarded with a 404 Not Found status.
|
||||
/// request is forwarded with a 500 Internal Server Error status.
|
||||
///
|
||||
/// * **Option<T>** _where_ **T: FromRequest**
|
||||
///
|
||||
|
@ -422,7 +408,7 @@ impl<'r> FromRequest<'r> for &'r Host<'r> {
|
|||
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Infallible> {
|
||||
match request.host() {
|
||||
Some(host) => Success(host),
|
||||
None => Forward(Status::NotFound)
|
||||
None => Forward(Status::InternalServerError)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -455,7 +441,7 @@ impl<'r> FromRequest<'r> for &'r Accept {
|
|||
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Infallible> {
|
||||
match request.accept() {
|
||||
Some(accept) => Success(accept),
|
||||
None => Forward(Status::NotFound)
|
||||
None => Forward(Status::InternalServerError)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -467,7 +453,7 @@ impl<'r> FromRequest<'r> for &'r ContentType {
|
|||
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Infallible> {
|
||||
match request.content_type() {
|
||||
Some(content_type) => Success(content_type),
|
||||
None => Forward(Status::NotFound)
|
||||
None => Forward(Status::InternalServerError)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -479,7 +465,7 @@ impl<'r> FromRequest<'r> for IpAddr {
|
|||
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Infallible> {
|
||||
match request.client_ip() {
|
||||
Some(addr) => Success(addr),
|
||||
None => Forward(Status::NotFound)
|
||||
None => Forward(Status::InternalServerError)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -491,7 +477,7 @@ impl<'r> FromRequest<'r> for SocketAddr {
|
|||
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Infallible> {
|
||||
match request.remote() {
|
||||
Some(addr) => Success(addr),
|
||||
None => Forward(Status::NotFound)
|
||||
None => Forward(Status::InternalServerError)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -259,7 +259,7 @@ impl<'r> FromRequest<'r> for FlashMessage<'r> {
|
|||
Ok(i) if i <= kv.len() => Ok(Flash::named(&kv[..i], &kv[i..], req)),
|
||||
_ => Err(())
|
||||
}
|
||||
}).into_outcome(Status::BadRequest)
|
||||
}).or_error(Status::BadRequest)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -218,31 +218,6 @@ impl<'r, 'o: 'r> Outcome<'o> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Return the `Outcome` of response to `req` from `responder`.
|
||||
///
|
||||
/// If the responder returns `Ok`, an outcome of `Success` is returned with
|
||||
/// the response. If the responder returns `Err`, an outcome of `Forward`
|
||||
/// with a status of `404 Not Found` is returned.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use rocket::{Request, Data, route};
|
||||
///
|
||||
/// fn str_responder<'r>(req: &'r Request, data: Data<'r>) -> route::Outcome<'r> {
|
||||
/// route::Outcome::from_or_forward(req, data, "Hello, world!")
|
||||
/// }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn from_or_forward<R>(req: &'r Request<'_>, data: Data<'r>, responder: R) -> Outcome<'r>
|
||||
where R: Responder<'r, 'o>
|
||||
{
|
||||
match responder.respond_to(req) {
|
||||
Ok(response) => Outcome::Success(response),
|
||||
Err(_) => Outcome::Forward((data, Status::NotFound))
|
||||
}
|
||||
}
|
||||
|
||||
/// Return an `Outcome` of `Error` with the status code `code`. This is
|
||||
/// equivalent to `Outcome::Error(code)`.
|
||||
///
|
||||
|
@ -263,8 +238,8 @@ impl<'r, 'o: 'r> Outcome<'o> {
|
|||
Outcome::Error(code)
|
||||
}
|
||||
|
||||
/// Return an `Outcome` of `Forward` with the data `data`. This is
|
||||
/// equivalent to `Outcome::Forward((data, Status::NotFound))`.
|
||||
/// Return an `Outcome` of `Forward` with the data `data` and status
|
||||
/// `status`. This is equivalent to `Outcome::Forward((data, status))`.
|
||||
///
|
||||
/// This method exists to be used during manual routing.
|
||||
///
|
||||
|
@ -272,14 +247,15 @@ impl<'r, 'o: 'r> Outcome<'o> {
|
|||
///
|
||||
/// ```rust
|
||||
/// use rocket::{Request, Data, route};
|
||||
/// use rocket::http::Status;
|
||||
///
|
||||
/// fn always_forward<'r>(_: &'r Request, data: Data<'r>) -> route::Outcome<'r> {
|
||||
/// route::Outcome::forward(data)
|
||||
/// route::Outcome::forward(data, Status::InternalServerError)
|
||||
/// }
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn forward(data: Data<'r>) -> Outcome<'r> {
|
||||
Outcome::Forward((data, Status::NotFound))
|
||||
pub fn forward(data: Data<'r>, status: Status) -> Outcome<'r> {
|
||||
Outcome::Forward((data, status))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -334,7 +334,7 @@ impl Rocket<Orbit> {
|
|||
// Check if the request processing completed (Some) or if the
|
||||
// request needs to be forwarded. If it does, continue the loop
|
||||
// (None) to try again.
|
||||
info_!("{} {}", "Outcome:".primary().bold(), outcome);
|
||||
info_!("{}", outcome.log_display());
|
||||
match outcome {
|
||||
o@Outcome::Success(_) | o@Outcome::Error(_) => return o,
|
||||
Outcome::Forward(forwarded) => (data, status) = forwarded,
|
||||
|
|
|
@ -46,7 +46,15 @@ fn forced_error() {
|
|||
fn test_hello_invalid_age() {
|
||||
let client = Client::tracked(super::rocket()).unwrap();
|
||||
|
||||
for path in &["Ford/-129", "Trillian/128", "foo/bar/baz"] {
|
||||
for path in &["Ford/-129", "Trillian/128"] {
|
||||
let request = client.get(format!("/hello/{}", path));
|
||||
let expected = super::default_catcher(Status::UnprocessableEntity, request.inner());
|
||||
let response = request.dispatch();
|
||||
assert_eq!(response.status(), Status::UnprocessableEntity);
|
||||
assert_eq!(response.into_string().unwrap(), expected.1);
|
||||
}
|
||||
|
||||
for path in &["foo/bar/baz"] {
|
||||
let request = client.get(format!("/hello/{}", path));
|
||||
let expected = super::hello_not_found(request.inner());
|
||||
let response = request.dispatch();
|
||||
|
@ -59,7 +67,15 @@ fn test_hello_invalid_age() {
|
|||
fn test_hello_sergio() {
|
||||
let client = Client::tracked(super::rocket()).unwrap();
|
||||
|
||||
for path in &["oops", "-129", "foo/bar", "/foo/bar/baz"] {
|
||||
for path in &["oops", "-129"] {
|
||||
let request = client.get(format!("/hello/Sergio/{}", path));
|
||||
let expected = super::sergio_error();
|
||||
let response = request.dispatch();
|
||||
assert_eq!(response.status(), Status::UnprocessableEntity);
|
||||
assert_eq!(response.into_string().unwrap(), expected);
|
||||
}
|
||||
|
||||
for path in &["foo/bar", "/foo/bar/baz"] {
|
||||
let request = client.get(format!("/hello/Sergio/{}", path));
|
||||
let expected = super::sergio_error();
|
||||
let response = request.dispatch();
|
||||
|
|
|
@ -62,10 +62,10 @@ fn wave() {
|
|||
let response = client.get(uri).dispatch();
|
||||
assert_eq!(response.into_string().unwrap(), expected);
|
||||
|
||||
for bad_age in &["1000", "-1", "bird", "?"] {
|
||||
for bad_age in &["1000", "-1", "bird"] {
|
||||
let bad_uri = format!("/wave/{}/{}", name, bad_age);
|
||||
let response = client.get(bad_uri).dispatch();
|
||||
assert_eq!(response.status(), Status::NotFound);
|
||||
assert_eq!(response.status(), Status::UnprocessableEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ use rocket::outcome::{try_outcome, IntoOutcome};
|
|||
use rocket::tokio::fs::File;
|
||||
|
||||
fn forward<'r>(_req: &'r Request, data: Data<'r>) -> route::BoxFuture<'r> {
|
||||
Box::pin(async move { route::Outcome::forward(data) })
|
||||
Box::pin(async move { route::Outcome::forward(data, Status::NotFound) })
|
||||
}
|
||||
|
||||
fn hi<'r>(req: &'r Request, _: Data<'r>) -> route::BoxFuture<'r> {
|
||||
|
@ -27,7 +27,7 @@ fn name<'r>(req: &'r Request, _: Data<'r>) -> route::BoxFuture<'r> {
|
|||
fn echo_url<'r>(req: &'r Request, _: Data<'r>) -> route::BoxFuture<'r> {
|
||||
let param_outcome = req.param::<&str>(1)
|
||||
.and_then(Result::ok)
|
||||
.into_outcome(Status::BadRequest);
|
||||
.or_error(Status::BadRequest);
|
||||
|
||||
Box::pin(async move {
|
||||
route::Outcome::from(req, try_outcome!(param_outcome))
|
||||
|
|
|
@ -34,8 +34,7 @@ fn json_bad_get_put() {
|
|||
|
||||
// Try to get a message with an invalid ID.
|
||||
let res = client.get("/json/hi").header(ContentType::JSON).dispatch();
|
||||
assert_eq!(res.status(), Status::NotFound);
|
||||
assert!(res.into_string().unwrap().contains("error"));
|
||||
assert_eq!(res.status(), Status::UnprocessableEntity);
|
||||
|
||||
// Try to put a message without a proper body.
|
||||
let res = client.put("/json/80").header(ContentType::JSON).dispatch();
|
||||
|
@ -134,5 +133,5 @@ fn uuid() {
|
|||
}
|
||||
|
||||
let res = client.get("/people/not-a-uuid").dispatch();
|
||||
assert_eq!(res.status(), Status::NotFound);
|
||||
assert_eq!(res.status(), Status::UnprocessableEntity);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue