Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor Rejection system #311

Merged
merged 1 commit into from Nov 12, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
80 changes: 34 additions & 46 deletions examples/errors.rs
@@ -1,44 +1,36 @@
#![deny(warnings)]

use std::error::Error as StdError;
use std::fmt::{self, Display};

use serde_derive::Serialize;
use warp::http::StatusCode;
use warp::{Future, Filter, Rejection, Reply};
use warp::{reject, Filter, Rejection, Reply};

#[derive(Copy, Clone, Debug)]
/// A custom `Reject` type.
#[derive(Debug)]
enum Error {
Oops,
Nope,
}

impl reject::Reject for Error {}

/// A serialized message to report in JSON format.
#[derive(Serialize)]
struct ErrorMessage {
struct ErrorMessage<'a> {
code: u16,
message: String,
}

impl Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(match self {
Error::Oops => ":fire: this is fine",
Error::Nope => "Nope!",
})
}
message: &'a str,
}

impl StdError for Error {}

#[tokio::main]
async fn main() {
let hello = warp::path::end().map(warp::reply);

let oops =
warp::path("oops").and_then(|| futures::future::err::<StatusCode, _>(warp::reject::custom(Error::Oops)));
let oops = warp::path("oops").and_then(|| async {
Err::<StatusCode, _>(reject::custom(Error::Oops))
});

let nope =
warp::path("nope").and_then(|| futures::future::err::<StatusCode, _>(warp::reject::custom(Error::Nope)));
let nope = warp::path("nope").and_then(|| async {
Err::<StatusCode, _>(reject::custom(Error::Nope))
});

let routes = warp::get()
.and(hello.or(oops).or(nope))
Expand All @@ -49,34 +41,30 @@ async fn main() {

// This function receives a `Rejection` and tries to return a custom
// value, othewise simply passes the rejection along.
fn customize_error(err: Rejection) -> impl Future< Output = Result<impl Reply, Rejection>> {
let err = {
if let Some(&err) = err.find_cause::<Error>() {
let code = match err {
Error::Nope => StatusCode::BAD_REQUEST,
Error::Oops => StatusCode::INTERNAL_SERVER_ERROR,
};
let msg = err.to_string();
async fn customize_error(err: Rejection) -> Result<impl Reply, Rejection> {
if let Some(err) = err.find::<Error>() {
let (code, msg) = match err {
Error::Nope => (StatusCode::BAD_REQUEST, "Nope!"),
Error::Oops => (StatusCode::INTERNAL_SERVER_ERROR, ":fire: this is fine"),
};

let json = warp::reply::json(&ErrorMessage {
code: code.as_u16(),
message: msg,
});
Ok(warp::reply::with_status(json, code))
} else if let Some(_) = err.find_cause::<warp::reject::MethodNotAllowed>() {
// We can handle a specific error, here METHOD_NOT_ALLOWED,
// and render it however we want
let code = StatusCode::METHOD_NOT_ALLOWED;
let json = warp::reply::json(&ErrorMessage {
code: code.as_u16(),
message: "oops, you aren't allowed to use this method.".into(),
});
Ok(warp::reply::with_status(json, code))
} else {
// Could be a NOT_FOUND, or any other internal error... here we just
// let warp use its default rendering.
Err(err)
}
};
futures::future::ready(err)
} else if let Some(_) = err.find::<warp::reject::MethodNotAllowed>() {
// We can handle a specific error, here METHOD_NOT_ALLOWED,
// and render it however we want
let code = StatusCode::METHOD_NOT_ALLOWED;
let json = warp::reply::json(&ErrorMessage {
code: code.as_u16(),
message: "oops, you aren't allowed to use this method.".into(),
});
Ok(warp::reply::with_status(json, code))
} else {
// Could be a NOT_FOUND, or any other internal error... here we just
// let warp use its default rendering.
Err(err)
}
}
2 changes: 1 addition & 1 deletion examples/futures.rs
Expand Up @@ -23,7 +23,7 @@ impl FromStr for Seconds {

#[tokio::main]
async fn main() {
// Match `/:u32`...
// Match `/:Seconds`...
let routes = warp::path::param()
// and_then create a `Future` that will simply wait N seconds...
.and_then(|Seconds(seconds): Seconds| async move {
Expand Down
12 changes: 8 additions & 4 deletions examples/sse_chat.rs
@@ -1,4 +1,4 @@
use futures::{future, Stream, StreamExt};
use futures::{Stream, StreamExt};
use std::collections::HashMap;
use std::sync::{
atomic::{AtomicUsize, Ordering},
Expand All @@ -16,6 +16,10 @@ enum Message {
Reply(String),
}

#[derive(Debug)]
struct NotUtf8;
impl warp::reject::Reject for NotUtf8 {}

/// Our state of currently connected users.
///
/// - Key is their id
Expand All @@ -37,10 +41,10 @@ async fn main() {
.and(warp::post())
.and(warp::path::param::<usize>())
.and(warp::body::content_length_limit(500))
.and(warp::body::concat().and_then(|body: warp::body::FullBody| {
future::ready(std::str::from_utf8(body.bytes())
.and(warp::body::concat().and_then(|body: warp::body::FullBody| async move {
std::str::from_utf8(body.bytes())
.map(String::from)
.map_err(warp::reject::custom))
.map_err(|_e| warp::reject::custom(NotUtf8))
}))
.and(users.clone())
.map(|my_id, msg, users| {
Expand Down
4 changes: 2 additions & 2 deletions src/filter/map_err.rs
Expand Up @@ -6,7 +6,7 @@ use pin_project::pin_project;
use futures::TryFuture;

use super::{Filter, FilterBase};
use crate::reject::Reject;
use crate::reject::IsReject;

#[derive(Clone, Copy, Debug)]
pub struct MapErr<T, F> {
Expand All @@ -18,7 +18,7 @@ impl<T, F, E> FilterBase for MapErr<T, F>
where
T: Filter,
F: Fn(T::Error) -> E + Clone + Send,
E: Reject,
E: IsReject,
{
type Extract = T::Extract;
type Error = E;
Expand Down
10 changes: 5 additions & 5 deletions src/filter/mod.rs
Expand Up @@ -17,7 +17,7 @@ use std::future::Future;
use futures::{future, TryFuture, TryFutureExt};

pub(crate) use crate::generic::{one, Combine, Either, Func, HList, One, Tuple};
use crate::reject::{CombineRejection, Reject, Rejection};
use crate::reject::{CombineRejection, IsReject, Rejection};
use crate::route::{self, Route};

pub(crate) use self::and::And;
Expand All @@ -36,7 +36,7 @@ pub(crate) use self::wrap::{Wrap, WrapSealed};
// signatures without it being a breaking change.
pub trait FilterBase {
type Extract: Tuple; // + Send;
type Error: Reject;
type Error: IsReject;
type Future: Future<Output = Result<Self::Extract, Self::Error>> + Send;

fn filter(&self) -> Self::Future;
Expand Down Expand Up @@ -416,7 +416,7 @@ where
F: Fn(&mut Route) -> U,
U: TryFuture,
U::Ok: Tuple,
U::Error: Reject,
U::Error: IsReject,
{
FilterFn { func }
}
Expand All @@ -427,7 +427,7 @@ pub(crate) fn filter_fn_one<F, U>(
where
F: Fn(&mut Route) -> U + Copy,
U: TryFuture,
U::Error: Reject,
U::Error: IsReject,
{
filter_fn(move |route| func(route).map_ok(tup_one as _))
}
Expand All @@ -448,7 +448,7 @@ where
F: Fn(&mut Route) -> U,
U: TryFuture + Send + 'static,
U::Ok: Tuple + Send,
U::Error: Reject,
U::Error: IsReject,
{
type Extract = U::Ok;
type Error = U::Error;
Expand Down
12 changes: 6 additions & 6 deletions src/filter/service.rs
Expand Up @@ -6,7 +6,7 @@ use std::future::Future;
use pin_project::pin_project;
use futures::future::TryFuture;

use crate::reject::Reject;
use crate::reject::IsReject;
use crate::reply::Reply;
use crate::route::{self, Route};
use crate::server::{IntoWarpService, WarpService};
Expand All @@ -21,7 +21,7 @@ impl<F> WarpService for FilteredService<F>
where
F: Filter,
<F::Future as TryFuture>::Ok: Reply,
<F::Future as TryFuture>::Error: Reject,
<F::Future as TryFuture>::Error: IsReject,
{
type Reply = FilteredFuture<F::Future>;

Expand Down Expand Up @@ -66,8 +66,8 @@ impl<F> IntoWarpService for FilteredService<F>
where
F: Filter + Send + Sync + 'static,
F::Extract: Reply,
F::Error: Reject,
{
F::Error: IsReject,
{
type Service = FilteredService<F>;

#[inline]
Expand All @@ -80,8 +80,8 @@ impl<F> IntoWarpService for F
where
F: Filter + Send + Sync + 'static,
F::Extract: Reply,
F::Error: Reject,
{
F::Error: IsReject,
{
type Service = FilteredService<F>;

#[inline]
Expand Down
10 changes: 5 additions & 5 deletions src/filters/log.rs
Expand Up @@ -8,7 +8,7 @@ use http::{self, header, StatusCode};
use tokio::clock;

use crate::filter::{Filter, WrapSealed};
use crate::reject::Reject;
use crate::reject::IsReject;
use crate::reply::Reply;
use crate::route::Route;

Expand Down Expand Up @@ -97,7 +97,7 @@ where
FN: Fn(Info) + Clone + Send,
F: Filter + Clone + Send,
F::Extract: Reply,
F::Error: Reject,
F::Error: IsReject,
{
type Wrapped = WithLog<FN, F>;

Expand Down Expand Up @@ -188,7 +188,7 @@ mod internal {

use super::{Info, Log};
use crate::filter::{Filter, FilterBase};
use crate::reject::Reject;
use crate::reject::IsReject;
use crate::reply::{Reply, Response};
use crate::route;

Expand All @@ -214,7 +214,7 @@ mod internal {
FN: Fn(Info) + Clone + Send,
F: Filter + Clone + Send,
F::Extract: Reply,
F::Error: Reject,
F::Error: IsReject,
{
type Extract = (Logged,);
type Error = F::Error;
Expand Down Expand Up @@ -244,7 +244,7 @@ mod internal {
FN: Fn(Info),
F: TryFuture,
F::Ok: Reply,
F::Error: Reject,
F::Error: IsReject,
{
type Output = Result<(Logged,), F::Error>;

Expand Down
36 changes: 0 additions & 36 deletions src/filters/path.rs
Expand Up @@ -238,42 +238,6 @@ pub fn param<T: FromStr + Send + 'static>() -> impl Filter<Extract = One<T>, Err
})
}

/// Extract a parameter from a path segment.
///
/// This will try to parse a value from the current request path
/// segment, and if successful, the value is returned as the `Filter`'s
/// "extracted" value.
///
/// If the value could not be parsed, rejects with a `404 Not Found`. In
/// contrast of `param` method, it reports an error cause in response.
///
/// # Example
///
/// ```
/// use warp::Filter;
///
/// let route = warp::path::param2()
/// .map(|id: u32| {
/// format!("You asked for /{}", id)
/// });
/// ```
pub fn param2<T>() -> impl Filter<Extract = One<T>, Error = Rejection> + Copy
where
T: FromStr + Send + 'static,
T::Err: Into<crate::reject::Cause>,
{
segment(|seg| {
log::trace!("param?: {:?}", seg);
if seg.is_empty() {
return Err(reject::not_found());
}
T::from_str(seg).map(one).map_err(|err| {
#[allow(deprecated)]
reject::not_found().with(err.into())
})
})
}

/// Extract the unmatched tail of the path.
///
/// This will return a `Tail`, which allows access to the rest of the path
Expand Down
10 changes: 2 additions & 8 deletions src/filters/sse.rs
Expand Up @@ -304,10 +304,7 @@ where
header::header("last-event-id")
.map(Some)
.or_else(|rejection: Rejection| {
if rejection
.find_cause::<crate::reject::MissingHeader>()
.is_some()
{
if rejection.find::<crate::reject::MissingHeader>().is_some() {
return future::ok((None,));
}
future::err(rejection)
Expand Down Expand Up @@ -336,10 +333,7 @@ pub fn sse() -> impl Filter<Extract = One<Sse>, Error = Rejection> + Copy {
.and(
header::exact_ignore_case("connection", "keep-alive").or_else(
|rejection: Rejection| {
if rejection
.find_cause::<crate::reject::MissingHeader>()
.is_some()
{
if rejection.find::<crate::reject::MissingHeader>().is_some() {
return future::ok(());
}
future::err(rejection)
Expand Down